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
--- 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
before · pallets/unique/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#![recursion_limit = "1024"]18#![cfg_attr(not(feature = "std"), no_std)]19#![allow(20	clippy::too_many_arguments,21	clippy::unnecessary_mut_passed,22	clippy::unused_unit23)]2425extern crate alloc;2627use frame_support::{28	decl_module, decl_storage, decl_error, decl_event,29	dispatch::DispatchResult,30	ensure, fail,31	weights::{Weight},32	transactional,33	pallet_prelude::{DispatchResultWithPostInfo, ConstU32},34	BoundedVec,35};36use scale_info::TypeInfo;37use frame_system::{self as system, ensure_signed};38use sp_runtime::{sp_std::prelude::Vec};39use up_data_structs::{40	MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH,41	CreateItemData, CollectionLimits, CollectionPermissions, CollectionId, CollectionMode, TokenId,42	SponsorshipState, CreateCollectionData, CreateItemExData, budget, Property, PropertyKey,43	PropertyKeyPermission,44};45use pallet_evm::account::CrossAccountId;46use pallet_common::{47	CollectionHandle, Pallet as PalletCommon, CommonWeightInfo, dispatch::dispatch_tx,48	dispatch::CollectionDispatch, RefungibleExtensionsWeightInfo,49};50pub mod eth;5152#[cfg(feature = "runtime-benchmarks")]53mod benchmarking;54pub mod weights;55use weights::WeightInfo;5657const NESTING_BUDGET: u32 = 5;5859decl_error! {60	/// Error for non-fungible-token module.61	pub enum Error for Module<T: Config> {62		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.63		CollectionDecimalPointLimitExceeded,64		/// This address is not set as sponsor, use setCollectionSponsor first.65		ConfirmUnsetSponsorFail,66		/// Length of items properties must be greater than 0.67		EmptyArgument,68		/// Repertition is only supported by refungible collection69		RepartitionCalledOnNonRefungibleCollection,70	}71}7273pub trait Config: system::Config + pallet_common::Config + Sized + TypeInfo {74	type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;7576	/// Weight information for extrinsics in this pallet.77	type WeightInfo: WeightInfo;78	type CommonWeightInfo: CommonWeightInfo<Self::CrossAccountId>;79	type RefungibleExtensionsWeightInfo: RefungibleExtensionsWeightInfo;80}8182decl_event! {83	pub enum Event<T>84	where85		<T as frame_system::Config>::AccountId,86		<T as pallet_evm::account::Config>::CrossAccountId,87	{88		/// Collection sponsor was removed89		///90		/// # Arguments91		///92		/// * collection_id: Globally unique collection identifier.93		CollectionSponsorRemoved(CollectionId),9495		/// Collection admin was added96		///97		/// # Arguments98		///99		/// * collection_id: Globally unique collection identifier.100		///101		/// * admin:  Admin address.102		CollectionAdminAdded(CollectionId, CrossAccountId),103104		/// Collection owned was change105		///106		/// # Arguments107		///108		/// * collection_id: Globally unique collection identifier.109		///110		/// * owner:  New owner address.111		CollectionOwnedChanged(CollectionId, AccountId),112113		/// Collection sponsor was set114		///115		/// # Arguments116		///117		/// * collection_id: Globally unique collection identifier.118		///119		/// * owner:  New sponsor address.120		CollectionSponsorSet(CollectionId, AccountId),121122		/// New sponsor was confirm123		///124		/// # Arguments125		///126		/// * collection_id: Globally unique collection identifier.127		///128		/// * sponsor:  New sponsor address.129		SponsorshipConfirmed(CollectionId, AccountId),130131		/// Collection admin was removed132		///133		/// # Arguments134		///135		/// * collection_id: Globally unique collection identifier.136		///137		/// * admin:  Admin address.138		CollectionAdminRemoved(CollectionId, CrossAccountId),139140		/// Address was remove from allow list141		///142		/// # Arguments143		///144		/// * collection_id: Globally unique collection identifier.145		///146		/// * user:  Address.147		AllowListAddressRemoved(CollectionId, CrossAccountId),148149		/// Address was add to allow list150		///151		/// # Arguments152		///153		/// * collection_id: Globally unique collection identifier.154		///155		/// * user:  Address.156		AllowListAddressAdded(CollectionId, CrossAccountId),157158		/// Collection limits was set159		///160		/// # Arguments161		///162		/// * collection_id: Globally unique collection identifier.163		CollectionLimitSet(CollectionId),164165		CollectionPermissionSet(CollectionId),166	}167}168169type SelfWeightOf<T> = <T as Config>::WeightInfo;170171// # Used definitions172//173// ## User control levels174//175// chain-controlled - key is uncontrolled by user176//                    i.e autoincrementing index177//                    can use non-cryptographic hash178// real - key is controlled by user179//        but it is hard to generate enough colliding values, i.e owner of signed txs180//        can use non-cryptographic hash181// controlled - key is completly controlled by users182//              i.e maps with mutable keys183//              should use cryptographic hash184//185// ## User control level downgrade reasons186//187// ?1 - chain-controlled -> controlled188//      collections/tokens can be destroyed, resulting in massive holes189// ?2 - chain-controlled -> controlled190//      same as ?1, but can be only added, resulting in easier exploitation191// ?3 - real -> controlled192//      no confirmation required, so addresses can be easily generated193decl_storage! {194	trait Store for Module<T: Config> as Unique {195196		//#region Private members197		/// Used for migrations198		ChainVersion: u64;199		//#endregion200201		//#region Tokens transfer rate limit baskets202		/// (Collection id (controlled?2), who created (real))203		/// TODO: Off chain worker should remove from this map when collection gets removed204		pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => Option<T::BlockNumber>;205		/// Collection id (controlled?2), token id (controlled?2)206		pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;207		/// Collection id (controlled?2), owning user (real)208		pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;209		/// Collection id (controlled?2), token id (controlled?2)210		pub ReFungibleTransferBasket get(fn refungible_transfer_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;211		//#endregion212213		/// Variable metadata sponsoring214		/// Collection id (controlled?2), token id (controlled?2)215		#[deprecated]216		pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;217		pub TokenPropertyBasket get(fn token_property_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;218219		/// Approval sponsoring220		pub NftApproveBasket get(fn nft_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;221		pub FungibleApproveBasket get(fn fungible_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;222		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>;223	}224}225226decl_module! {227	pub struct Module<T: Config> for enum Call228	where229		origin: T::Origin230	{231		type Error = Error<T>;232233		fn deposit_event() = default;234235		fn on_initialize(_now: T::BlockNumber) -> Weight {236			0237		}238239		fn on_runtime_upgrade() -> Weight {240			let limit = None;241242			<VariableMetaDataBasket<T>>::remove_all(limit);243244			0245		}246247		/// This method creates a Collection of NFTs. Each Token may have multiple properties encoded as an array of bytes of certain length. The initial owner of the collection is set to the address that signed the transaction and can be changed later.248		///249		/// # Permissions250		///251		/// * Anyone.252		///253		/// # Arguments254		///255		/// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.256		///257		/// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.258		///259		/// * token_prefix: UTF-8 string with token prefix.260		///261		/// * mode: [CollectionMode] collection type and type dependent data.262		// returns collection ID263		#[weight = <SelfWeightOf<T>>::create_collection()]264		#[transactional]265		#[deprecated]266		pub fn create_collection(origin,267								 collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,268								 collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,269								 token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,270								 mode: CollectionMode) -> DispatchResult  {271			let data: CreateCollectionData<T::AccountId> = CreateCollectionData {272				name: collection_name,273				description: collection_description,274				token_prefix,275				mode,276				..Default::default()277			};278			Self::create_collection_ex(origin, data)279		}280281		/// This method creates a collection282		///283		/// Prefer it to deprecated [`created_collection`] method284		#[weight = <SelfWeightOf<T>>::create_collection()]285		#[transactional]286		pub fn create_collection_ex(origin, data: CreateCollectionData<T::AccountId>) -> DispatchResult {287			let sender = ensure_signed(origin)?;288289			// =========290291			T::CollectionDispatch::create(T::CrossAccountId::from_sub(sender), data)?;292293			Ok(())294		}295296		/// Destroys collection if no tokens within this collection297		///298		/// # Permissions299		///300		/// * Collection Owner.301		///302		/// # Arguments303		///304		/// * collection_id: collection to destroy.305		#[weight = <SelfWeightOf<T>>::destroy_collection()]306		#[transactional]307		pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {308			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);309			let collection = <CollectionHandle<T>>::try_get(collection_id)?;310			collection.check_is_internal()?;311312			// =========313314			T::CollectionDispatch::destroy(sender, collection)?;315316			<NftTransferBasket<T>>::remove_prefix(collection_id, None);317			<FungibleTransferBasket<T>>::remove_prefix(collection_id, None);318			<ReFungibleTransferBasket<T>>::remove_prefix((collection_id,), None);319320			<NftApproveBasket<T>>::remove_prefix(collection_id, None);321			<FungibleApproveBasket<T>>::remove_prefix(collection_id, None);322			<RefungibleApproveBasket<T>>::remove_prefix((collection_id,), None);323324			Ok(())325		}326327		/// Add an address to allow list.328		///329		/// # Permissions330		///331		/// * Collection Owner332		/// * Collection Admin333		///334		/// # Arguments335		///336		/// * collection_id.337		///338		/// * address.339		#[weight = <SelfWeightOf<T>>::add_to_allow_list()]340		#[transactional]341		pub fn add_to_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{342343			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);344			let collection = <CollectionHandle<T>>::try_get(collection_id)?;345			collection.check_is_internal()?;346347			<PalletCommon<T>>::toggle_allowlist(348				&collection,349				&sender,350				&address,351				true,352			)?;353354			Self::deposit_event(Event::<T>::AllowListAddressAdded(355				collection_id,356				address357			));358359			Ok(())360		}361362		/// Remove an address from allow list.363		///364		/// # Permissions365		///366		/// * Collection Owner367		/// * Collection Admin368		///369		/// # Arguments370		///371		/// * collection_id.372		///373		/// * address.374		#[weight = <SelfWeightOf<T>>::remove_from_allow_list()]375		#[transactional]376		pub fn remove_from_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{377378			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);379			let collection = <CollectionHandle<T>>::try_get(collection_id)?;380			collection.check_is_internal()?;381382			<PalletCommon<T>>::toggle_allowlist(383				&collection,384				&sender,385				&address,386				false,387			)?;388389			<Pallet<T>>::deposit_event(Event::<T>::AllowListAddressRemoved(390				collection_id,391				address392			));393394			Ok(())395		}396397		/// Change the owner of the collection.398		///399		/// # Permissions400		///401		/// * Collection Owner.402		///403		/// # Arguments404		///405		/// * collection_id.406		///407		/// * new_owner.408		#[weight = <SelfWeightOf<T>>::change_collection_owner()]409		#[transactional]410		pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {411412			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);413414			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;415			target_collection.check_is_internal()?;416			target_collection.check_is_owner(&sender)?;417418			target_collection.owner = new_owner.clone();419			<Pallet<T>>::deposit_event(Event::<T>::CollectionOwnedChanged(420				collection_id,421				new_owner422			));423424			target_collection.save()425		}426427		/// Adds an admin of the Collection.428		/// 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.429		///430		/// # Permissions431		///432		/// * Collection Owner.433		/// * Collection Admin.434		///435		/// # Arguments436		///437		/// * collection_id: ID of the Collection to add admin for.438		///439		/// * new_admin_id: Address of new admin to add.440		#[weight = <SelfWeightOf<T>>::add_collection_admin()]441		#[transactional]442		pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {443			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);444			let collection = <CollectionHandle<T>>::try_get(collection_id)?;445			collection.check_is_internal()?;446447			<Pallet<T>>::deposit_event(Event::<T>::CollectionAdminAdded(448				collection_id,449				new_admin_id.clone()450			));451452			<PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)453		}454455		/// 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.456		///457		/// # Permissions458		///459		/// * Collection Owner.460		/// * Collection Admin.461		///462		/// # Arguments463		///464		/// * collection_id: ID of the Collection to remove admin for.465		///466		/// * account_id: Address of admin to remove.467		#[weight = <SelfWeightOf<T>>::remove_collection_admin()]468		#[transactional]469		pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {470			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);471			let collection = <CollectionHandle<T>>::try_get(collection_id)?;472			collection.check_is_internal()?;473474			<Pallet<T>>::deposit_event(Event::<T>::CollectionAdminRemoved(475				collection_id,476				account_id.clone()477			));478479			<PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)480		}481482		/// # Permissions483		///484		/// * Collection Owner485		///486		/// # Arguments487		///488		/// * collection_id.489		///490		/// * new_sponsor.491		#[weight = <SelfWeightOf<T>>::set_collection_sponsor()]492		#[transactional]493		pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {494			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);495496			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;497			target_collection.check_is_owner_or_admin(&sender)?;498			target_collection.check_is_internal()?;499500			target_collection.set_sponsor(new_sponsor.clone())?;501502			<Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(503				collection_id,504				new_sponsor505			));506507			target_collection.save()508		}509510		/// # Permissions511		///512		/// * Sponsor.513		///514		/// # Arguments515		///516		/// * collection_id.517		#[weight = <SelfWeightOf<T>>::confirm_sponsorship()]518		#[transactional]519		pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {520			let sender = ensure_signed(origin)?;521522			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;523			target_collection.check_is_internal()?;524			ensure!(525				target_collection.confirm_sponsorship(&sender)?,526				Error::<T>::ConfirmUnsetSponsorFail527			);528529			<Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(530				collection_id,531				sender532			));533534			target_collection.save()535		}536537		/// Switch back to pay-per-own-transaction model.538		///539		/// # Permissions540		///541		/// * Collection owner.542		///543		/// # Arguments544		///545		/// * collection_id.546		#[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]547		#[transactional]548		pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {549			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);550551			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;552			target_collection.check_is_internal()?;553			target_collection.check_is_owner(&sender)?;554555			target_collection.sponsorship = SponsorshipState::Disabled;556557			<Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(558				collection_id559			));560			target_collection.save()561		}562563		/// This method creates a concrete instance of NFT Collection created with CreateCollection method.564		///565		/// # Permissions566		///567		/// * Collection Owner.568		/// * Collection Admin.569		/// * Anyone if570		///     * Allow List is enabled, and571		///     * Address is added to allow list, and572		///     * MintPermission is enabled (see SetMintPermission method)573		///574		/// # Arguments575		///576		/// * collection_id: ID of the collection.577		///578		/// * owner: Address, initial owner of the NFT.579		///580		/// * data: Token data to store on chain.581		#[weight = T::CommonWeightInfo::create_item()]582		#[transactional]583		pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {584			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);585			let budget = budget::Value::new(NESTING_BUDGET);586587			dispatch_tx::<T, _>(collection_id, |d| d.create_item(sender, owner, data, &budget))588		}589590		/// This method creates multiple items in a collection created with CreateCollection method.591		///592		/// # Permissions593		///594		/// * Collection Owner.595		/// * Collection Admin.596		/// * Anyone if597		///     * Allow List is enabled, and598		///     * Address is added to allow list, and599		///     * MintPermission is enabled (see SetMintPermission method)600		///601		/// # Arguments602		///603		/// * collection_id: ID of the collection.604		///605		/// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].606		///607		/// * owner: Address, initial owner of the NFT.608		#[weight = T::CommonWeightInfo::create_multiple_items(&items_data)]609		#[transactional]610		pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {611			ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);612			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);613			let budget = budget::Value::new(NESTING_BUDGET);614615			dispatch_tx::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data, &budget))616		}617618		#[weight = T::CommonWeightInfo::set_collection_properties(properties.len() as u32)]619		#[transactional]620		pub fn set_collection_properties(621			origin,622			collection_id: CollectionId,623			properties: Vec<Property>624		) -> DispatchResultWithPostInfo {625			ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);626627			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);628629			dispatch_tx::<T, _>(collection_id, |d| d.set_collection_properties(sender, properties))630		}631632		#[weight = T::CommonWeightInfo::delete_collection_properties(property_keys.len() as u32)]633		#[transactional]634		pub fn delete_collection_properties(635			origin,636			collection_id: CollectionId,637			property_keys: Vec<PropertyKey>,638		) -> DispatchResultWithPostInfo {639			ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);640641			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);642643			dispatch_tx::<T, _>(collection_id, |d| d.delete_collection_properties(&sender, property_keys))644		}645646		#[weight = T::CommonWeightInfo::set_token_properties(properties.len() as u32)]647		#[transactional]648		pub fn set_token_properties(649			origin,650			collection_id: CollectionId,651			token_id: TokenId,652			properties: Vec<Property>653		) -> DispatchResultWithPostInfo {654			ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);655656			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);657			let budget = budget::Value::new(NESTING_BUDGET);658659			dispatch_tx::<T, _>(collection_id, |d| d.set_token_properties(sender, token_id, properties, &budget))660		}661662		#[weight = T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32)]663		#[transactional]664		pub fn delete_token_properties(665			origin,666			collection_id: CollectionId,667			token_id: TokenId,668			property_keys: Vec<PropertyKey>669		) -> DispatchResultWithPostInfo {670			ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);671672			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);673			let budget = budget::Value::new(NESTING_BUDGET);674675			dispatch_tx::<T, _>(collection_id, |d| d.delete_token_properties(sender, token_id, property_keys, &budget))676		}677678		#[weight = T::CommonWeightInfo::set_token_property_permissions(property_permissions.len() as u32)]679		#[transactional]680		pub fn set_token_property_permissions(681			origin,682			collection_id: CollectionId,683			property_permissions: Vec<PropertyKeyPermission>,684		) -> DispatchResultWithPostInfo {685			ensure!(!property_permissions.is_empty(), Error::<T>::EmptyArgument);686687			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);688689			dispatch_tx::<T, _>(collection_id, |d| d.set_token_property_permissions(&sender, property_permissions))690		}691692		#[weight = T::CommonWeightInfo::create_multiple_items_ex(&data)]693		#[transactional]694		pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {695			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);696			let budget = budget::Value::new(NESTING_BUDGET);697698			dispatch_tx::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data, &budget))699		}700701		/// Set transfers_enabled value for particular collection702		///703		/// # Permissions704		///705		/// * Collection Owner.706		///707		/// # Arguments708		///709		/// * collection_id: ID of the collection.710		///711		/// * value: New flag value.712		#[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]713		#[transactional]714		pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {715			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);716			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;717			target_collection.check_is_internal()?;718			target_collection.check_is_owner(&sender)?;719720			// =========721722			target_collection.limits.transfers_enabled = Some(value);723			target_collection.save()724		}725726		/// Destroys a concrete instance of NFT.727		///728		/// # Permissions729		///730		/// * Collection Owner.731		/// * Collection Admin.732		/// * Current NFT Owner.733		///734		/// # Arguments735		///736		/// * collection_id: ID of the collection.737		///738		/// * item_id: ID of NFT to burn.739		#[weight = T::CommonWeightInfo::burn_item()]740		#[transactional]741		pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {742			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);743744			let post_info = dispatch_tx::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;745			if value == 1 {746				<NftTransferBasket<T>>::remove(collection_id, item_id);747				<NftApproveBasket<T>>::remove(collection_id, item_id);748			}749			// Those maps should be cleared only if token disappears completly, need to move this part of logic to pallets?750			// <FungibleApproveBasket<T>>::remove(collection_id, sender.as_sub());751			// <RefungibleApproveBasket<T>>::remove((collection_id, item_id, sender.as_sub()));752			Ok(post_info)753		}754755		/// Destroys a concrete instance of NFT on behalf of the owner756		/// See also: [`approve`]757		///758		/// # Permissions759		///760		/// * Collection Owner.761		/// * Collection Admin.762		/// * Current NFT Owner.763		///764		/// # Arguments765		///766		/// * collection_id: ID of the collection.767		///768		/// * item_id: ID of NFT to burn.769		///770		/// * from: owner of item771		#[weight = T::CommonWeightInfo::burn_from()]772		#[transactional]773		pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {774			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);775			let budget = budget::Value::new(NESTING_BUDGET);776777			dispatch_tx::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value, &budget))778		}779780		/// Change ownership of the token.781		///782		/// # Permissions783		///784		/// * Collection Owner785		/// * Collection Admin786		/// * Current NFT owner787		///788		/// # Arguments789		///790		/// * recipient: Address of token recipient.791		///792		/// * collection_id.793		///794		/// * item_id: ID of the item795		///     * Non-Fungible Mode: Required.796		///     * Fungible Mode: Ignored.797		///     * Re-Fungible Mode: Required.798		///799		/// * value: Amount to transfer.800		///     * Non-Fungible Mode: Ignored801		///     * Fungible Mode: Must specify transferred amount802		///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)803		#[weight = T::CommonWeightInfo::transfer()]804		#[transactional]805		pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {806			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);807			let budget = budget::Value::new(NESTING_BUDGET);808809			dispatch_tx::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value, &budget))810		}811812		/// Set, change, or remove approved address to transfer the ownership of the NFT.813		///814		/// # Permissions815		///816		/// * Collection Owner817		/// * Collection Admin818		/// * Current NFT owner819		///820		/// # Arguments821		///822		/// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).823		///824		/// * collection_id.825		///826		/// * item_id: ID of the item.827		#[weight = T::CommonWeightInfo::approve()]828		#[transactional]829		pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {830			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);831832			dispatch_tx::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))833		}834835		/// 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.836		///837		/// # Permissions838		/// * Collection Owner839		/// * Collection Admin840		/// * Current NFT owner841		/// * Address approved by current NFT owner842		///843		/// # Arguments844		///845		/// * from: Address that owns token.846		///847		/// * recipient: Address of token recipient.848		///849		/// * collection_id.850		///851		/// * item_id: ID of the item.852		///853		/// * value: Amount to transfer.854		#[weight = T::CommonWeightInfo::transfer_from()]855		#[transactional]856		pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {857			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);858			let budget = budget::Value::new(NESTING_BUDGET);859860			dispatch_tx::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value, &budget))861		}862863		#[weight = <SelfWeightOf<T>>::set_collection_limits()]864		#[transactional]865		pub fn set_collection_limits(866			origin,867			collection_id: CollectionId,868			new_limit: CollectionLimits,869		) -> DispatchResult {870			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);871			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;872			target_collection.check_is_internal()?;873			target_collection.check_is_owner_or_admin(&sender)?;874			let old_limit = &target_collection.limits;875876			target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;877878			<Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(879				collection_id880			));881882			target_collection.save()883		}884885		#[weight = <SelfWeightOf<T>>::set_collection_limits()]886		#[transactional]887		pub fn set_collection_permissions(888			origin,889			collection_id: CollectionId,890			new_limit: CollectionPermissions,891		) -> DispatchResult {892			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);893			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;894			target_collection.check_is_internal()?;895			target_collection.check_is_owner_or_admin(&sender)?;896			let old_limit = &target_collection.permissions;897898			target_collection.permissions = <PalletCommon<T>>::clamp_permissions(target_collection.mode.clone(), &old_limit, new_limit)?;899900			<Pallet<T>>::deposit_event(Event::<T>::CollectionPermissionSet(901				collection_id902			));903904			target_collection.save()905		}906907		#[weight = T::RefungibleExtensionsWeightInfo::repartition()]908		#[transactional]909		pub fn repartition(910			origin,911			collection_id: CollectionId,912			token: TokenId,913			amount: u128,914		) -> DispatchResultWithPostInfo {915			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);916			dispatch_tx::<T, _>(collection_id, |d| {917				if let Some(refungible_extensions) = d.refungible_extensions() {918					refungible_extensions.repartition(&sender, token, amount)919				} else {920					fail!(<Error<T>>::RepartitionCalledOnNonRefungibleCollection)921				}922			})923		}924	}925}
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>'),