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
--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -299,46 +299,48 @@
 		///
 		/// # Arguments
 		///
-		/// * collection_id: Globally unique identifier of collection.
+		/// * collection_id: Globally unique identifier of collection that has been destroyed.
 		CollectionDestroyed(CollectionId),
 
 		/// New item was created.
 		///
 		/// # Arguments
 		///
-		/// * collection_id: Id of the collection where item was created.
+		/// * collection_id: ID of the collection where the item was created.
 		///
-		/// * item_id: Id of an item. Unique within the collection.
+		/// * item_id: ID of the item. Unique within the collection.
 		///
-		/// * recipient: Owner of newly created item
+		/// * recipient: Owner of the newly created item.
 		///
-		/// * amount: Always 1 for NFT
+		/// * amount: The amount of tokens that were created (always 1 for NFT).
 		ItemCreated(CollectionId, TokenId, T::CrossAccountId, u128),
 
 		/// Collection item was burned.
 		///
 		/// # Arguments
 		///
-		/// * collection_id.
+		/// * collection_id: Identifier of the collection to which the burned NFT belonged.
 		///
 		/// * item_id: Identifier of burned NFT.
 		///
-		/// * owner: which user has destroyed its tokens
+		/// * owner: Which user has destroyed their tokens.
 		///
-		/// * amount: Always 1 for NFT
+		/// * amount: The amount of tokens that were destroyed (always 1 for NFT).
 		ItemDestroyed(CollectionId, TokenId, T::CrossAccountId, u128),
 
-		/// Item was transferred
+		/// Item was transferred.
+		/// 
+		/// # Arguments
 		///
-		/// * collection_id: Id of collection to which item is belong
+		/// * collection_id: ID of the collection to which the item belongs.
 		///
-		/// * item_id: Id of an item
+		/// * item_id: ID of the item trasnferred.
 		///
-		/// * sender: Original owner of item
+		/// * sender: Original owner of the item.
 		///
-		/// * recipient: New owner of item
+		/// * recipient: New owner of the item.
 		///
-		/// * amount: Always 1 for NFT
+		/// * amount: The amount of tokens that were transferred (always 1 for NFT).
 		Transfer(
 			CollectionId,
 			TokenId,
@@ -347,6 +349,10 @@
 			u128,
 		),
 
+		/// Sponsoring allowance was approved.
+		/// 
+		/// # Arguments
+		/// 
 		/// * collection_id
 		///
 		/// * item_id
@@ -364,14 +370,53 @@
 			u128,
 		),
 
+		/// Collection property was added or edited.
+		/// 
+		/// # Arguments
+		/// 
+		/// * collection_id: ID of the collection, whose property was just set.
+		/// 
+		/// * property_key: Key of the property that was just set.
 		CollectionPropertySet(CollectionId, PropertyKey),
 
+		/// Collection property was deleted.
+		/// 
+		/// # Arguments
+		/// 
+		/// * collection_id: ID of the collection, whose property was just deleted.
+		/// 
+		/// * property_key: Key of the property that was just deleted.
 		CollectionPropertyDeleted(CollectionId, PropertyKey),
 
+		/// Item property was added or edited.
+		/// 
+		/// # Arguments
+		/// 
+		/// * collection_id: ID of the collection, whose token's property was just set.
+		/// 
+		/// * item_id: ID of the item, whose property was just set.
+		/// 
+		/// * property_key: Key of the property that was just set.
 		TokenPropertySet(CollectionId, TokenId, PropertyKey),
 
+		/// Item property was deleted.
+		/// 
+		/// # Arguments
+		/// 
+		/// * collection_id: ID of the collection, whose token's property was just deleted.
+		/// 
+		/// * item_id: ID of the item, whose property was just deleted.
+		/// 
+		/// * property_key: Key of the property that was just deleted.
 		TokenPropertyDeleted(CollectionId, TokenId, PropertyKey),
 
+		/// Token property permission was added or updated for a collection.
+		/// 
+		/// # Arguments
+		/// 
+		/// * collection_id: ID of the collection, whose permissions were just set/updated.
+		/// 
+		/// * property_key: Key of the property of the set/updated permission.
 		PropertyPermissionSet(CollectionId, PropertyKey),
 	}
 
@@ -413,26 +458,26 @@
 		/// Metadata flag frozen
 		MetadataFlagFrozen,
 
-		/// Item not exists.
+		/// Item does not exist
 		TokenNotFound,
-		/// Item balance not enough.
+		/// Item is balance not enough
 		TokenValueTooLow,
-		/// Requested value more than approved.
+		/// Requested value is more than the approved
 		ApprovedValueTooLow,
 		/// Tried to approve more than owned
 		CantApproveMoreThanOwned,
 
 		/// Can't transfer tokens to ethereum zero address
 		AddressIsZero,
-		/// Target collection doesn't supports this operation
+		/// Target collection doesn't support this operation
 		UnsupportedOperation,
 
-		/// Not sufficient funds to perform action
+		/// Insufficient funds to perform an action
 		NotSufficientFounds,
 
-		/// User not passed nesting rule
+		/// User does not satisfy the nesting rule
 		UserIsNotAllowedToNest,
-		/// Only tokens from specific collections may nest tokens under this
+		/// Only tokens from specific collections may nest tokens under this one
 		SourceCollectionIsNotAllowedToNest,
 
 		/// Tried to store more data than allowed in collection field
@@ -447,7 +492,7 @@
 		/// Property key is too long
 		PropertyKeyIsTooLong,
 
-		/// Only ASCII letters, digits, and '_', '-' are allowed
+		/// Only ASCII letters, digits, and symbols '_', '-', and '.' are allowed
 		InvalidCharacterInPropertyKey,
 
 		/// Empty property keys are forbidden
@@ -460,8 +505,11 @@
 		CollectionIsInternal,
 	}
 
+	/// The number of created collections. Essentially contains the last collection ID.
 	#[pallet::storage]
 	pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;
+
+	/// The number of destroyed collections
 	#[pallet::storage]
 	pub type DestroyedCollectionCount<T> =
 		StorageValue<Value = CollectionId, QueryKind = ValueQuery>;
@@ -486,6 +534,7 @@
 		OnEmpty = up_data_structs::CollectionProperties,
 	>;
 
+	/// Token permissions of a collection
 	#[pallet::storage]
 	#[pallet::getter(fn property_permissions)]
 	pub type CollectionPropertyPermissions<T> = StorageMap<
@@ -495,6 +544,7 @@
 		QueryKind = ValueQuery,
 	>;
 
