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
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	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 that has been destroyed.303		CollectionDestroyed(CollectionId),304305		/// New item was created.306		///307		/// # Arguments308		///309		/// * collection_id: ID of the collection where the item was created.310		///311		/// * item_id: ID of the item. Unique within the collection.312		///313		/// * recipient: Owner of the newly created item.314		///315		/// * amount: The amount of tokens that were created (always 1 for NFT).316		ItemCreated(CollectionId, TokenId, T::CrossAccountId, u128),317318		/// Collection item was burned.319		///320		/// # Arguments321		///322		/// * collection_id: Identifier of the collection to which the burned NFT belonged.323		///324		/// * item_id: Identifier of burned NFT.325		///326		/// * owner: Which user has destroyed their tokens.327		///328		/// * amount: The amount of tokens that were destroyed (always 1 for NFT).329		ItemDestroyed(CollectionId, TokenId, T::CrossAccountId, u128),330331		/// Item was transferred.332		/// 333		/// # Arguments334		///335		/// * collection_id: ID of the collection to which the item belongs.336		///337		/// * item_id: ID of the item trasnferred.338		///339		/// * sender: Original owner of the item.340		///341		/// * recipient: New owner of the item.342		///343		/// * amount: The amount of tokens that were transferred (always 1 for NFT).344		Transfer(345			CollectionId,346			TokenId,347			T::CrossAccountId,348			T::CrossAccountId,349			u128,350		),351352		/// Sponsoring allowance was approved.353		/// 354		/// # Arguments355		/// 356		/// * collection_id357		///358		/// * item_id359		///360		/// * sender361		///362		/// * spender363		///364		/// * amount365		Approved(366			CollectionId,367			TokenId,368			T::CrossAccountId,369			T::CrossAccountId,370			u128,371		),372373		/// Collection property was added or edited.374		/// 375		/// # Arguments376		/// 377		/// * collection_id: ID of the collection, whose property was just set.378		/// 379		/// * property_key: Key of the property that was just set.380		CollectionPropertySet(CollectionId, PropertyKey),381382		/// Collection property was deleted.383		/// 384		/// # Arguments385		/// 386		/// * collection_id: ID of the collection, whose property was just deleted.387		/// 388		/// * property_key: Key of the property that was just deleted.389		CollectionPropertyDeleted(CollectionId, PropertyKey),390391		/// Item property was added or edited.392		/// 393		/// # Arguments394		/// 395		/// * collection_id: ID of the collection, whose token's property was just set.396		/// 397		/// * item_id: ID of the item, whose property was just set.398		/// 399		/// * property_key: Key of the property that was just set.400		TokenPropertySet(CollectionId, TokenId, PropertyKey),401402		/// Item property was deleted.403		/// 404		/// # Arguments405		/// 406		/// * collection_id: ID of the collection, whose token's property was just deleted.407		/// 408		/// * item_id: ID of the item, whose property was just deleted.409		/// 410		/// * property_key: Key of the property that was just deleted.411		TokenPropertyDeleted(CollectionId, TokenId, PropertyKey),412413		/// Token property permission was added or updated for a collection.414		/// 415		/// # Arguments416		/// 417		/// * collection_id: ID of the collection, whose permissions were just set/updated.418		/// 419		/// * property_key: Key of the property of the set/updated permission.420		PropertyPermissionSet(CollectionId, PropertyKey),421	}422423	#[pallet::error]424	pub enum Error<T> {425		/// This collection does not exist.426		CollectionNotFound,427		/// Sender parameter and item owner must be equal.428		MustBeTokenOwner,429		/// No permission to perform action430		NoPermission,431		/// Destroying only empty collections is allowed432		CantDestroyNotEmptyCollection,433		/// Collection is not in mint mode.434		PublicMintingNotAllowed,435		/// Address is not in allow list.436		AddressNotInAllowlist,437438		/// Collection name can not be longer than 63 char.439		CollectionNameLimitExceeded,440		/// Collection description can not be longer than 255 char.441		CollectionDescriptionLimitExceeded,442		/// Token prefix can not be longer than 15 char.443		CollectionTokenPrefixLimitExceeded,444		/// Total collections bound exceeded.445		TotalCollectionsLimitExceeded,446		/// Exceeded max admin count447		CollectionAdminCountExceeded,448		/// Collection limit bounds per collection exceeded449		CollectionLimitBoundsExceeded,450		/// Tried to enable permissions which are only permitted to be disabled451		OwnerPermissionsCantBeReverted,452		/// Collection settings not allowing items transferring453		TransferNotAllowed,454		/// Account token limit exceeded per collection455		AccountTokenLimitExceeded,456		/// Collection token limit exceeded457		CollectionTokenLimitExceeded,458		/// Metadata flag frozen459		MetadataFlagFrozen,460461		/// Item does not exist462		TokenNotFound,463		/// Item is balance not enough464		TokenValueTooLow,465		/// Requested value is more than the approved466		ApprovedValueTooLow,467		/// Tried to approve more than owned468		CantApproveMoreThanOwned,469470		/// Can't transfer tokens to ethereum zero address471		AddressIsZero,472		/// Target collection doesn't support this operation473		UnsupportedOperation,474475		/// Insufficient funds to perform an action476		NotSufficientFounds,477478		/// User does not satisfy the nesting rule479		UserIsNotAllowedToNest,480		/// Only tokens from specific collections may nest tokens under this one481		SourceCollectionIsNotAllowedToNest,482483		/// Tried to store more data than allowed in collection field484		CollectionFieldSizeExceeded,485486		/// Tried to store more property data than allowed487		NoSpaceForProperty,488489		/// Tried to store more property keys than allowed490		PropertyLimitReached,491492		/// Property key is too long493		PropertyKeyIsTooLong,494495		/// Only ASCII letters, digits, and symbols '_', '-', and '.' are allowed496		InvalidCharacterInPropertyKey,497498		/// Empty property keys are forbidden499		EmptyPropertyKey,500501		/// Tried to access an external collection with an internal API502		CollectionIsExternal,503504		/// Tried to access an internal collection with an external API505		CollectionIsInternal,506	}507508	/// The number of created collections. Essentially contains the last collection ID.509	#[pallet::storage]510	pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;511512	/// The number of destroyed collections513	#[pallet::storage]514	pub type DestroyedCollectionCount<T> =515		StorageValue<Value = CollectionId, QueryKind = ValueQuery>;516517	/// Collection info518	#[pallet::storage]519	pub type CollectionById<T> = StorageMap<520		Hasher = Blake2_128Concat,521		Key = CollectionId,522		Value = Collection<<T as frame_system::Config>::AccountId>,523		QueryKind = OptionQuery,524	>;525526	/// Collection properties527	#[pallet::storage]528	#[pallet::getter(fn collection_properties)]529	pub type CollectionProperties<T> = StorageMap<530		Hasher = Blake2_128Concat,531		Key = CollectionId,532		Value = Properties,533		QueryKind = ValueQuery,534		OnEmpty = up_data_structs::CollectionProperties,535	>;536537	/// Token permissions of a collection538	#[pallet::storage]539	#[pallet::getter(fn property_permissions)]540	pub type CollectionPropertyPermissions<T> = StorageMap<541		Hasher = Blake2_128Concat,542		Key = CollectionId,543		Value = PropertiesPermissionMap,544		QueryKind = ValueQuery,545	>;546547	/// Amount of collection admins548	#[pallet::storage]549	pub type AdminAmount<T> = StorageMap<550		Hasher = Blake2_128Concat,551		Key = CollectionId,552		Value = u32,553		QueryKind = ValueQuery,554	>;555556	/// List of collection admins557	#[pallet::storage]558	pub type IsAdmin<T: Config> = StorageNMap<559		Key = (560			Key<Blake2_128Concat, CollectionId>,561			Key<Blake2_128Concat, T::CrossAccountId>,562		),563		Value = bool,564		QueryKind = ValueQuery,565	>;566567	/// Allowlisted collection users568	#[pallet::storage]569	pub type Allowlist<T: Config> = StorageNMap<570		Key = (571			Key<Blake2_128Concat, CollectionId>,572			Key<Blake2_128Concat, T::CrossAccountId>,573		),574		Value = bool,575		QueryKind = ValueQuery,576	>;577578	/// Not used by code, exists only to provide some types to metadata579	#[pallet::storage]580	pub type DummyStorageValue<T: Config> = StorageValue<581		Value = (582			CollectionStats,583			CollectionId,584			TokenId,585			TokenChild,586			PhantomType<(587				TokenData<T::CrossAccountId>,588				RpcCollection<T::AccountId>,589				// RMRK590				RmrkCollectionInfo<T::AccountId>,591				RmrkInstanceInfo<T::AccountId>,592				RmrkResourceInfo,593				RmrkPropertyInfo,594				RmrkBaseInfo<T::AccountId>,595				RmrkPartType,596				RmrkBoundedTheme,597				RmrkNftChild,598			)>,599		),600		QueryKind = OptionQuery,601	>;602603	#[pallet::hooks]604	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {605		fn on_runtime_upgrade() -> Weight {606			if StorageVersion::get::<Pallet<T>>() < StorageVersion::new(1) {607				use up_data_structs::{CollectionVersion1, CollectionVersion2};608				<CollectionById<T>>::translate::<CollectionVersion1<T::AccountId>, _>(|id, v| {609					let mut props = Vec::new();610					if !v.offchain_schema.is_empty() {611						props.push(Property {612							key: b"_old_offchainSchema".to_vec().try_into().unwrap(),613							value: v614								.offchain_schema615								.clone()616								.into_inner()617								.try_into()618								.expect("offchain schema too big"),619						});620					}621					if !v.variable_on_chain_schema.is_empty() {622						props.push(Property {623							key: b"_old_variableOnChainSchema".to_vec().try_into().unwrap(),624							value: v625								.variable_on_chain_schema626								.clone()627								.into_inner()628								.try_into()629								.expect("offchain schema too big"),630						});631					}632					if !v.const_on_chain_schema.is_empty() {633						props.push(Property {634							key: b"_old_constOnChainSchema".to_vec().try_into().unwrap(),635							value: v636								.const_on_chain_schema637								.clone()638								.into_inner()639								.try_into()640								.expect("offchain schema too big"),641						});642					}643					props.push(Property {644						key: b"_old_schemaVersion".to_vec().try_into().unwrap(),645						value: match v.schema_version {646							SchemaVersion::ImageURL => b"ImageUrl".as_slice(),647							SchemaVersion::Unique => b"Unique".as_slice(),648						}649						.to_vec()650						.try_into()651						.unwrap(),652					});653					Self::set_scoped_collection_properties(654						id,655						PropertyScope::None,656						props.into_iter(),657					)658					.expect("existing data larger than properties");659					let mut new = CollectionVersion2::from(v.clone());660					new.permissions.access = Some(v.access);661					new.permissions.mint_mode = Some(v.mint_mode);662					Some(new)663				});664			}665666			0667		}668	}669}670671impl<T: Config> Pallet<T> {672	/// Ethereum receiver 0x0000000000000000000000000000000000000000 is reserved, and shouldn't own tokens673	pub fn ensure_correct_receiver(receiver: &T::CrossAccountId) -> DispatchResult {674		ensure!(675			&T::CrossAccountId::from_eth(H160([0; 20])) != receiver,676			<Error<T>>::AddressIsZero677		);678		Ok(())679	}680	pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {681		<IsAdmin<T>>::iter_prefix((collection,))682			.map(|(a, _)| a)683			.collect()684	}685	pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {686		<Allowlist<T>>::iter_prefix((collection,))687			.map(|(a, _)| a)688			.collect()689	}690	pub fn allowed(collection: CollectionId, user: T::CrossAccountId) -> bool {691		<Allowlist<T>>::get((collection, user))692	}693	pub fn collection_stats() -> CollectionStats {694		let created = <CreatedCollectionCount<T>>::get();695		let destroyed = <DestroyedCollectionCount<T>>::get();696		CollectionStats {697			created: created.0,698			destroyed: destroyed.0,699			alive: created.0 - destroyed.0,700		}701	}702703	pub fn effective_collection_limits(collection: CollectionId) -> Option<CollectionLimits> {704		let collection = <CollectionById<T>>::get(collection);705		if collection.is_none() {706			return None;707		}708709		let collection = collection.unwrap();710		let limits = collection.limits;711		let effective_limits = CollectionLimits {712			account_token_ownership_limit: Some(limits.account_token_ownership_limit()),713			sponsored_data_size: Some(limits.sponsored_data_size()),714			sponsored_data_rate_limit: Some(715				limits716					.sponsored_data_rate_limit717					.unwrap_or(SponsoringRateLimit::SponsoringDisabled),718			),719			token_limit: Some(limits.token_limit()),720			sponsor_transfer_timeout: Some(limits.sponsor_transfer_timeout(721				match collection.mode {722					CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,723					CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,724					CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,725				},726			)),727			sponsor_approve_timeout: Some(limits.sponsor_approve_timeout()),728			owner_can_transfer: Some(limits.owner_can_transfer()),729			owner_can_destroy: Some(limits.owner_can_destroy()),730			transfers_enabled: Some(limits.transfers_enabled()),731		};732733		Some(effective_limits)734	}735736	pub fn rpc_collection(collection: CollectionId) -> Option<RpcCollection<T::AccountId>> {737		let Collection {738			name,739			description,740			owner,741			mode,742			token_prefix,743			sponsorship,744			limits,745			permissions,746			external_collection,747		} = <CollectionById<T>>::get(collection)?;748749		let token_property_permissions = <CollectionPropertyPermissions<T>>::get(collection)750			.into_iter()751			.map(|(key, permission)| PropertyKeyPermission { key, permission })752			.collect();753754		let properties = <CollectionProperties<T>>::get(collection)755			.into_iter()756			.map(|(key, value)| Property { key, value })757			.collect();758759		let permissions = CollectionPermissions {760			access: Some(permissions.access()),761			mint_mode: Some(permissions.mint_mode()),762			nesting: Some(permissions.nesting().clone()),763		};764765		Some(RpcCollection {766			name: name.into_inner(),767			description: description.into_inner(),768			owner,769			mode,770			token_prefix: token_prefix.into_inner(),771			sponsorship,772			limits,773			permissions,774			token_property_permissions,775			properties,776			read_only: external_collection,777		})778	}779}780781macro_rules! limit_default {782	($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{783		$(784			if let Some($new) = $new.$field {785				let $old = $old.$field($($arg)?);786				let _ = $new;787				let _ = $old;788				$check789			} else {790				$new.$field = $old.$field791			}792		)*793	}};794}795macro_rules! limit_default_clone {796	($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{797		$(798			if let Some($new) = $new.$field.clone() {799				let $old = $old.$field($($arg)?);800				let _ = $new;801				let _ = $old;802				$check803			} else {804				$new.$field = $old.$field.clone()805			}806		)*807	}};808}809810impl<T: Config> Pallet<T> {811	pub fn init_collection(812		owner: T::CrossAccountId,813		data: CreateCollectionData<T::AccountId>,814		is_external: bool,815	) -> Result<CollectionId, DispatchError> {816		{817			ensure!(818				data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,819				Error::<T>::CollectionTokenPrefixLimitExceeded820			);821		}822823		let created_count = <CreatedCollectionCount<T>>::get()824			.0825			.checked_add(1)826			.ok_or(ArithmeticError::Overflow)?;827		let destroyed_count = <DestroyedCollectionCount<T>>::get().0;828		let id = CollectionId(created_count);829830		// bound Total number of collections831		ensure!(832			created_count - destroyed_count <= COLLECTION_NUMBER_LIMIT,833			<Error<T>>::TotalCollectionsLimitExceeded834		);835836		// =========837838		let collection = Collection {839			owner: owner.as_sub().clone(),840			name: data.name,841			mode: data.mode.clone(),842			description: data.description,843			token_prefix: data.token_prefix,844			sponsorship: data845				.pending_sponsor846				.map(SponsorshipState::Unconfirmed)847				.unwrap_or_default(),848			limits: data849				.limits850				.map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits))851				.unwrap_or_else(|| Ok(CollectionLimits::default()))?,852			permissions: data853				.permissions854				.map(|permissions| {855					Self::clamp_permissions(data.mode.clone(), &Default::default(), permissions)856				})857				.unwrap_or_else(|| Ok(CollectionPermissions::default()))?,858			external_collection: is_external,859		};860861		let mut collection_properties = up_data_structs::CollectionProperties::get();862		collection_properties863			.try_set_from_iter(data.properties.into_iter())864			.map_err(<Error<T>>::from)?;865866		CollectionProperties::<T>::insert(id, collection_properties);867868		let mut token_props_permissions = PropertiesPermissionMap::new();869		token_props_permissions870			.try_set_from_iter(data.token_property_permissions.into_iter())871			.map_err(<Error<T>>::from)?;872873		CollectionPropertyPermissions::<T>::insert(id, token_props_permissions);874875		// Take a (non-refundable) deposit of collection creation876		{877			let mut imbalance =878				<<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();879			imbalance.subsume(880				<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(881					&T::TreasuryAccountId::get(),882					T::CollectionCreationPrice::get(),883				),884			);885			<T as Config>::Currency::settle(886				&owner.as_sub(),887				imbalance,888				WithdrawReasons::TRANSFER,889				ExistenceRequirement::KeepAlive,890			)891			.map_err(|_| Error::<T>::NotSufficientFounds)?;892		}893894		<CreatedCollectionCount<T>>::put(created_count);895		<Pallet<T>>::deposit_event(Event::CollectionCreated(896			id,897			data.mode.id(),898			owner.as_sub().clone(),899		));900		<PalletEvm<T>>::deposit_log(901			erc::CollectionHelpersEvents::CollectionCreated {902				owner: *owner.as_eth(),903				collection_id: eth::collection_id_to_address(id),904			}905			.to_log(T::ContractAddress::get()),906		);907		<CollectionById<T>>::insert(id, collection);908		Ok(id)909	}910911	pub fn destroy_collection(912		collection: CollectionHandle<T>,913		sender: &T::CrossAccountId,914	) -> DispatchResult {915		ensure!(916			collection.limits.owner_can_destroy(),917			<Error<T>>::NoPermission,918		);919		collection.check_is_owner(sender)?;920921		let destroyed_collections = <DestroyedCollectionCount<T>>::get()922			.0923			.checked_add(1)924			.ok_or(ArithmeticError::Overflow)?;925926		// =========927928		<DestroyedCollectionCount<T>>::put(destroyed_collections);929		<CollectionById<T>>::remove(collection.id);930		<AdminAmount<T>>::remove(collection.id);931		<IsAdmin<T>>::remove_prefix((collection.id,), None);932		<Allowlist<T>>::remove_prefix((collection.id,), None);933		<CollectionProperties<T>>::remove(collection.id);934935		<Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));936		Ok(())937	}938939	pub fn set_collection_property(940		collection: &CollectionHandle<T>,941		sender: &T::CrossAccountId,942		property: Property,943	) -> DispatchResult {944		collection.check_is_owner_or_admin(sender)?;945946		CollectionProperties::<T>::try_mutate(collection.id, |properties| {947			let property = property.clone();948			properties.try_set(property.key, property.value)949		})950		.map_err(<Error<T>>::from)?;951952		Self::deposit_event(Event::CollectionPropertySet(collection.id, property.key));953954		Ok(())955	}956957	pub fn set_scoped_collection_property(958		collection_id: CollectionId,959		scope: PropertyScope,960		property: Property,961	) -> DispatchResult {962		CollectionProperties::<T>::try_mutate(collection_id, |properties| {963			properties.try_scoped_set(scope, property.key, property.value)964		})965		.map_err(<Error<T>>::from)?;966967		Ok(())968	}969970	pub fn set_scoped_collection_properties(971		collection_id: CollectionId,972		scope: PropertyScope,973		properties: impl Iterator<Item = Property>,974	) -> DispatchResult {975		CollectionProperties::<T>::try_mutate(collection_id, |stored_properties| {976			stored_properties.try_scoped_set_from_iter(scope, properties)977		})978		.map_err(<Error<T>>::from)?;979980		Ok(())981	}982983	#[transactional]984	pub fn set_collection_properties(985		collection: &CollectionHandle<T>,986		sender: &T::CrossAccountId,987		properties: Vec<Property>,988	) -> DispatchResult {989		for property in properties {990			Self::set_collection_property(collection, sender, property)?;991		}992993		Ok(())994	}995996	pub fn delete_collection_property(997		collection: &CollectionHandle<T>,998		sender: &T::CrossAccountId,999		property_key: PropertyKey,1000	) -> DispatchResult {1001		collection.check_is_owner_or_admin(sender)?;10021003		CollectionProperties::<T>::try_mutate(collection.id, |properties| {1004			properties.remove(&property_key)1005		})1006		.map_err(<Error<T>>::from)?;10071008		Self::deposit_event(Event::CollectionPropertyDeleted(1009			collection.id,1010			property_key,1011		));10121013		Ok(())1014	}10151016	#[transactional]1017	pub fn delete_collection_properties(1018		collection: &CollectionHandle<T>,1019		sender: &T::CrossAccountId,1020		property_keys: Vec<PropertyKey>,1021	) -> DispatchResult {1022		for key in property_keys {1023			Self::delete_collection_property(collection, sender, key)?;1024		}10251026		Ok(())1027	}10281029	// For migrations1030	pub fn set_property_permission_unchecked(1031		collection: CollectionId,1032		property_permission: PropertyKeyPermission,1033	) -> DispatchResult {1034		<CollectionPropertyPermissions<T>>::try_mutate(collection, |permissions| {1035			permissions.try_set(property_permission.key, property_permission.permission)1036		})1037		.map_err(<Error<T>>::from)?;1038		Ok(())1039	}10401041	pub fn set_property_permission(1042		collection: &CollectionHandle<T>,1043		sender: &T::CrossAccountId,1044		property_permission: PropertyKeyPermission,1045	) -> DispatchResult {1046		collection.check_is_owner_or_admin(sender)?;10471048		let all_permissions = CollectionPropertyPermissions::<T>::get(collection.id);1049		let current_permission = all_permissions.get(&property_permission.key);1050		if matches![1051			current_permission,1052			Some(PropertyPermission { mutable: false, .. })1053		] {1054			return Err(<Error<T>>::NoPermission.into());1055		}10561057		CollectionPropertyPermissions::<T>::try_mutate(collection.id, |permissions| {1058			let property_permission = property_permission.clone();1059			permissions.try_set(property_permission.key, property_permission.permission)1060		})1061		.map_err(<Error<T>>::from)?;10621063		Self::deposit_event(Event::PropertyPermissionSet(1064			collection.id,1065			property_permission.key,1066		));10671068		Ok(())1069	}10701071	#[transactional]1072	pub fn set_token_property_permissions(1073		collection: &CollectionHandle<T>,1074		sender: &T::CrossAccountId,1075		property_permissions: Vec<PropertyKeyPermission>,1076	) -> DispatchResult {1077		for prop_pemission in property_permissions {1078			Self::set_property_permission(collection, sender, prop_pemission)?;1079		}10801081		Ok(())1082	}10831084	pub fn get_collection_property(1085		collection_id: CollectionId,1086		key: &PropertyKey,1087	) -> Option<PropertyValue> {1088		Self::collection_properties(collection_id).get(key).cloned()1089	}10901091	pub fn bytes_keys_to_property_keys(1092		keys: Vec<Vec<u8>>,1093	) -> Result<Vec<PropertyKey>, DispatchError> {1094		keys.into_iter()1095			.map(|key| -> Result<PropertyKey, DispatchError> {1096				key.try_into()1097					.map_err(|_| <Error<T>>::PropertyKeyIsTooLong.into())1098			})1099			.collect::<Result<Vec<PropertyKey>, DispatchError>>()1100	}11011102	pub fn filter_collection_properties(1103		collection_id: CollectionId,1104		keys: Option<Vec<PropertyKey>>,1105	) -> Result<Vec<Property>, DispatchError> {1106		let properties = Self::collection_properties(collection_id);11071108		let properties = keys1109			.map(|keys| {1110				keys.into_iter()1111					.filter_map(|key| {1112						properties.get(&key).map(|value| Property {1113							key,1114							value: value.clone(),1115						})1116					})1117					.collect()1118			})1119			.unwrap_or_else(|| {1120				properties1121					.into_iter()1122					.map(|(key, value)| Property { key, value })1123					.collect()1124			});11251126		Ok(properties)1127	}11281129	pub fn filter_property_permissions(1130		collection_id: CollectionId,1131		keys: Option<Vec<PropertyKey>>,1132	) -> Result<Vec<PropertyKeyPermission>, DispatchError> {1133		let permissions = Self::property_permissions(collection_id);11341135		let key_permissions = keys1136			.map(|keys| {1137				keys.into_iter()1138					.filter_map(|key| {1139						permissions1140							.get(&key)1141							.map(|permission| PropertyKeyPermission {1142								key,1143								permission: permission.clone(),1144							})1145					})1146					.collect()1147			})1148			.unwrap_or_else(|| {1149				permissions1150					.into_iter()1151					.map(|(key, permission)| PropertyKeyPermission { key, permission })1152					.collect()1153			});11541155		Ok(key_permissions)1156	}11571158	pub fn toggle_allowlist(1159		collection: &CollectionHandle<T>,1160		sender: &T::CrossAccountId,1161		user: &T::CrossAccountId,1162		allowed: bool,1163	) -> DispatchResult {1164		collection.check_is_owner_or_admin(sender)?;11651166		// =========11671168		if allowed {1169			<Allowlist<T>>::insert((collection.id, user), true);1170		} else {1171			<Allowlist<T>>::remove((collection.id, user));1172		}11731174		Ok(())1175	}11761177	pub fn toggle_admin(1178		collection: &CollectionHandle<T>,1179		sender: &T::CrossAccountId,1180		user: &T::CrossAccountId,1181		admin: bool,1182	) -> DispatchResult {1183		collection.check_is_owner(sender)?;11841185		let was_admin = <IsAdmin<T>>::get((collection.id, user));1186		if was_admin == admin {1187			return Ok(());1188		}1189		let amount = <AdminAmount<T>>::get(collection.id);11901191		if admin {1192			let amount = amount1193				.checked_add(1)1194				.ok_or(<Error<T>>::CollectionAdminCountExceeded)?;1195			ensure!(1196				amount <= Self::collection_admins_limit(),1197				<Error<T>>::CollectionAdminCountExceeded,1198			);11991200			// =========12011202			<AdminAmount<T>>::insert(collection.id, amount);1203			<IsAdmin<T>>::insert((collection.id, user), true);1204		} else {1205			<AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));1206			<IsAdmin<T>>::remove((collection.id, user));1207		}12081209		Ok(())1210	}12111212	pub fn clamp_limits(1213		mode: CollectionMode,1214		old_limit: &CollectionLimits,1215		mut new_limit: CollectionLimits,1216	) -> Result<CollectionLimits, DispatchError> {1217		let limits = old_limit;1218		limit_default!(old_limit, new_limit,1219			account_token_ownership_limit => ensure!(1220				new_limit <= MAX_TOKEN_OWNERSHIP,1221				<Error<T>>::CollectionLimitBoundsExceeded,1222			),1223			sponsored_data_size => ensure!(1224				new_limit <= CUSTOM_DATA_LIMIT,1225				<Error<T>>::CollectionLimitBoundsExceeded,1226			),12271228			sponsored_data_rate_limit => {},1229			token_limit => ensure!(1230				old_limit >= new_limit && new_limit > 0,1231				<Error<T>>::CollectionTokenLimitExceeded1232			),12331234			sponsor_transfer_timeout(match mode {1235				CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,1236				CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1237				CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1238			}) => ensure!(1239				new_limit <= MAX_SPONSOR_TIMEOUT,1240				<Error<T>>::CollectionLimitBoundsExceeded,1241			),1242			sponsor_approve_timeout => {},1243			owner_can_transfer => ensure!(1244				!limits.owner_can_transfer_instaled() ||1245				old_limit || !new_limit,1246				<Error<T>>::OwnerPermissionsCantBeReverted,1247			),1248			owner_can_destroy => ensure!(1249				old_limit || !new_limit,1250				<Error<T>>::OwnerPermissionsCantBeReverted,1251			),1252			transfers_enabled => {},1253		);1254		Ok(new_limit)1255	}12561257	pub fn clamp_permissions(1258		_mode: CollectionMode,1259		old_limit: &CollectionPermissions,1260		mut new_limit: CollectionPermissions,1261	) -> Result<CollectionPermissions, DispatchError> {1262		limit_default_clone!(old_limit, new_limit,1263			access => {},1264			mint_mode => {},1265			nesting => { /* todo check for permissive, if only it gets out of benchmarks */ },1266		);1267		Ok(new_limit)1268	}1269}12701271#[macro_export]1272macro_rules! unsupported {1273	() => {1274		Err(<Error<T>>::UnsupportedOperation.into())1275	};1276}12771278/// Worst cases1279pub trait CommonWeightInfo<CrossAccountId> {1280	fn create_item() -> Weight;1281	fn create_multiple_items(amount: &[CreateItemData]) -> Weight;1282	fn create_multiple_items_ex(cost: &CreateItemExData<CrossAccountId>) -> Weight;1283	fn burn_item() -> Weight;1284	fn set_collection_properties(amount: u32) -> Weight;1285	fn delete_collection_properties(amount: u32) -> Weight;1286	fn set_token_properties(amount: u32) -> Weight;1287	fn delete_token_properties(amount: u32) -> Weight;1288	fn set_token_property_permissions(amount: u32) -> Weight;1289	fn transfer() -> Weight;1290	fn approve() -> Weight;1291	fn transfer_from() -> Weight;1292	fn burn_from() -> Weight;12931294	/// Differs from burn_item in case of Fungible and Refungible, as it should burn1295	/// whole users's balance1296	///1297	/// This method shouldn't be used directly, as it doesn't count breadth price, use `burn_recursively` instead1298	fn burn_recursively_self_raw() -> Weight;1299	/// Cost of iterating over `amount` children while burning, without counting child burning itself1300	///1301	/// This method shouldn't be used directly, as it doesn't count depth price, use `burn_recursively` instead1302	fn burn_recursively_breadth_raw(amount: u32) -> Weight;13031304	fn burn_recursively(max_selfs: u32, max_breadth: u32) -> Weight {1305		Self::burn_recursively_self_raw()1306			.saturating_mul(max_selfs.max(1) as u64)1307			.saturating_add(Self::burn_recursively_breadth_raw(max_breadth))1308	}1309}13101311pub trait RefungibleExtensionsWeightInfo {1312	fn repartition() -> Weight;1313}13141315pub trait CommonCollectionOperations<T: Config> {1316	fn create_item(1317		&self,1318		sender: T::CrossAccountId,1319		to: T::CrossAccountId,1320		data: CreateItemData,1321		nesting_budget: &dyn Budget,1322	) -> DispatchResultWithPostInfo;1323	fn create_multiple_items(1324		&self,1325		sender: T::CrossAccountId,1326		to: T::CrossAccountId,1327		data: Vec<CreateItemData>,1328		nesting_budget: &dyn Budget,1329	) -> DispatchResultWithPostInfo;1330	fn create_multiple_items_ex(1331		&self,1332		sender: T::CrossAccountId,1333		data: CreateItemExData<T::CrossAccountId>,1334		nesting_budget: &dyn Budget,1335	) -> DispatchResultWithPostInfo;1336	fn burn_item(1337		&self,1338		sender: T::CrossAccountId,1339		token: TokenId,1340		amount: u128,1341	) -> DispatchResultWithPostInfo;1342	fn burn_item_recursively(1343		&self,1344		sender: T::CrossAccountId,1345		token: TokenId,1346		self_budget: &dyn Budget,1347		breadth_budget: &dyn Budget,1348	) -> DispatchResultWithPostInfo;1349	fn set_collection_properties(1350		&self,1351		sender: T::CrossAccountId,1352		properties: Vec<Property>,1353	) -> DispatchResultWithPostInfo;1354	fn delete_collection_properties(1355		&self,1356		sender: &T::CrossAccountId,1357		property_keys: Vec<PropertyKey>,1358	) -> DispatchResultWithPostInfo;1359	fn set_token_properties(1360		&self,1361		sender: T::CrossAccountId,1362		token_id: TokenId,1363		property: Vec<Property>,1364		nesting_budget: &dyn Budget,1365	) -> DispatchResultWithPostInfo;1366	fn delete_token_properties(1367		&self,1368		sender: T::CrossAccountId,1369		token_id: TokenId,1370		property_keys: Vec<PropertyKey>,1371		nesting_budget: &dyn Budget,1372	) -> DispatchResultWithPostInfo;1373	fn set_token_property_permissions(1374		&self,1375		sender: &T::CrossAccountId,1376		property_permissions: Vec<PropertyKeyPermission>,1377	) -> DispatchResultWithPostInfo;1378	fn transfer(1379		&self,1380		sender: T::CrossAccountId,1381		to: T::CrossAccountId,1382		token: TokenId,1383		amount: u128,1384		nesting_budget: &dyn Budget,1385	) -> DispatchResultWithPostInfo;1386	fn approve(1387		&self,1388		sender: T::CrossAccountId,1389		spender: T::CrossAccountId,1390		token: TokenId,1391		amount: u128,1392	) -> DispatchResultWithPostInfo;1393	fn transfer_from(1394		&self,1395		sender: T::CrossAccountId,1396		from: T::CrossAccountId,1397		to: T::CrossAccountId,1398		token: TokenId,1399		amount: u128,1400		nesting_budget: &dyn Budget,1401	) -> DispatchResultWithPostInfo;1402	fn burn_from(1403		&self,1404		sender: T::CrossAccountId,1405		from: T::CrossAccountId,1406		token: TokenId,1407		amount: u128,1408		nesting_budget: &dyn Budget,1409	) -> DispatchResultWithPostInfo;14101411	fn check_nesting(1412		&self,1413		sender: T::CrossAccountId,1414		from: (CollectionId, TokenId),1415		under: TokenId,1416		nesting_budget: &dyn Budget,1417	) -> DispatchResult;14181419	fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId));14201421	fn unnest(&self, under: TokenId, to_nest: (CollectionId, TokenId));14221423	fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;1424	fn collection_tokens(&self) -> Vec<TokenId>;1425	fn token_exists(&self, token: TokenId) -> bool;1426	fn last_token_id(&self) -> TokenId;14271428	fn token_owner(&self, token: TokenId) -> Option<T::CrossAccountId>;1429	fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue>;1430	fn token_properties(&self, token_id: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property>;1431	/// Amount of unique collection tokens1432	fn total_supply(&self) -> u32;1433	/// Amount of different tokens account has (Applicable to nonfungible/refungible)1434	fn account_balance(&self, account: T::CrossAccountId) -> u32;1435	/// Amount of specific token account have (Applicable to fungible/refungible)1436	fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;1437	/// Amount of token pieces1438	fn total_pieces(&self, token: TokenId) -> Option<u128>;1439	fn allowance(1440		&self,1441		sender: T::CrossAccountId,1442		spender: T::CrossAccountId,1443		token: TokenId,1444	) -> u128;1445	fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>>;1446}14471448pub trait RefungibleExtensions<T>1449where1450	T: Config,1451{1452	fn repartition(1453		&self,1454		owner: &T::CrossAccountId,1455		token: TokenId,1456		amount: u128,1457	) -> DispatchResultWithPostInfo;1458}14591460// Flexible enough for implementing CommonCollectionOperations1461pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {1462	let post_info = PostDispatchInfo {1463		actual_weight: Some(weight),1464		pays_fee: Pays::Yes,1465	};1466	match res {1467		Ok(()) => Ok(post_info),1468		Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),1469	}1470}14711472impl<T: Config> From<PropertiesError> for Error<T> {1473	fn from(error: PropertiesError) -> Self {1474		match error {1475			PropertiesError::NoSpaceForProperty => Self::NoSpaceForProperty,1476			PropertiesError::PropertyLimitReached => Self::PropertyLimitReached,1477			PropertiesError::InvalidCharacterInPropertyKey => Self::InvalidCharacterInPropertyKey,1478			PropertiesError::PropertyKeyIsTooLong => Self::PropertyKeyIsTooLong,1479			PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,1480		}1481	}1482}
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>'),