+	/// Amount of collection admins
 	#[pallet::storage]
 	pub type AdminAmount<T> = StorageMap<
 		Hasher = Blake2_128Concat,
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
before · pallets/refungible/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)]1819use frame_support::{ensure, BoundedVec};20use up_data_structs::{21	AccessMode, CollectionId, CustomDataLimit, MAX_REFUNGIBLE_PIECES, TokenId,22	CreateCollectionData, CreateRefungibleExData, mapping::TokenAddressMapping, budget::Budget,23};24use pallet_evm::account::CrossAccountId;25use pallet_common::{Error as CommonError, Event as CommonEvent, Pallet as PalletCommon};26use pallet_structure::Pallet as PalletStructure;27use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};28use sp_std::{vec::Vec, vec, collections::btree_map::BTreeMap};29use core::ops::Deref;30use codec::{Encode, Decode, MaxEncodedLen};31use scale_info::TypeInfo;3233pub use pallet::*;34#[cfg(feature = "runtime-benchmarks")]35pub mod benchmarking;36pub mod common;37pub mod erc;38pub mod weights;39pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;4041#[struct_versioning::versioned(version = 2, upper)]42#[derive(Encode, Decode, Default, TypeInfo, MaxEncodedLen)]43pub struct ItemData {44	pub const_data: BoundedVec<u8, CustomDataLimit>,4546	#[version(..2)]47	pub variable_data: BoundedVec<u8, CustomDataLimit>,48}4950#[frame_support::pallet]51pub mod pallet {52	use super::*;53	use frame_support::{54		Blake2_128, Blake2_128Concat, Twox64Concat, pallet_prelude::*, storage::Key,55		traits::StorageVersion,56	};57	use frame_system::pallet_prelude::*;58	use up_data_structs::{CollectionId, TokenId};59	use super::weights::WeightInfo;6061	#[pallet::error]62	pub enum Error<T> {63		/// Not Refungible item data used to mint in Refungible collection.64		NotRefungibleDataUsedToMintFungibleCollectionToken,65		/// Maximum refungibility exceeded66		WrongRefungiblePieces,67		/// Refungible token can't be repartitioned by user who isn't owns all pieces68		RepartitionWhileNotOwningAllPieces,69		/// Refungible token can't nest other tokens70		RefungibleDisallowsNesting,71		/// Setting item properties is not allowed72		SettingPropertiesNotAllowed,73	}7475	#[pallet::config]76	pub trait Config:77		frame_system::Config + pallet_common::Config + pallet_structure::Config78	{79		type WeightInfo: WeightInfo;80	}8182	const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);8384	#[pallet::pallet]85	#[pallet::storage_version(STORAGE_VERSION)]86	#[pallet::generate_store(pub(super) trait Store)]87	pub struct Pallet<T>(_);8889	#[pallet::storage]90	pub type TokensMinted<T: Config> =91		StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;92	#[pallet::storage]93	pub type TokensBurnt<T: Config> =94		StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;9596	#[pallet::storage]97	pub type TokenData<T: Config> = StorageNMap<98		Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),99		Value = ItemData,100		QueryKind = ValueQuery,101	>;102103	#[pallet::storage]104	pub type TotalSupply<T: Config> = StorageNMap<105		Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),106		Value = u128,107		QueryKind = ValueQuery,108	>;109110	/// Used to enumerate tokens owned by account111	#[pallet::storage]112	pub type Owned<T: Config> = StorageNMap<113		Key = (114			Key<Twox64Concat, CollectionId>,115			Key<Blake2_128Concat, T::CrossAccountId>,116			Key<Twox64Concat, TokenId>,117		),118		Value = bool,119		QueryKind = ValueQuery,120	>;121122	#[pallet::storage]123	pub type AccountBalance<T: Config> = StorageNMap<124		Key = (125			Key<Twox64Concat, CollectionId>,126			// Owner127			Key<Blake2_128Concat, T::CrossAccountId>,128		),129		Value = u32,130		QueryKind = ValueQuery,131	>;132133	#[pallet::storage]134	pub type Balance<T: Config> = StorageNMap<135		Key = (136			Key<Twox64Concat, CollectionId>,137			Key<Twox64Concat, TokenId>,138			// Owner139			Key<Blake2_128Concat, T::CrossAccountId>,140		),141		Value = u128,142		QueryKind = ValueQuery,143	>;144145	#[pallet::storage]146	pub type Allowance<T: Config> = StorageNMap<147		Key = (148			Key<Twox64Concat, CollectionId>,149			Key<Twox64Concat, TokenId>,150			// Owner151			Key<Blake2_128, T::CrossAccountId>,152			// Spender153			Key<Blake2_128Concat, T::CrossAccountId>,154		),155		Value = u128,156		QueryKind = ValueQuery,157	>;158159	#[pallet::hooks]160	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {161		fn on_runtime_upgrade() -> Weight {162			if StorageVersion::get::<Pallet<T>>() < StorageVersion::new(1) {163				<TokenData<T>>::translate_values::<ItemDataVersion1, _>(|v| {164					Some(<ItemDataVersion2>::from(v))165				})166			}167168			0169		}170	}171}172173pub struct RefungibleHandle<T: Config>(pallet_common::CollectionHandle<T>);174impl<T: Config> RefungibleHandle<T> {175	pub fn cast(inner: pallet_common::CollectionHandle<T>) -> Self {176		Self(inner)177	}178	pub fn into_inner(self) -> pallet_common::CollectionHandle<T> {179		self.0180	}181}182impl<T: Config> Deref for RefungibleHandle<T> {183	type Target = pallet_common::CollectionHandle<T>;184185	fn deref(&self) -> &Self::Target {186		&self.0187	}188}189190impl<T: Config> Pallet<T> {191	pub fn total_supply(collection: &RefungibleHandle<T>) -> u32 {192		<TokensMinted<T>>::get(collection.id) - <TokensBurnt<T>>::get(collection.id)193	}194	pub fn token_exists(collection: &RefungibleHandle<T>, token: TokenId) -> bool {195		<TotalSupply<T>>::contains_key((collection.id, token))196	}197}198199// unchecked calls skips any permission checks200impl<T: Config> Pallet<T> {201	pub fn init_collection(202		owner: T::CrossAccountId,203		data: CreateCollectionData<T::AccountId>,204	) -> Result<CollectionId, DispatchError> {205		<PalletCommon<T>>::init_collection(owner, data, false)206	}207	pub fn destroy_collection(208		collection: RefungibleHandle<T>,209		sender: &T::CrossAccountId,210	) -> DispatchResult {211		let id = collection.id;212213		if Self::collection_has_tokens(id) {214			return Err(<CommonError<T>>::CantDestroyNotEmptyCollection.into());215		}216217		// =========218219		PalletCommon::destroy_collection(collection.0, sender)?;220221		<TokensMinted<T>>::remove(id);222		<TokensBurnt<T>>::remove(id);223		<TokenData<T>>::remove_prefix((id,), None);224		<TotalSupply<T>>::remove_prefix((id,), None);225		<Balance<T>>::remove_prefix((id,), None);226		<Allowance<T>>::remove_prefix((id,), None);227		<Owned<T>>::remove_prefix((id,), None);228		<AccountBalance<T>>::remove_prefix((id,), None);229		Ok(())230	}231232	fn collection_has_tokens(collection_id: CollectionId) -> bool {233		<TokenData<T>>::iter_prefix((collection_id,))234			.next()235			.is_some()236	}237238	pub fn burn_token(collection: &RefungibleHandle<T>, token_id: TokenId) -> DispatchResult {239		let burnt = <TokensBurnt<T>>::get(collection.id)240			.checked_add(1)241			.ok_or(ArithmeticError::Overflow)?;242243		<TokensBurnt<T>>::insert(collection.id, burnt);244		<TokenData<T>>::remove((collection.id, token_id));245		<TotalSupply<T>>::remove((collection.id, token_id));246		<Balance<T>>::remove_prefix((collection.id, token_id), None);247		<Allowance<T>>::remove_prefix((collection.id, token_id), None);248		// TODO: ERC721 transfer event249		Ok(())250	}251252	pub fn burn(253		collection: &RefungibleHandle<T>,254		owner: &T::CrossAccountId,255		token: TokenId,256		amount: u128,257	) -> DispatchResult {258		let total_supply = <TotalSupply<T>>::get((collection.id, token))259			.checked_sub(amount)260			.ok_or(<CommonError<T>>::TokenValueTooLow)?;261262		// This was probally last owner of this token?263		if total_supply == 0 {264			// Ensure user actually owns this amount265			ensure!(266				<Balance<T>>::get((collection.id, token, owner)) == amount,267				<CommonError<T>>::TokenValueTooLow268			);269			let account_balance = <AccountBalance<T>>::get((collection.id, owner))270				.checked_sub(1)271				// Should not occur272				.ok_or(ArithmeticError::Underflow)?;273274			// =========275276			<Owned<T>>::remove((collection.id, owner, token));277			<PalletStructure<T>>::unnest_if_nested(owner, collection.id, token);278			<AccountBalance<T>>::insert((collection.id, owner), account_balance);279			Self::burn_token(collection, token)?;280			<PalletCommon<T>>::deposit_event(CommonEvent::ItemDestroyed(281				collection.id,282				token,283				owner.clone(),284				amount,285			));286			return Ok(());287		}288289		let balance = <Balance<T>>::get((collection.id, token, owner))290			.checked_sub(amount)291			.ok_or(<CommonError<T>>::TokenValueTooLow)?;292		let account_balance = if balance == 0 {293			<AccountBalance<T>>::get((collection.id, owner))294				.checked_sub(1)295				// Should not occur296				.ok_or(ArithmeticError::Underflow)?297		} else {298			0299		};300301		// =========302303		if balance == 0 {304			<Owned<T>>::remove((collection.id, owner, token));305			<PalletStructure<T>>::unnest_if_nested(owner, collection.id, token);306			<Balance<T>>::remove((collection.id, token, owner));307			<AccountBalance<T>>::insert((collection.id, owner), account_balance);308		} else {309			<Balance<T>>::insert((collection.id, token, owner), balance);310		}311		<TotalSupply<T>>::insert((collection.id, token), total_supply);312		// TODO: ERC20 transfer event313		<PalletCommon<T>>::deposit_event(CommonEvent::ItemDestroyed(314			collection.id,315			token,316			owner.clone(),317			amount,318		));319		Ok(())320	}321322	pub fn transfer(323		collection: &RefungibleHandle<T>,324		from: &T::CrossAccountId,325		to: &T::CrossAccountId,326		token: TokenId,327		amount: u128,328		nesting_budget: &dyn Budget,329	) -> DispatchResult {330		ensure!(331			collection.limits.transfers_enabled(),332			<CommonError<T>>::TransferNotAllowed333		);334335		if collection.permissions.access() == AccessMode::AllowList {336			collection.check_allowlist(from)?;337			collection.check_allowlist(to)?;338		}339		<PalletCommon<T>>::ensure_correct_receiver(to)?;340341		let balance_from = <Balance<T>>::get((collection.id, token, from))342			.checked_sub(amount)343			.ok_or(<CommonError<T>>::TokenValueTooLow)?;344		let mut create_target = false;345		let from_to_differ = from != to;346		let balance_to = if from != to {347			let old_balance = <Balance<T>>::get((collection.id, token, to));348			if old_balance == 0 {349				create_target = true;350			}351			Some(352				old_balance353					.checked_add(amount)354					.ok_or(ArithmeticError::Overflow)?,355			)356		} else {357			None358		};359360		let account_balance_from = if balance_from == 0 {361			Some(362				<AccountBalance<T>>::get((collection.id, from))363					.checked_sub(1)364					// Should not occur365					.ok_or(ArithmeticError::Underflow)?,366			)367		} else {368			None369		};370		// Account data is created in token, AccountBalance should be increased371		// But only if from != to as we shouldn't check overflow in this case372		let account_balance_to = if create_target && from_to_differ {373			let account_balance_to = <AccountBalance<T>>::get((collection.id, to))374				.checked_add(1)375				.ok_or(ArithmeticError::Overflow)?;376			ensure!(377				account_balance_to < collection.limits.account_token_ownership_limit(),378				<CommonError<T>>::AccountTokenLimitExceeded,379			);380381			Some(account_balance_to)382		} else {383			None384		};385386		// =========387388		<PalletStructure<T>>::nest_if_sent_to_token(389			from.clone(),390			to,391			collection.id,392			token,393			nesting_budget,394		)?;395396		if let Some(balance_to) = balance_to {397			// from != to398			if balance_from == 0 {399				<Balance<T>>::remove((collection.id, token, from));400				<PalletStructure<T>>::unnest_if_nested(from, collection.id, token);401			} else {402				<Balance<T>>::insert((collection.id, token, from), balance_from);403			}404			<Balance<T>>::insert((collection.id, token, to), balance_to);405			if let Some(account_balance_from) = account_balance_from {406				<AccountBalance<T>>::insert((collection.id, from), account_balance_from);407				<Owned<T>>::remove((collection.id, from, token));408			}409			if let Some(account_balance_to) = account_balance_to {410				<AccountBalance<T>>::insert((collection.id, to), account_balance_to);411				<Owned<T>>::insert((collection.id, to, token), true);412			}413		}414415		// TODO: ERC20 transfer event416		<PalletCommon<T>>::deposit_event(CommonEvent::Transfer(417			collection.id,418			token,419			from.clone(),420			to.clone(),421			amount,422		));423		Ok(())424	}425426	pub fn create_multiple_items(427		collection: &RefungibleHandle<T>,428		sender: &T::CrossAccountId,429		data: Vec<CreateRefungibleExData<T::CrossAccountId>>,430		nesting_budget: &dyn Budget,431	) -> DispatchResult {432		if !collection.is_owner_or_admin(sender) {433			ensure!(434				collection.permissions.mint_mode(),435				<CommonError<T>>::PublicMintingNotAllowed436			);437			collection.check_allowlist(sender)?;438439			for item in data.iter() {440				for user in item.users.keys() {441					collection.check_allowlist(user)?;442				}443			}444		}445446		for item in data.iter() {447			for (owner, _) in item.users.iter() {448				<PalletCommon<T>>::ensure_correct_receiver(owner)?;449			}450		}451452		// Total pieces per tokens453		let totals = data454			.iter()455			.map(|data| {456				Ok(data457					.users458					.iter()459					.map(|u| u.1)460					.try_fold(0u128, |acc, v| acc.checked_add(*v))461					.ok_or(ArithmeticError::Overflow)?)462			})463			.collect::<Result<Vec<_>, DispatchError>>()?;464		for total in &totals {465			ensure!(466				*total <= MAX_REFUNGIBLE_PIECES,467				<Error<T>>::WrongRefungiblePieces468			);469		}470471		let first_token_id = <TokensMinted<T>>::get(collection.id);472		let tokens_minted = first_token_id473			.checked_add(data.len() as u32)474			.ok_or(ArithmeticError::Overflow)?;475		ensure!(476			tokens_minted < collection.limits.token_limit(),477			<CommonError<T>>::CollectionTokenLimitExceeded478		);479480		let mut balances = BTreeMap::new();481		for data in &data {482			for owner in data.users.keys() {483				let balance = balances484					.entry(owner)485					.or_insert_with(|| <AccountBalance<T>>::get((collection.id, owner)));486				*balance = balance.checked_add(1).ok_or(ArithmeticError::Overflow)?;487488				ensure!(489					*balance <= collection.limits.account_token_ownership_limit(),490					<CommonError<T>>::AccountTokenLimitExceeded,491				);492			}493		}494495		for (i, token) in data.iter().enumerate() {496			let token_id = TokenId(first_token_id + i as u32 + 1);497			for (to, _) in token.users.iter() {498				<PalletStructure<T>>::check_nesting(499					sender.clone(),500					to,501					collection.id,502					token_id,503					nesting_budget,504				)?;505			}506		}507508		// =========509510		<TokensMinted<T>>::insert(collection.id, tokens_minted);511		for (account, balance) in balances {512			<AccountBalance<T>>::insert((collection.id, account), balance);513		}514		for (i, token) in data.into_iter().enumerate() {515			let token_id = first_token_id + i as u32 + 1;516			<TotalSupply<T>>::insert((collection.id, token_id), totals[i]);517518			<TokenData<T>>::insert(519				(collection.id, token_id),520				ItemData {521					const_data: token.const_data,522				},523			);524525			for (user, amount) in token.users.into_iter() {526				if amount == 0 {527					continue;528				}529				<Balance<T>>::insert((collection.id, token_id, &user), amount);530				<Owned<T>>::insert((collection.id, &user, TokenId(token_id)), true);531				<PalletStructure<T>>::nest_if_sent_to_token_unchecked(532					&user,533					collection.id,534					TokenId(token_id),535				);536537				// TODO: ERC20 transfer event538				<PalletCommon<T>>::deposit_event(CommonEvent::ItemCreated(539					collection.id,540					TokenId(token_id),541					user,542					amount,543				));544			}545		}546		Ok(())547	}548549	pub fn set_allowance_unchecked(550		collection: &RefungibleHandle<T>,551		sender: &T::CrossAccountId,552		spender: &T::CrossAccountId,553		token: TokenId,554		amount: u128,555	) {556		if amount == 0 {557			<Allowance<T>>::remove((collection.id, token, sender, spender));558		} else {559			<Allowance<T>>::insert((collection.id, token, sender, spender), amount);560		}561		// TODO: ERC20 approval event562		<PalletCommon<T>>::deposit_event(CommonEvent::Approved(563			collection.id,564			token,565			sender.clone(),566			spender.clone(),567			amount,568		))569	}570571	pub fn set_allowance(572		collection: &RefungibleHandle<T>,573		sender: &T::CrossAccountId,574		spender: &T::CrossAccountId,575		token: TokenId,576		amount: u128,577	) -> DispatchResult {578		if collection.permissions.access() == AccessMode::AllowList {579			collection.check_allowlist(sender)?;580			collection.check_allowlist(spender)?;581		}582583		<PalletCommon<T>>::ensure_correct_receiver(spender)?;584585		if <Balance<T>>::get((collection.id, token, sender)) < amount {586			ensure!(587				collection.ignores_owned_amount(sender) && Self::token_exists(collection, token),588				<CommonError<T>>::CantApproveMoreThanOwned589			);590		}591592		// =========593594		Self::set_allowance_unchecked(collection, sender, spender, token, amount);595		Ok(())596	}597598	/// Returns allowance, which should be set after transaction599	fn check_allowed(600		collection: &RefungibleHandle<T>,601		spender: &T::CrossAccountId,602		from: &T::CrossAccountId,603		token: TokenId,604		amount: u128,605		nesting_budget: &dyn Budget,606	) -> Result<Option<u128>, DispatchError> {607		if spender.conv_eq(from) {608			return Ok(None);609		}610		if collection.permissions.access() == AccessMode::AllowList {611			// `from`, `to` checked in [`transfer`]612			collection.check_allowlist(spender)?;613		}614		if let Some(source) = T::CrossTokenAddressMapping::address_to_token(from) {615			// TODO: should collection owner be allowed to perform this transfer?616			ensure!(617				<PalletStructure<T>>::check_indirectly_owned(618					spender.clone(),619					source.0,620					source.1,621					None,622					nesting_budget623				)?,624				<CommonError<T>>::ApprovedValueTooLow,625			);626			return Ok(None);627		}628		let allowance =629			<Allowance<T>>::get((collection.id, token, from, &spender)).checked_sub(amount);630		if allowance.is_none() {631			ensure!(632				collection.ignores_allowance(spender),633				<CommonError<T>>::ApprovedValueTooLow634			);635		}636		Ok(allowance)637	}638639	pub fn transfer_from(640		collection: &RefungibleHandle<T>,641		spender: &T::CrossAccountId,642		from: &T::CrossAccountId,643		to: &T::CrossAccountId,644		token: TokenId,645		amount: u128,646		nesting_budget: &dyn Budget,647	) -> DispatchResult {648		let allowance =649			Self::check_allowed(collection, spender, from, token, amount, nesting_budget)?;650651		// =========652653		Self::transfer(collection, from, to, token, amount, nesting_budget)?;654		if let Some(allowance) = allowance {655			Self::set_allowance_unchecked(collection, from, spender, token, allowance);656		}657		Ok(())658	}659660	pub fn burn_from(661		collection: &RefungibleHandle<T>,662		spender: &T::CrossAccountId,663		from: &T::CrossAccountId,664		token: TokenId,665		amount: u128,666		nesting_budget: &dyn Budget,667	) -> DispatchResult {668		let allowance =669			Self::check_allowed(collection, spender, from, token, amount, nesting_budget)?;670671		// =========672673		Self::burn(collection, from, token, amount)?;674		if let Some(allowance) = allowance {675			Self::set_allowance_unchecked(collection, from, spender, token, allowance);676		}677		Ok(())678	}679680	/// Delegated to `create_multiple_items`681	pub fn create_item(682		collection: &RefungibleHandle<T>,683		sender: &T::CrossAccountId,684		data: CreateRefungibleExData<T::CrossAccountId>,685		nesting_budget: &dyn Budget,686	) -> DispatchResult {687		Self::create_multiple_items(collection, sender, vec![data], nesting_budget)688	}689690	pub fn repartition(691		collection: &RefungibleHandle<T>,692		owner: &T::CrossAccountId,693		token: TokenId,694		amount: u128,695	) -> DispatchResult {696		ensure!(697			amount <= MAX_REFUNGIBLE_PIECES,698			<Error<T>>::WrongRefungiblePieces699		);700		ensure!(amount > 0, <CommonError<T>>::TokenValueTooLow);701		// Ensure user owns all pieces702		let total_supply = <TotalSupply<T>>::get((collection.id, token));703		let balance = <Balance<T>>::get((collection.id, token, owner));704		ensure!(705			total_supply == balance,706			<Error<T>>::RepartitionWhileNotOwningAllPieces707		);708709		<Balance<T>>::insert((collection.id, token, owner), amount);710		<TotalSupply<T>>::insert((collection.id, token), amount);711		Ok(())712	}713714	fn total_pieces(collection_id: CollectionId, token_id: TokenId) -> Option<u128> {715		<TotalSupply<T>>::try_get((collection_id, token_id)).ok()716	}717}
after · pallets/refungible/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)]1819use frame_support::{ensure, BoundedVec};20use up_data_structs::{21	AccessMode, CollectionId, CustomDataLimit, MAX_REFUNGIBLE_PIECES, TokenId,22	CreateCollectionData, CreateRefungibleExData, mapping::TokenAddressMapping, budget::Budget,23};24use pallet_evm::account::CrossAccountId;25use pallet_common::{Error as CommonError, Event as CommonEvent, Pallet as PalletCommon};26use pallet_structure::Pallet as PalletStructure;27use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};28use sp_std::{vec::Vec, vec, collections::btree_map::BTreeMap};29use core::ops::Deref;30use codec::{Encode, Decode, MaxEncodedLen};31use scale_info::TypeInfo;3233pub use pallet::*;34#[cfg(feature = "runtime-benchmarks")]35pub mod benchmarking;36pub mod common;37pub mod erc;38pub mod weights;39pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;4041/// Token data, stored independently from other data used to describe it.42/// Notably contains the token metadata.43#[struct_versioning::versioned(version = 2, upper)]44#[derive(Encode, Decode, Default, TypeInfo, MaxEncodedLen)]45pub struct ItemData {46	pub const_data: BoundedVec<u8, CustomDataLimit>,4748	#[version(..2)]49	pub variable_data: BoundedVec<u8, CustomDataLimit>,50}5152#[frame_support::pallet]53pub mod pallet {54	use super::*;55	use frame_support::{56		Blake2_128, Blake2_128Concat, Twox64Concat, pallet_prelude::*, storage::Key,57		traits::StorageVersion,58	};59	use frame_system::pallet_prelude::*;60	use up_data_structs::{CollectionId, TokenId};61	use super::weights::WeightInfo;6263	#[pallet::error]64	pub enum Error<T> {65		/// Not Refungible item data used to mint in Refungible collection.66		NotRefungibleDataUsedToMintFungibleCollectionToken,67		/// Maximum refungibility exceeded68		WrongRefungiblePieces,69		/// Refungible token can't be repartitioned by user who isn't owns all pieces70		RepartitionWhileNotOwningAllPieces,71		/// Refungible token can't nest other tokens72		RefungibleDisallowsNesting,73		/// Setting item properties is not allowed74		SettingPropertiesNotAllowed,75	}7677	#[pallet::config]78	pub trait Config:79		frame_system::Config + pallet_common::Config + pallet_structure::Config80	{81		type WeightInfo: WeightInfo;82	}8384	const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);8586	#[pallet::pallet]87	#[pallet::storage_version(STORAGE_VERSION)]88	#[pallet::generate_store(pub(super) trait Store)]89	pub struct Pallet<T>(_);9091	/// Total amount of minted tokens in a collection.92	#[pallet::storage]93	pub type TokensMinted<T: Config> =94		StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;95	96	/// Amount of tokens burnt in a collection.97	#[pallet::storage]98	pub type TokensBurnt<T: Config> =99		StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;100101	/// Token data, used to partially describe a token.102	#[pallet::storage]103	pub type TokenData<T: Config> = StorageNMap<104		Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),105		Value = ItemData,106		QueryKind = ValueQuery,107	>;108109	/// Amount of pieces a refungible token is split into.110	#[pallet::storage]111	pub type TotalSupply<T: Config> = StorageNMap<112		Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),113		Value = u128,114		QueryKind = ValueQuery,115	>;116117	/// Used to enumerate tokens owned by account.118	#[pallet::storage]119	pub type Owned<T: Config> = StorageNMap<120		Key = (121			Key<Twox64Concat, CollectionId>,122			Key<Blake2_128Concat, T::CrossAccountId>,123			Key<Twox64Concat, TokenId>,124		),125		Value = bool,126		QueryKind = ValueQuery,127	>;128129	/// Amount of tokens (not pieces) partially owned by an account within a collection.130	#[pallet::storage]131	pub type AccountBalance<T: Config> = StorageNMap<132		Key = (133			Key<Twox64Concat, CollectionId>,134			// Owner135			Key<Blake2_128Concat, T::CrossAccountId>,136		),137		Value = u32,138		QueryKind = ValueQuery,139	>;140141	/// Amount of pieces of a token owned by an account.142	#[pallet::storage]143	pub type Balance<T: Config> = StorageNMap<144		Key = (145			Key<Twox64Concat, CollectionId>,146			Key<Twox64Concat, TokenId>,147			// Owner148			Key<Blake2_128Concat, T::CrossAccountId>,149		),150		Value = u128,151		QueryKind = ValueQuery,152	>;153154	/// todo:doc155	#[pallet::storage]156	pub type Allowance<T: Config> = StorageNMap<157		Key = (158			Key<Twox64Concat, CollectionId>,159			Key<Twox64Concat, TokenId>,160			// Owner161			Key<Blake2_128, T::CrossAccountId>,162			// Spender163			Key<Blake2_128Concat, T::CrossAccountId>,164		),165		Value = u128,166		QueryKind = ValueQuery,167	>;168169	#[pallet::hooks]170	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {171		fn on_runtime_upgrade() -> Weight {172			if StorageVersion::get::<Pallet<T>>() < StorageVersion::new(1) {173				<TokenData<T>>::translate_values::<ItemDataVersion1, _>(|v| {174					Some(<ItemDataVersion2>::from(v))175				})176			}177178			0179		}180	}181}182183pub struct RefungibleHandle<T: Config>(pallet_common::CollectionHandle<T>);184impl<T: Config> RefungibleHandle<T> {185	pub fn cast(inner: pallet_common::CollectionHandle<T>) -> Self {186		Self(inner)187	}188	pub fn into_inner(self) -> pallet_common::CollectionHandle<T> {189		self.0190	}191}192impl<T: Config> Deref for RefungibleHandle<T> {193	type Target = pallet_common::CollectionHandle<T>;194195	fn deref(&self) -> &Self::Target {196		&self.0197	}198}199200impl<T: Config> Pallet<T> {201	pub fn total_supply(collection: &RefungibleHandle<T>) -> u32 {202		<TokensMinted<T>>::get(collection.id) - <TokensBurnt<T>>::get(collection.id)203	}204	pub fn token_exists(collection: &RefungibleHandle<T>, token: TokenId) -> bool {205		<TotalSupply<T>>::contains_key((collection.id, token))206	}207}208209// unchecked calls skips any permission checks210impl<T: Config> Pallet<T> {211	pub fn init_collection(212		owner: T::CrossAccountId,213		data: CreateCollectionData<T::AccountId>,214	) -> Result<CollectionId, DispatchError> {215		<PalletCommon<T>>::init_collection(owner, data, false)216	}217	pub fn destroy_collection(218		collection: RefungibleHandle<T>,219		sender: &T::CrossAccountId,220	) -> DispatchResult {221		let id = collection.id;222223		if Self::collection_has_tokens(id) {224			return Err(<CommonError<T>>::CantDestroyNotEmptyCollection.into());225		}226227		// =========228229		PalletCommon::destroy_collection(collection.0, sender)?;230231		<TokensMinted<T>>::remove(id);232		<TokensBurnt<T>>::remove(id);233		<TokenData<T>>::remove_prefix((id,), None);234		<TotalSupply<T>>::remove_prefix((id,), None);235		<Balance<T>>::remove_prefix((id,), None);236		<Allowance<T>>::remove_prefix((id,), None);237		<Owned<T>>::remove_prefix((id,), None);238		<AccountBalance<T>>::remove_prefix((id,), None);239		Ok(())240	}241242	fn collection_has_tokens(collection_id: CollectionId) -> bool {243		<TokenData<T>>::iter_prefix((collection_id,))244			.next()245			.is_some()246	}247248	pub fn burn_token(collection: &RefungibleHandle<T>, token_id: TokenId) -> DispatchResult {249		let burnt = <TokensBurnt<T>>::get(collection.id)250			.checked_add(1)251			.ok_or(ArithmeticError::Overflow)?;252253		<TokensBurnt<T>>::insert(collection.id, burnt);254		<TokenData<T>>::remove((collection.id, token_id));255		<TotalSupply<T>>::remove((collection.id, token_id));256		<Balance<T>>::remove_prefix((collection.id, token_id), None);257		<Allowance<T>>::remove_prefix((collection.id, token_id), None);258		// TODO: ERC721 transfer event259		Ok(())260	}261	262	pub fn burn(263		collection: &RefungibleHandle<T>,264		owner: &T::CrossAccountId,265		token: TokenId,266		amount: u128,267	) -> DispatchResult {268		let total_supply = <TotalSupply<T>>::get((collection.id, token))269			.checked_sub(amount)270			.ok_or(<CommonError<T>>::TokenValueTooLow)?;271272		// This was probally last owner of this token?273		if total_supply == 0 {274			// Ensure user actually owns this amount275			ensure!(276				<Balance<T>>::get((collection.id, token, owner)) == amount,277				<CommonError<T>>::TokenValueTooLow278			);279			let account_balance = <AccountBalance<T>>::get((collection.id, owner))280				.checked_sub(1)281				// Should not occur282				.ok_or(ArithmeticError::Underflow)?;283284			// =========285286			<Owned<T>>::remove((collection.id, owner, token));287			<PalletStructure<T>>::unnest_if_nested(owner, collection.id, token);288			<AccountBalance<T>>::insert((collection.id, owner), account_balance);289			Self::burn_token(collection, token)?;290			<PalletCommon<T>>::deposit_event(CommonEvent::ItemDestroyed(291				collection.id,292				token,293				owner.clone(),294				amount,295			));296			return Ok(());297		}298299		let balance = <Balance<T>>::get((collection.id, token, owner))300			.checked_sub(amount)301			.ok_or(<CommonError<T>>::TokenValueTooLow)?;302		let account_balance = if balance == 0 {303			<AccountBalance<T>>::get((collection.id, owner))304				.checked_sub(1)305				// Should not occur306				.ok_or(ArithmeticError::Underflow)?307		} else {308			0309		};310311		// =========312313		if balance == 0 {314			<Owned<T>>::remove((collection.id, owner, token));315			<PalletStructure<T>>::unnest_if_nested(owner, collection.id, token);316			<Balance<T>>::remove((collection.id, token, owner));317			<AccountBalance<T>>::insert((collection.id, owner), account_balance);318		} else {319			<Balance<T>>::insert((collection.id, token, owner), balance);320		}321		<TotalSupply<T>>::insert((collection.id, token), total_supply);322		// TODO: ERC20 transfer event323		<PalletCommon<T>>::deposit_event(CommonEvent::ItemDestroyed(324			collection.id,325			token,326			owner.clone(),327			amount,328		));329		Ok(())330	}331332	pub fn transfer(333		collection: &RefungibleHandle<T>,334		from: &T::CrossAccountId,335		to: &T::CrossAccountId,336		token: TokenId,337		amount: u128,338		nesting_budget: &dyn Budget,339	) -> DispatchResult {340		ensure!(341			collection.limits.transfers_enabled(),342			<CommonError<T>>::TransferNotAllowed343		);344345		if collection.permissions.access() == AccessMode::AllowList {346			collection.check_allowlist(from)?;347			collection.check_allowlist(to)?;348		}349		<PalletCommon<T>>::ensure_correct_receiver(to)?;350351		let balance_from = <Balance<T>>::get((collection.id, token, from))352			.checked_sub(amount)353			.ok_or(<CommonError<T>>::TokenValueTooLow)?;354		let mut create_target = false;355		let from_to_differ = from != to;356		let balance_to = if from != to {357			let old_balance = <Balance<T>>::get((collection.id, token, to));358			if old_balance == 0 {359				create_target = true;360			}361			Some(362				old_balance363					.checked_add(amount)364					.ok_or(ArithmeticError::Overflow)?,365			)366		} else {367			None368		};369370		let account_balance_from = if balance_from == 0 {371			Some(372				<AccountBalance<T>>::get((collection.id, from))373					.checked_sub(1)374					// Should not occur375					.ok_or(ArithmeticError::Underflow)?,376			)377		} else {378			None379		};380		// Account data is created in token, AccountBalance should be increased381		// But only if from != to as we shouldn't check overflow in this case382		let account_balance_to = if create_target && from_to_differ {383			let account_balance_to = <AccountBalance<T>>::get((collection.id, to))384				.checked_add(1)385				.ok_or(ArithmeticError::Overflow)?;386			ensure!(387				account_balance_to < collection.limits.account_token_ownership_limit(),388				<CommonError<T>>::AccountTokenLimitExceeded,389			);390391			Some(account_balance_to)392		} else {393			None394		};395396		// =========397398		<PalletStructure<T>>::nest_if_sent_to_token(399			from.clone(),400			to,401			collection.id,402			token,403			nesting_budget,404		)?;405406		if let Some(balance_to) = balance_to {407			// from != to408			if balance_from == 0 {409				<Balance<T>>::remove((collection.id, token, from));410				<PalletStructure<T>>::unnest_if_nested(from, collection.id, token);411			} else {412				<Balance<T>>::insert((collection.id, token, from), balance_from);413			}414			<Balance<T>>::insert((collection.id, token, to), balance_to);415			if let Some(account_balance_from) = account_balance_from {416				<AccountBalance<T>>::insert((collection.id, from), account_balance_from);417				<Owned<T>>::remove((collection.id, from, token));418			}419			if let Some(account_balance_to) = account_balance_to {420				<AccountBalance<T>>::insert((collection.id, to), account_balance_to);421				<Owned<T>>::insert((collection.id, to, token), true);422			}423		}424425		// TODO: ERC20 transfer event426		<PalletCommon<T>>::deposit_event(CommonEvent::Transfer(427			collection.id,428			token,429			from.clone(),430			to.clone(),431			amount,432		));433		Ok(())434	}435436	pub fn create_multiple_items(437		collection: &RefungibleHandle<T>,438		sender: &T::CrossAccountId,439		data: Vec<CreateRefungibleExData<T::CrossAccountId>>,440		nesting_budget: &dyn Budget,441	) -> DispatchResult {442		if !collection.is_owner_or_admin(sender) {443			ensure!(444				collection.permissions.mint_mode(),445				<CommonError<T>>::PublicMintingNotAllowed446			);447			collection.check_allowlist(sender)?;448449			for item in data.iter() {450				for user in item.users.keys() {451					collection.check_allowlist(user)?;452				}453			}454		}455456		for item in data.iter() {457			for (owner, _) in item.users.iter() {458				<PalletCommon<T>>::ensure_correct_receiver(owner)?;459			}460		}461462		// Total pieces per tokens463		let totals = data464			.iter()465			.map(|data| {466				Ok(data467					.users468					.iter()469					.map(|u| u.1)470					.try_fold(0u128, |acc, v| acc.checked_add(*v))471					.ok_or(ArithmeticError::Overflow)?)472			})473			.collect::<Result<Vec<_>, DispatchError>>()?;474		for total in &totals {475			ensure!(476				*total <= MAX_REFUNGIBLE_PIECES,477				<Error<T>>::WrongRefungiblePieces478			);479		}480481		let first_token_id = <TokensMinted<T>>::get(collection.id);482		let tokens_minted = first_token_id483			.checked_add(data.len() as u32)484			.ok_or(ArithmeticError::Overflow)?;485		ensure!(486			tokens_minted < collection.limits.token_limit(),487			<CommonError<T>>::CollectionTokenLimitExceeded488		);489490		let mut balances = BTreeMap::new();491		for data in &data {492			for owner in data.users.keys() {493				let balance = balances494					.entry(owner)495					.or_insert_with(|| <AccountBalance<T>>::get((collection.id, owner)));496				*balance = balance.checked_add(1).ok_or(ArithmeticError::Overflow)?;497498				ensure!(499					*balance <= collection.limits.account_token_ownership_limit(),500					<CommonError<T>>::AccountTokenLimitExceeded,501				);502			}503		}504505		for (i, token) in data.iter().enumerate() {506			let token_id = TokenId(first_token_id + i as u32 + 1);507			for (to, _) in token.users.iter() {508				<PalletStructure<T>>::check_nesting(509					sender.clone(),510					to,511					collection.id,512					token_id,513					nesting_budget,514				)?;515			}516		}517518		// =========519520		<TokensMinted<T>>::insert(collection.id, tokens_minted);521		for (account, balance) in balances {522			<AccountBalance<T>>::insert((collection.id, account), balance);523		}524		for (i, token) in data.into_iter().enumerate() {525			let token_id = first_token_id + i as u32 + 1;526			<TotalSupply<T>>::insert((collection.id, token_id), totals[i]);527528			<TokenData<T>>::insert(529				(collection.id, token_id),530				ItemData {531					const_data: token.const_data,532				},533			);534535			for (user, amount) in token.users.into_iter() {536				if amount == 0 {537					continue;538				}539				<Balance<T>>::insert((collection.id, token_id, &user), amount);540				<Owned<T>>::insert((collection.id, &user, TokenId(token_id)), true);541				<PalletStructure<T>>::nest_if_sent_to_token_unchecked(542					&user,543					collection.id,544					TokenId(token_id),545				);546547				// TODO: ERC20 transfer event548				<PalletCommon<T>>::deposit_event(CommonEvent::ItemCreated(549					collection.id,550					TokenId(token_id),551					user,552					amount,553				));554			}555		}556		Ok(())557	}558559	pub fn set_allowance_unchecked(560		collection: &RefungibleHandle<T>,561		sender: &T::CrossAccountId,562		spender: &T::CrossAccountId,563		token: TokenId,564		amount: u128,565	) {566		if amount == 0 {567			<Allowance<T>>::remove((collection.id, token, sender, spender));568		} else {569			<Allowance<T>>::insert((collection.id, token, sender, spender), amount);570		}571		// TODO: ERC20 approval event572		<PalletCommon<T>>::deposit_event(CommonEvent::Approved(573			collection.id,574			token,575			sender.clone(),576			spender.clone(),577			amount,578		))579	}580581	pub fn set_allowance(582		collection: &RefungibleHandle<T>,583		sender: &T::CrossAccountId,584		spender: &T::CrossAccountId,585		token: TokenId,586		amount: u128,587	) -> DispatchResult {588		if collection.permissions.access() == AccessMode::AllowList {589			collection.check_allowlist(sender)?;590			collection.check_allowlist(spender)?;591		}592593		<PalletCommon<T>>::ensure_correct_receiver(spender)?;594595		if <Balance<T>>::get((collection.id, token, sender)) < amount {596			ensure!(597				collection.ignores_owned_amount(sender) && Self::token_exists(collection, token),598				<CommonError<T>>::CantApproveMoreThanOwned599			);600		}601602		// =========603604		Self::set_allowance_unchecked(collection, sender, spender, token, amount);605		Ok(())606	}607608	/// todo:doc oh look, a precedent. not pub, too. but it has an unclear use-case.609	/// Returns allowance, which should be set after transaction610	fn check_allowed(611		collection: &RefungibleHandle<T>,612		spender: &T::CrossAccountId,613		from: &T::CrossAccountId,614		token: TokenId,615		amount: u128,616		nesting_budget: &dyn Budget,617	) -> Result<Option<u128>, DispatchError> {618		if spender.conv_eq(from) {619			return Ok(None);620		}621		if collection.permissions.access() == AccessMode::AllowList {622			// `from`, `to` checked in [`transfer`]623			collection.check_allowlist(spender)?;624		}625		if let Some(source) = T::CrossTokenAddressMapping::address_to_token(from) {626			// TODO: should collection owner be allowed to perform this transfer?627			ensure!(628				<PalletStructure<T>>::check_indirectly_owned(629					spender.clone(),630					source.0,631					source.1,632					None,633					nesting_budget634				)?,635				<CommonError<T>>::ApprovedValueTooLow,636			);637			return Ok(None);638		}639		let allowance =640			<Allowance<T>>::get((collection.id, token, from, &spender)).checked_sub(amount);641		if allowance.is_none() {642			ensure!(643				collection.ignores_allowance(spender),644				<CommonError<T>>::ApprovedValueTooLow645			);646		}647		Ok(allowance)648	}649650	pub fn transfer_from(651		collection: &RefungibleHandle<T>,652		spender: &T::CrossAccountId,653		from: &T::CrossAccountId,654		to: &T::CrossAccountId,655		token: TokenId,656		amount: u128,657		nesting_budget: &dyn Budget,658	) -> DispatchResult {659		let allowance =660			Self::check_allowed(collection, spender, from, token, amount, nesting_budget)?;661662		// =========663664		Self::transfer(collection, from, to, token, amount, nesting_budget)?;665		if let Some(allowance) = allowance {666			Self::set_allowance_unchecked(collection, from, spender, token, allowance);667		}668		Ok(())669	}670671	pub fn burn_from(672		collection: &RefungibleHandle<T>,673		spender: &T::CrossAccountId,674		from: &T::CrossAccountId,675		token: TokenId,676		amount: u128,677		nesting_budget: &dyn Budget,678	) -> DispatchResult {679		let allowance =680			Self::check_allowed(collection, spender, from, token, amount, nesting_budget)?;681682		// =========683684		Self::burn(collection, from, token, amount)?;685		if let Some(allowance) = allowance {686			Self::set_allowance_unchecked(collection, from, spender, token, allowance);687		}688		Ok(())689	}690691	/// Delegated to `create_multiple_items`692	pub fn create_item(693		collection: &RefungibleHandle<T>,694		sender: &T::CrossAccountId,695		data: CreateRefungibleExData<T::CrossAccountId>,696		nesting_budget: &dyn Budget,697	) -> DispatchResult {698		Self::create_multiple_items(collection, sender, vec![data], nesting_budget)699	}700701	pub fn repartition(702		collection: &RefungibleHandle<T>,703		owner: &T::CrossAccountId,704		token: TokenId,705		amount: u128,706	) -> DispatchResult {707		ensure!(708			amount <= MAX_REFUNGIBLE_PIECES,709			<Error<T>>::WrongRefungiblePieces710		);711		ensure!(amount > 0, <CommonError<T>>::TokenValueTooLow);712		// Ensure user owns all pieces713		let total_supply = <TotalSupply<T>>::get((collection.id, token));714		let balance = <Balance<T>>::get((collection.id, token, owner));715		ensure!(716			total_supply == balance,717			<Error<T>>::RepartitionWhileNotOwningAllPieces718		);719720		<Balance<T>>::insert((collection.id, token, owner), amount);721		<TotalSupply<T>>::insert((collection.id, token), amount);722		Ok(())723	}724725	fn total_pieces(collection_id: CollectionId, token_id: TokenId) -> Option<u128> {726		<TotalSupply<T>>::try_get((collection_id, token_id)).ok()727	}728}
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>'),