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
before · pallets/scheduler/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// Original license:18// This file is part of Substrate.1920// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd.21// SPDX-License-Identifier: Apache-2.02223// Licensed under the Apache License, Version 2.0 (the "License");24// you may not use this file except in compliance with the License.25// You may obtain a copy of the License at26//27// 	http://www.apache.org/licenses/LICENSE-2.028//29// Unless required by applicable law or agreed to in writing, software30// distributed under the License is distributed on an "AS IS" BASIS,31// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.32// See the License for the specific language governing permissions and33// limitations under the License.3435//! # Schedulerdo_reschedule36//!37//! This Pallet exposes capabilities for scheduling dispatches to occur at a38//! specified block number or at a specified period. These scheduled dispatches39//! may be named or anonymous and may be canceled.40//!41//! **NOTE:** The scheduled calls will be dispatched with the default filter42//! for the origin: namely `frame_system::Config::BaseCallFilter` for all origin43//! except root which will get no filter. And not the filter contained in origin44//! use to call `fn schedule`.45//!46//! If a call is scheduled using proxy or whatever mecanism which adds filter,47//! then those filter will not be used when dispatching the schedule call.48//!49//! ## Interface50//!51//! ### Dispatchable Functions52//!53//! * `schedule` - schedule a dispatch, which may be periodic, to occur at a specified block and54//!   with a specified priority.55//! * `cancel` - cancel a scheduled dispatch, specified by block number and index.56//! * `schedule_named` - augments the `schedule` interface with an additional `Vec<u8>` parameter57//!   that can be used for identification.58//! * `cancel_named` - the named complement to the cancel function.5960// Ensure we're `no_std` when compiling for Wasm.61#![cfg_attr(not(feature = "std"), no_std)]6263#[cfg(feature = "runtime-benchmarks")]64mod benchmarking;6566pub mod weights;6768use sp_core::H160;69use codec::{Codec, Decode, Encode};70use frame_system::{self as system, ensure_signed};71pub use pallet::*;72use scale_info::TypeInfo;73use sp_runtime::{74	traits::{BadOrigin, One, Saturating, Zero},75	RuntimeDebug, DispatchErrorWithPostInfo,76};77use sp_std::{borrow::Borrow, cmp::Ordering, marker::PhantomData, prelude::*};7879use frame_support::{80	dispatch::{DispatchError, DispatchResult, Dispatchable, Parameter},81	traits::{82		schedule::{self, DispatchTime, MaybeHashed},83		NamedReservableCurrency, EnsureOrigin, Get, IsType, OriginTrait, PrivilegeCmp,84		StorageVersion,85	},86	weights::{GetDispatchInfo, Weight},87};8889pub use weights::WeightInfo;9091/// Just a simple index for naming period tasks.92pub type PeriodicIndex = u32;93/// The location of a scheduled task that can be used to remove it.94pub type TaskAddress<BlockNumber> = (BlockNumber, u32);95pub const MAX_TASK_ID_LENGTH_IN_BYTES: u8 = 16;9697type ScheduledId = [u8; MAX_TASK_ID_LENGTH_IN_BYTES as usize];98pub type CallOrHashOf<T> = MaybeHashed<<T as Config>::Call, <T as frame_system::Config>::Hash>;99100/// Information regarding an item to be executed in the future.101#[cfg_attr(any(feature = "std", test), derive(PartialEq, Eq))]102#[derive(Clone, RuntimeDebug, Encode, Decode, TypeInfo)]103pub struct ScheduledV3<Call, BlockNumber, PalletsOrigin, AccountId> {104	/// The unique identity for this task, if there is one.105	maybe_id: Option<ScheduledId>,106	/// This task's priority.107	priority: schedule::Priority,108	/// The call to be dispatched.109	call: Call,110	/// If the call is periodic, then this points to the information concerning that.111	maybe_periodic: Option<schedule::Period<BlockNumber>>,112	/// The origin to dispatch the call.113	origin: PalletsOrigin,114	_phantom: PhantomData<AccountId>,115}116117pub type ScheduledV3Of<T> = ScheduledV3<118	CallOrHashOf<T>,119	<T as frame_system::Config>::BlockNumber,120	<T as Config>::PalletsOrigin,121	<T as frame_system::Config>::AccountId,122>;123124pub type ScheduledOf<T> = ScheduledV3Of<T>;125126/// The current version of Scheduled struct.127pub type Scheduled<Call, BlockNumber, PalletsOrigin, AccountId> =128	ScheduledV3<Call, BlockNumber, PalletsOrigin, AccountId>;129130#[cfg(feature = "runtime-benchmarks")]131mod preimage_provider {132	use frame_support::traits::PreimageRecipient;133	pub trait PreimageProviderAndMaybeRecipient<H>: PreimageRecipient<H> {}134	impl<H, T: PreimageRecipient<H>> PreimageProviderAndMaybeRecipient<H> for T {}135}136137#[cfg(not(feature = "runtime-benchmarks"))]138mod preimage_provider {139	use frame_support::traits::PreimageProvider;140	pub trait PreimageProviderAndMaybeRecipient<H>: PreimageProvider<H> {}141	impl<H, T: PreimageProvider<H>> PreimageProviderAndMaybeRecipient<H> for T {}142}143144pub use preimage_provider::PreimageProviderAndMaybeRecipient;145146pub(crate) trait MarginalWeightInfo: WeightInfo {147	fn item(periodic: bool, named: bool, resolved: Option<bool>) -> Weight {148		match (periodic, named, resolved) {149			(_, false, None) => Self::on_initialize_aborted(2) - Self::on_initialize_aborted(1),150			(_, true, None) => {151				Self::on_initialize_named_aborted(2) - Self::on_initialize_named_aborted(1)152			}153			(false, false, Some(false)) => Self::on_initialize(2) - Self::on_initialize(1),154			(false, true, Some(false)) => {155				Self::on_initialize_named(2) - Self::on_initialize_named(1)156			}157			(true, false, Some(false)) => {158				Self::on_initialize_periodic(2) - Self::on_initialize_periodic(1)159			}160			(true, true, Some(false)) => {161				Self::on_initialize_periodic_named_resolved(2)162					- Self::on_initialize_periodic_named_resolved(1)163			}164			(false, false, Some(true)) => Self::on_initialize(2) - Self::on_initialize(1),165			(false, true, Some(true)) => {166				Self::on_initialize_named_resolved(2) - Self::on_initialize_named_resolved(1)167			}168			(true, false, Some(true)) => {169				Self::on_initialize_periodic_resolved(2) - Self::on_initialize_periodic_resolved(1)170			}171			(true, true, Some(true)) => {172				Self::on_initialize_periodic_named_resolved(2)173					- Self::on_initialize_periodic_named_resolved(1)174			}175		}176	}177}178impl<T: WeightInfo> MarginalWeightInfo for T {}179180#[frame_support::pallet]181pub mod pallet {182	use super::*;183	use frame_support::{184		dispatch::PostDispatchInfo,185		pallet_prelude::*,186		traits::{schedule::LookupError, PreimageProvider},187	};188	use frame_system::pallet_prelude::*;189190	/// The current storage version.191	const STORAGE_VERSION: StorageVersion = StorageVersion::new(3);192193	#[pallet::pallet]194	#[pallet::generate_store(pub(super) trait Store)]195	#[pallet::storage_version(STORAGE_VERSION)]196	#[pallet::without_storage_info]197	pub struct Pallet<T>(_);198199	/// `system::Config` should always be included in our implied traits.200	#[pallet::config]201	pub trait Config: frame_system::Config {202		/// The overarching event type.203		type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;204205		/// The aggregated origin which the dispatch will take.206		type Origin: OriginTrait<PalletsOrigin = Self::PalletsOrigin>207			+ From<Self::PalletsOrigin>208			+ IsType<<Self as system::Config>::Origin>;209210		/// The caller origin, overarching type of all pallets origins.211		type PalletsOrigin: From<system::RawOrigin<Self::AccountId>> + Codec + Clone + Eq + TypeInfo;212213		type Currency: NamedReservableCurrency<Self::AccountId, ReserveIdentifier = ScheduledId>;214215		/// The aggregated call type.216		type Call: Parameter217			+ Dispatchable<Origin = <Self as Config>::Origin, PostInfo = PostDispatchInfo>218			+ GetDispatchInfo219			+ From<system::Call<Self>>;220221		/// The maximum weight that may be scheduled per block for any dispatchables of less222		/// priority than `schedule::HARD_DEADLINE`.223		#[pallet::constant]224		type MaximumWeight: Get<Weight>;225226		/// Required origin to schedule or cancel calls.227		type ScheduleOrigin: EnsureOrigin<<Self as system::Config>::Origin>;228229		/// Compare the privileges of origins.230		///231		/// This will be used when canceling a task, to ensure that the origin that tries232		/// to cancel has greater or equal privileges as the origin that created the scheduled task.233		///234		/// For simplicity the [`EqualPrivilegeOnly`](frame_support::traits::EqualPrivilegeOnly) can235		/// be used. This will only check if two given origins are equal.236		type OriginPrivilegeCmp: PrivilegeCmp<Self::PalletsOrigin>;237238		/// The maximum number of scheduled calls in the queue for a single block.239		/// Not strictly enforced, but used for weight estimation.240		#[pallet::constant]241		type MaxScheduledPerBlock: Get<u32>;242243		/// Weight information for extrinsics in this pallet.244		type WeightInfo: WeightInfo;245246		/// The preimage provider with which we look up call hashes to get the call.247		type PreimageProvider: PreimageProviderAndMaybeRecipient<Self::Hash>;248249		/// If `Some` then the number of blocks to postpone execution for when the item is delayed.250		type NoPreimagePostponement: Get<Option<Self::BlockNumber>>;251252		/// Sponsoring function.253		// type SponsorshipHandler: SponsorshipHandler<Self::AccountId, <Self as Config>::Call>;254255		/// The helper type used for custom transaction fee logic.256		type CallExecutor: DispatchCall<Self, H160>;257	}258259	/// A Scheduler-Runtime interface for finer payment handling.260	pub trait DispatchCall<T: frame_system::Config + Config, SelfContainedSignedInfo> {261		fn reserve_balance(262			id: ScheduledId,263			sponsor: <T as frame_system::Config>::AccountId,264			call: <T as Config>::Call,265			count: u32,266		) -> Result<(), DispatchError>;267268		fn pay_for_call(269			id: ScheduledId,270			sponsor: <T as frame_system::Config>::AccountId,271			call: <T as Config>::Call,272		) -> Result<u128, DispatchError>;273274		/// Resolve the call dispatch, including any post-dispatch operations.275		fn dispatch_call(276			signer: T::AccountId,277			function: <T as Config>::Call,278		) -> Result<279			Result<PostDispatchInfo, DispatchErrorWithPostInfo<PostDispatchInfo>>,280			TransactionValidityError,281		>;282283		fn cancel_reserve(284			id: ScheduledId,285			sponsor: <T as frame_system::Config>::AccountId,286		) -> Result<u128, DispatchError>;287	}288289	/// Items to be executed, indexed by the block number that they should be executed on.290	#[pallet::storage]291	pub type Agenda<T: Config> =292		StorageMap<_, Twox64Concat, T::BlockNumber, Vec<Option<ScheduledV3Of<T>>>, ValueQuery>;293294	/// Lookup from identity to the block number and index of the task.295	#[pallet::storage]296	pub(crate) type Lookup<T: Config> =297		StorageMap<_, Twox64Concat, ScheduledId, TaskAddress<T::BlockNumber>>;298299	/// Events type.300	#[pallet::event]301	#[pallet::generate_deposit(pub(super) fn deposit_event)]302	pub enum Event<T: Config> {303		/// Scheduled some task.304		Scheduled { when: T::BlockNumber, index: u32 },305		/// Canceled some task.306		Canceled { when: T::BlockNumber, index: u32 },307		/// Dispatched some task.308		Dispatched {309			task: TaskAddress<T::BlockNumber>,310			id: Option<ScheduledId>,311			result: DispatchResult,312		},313		/// The call for the provided hash was not found so the task has been aborted.314		CallLookupFailed {315			task: TaskAddress<T::BlockNumber>,316			id: Option<ScheduledId>,317			error: LookupError,318		},319	}320321	#[pallet::error]322	pub enum Error<T> {323		/// Failed to schedule a call324		FailedToSchedule,325		/// Cannot find the scheduled call.326		NotFound,327		/// Given target block number is in the past.328		TargetBlockNumberInPast,329		/// Reschedule failed because it does not change scheduled time.330		RescheduleNoChange,331	}332333	#[pallet::hooks]334	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {335		/// Execute the scheduled calls336		fn on_initialize(now: T::BlockNumber) -> Weight {337			let limit = T::MaximumWeight::get();338339			let mut queued = Agenda::<T>::take(now)340				.into_iter()341				.enumerate()342				.filter_map(|(index, s)| Some((index as u32, s?)))343				.collect::<Vec<_>>();344345			if queued.len() as u32 > T::MaxScheduledPerBlock::get() {346				log::warn!(347					target: "runtime::scheduler",348					"Warning: This block has more items queued in Scheduler than \349					expected from the runtime configuration. An update might be needed."350				);351			}352353			queued.sort_by_key(|(_, s)| s.priority);354355			let next = now + One::one();356357			let mut total_weight: Weight = T::WeightInfo::on_initialize(0);358			for (order, (index, mut s)) in queued.into_iter().enumerate() {359				let named = if let Some(ref id) = s.maybe_id {360					Lookup::<T>::remove(id);361					true362				} else {363					false364				};365366				let (call, maybe_completed) = s.call.resolved::<T::PreimageProvider>();367				s.call = call;368369				let resolved = if let Some(completed) = maybe_completed {370					T::PreimageProvider::unrequest_preimage(&completed);371					true372				} else {373					false374				};375				let call = match s.call.as_value().cloned() {376					Some(c) => c,377					None => {378						// Preimage not available - postpone until some block.379						total_weight.saturating_accrue(T::WeightInfo::item(false, named, None));380						if let Some(delay) = T::NoPreimagePostponement::get() {381							let until = now.saturating_add(delay);382							if let Some(ref id) = s.maybe_id {383								let index = Agenda::<T>::decode_len(until).unwrap_or(0);384								Lookup::<T>::insert(id, (until, index as u32));385							}386							Agenda::<T>::append(until, Some(s));387						}388						continue;389					}390				};391392				let periodic = s.maybe_periodic.is_some();393				let call_weight = call.get_dispatch_info().weight;394				let mut item_weight = T::WeightInfo::item(periodic, named, Some(resolved));395				let origin =396					<<T as Config>::Origin as From<T::PalletsOrigin>>::from(s.origin.clone())397						.into();398				if ensure_signed(origin).is_ok() {399					// Weights of Signed dispatches expect their signing account to be whitelisted.400					item_weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));401				}402403				// We allow a scheduled call if any is true:404				// - It's priority is `HARD_DEADLINE`405				// - It does not push the weight past the limit.406				// - It is the first item in the schedule407				let hard_deadline = s.priority <= schedule::HARD_DEADLINE;408				let test_weight = total_weight409					.saturating_add(call_weight)410					.saturating_add(item_weight);411				if !hard_deadline && order > 0 && test_weight > limit {412					// Cannot be scheduled this block - postpone until next.413					total_weight.saturating_accrue(T::WeightInfo::item(false, named, None));414					if let Some(ref id) = s.maybe_id {415						// NOTE: We could reasonably not do this (in which case there would be one416						// block where the named and delayed item could not be referenced by name),417						// but we will do it anyway since it should be mostly free in terms of418						// weight and it is slightly cleaner.419						let index = Agenda::<T>::decode_len(next).unwrap_or(0);420						Lookup::<T>::insert(id, (next, index as u32));421					}422					Agenda::<T>::append(next, Some(s));423					continue;424				}425426				let sender = ensure_signed(427					<<T as Config>::Origin as From<T::PalletsOrigin>>::from(s.origin.clone())428						.into(),429				)430				.unwrap();431432				// // if call have id it was be reserved433				// if s.maybe_id.is_some() {434				// 	let _ = T::CallExecutor::pay_for_call(435				// 		s.maybe_id.unwrap(),436				// 		sender.clone(),437				// 		call.clone(),438				// 	);439				// }440441				let r = T::CallExecutor::dispatch_call(sender, call.clone());442443				let mut actual_call_weight: Weight = item_weight;444				let result: Result<_, DispatchError> = match r {445					Ok(o) => match o {446						Ok(di) => {447							actual_call_weight = di.actual_weight.unwrap_or(item_weight);448							Ok(())449						}450						Err(err) => Err(err.error),451					},452					Err(_) => {453						log::error!(454							target: "runtime::scheduler",455							"Warning: Scheduler has failed to execute a post-dispatch transaction. \456							This block might have become invalid.");457						Err(DispatchError::CannotLookup)458					} // todo possibly force a skip/return here, do something with the error459				};460461				total_weight.saturating_accrue(item_weight);462				total_weight.saturating_accrue(actual_call_weight);463464				Self::deposit_event(Event::Dispatched {465					task: (now, index),466					id: s.maybe_id.clone(),467					result,468				});469470				if let &Some((period, count)) = &s.maybe_periodic {471					if count > 1 {472						s.maybe_periodic = Some((period, count - 1));473					} else {474						s.maybe_periodic = None;475					}476					let wake = now + period;477					// If scheduled is named, place its information in `Lookup`478					if let Some(ref id) = s.maybe_id {479						let wake_index = Agenda::<T>::decode_len(wake).unwrap_or(0);480						Lookup::<T>::insert(id, (wake, wake_index as u32));481					}482					Agenda::<T>::append(wake, Some(s));483				}484			}485			0486			//total_weight487		}488	}489490	#[pallet::call]491	impl<T: Config> Pallet<T> {492		/// Schedule a named task.493		#[pallet::weight(<T as Config>::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get()))]494		pub fn schedule_named(495			origin: OriginFor<T>,496			id: ScheduledId,497			when: T::BlockNumber,498			maybe_periodic: Option<schedule::Period<T::BlockNumber>>,499			priority: schedule::Priority,500			call: Box<CallOrHashOf<T>>,501		) -> DispatchResult {502			T::ScheduleOrigin::ensure_origin(origin.clone())?;503			let origin = <T as Config>::Origin::from(origin);504			Self::do_schedule_named(505				id,506				DispatchTime::At(when),507				maybe_periodic,508				priority,509				origin.caller().clone(),510				*call,511			)?;512			Ok(())513		}514515		/// Cancel a named scheduled task.516		#[pallet::weight(<T as Config>::WeightInfo::cancel_named(T::MaxScheduledPerBlock::get()))]517		pub fn cancel_named(origin: OriginFor<T>, id: ScheduledId) -> DispatchResult {518			T::ScheduleOrigin::ensure_origin(origin.clone())?;519			let origin = <T as Config>::Origin::from(origin);520			Self::do_cancel_named(Some(origin.caller().clone()), id)?;521			Ok(())522		}523524		/// Schedule a named task after a delay.525		///526		/// # <weight>527		/// Same as [`schedule_named`](Self::schedule_named).528		/// # </weight>529		#[pallet::weight(<T as Config>::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get()))]530		pub fn schedule_named_after(531			origin: OriginFor<T>,532			id: ScheduledId,533			after: T::BlockNumber,534			maybe_periodic: Option<schedule::Period<T::BlockNumber>>,535			priority: schedule::Priority,536			call: Box<CallOrHashOf<T>>,537		) -> DispatchResult {538			T::ScheduleOrigin::ensure_origin(origin.clone())?;539			let origin = <T as Config>::Origin::from(origin);540			Self::do_schedule_named(541				id,542				DispatchTime::After(after),543				maybe_periodic,544				priority,545				origin.caller().clone(),546				*call,547			)?;548			Ok(())549		}550	}551}552553impl<T: Config> Pallet<T> {554	#[cfg(feature = "try-runtime")]555	pub fn pre_migrate_to_v3() -> Result<(), &'static str> {556		Ok(())557	}558559	#[cfg(feature = "try-runtime")]560	pub fn post_migrate_to_v3() -> Result<(), &'static str> {561		use frame_support::dispatch::GetStorageVersion;562563		assert!(Self::current_storage_version() == 3);564		for k in Agenda::<T>::iter_keys() {565			let _ = Agenda::<T>::try_get(k).map_err(|()| "Invalid item in Agenda")?;566		}567		Ok(())568	}569570	/// Helper to migrate scheduler when the pallet origin type has changed.571	pub fn migrate_origin<OldOrigin: Into<T::PalletsOrigin> + codec::Decode>() {572		Agenda::<T>::translate::<573			Vec<Option<Scheduled<CallOrHashOf<T>, T::BlockNumber, OldOrigin, T::AccountId>>>,574			_,575		>(|_, agenda| {576			Some(577				agenda578					.into_iter()579					.map(|schedule| {580						schedule.map(|schedule| Scheduled {581							maybe_id: schedule.maybe_id,582							priority: schedule.priority,583							call: schedule.call,584							maybe_periodic: schedule.maybe_periodic,585							origin: schedule.origin.into(),586							_phantom: Default::default(),587						})588					})589					.collect::<Vec<_>>(),590			)591		});592	}593594	fn resolve_time(when: DispatchTime<T::BlockNumber>) -> Result<T::BlockNumber, DispatchError> {595		let now = frame_system::Pallet::<T>::block_number();596597		let when = match when {598			DispatchTime::At(x) => x,599			// The current block has already completed it's scheduled tasks, so600			// Schedule the task at lest one block after this current block.601			DispatchTime::After(x) => now.saturating_add(x).saturating_add(One::one()),602		};603604		if when <= now {605			return Err(Error::<T>::TargetBlockNumberInPast.into());606		}607608		Ok(when)609	}610611	fn do_schedule_named(612		id: ScheduledId,613		when: DispatchTime<T::BlockNumber>,614		maybe_periodic: Option<schedule::Period<T::BlockNumber>>,615		priority: schedule::Priority,616		origin: T::PalletsOrigin,617		call: CallOrHashOf<T>,618	) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {619		// ensure id it is unique620		if Lookup::<T>::contains_key(&id) {621			return Err(Error::<T>::FailedToSchedule)?;622		}623624		let when = Self::resolve_time(when)?;625626		call.ensure_requested::<T::PreimageProvider>();627628		// sanitize maybe_periodic629		let maybe_periodic = maybe_periodic630			.filter(|p| p.1 > 1 && !p.0.is_zero())631			// Remove one from the number of repetitions since we will schedule one now.632			.map(|(p, c)| (p, c - 1));633634		let s = Scheduled {635			maybe_id: Some(id.clone()),636			priority,637			call: call.clone(),638			maybe_periodic,639			origin: origin.clone(),640			_phantom: Default::default(),641		};642643		// reserve balance for periodic execution644		// let sender =645		// 	ensure_signed(<<T as Config>::Origin as From<T::PalletsOrigin>>::from(origin).into())?;646		// let repeats = match maybe_periodic {647		// 	Some(p) => p.1,648		// 	None => 1,649		// };650		// let _ = T::CallExecutor::reserve_balance(651		// 	id.clone(),652		// 	sender,653		// 	call.as_value().unwrap().clone(),654		// 	repeats,655		// );656657		Agenda::<T>::append(when, Some(s));658		let index = Agenda::<T>::decode_len(when).unwrap_or(1) as u32 - 1;659		let address = (when, index);660		Lookup::<T>::insert(&id, &address);661		Self::deposit_event(Event::Scheduled { when, index });662663		Ok(address)664	}665666	fn do_cancel_named(origin: Option<T::PalletsOrigin>, id: ScheduledId) -> DispatchResult {667		Lookup::<T>::try_mutate_exists(id, |lookup| -> DispatchResult {668			if let Some((when, index)) = lookup.take() {669				let i = index as usize;670				Agenda::<T>::try_mutate(when, |agenda| -> DispatchResult {671					if let Some(s) = agenda.get_mut(i) {672						if let (Some(ref o), Some(ref s)) = (origin.clone(), s.borrow()) {673							if matches!(674								T::OriginPrivilegeCmp::cmp_privilege(o, &s.origin),675								Some(Ordering::Less) | None676							) {677								return Err(BadOrigin.into());678							}679							// release balance reserve680							// let sender = ensure_signed(681							// 	<<T as Config>::Origin as From<T::PalletsOrigin>>::from(682							// 		origin.unwrap(),683							// 	)684							// 	.into(),685							// )?;686							// let _ = T::CallExecutor::cancel_reserve(id, sender);687688							s.call.ensure_unrequested::<T::PreimageProvider>();689						}690						*s = None;691					}692					Ok(())693				})?;694695				Self::deposit_event(Event::Canceled { when, index });696				Ok(())697			} else {698				Err(Error::<T>::NotFound)?699			}700		})701	}702}
after · pallets/scheduler/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// Original license:18// This file is part of Substrate.1920// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd.21// SPDX-License-Identifier: Apache-2.02223// Licensed under the Apache License, Version 2.0 (the "License");24// you may not use this file except in compliance with the License.25// You may obtain a copy of the License at26//27// 	http://www.apache.org/licenses/LICENSE-2.028//29// Unless required by applicable law or agreed to in writing, software30// distributed under the License is distributed on an "AS IS" BASIS,31// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.32// See the License for the specific language governing permissions and33// limitations under the License.3435//! # Schedulerdo_reschedule36//!37//! This Pallet exposes capabilities for scheduling dispatches to occur at a38//! specified block number or at a specified period. These scheduled dispatches39//! may be named or anonymous and may be canceled.40//!41//! **NOTE:** The scheduled calls will be dispatched with the default filter42//! for the origin: namely `frame_system::Config::BaseCallFilter` for all origin43//! except root which will get no filter. And not the filter contained in origin44//! use to call `fn schedule`.45//!46//! If a call is scheduled using proxy or whatever mecanism which adds filter,47//! then those filter will not be used when dispatching the schedule call.48//!49//! ## Interface50//!51//! ### Dispatchable Functions52//!53//! * `schedule` - schedule a dispatch, which may be periodic, to occur at a specified block and54//!   with a specified priority.55//! * `cancel` - cancel a scheduled dispatch, specified by block number and index.56//! * `schedule_named` - augments the `schedule` interface with an additional `Vec<u8>` parameter57//!   that can be used for identification.58//! * `cancel_named` - the named complement to the cancel function.5960// Ensure we're `no_std` when compiling for Wasm.61#![cfg_attr(not(feature = "std"), no_std)]6263#[cfg(feature = "runtime-benchmarks")]64mod benchmarking;6566pub mod weights;6768use sp_core::H160;69use codec::{Codec, Decode, Encode};70use frame_system::{self as system, ensure_signed};71pub use pallet::*;72use scale_info::TypeInfo;73use sp_runtime::{74	traits::{BadOrigin, One, Saturating, Zero},75	RuntimeDebug, DispatchErrorWithPostInfo,76};77use sp_std::{borrow::Borrow, cmp::Ordering, marker::PhantomData, prelude::*};7879use frame_support::{80	dispatch::{DispatchError, DispatchResult, Dispatchable, Parameter},81	traits::{82		schedule::{self, DispatchTime, MaybeHashed},83		NamedReservableCurrency, EnsureOrigin, Get, IsType, OriginTrait, PrivilegeCmp,84		StorageVersion,85	},86	weights::{GetDispatchInfo, Weight},87};8889pub use weights::WeightInfo;9091/// Just a simple index for naming period tasks.92pub type PeriodicIndex = u32;93/// The location of a scheduled task that can be used to remove it.94pub type TaskAddress<BlockNumber> = (BlockNumber, u32);95pub const MAX_TASK_ID_LENGTH_IN_BYTES: u8 = 16;9697type ScheduledId = [u8; MAX_TASK_ID_LENGTH_IN_BYTES as usize];98pub type CallOrHashOf<T> = MaybeHashed<<T as Config>::Call, <T as frame_system::Config>::Hash>;99100/// Information regarding an item to be executed in the future.101#[cfg_attr(any(feature = "std", test), derive(PartialEq, Eq))]102#[derive(Clone, RuntimeDebug, Encode, Decode, TypeInfo)]103pub struct ScheduledV3<Call, BlockNumber, PalletsOrigin, AccountId> {104	/// The unique identity for this task, if there is one.105	maybe_id: Option<ScheduledId>,106	/// This task's priority.107	priority: schedule::Priority,108	/// The call to be dispatched.109	call: Call,110	/// If the call is periodic, then this points to the information concerning that.111	maybe_periodic: Option<schedule::Period<BlockNumber>>,112	/// The origin to dispatch the call.113	origin: PalletsOrigin,114	_phantom: PhantomData<AccountId>,115}116117pub type ScheduledV3Of<T> = ScheduledV3<118	CallOrHashOf<T>,119	<T as frame_system::Config>::BlockNumber,120	<T as Config>::PalletsOrigin,121	<T as frame_system::Config>::AccountId,122>;123124pub type ScheduledOf<T> = ScheduledV3Of<T>;125126/// The current version of Scheduled struct.127pub type Scheduled<Call, BlockNumber, PalletsOrigin, AccountId> =128	ScheduledV3<Call, BlockNumber, PalletsOrigin, AccountId>;129130#[cfg(feature = "runtime-benchmarks")]131mod preimage_provider {132	use frame_support::traits::PreimageRecipient;133	pub trait PreimageProviderAndMaybeRecipient<H>: PreimageRecipient<H> {}134	impl<H, T: PreimageRecipient<H>> PreimageProviderAndMaybeRecipient<H> for T {}135}136137#[cfg(not(feature = "runtime-benchmarks"))]138mod preimage_provider {139	use frame_support::traits::PreimageProvider;140	pub trait PreimageProviderAndMaybeRecipient<H>: PreimageProvider<H> {}141	impl<H, T: PreimageProvider<H>> PreimageProviderAndMaybeRecipient<H> for T {}142}143144pub use preimage_provider::PreimageProviderAndMaybeRecipient;145146pub(crate) trait MarginalWeightInfo: WeightInfo {147	fn item(periodic: bool, named: bool, resolved: Option<bool>) -> Weight {148		match (periodic, named, resolved) {149			(_, false, None) => Self::on_initialize_aborted(2) - Self::on_initialize_aborted(1),150			(_, true, None) => {151				Self::on_initialize_named_aborted(2) - Self::on_initialize_named_aborted(1)152			}153			(false, false, Some(false)) => Self::on_initialize(2) - Self::on_initialize(1),154			(false, true, Some(false)) => {155				Self::on_initialize_named(2) - Self::on_initialize_named(1)156			}157			(true, false, Some(false)) => {158				Self::on_initialize_periodic(2) - Self::on_initialize_periodic(1)159			}160			(true, true, Some(false)) => {161				Self::on_initialize_periodic_named_resolved(2)162					- Self::on_initialize_periodic_named_resolved(1)163			}164			(false, false, Some(true)) => Self::on_initialize(2) - Self::on_initialize(1),165			(false, true, Some(true)) => {166				Self::on_initialize_named_resolved(2) - Self::on_initialize_named_resolved(1)167			}168			(true, false, Some(true)) => {169				Self::on_initialize_periodic_resolved(2) - Self::on_initialize_periodic_resolved(1)170			}171			(true, true, Some(true)) => {172				Self::on_initialize_periodic_named_resolved(2)173					- Self::on_initialize_periodic_named_resolved(1)174			}175		}176	}177}178impl<T: WeightInfo> MarginalWeightInfo for T {}179180#[frame_support::pallet]181pub mod pallet {182	use super::*;183	use frame_support::{184		dispatch::PostDispatchInfo,185		pallet_prelude::*,186		traits::{schedule::LookupError, PreimageProvider},187	};188	use frame_system::pallet_prelude::*;189190	/// The current storage version.191	const STORAGE_VERSION: StorageVersion = StorageVersion::new(3);192193	#[pallet::pallet]194	#[pallet::generate_store(pub(super) trait Store)]195	#[pallet::storage_version(STORAGE_VERSION)]196	#[pallet::without_storage_info]197	pub struct Pallet<T>(_);198199	/// `system::Config` should always be included in our implied traits.200	#[pallet::config]201	pub trait Config: frame_system::Config {202		/// The overarching event type.203		type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;204205		/// The aggregated origin which the dispatch will take.206		type Origin: OriginTrait<PalletsOrigin = Self::PalletsOrigin>207			+ From<Self::PalletsOrigin>208			+ IsType<<Self as system::Config>::Origin>;209210		/// The caller origin, overarching type of all pallets origins.211		type PalletsOrigin: From<system::RawOrigin<Self::AccountId>> + Codec + Clone + Eq + TypeInfo;212213		type Currency: NamedReservableCurrency<Self::AccountId, ReserveIdentifier = ScheduledId>;214215		/// The aggregated call type.216		type Call: Parameter217			+ Dispatchable<Origin = <Self as Config>::Origin, PostInfo = PostDispatchInfo>218			+ GetDispatchInfo219			+ From<system::Call<Self>>;220221		/// The maximum weight that may be scheduled per block for any dispatchables of less222		/// priority than `schedule::HARD_DEADLINE`.223		#[pallet::constant]224		type MaximumWeight: Get<Weight>;225226		/// Required origin to schedule or cancel calls.227		type ScheduleOrigin: EnsureOrigin<<Self as system::Config>::Origin>;228229		/// Compare the privileges of origins.230		///231		/// This will be used when canceling a task, to ensure that the origin that tries232		/// to cancel has greater or equal privileges as the origin that created the scheduled task.233		///234		/// For simplicity the [`EqualPrivilegeOnly`](frame_support::traits::EqualPrivilegeOnly) can235		/// be used. This will only check if two given origins are equal.236		type OriginPrivilegeCmp: PrivilegeCmp<Self::PalletsOrigin>;237238		/// The maximum number of scheduled calls in the queue for a single block.239		/// Not strictly enforced, but used for weight estimation.240		#[pallet::constant]241		type MaxScheduledPerBlock: Get<u32>;242243		/// Weight information for extrinsics in this pallet.244		type WeightInfo: WeightInfo;245246		/// The preimage provider with which we look up call hashes to get the call.247		type PreimageProvider: PreimageProviderAndMaybeRecipient<Self::Hash>;248249		/// If `Some` then the number of blocks to postpone execution for when the item is delayed.250		type NoPreimagePostponement: Get<Option<Self::BlockNumber>>;251252		/// Sponsoring function.253		// type SponsorshipHandler: SponsorshipHandler<Self::AccountId, <Self as Config>::Call>;254255		/// The helper type used for custom transaction fee logic.256		type CallExecutor: DispatchCall<Self, H160>;257	}258259	/// A Scheduler-Runtime interface for finer payment handling.260	pub trait DispatchCall<T: frame_system::Config + Config, SelfContainedSignedInfo> {261		/// Reserve the maximum spendings on a call.262		fn reserve_balance(263			id: ScheduledId,264			sponsor: <T as frame_system::Config>::AccountId,265			call: <T as Config>::Call,266			count: u32,267		) -> Result<(), DispatchError>;268269		/// Pay for call dispatch (un-reserve) from the reserved funds, returning the change.270		fn pay_for_call(271			id: ScheduledId,272			sponsor: <T as frame_system::Config>::AccountId,273			call: <T as Config>::Call,274		) -> Result<u128, DispatchError>;275276		/// Resolve the call dispatch, including any post-dispatch operations.277		fn dispatch_call(278			signer: T::AccountId,279			function: <T as Config>::Call,280		) -> Result<281			Result<PostDispatchInfo, DispatchErrorWithPostInfo<PostDispatchInfo>>,282			TransactionValidityError,283		>;284285		/// Release reserved funds.286		fn cancel_reserve(287			id: ScheduledId,288			sponsor: <T as frame_system::Config>::AccountId,289		) -> Result<u128, DispatchError>;290	}291292	/// Items to be executed, indexed by the block number that they should be executed on.293	#[pallet::storage]294	pub type Agenda<T: Config> =295		StorageMap<_, Twox64Concat, T::BlockNumber, Vec<Option<ScheduledV3Of<T>>>, ValueQuery>;296297	/// Lookup from identity to the block number and index of the task.298	#[pallet::storage]299	pub(crate) type Lookup<T: Config> =300		StorageMap<_, Twox64Concat, ScheduledId, TaskAddress<T::BlockNumber>>;301302	/// Events type.303	#[pallet::event]304	#[pallet::generate_deposit(pub(super) fn deposit_event)]305	pub enum Event<T: Config> {306		/// Scheduled some task.307		Scheduled { when: T::BlockNumber, index: u32 },308		/// Canceled some task.309		Canceled { when: T::BlockNumber, index: u32 },310		/// Dispatched some task.311		Dispatched {312			task: TaskAddress<T::BlockNumber>,313			id: Option<ScheduledId>,314			result: DispatchResult,315		},316		/// The call for the provided hash was not found so the task has been aborted.317		CallLookupFailed {318			task: TaskAddress<T::BlockNumber>,319			id: Option<ScheduledId>,320			error: LookupError,321		},322	}323324	#[pallet::error]325	pub enum Error<T> {326		/// Failed to schedule a call327		FailedToSchedule,328		/// Cannot find the scheduled call.329		NotFound,330		/// Given target block number is in the past.331		TargetBlockNumberInPast,332		/// Reschedule failed because it does not change scheduled time.333		RescheduleNoChange,334	}335336	#[pallet::hooks]337	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {338		/// Execute the scheduled calls339		fn on_initialize(now: T::BlockNumber) -> Weight {340			let limit = T::MaximumWeight::get();341342			let mut queued = Agenda::<T>::take(now)343				.into_iter()344				.enumerate()345				.filter_map(|(index, s)| Some((index as u32, s?)))346				.collect::<Vec<_>>();347348			if queued.len() as u32 > T::MaxScheduledPerBlock::get() {349				log::warn!(350					target: "runtime::scheduler",351					"Warning: This block has more items queued in Scheduler than \352					expected from the runtime configuration. An update might be needed."353				);354			}355356			queued.sort_by_key(|(_, s)| s.priority);357358			let next = now + One::one();359360			let mut total_weight: Weight = T::WeightInfo::on_initialize(0);361			for (order, (index, mut s)) in queued.into_iter().enumerate() {362				let named = if let Some(ref id) = s.maybe_id {363					Lookup::<T>::remove(id);364					true365				} else {366					false367				};368369				let (call, maybe_completed) = s.call.resolved::<T::PreimageProvider>();370				s.call = call;371372				let resolved = if let Some(completed) = maybe_completed {373					T::PreimageProvider::unrequest_preimage(&completed);374					true375				} else {376					false377				};378				let call = match s.call.as_value().cloned() {379					Some(c) => c,380					None => {381						// Preimage not available - postpone until some block.382						total_weight.saturating_accrue(T::WeightInfo::item(false, named, None));383						if let Some(delay) = T::NoPreimagePostponement::get() {384							let until = now.saturating_add(delay);385							if let Some(ref id) = s.maybe_id {386								let index = Agenda::<T>::decode_len(until).unwrap_or(0);387								Lookup::<T>::insert(id, (until, index as u32));388							}389							Agenda::<T>::append(until, Some(s));390						}391						continue;392					}393				};394395				let periodic = s.maybe_periodic.is_some();396				let call_weight = call.get_dispatch_info().weight;397				let mut item_weight = T::WeightInfo::item(periodic, named, Some(resolved));398				let origin =399					<<T as Config>::Origin as From<T::PalletsOrigin>>::from(s.origin.clone())400						.into();401				if ensure_signed(origin).is_ok() {402					// Weights of Signed dispatches expect their signing account to be whitelisted.403					item_weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));404				}405406				// We allow a scheduled call if any is true:407				// - It's priority is `HARD_DEADLINE`408				// - It does not push the weight past the limit.409				// - It is the first item in the schedule410				let hard_deadline = s.priority <= schedule::HARD_DEADLINE;411				let test_weight = total_weight412					.saturating_add(call_weight)413					.saturating_add(item_weight);414				if !hard_deadline && order > 0 && test_weight > limit {415					// Cannot be scheduled this block - postpone until next.416					total_weight.saturating_accrue(T::WeightInfo::item(false, named, None));417					if let Some(ref id) = s.maybe_id {418						// NOTE: We could reasonably not do this (in which case there would be one419						// block where the named and delayed item could not be referenced by name),420						// but we will do it anyway since it should be mostly free in terms of421						// weight and it is slightly cleaner.422						let index = Agenda::<T>::decode_len(next).unwrap_or(0);423						Lookup::<T>::insert(id, (next, index as u32));424					}425					Agenda::<T>::append(next, Some(s));426					continue;427				}428429				let sender = ensure_signed(430					<<T as Config>::Origin as From<T::PalletsOrigin>>::from(s.origin.clone())431						.into(),432				)433				.unwrap();434435				// // if call have id it was be reserved436				// if s.maybe_id.is_some() {437				// 	let _ = T::CallExecutor::pay_for_call(438				// 		s.maybe_id.unwrap(),439				// 		sender.clone(),440				// 		call.clone(),441				// 	);442				// }443444				let r = T::CallExecutor::dispatch_call(sender, call.clone());445446				let mut actual_call_weight: Weight = item_weight;447				let result: Result<_, DispatchError> = match r {448					Ok(o) => match o {449						Ok(di) => {450							actual_call_weight = di.actual_weight.unwrap_or(item_weight);451							Ok(())452						}453						Err(err) => Err(err.error),454					},455					Err(_) => {456						log::error!(457							target: "runtime::scheduler",458							"Warning: Scheduler has failed to execute a post-dispatch transaction. \459							This block might have become invalid.");460						Err(DispatchError::CannotLookup)461					} // todo possibly force a skip/return here, do something with the error462				};463464				total_weight.saturating_accrue(item_weight);465				total_weight.saturating_accrue(actual_call_weight);466467				Self::deposit_event(Event::Dispatched {468					task: (now, index),469					id: s.maybe_id.clone(),470					result,471				});472473				if let &Some((period, count)) = &s.maybe_periodic {474					if count > 1 {475						s.maybe_periodic = Some((period, count - 1));476					} else {477						s.maybe_periodic = None;478					}479					let wake = now + period;480					// If scheduled is named, place its information in `Lookup`481					if let Some(ref id) = s.maybe_id {482						let wake_index = Agenda::<T>::decode_len(wake).unwrap_or(0);483						Lookup::<T>::insert(id, (wake, wake_index as u32));484					}485					Agenda::<T>::append(wake, Some(s));486				}487			}488			0489			//total_weight490		}491	}492493	#[pallet::call]494	impl<T: Config> Pallet<T> {495		/// Schedule a named task.496		#[pallet::weight(<T as Config>::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get()))]497		pub fn schedule_named(498			origin: OriginFor<T>,499			id: ScheduledId,500			when: T::BlockNumber,501			maybe_periodic: Option<schedule::Period<T::BlockNumber>>,502			priority: schedule::Priority,503			call: Box<CallOrHashOf<T>>,504		) -> DispatchResult {505			T::ScheduleOrigin::ensure_origin(origin.clone())?;506			let origin = <T as Config>::Origin::from(origin);507			Self::do_schedule_named(508				id,509				DispatchTime::At(when),510				maybe_periodic,511				priority,512				origin.caller().clone(),513				*call,514			)?;515			Ok(())516		}517518		/// Cancel a named scheduled task.519		#[pallet::weight(<T as Config>::WeightInfo::cancel_named(T::MaxScheduledPerBlock::get()))]520		pub fn cancel_named(origin: OriginFor<T>, id: ScheduledId) -> DispatchResult {521			T::ScheduleOrigin::ensure_origin(origin.clone())?;522			let origin = <T as Config>::Origin::from(origin);523			Self::do_cancel_named(Some(origin.caller().clone()), id)?;524			Ok(())525		}526527		/// Schedule a named task after a delay.528		///529		/// # <weight>530		/// Same as [`schedule_named`](Self::schedule_named).531		/// # </weight>532		#[pallet::weight(<T as Config>::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get()))]533		pub fn schedule_named_after(534			origin: OriginFor<T>,535			id: ScheduledId,536			after: T::BlockNumber,537			maybe_periodic: Option<schedule::Period<T::BlockNumber>>,538			priority: schedule::Priority,539			call: Box<CallOrHashOf<T>>,540		) -> DispatchResult {541			T::ScheduleOrigin::ensure_origin(origin.clone())?;542			let origin = <T as Config>::Origin::from(origin);543			Self::do_schedule_named(544				id,545				DispatchTime::After(after),546				maybe_periodic,547				priority,548				origin.caller().clone(),549				*call,550			)?;551			Ok(())552		}553	}554}555556impl<T: Config> Pallet<T> {557	#[cfg(feature = "try-runtime")]558	pub fn pre_migrate_to_v3() -> Result<(), &'static str> {559		Ok(())560	}561562	#[cfg(feature = "try-runtime")]563	pub fn post_migrate_to_v3() -> Result<(), &'static str> {564		use frame_support::dispatch::GetStorageVersion;565566		assert!(Self::current_storage_version() == 3);567		for k in Agenda::<T>::iter_keys() {568			let _ = Agenda::<T>::try_get(k).map_err(|()| "Invalid item in Agenda")?;569		}570		Ok(())571	}572573	/// Helper to migrate scheduler when the pallet origin type has changed.574	pub fn migrate_origin<OldOrigin: Into<T::PalletsOrigin> + codec::Decode>() {575		Agenda::<T>::translate::<576			Vec<Option<Scheduled<CallOrHashOf<T>, T::BlockNumber, OldOrigin, T::AccountId>>>,577			_,578		>(|_, agenda| {579			Some(580				agenda581					.into_iter()582					.map(|schedule| {583						schedule.map(|schedule| Scheduled {584							maybe_id: schedule.maybe_id,585							priority: schedule.priority,586							call: schedule.call,587							maybe_periodic: schedule.maybe_periodic,588							origin: schedule.origin.into(),589							_phantom: Default::default(),590						})591					})592					.collect::<Vec<_>>(),593			)594		});595	}596597	fn resolve_time(when: DispatchTime<T::BlockNumber>) -> Result<T::BlockNumber, DispatchError> {598		let now = frame_system::Pallet::<T>::block_number();599600		let when = match when {601			DispatchTime::At(x) => x,602			// The current block has already completed it's scheduled tasks, so603			// Schedule the task at lest one block after this current block.604			DispatchTime::After(x) => now.saturating_add(x).saturating_add(One::one()),605		};606607		if when <= now {608			return Err(Error::<T>::TargetBlockNumberInPast.into());609		}610611		Ok(when)612	}613614	fn do_schedule_named(615		id: ScheduledId,616		when: DispatchTime<T::BlockNumber>,617		maybe_periodic: Option<schedule::Period<T::BlockNumber>>,618		priority: schedule::Priority,619		origin: T::PalletsOrigin,620		call: CallOrHashOf<T>,621	) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {622		// ensure id it is unique623		if Lookup::<T>::contains_key(&id) {624			return Err(Error::<T>::FailedToSchedule)?;625		}626627		let when = Self::resolve_time(when)?;628629		call.ensure_requested::<T::PreimageProvider>();630631		// sanitize maybe_periodic632		let maybe_periodic = maybe_periodic633			.filter(|p| p.1 > 1 && !p.0.is_zero())634			// Remove one from the number of repetitions since we will schedule one now.635			.map(|(p, c)| (p, c - 1));636637		let s = Scheduled {638			maybe_id: Some(id.clone()),639			priority,640			call: call.clone(),641			maybe_periodic,642			origin: origin.clone(),643			_phantom: Default::default(),644		};645646		// reserve balance for periodic execution647		// let sender =648		// 	ensure_signed(<<T as Config>::Origin as From<T::PalletsOrigin>>::from(origin).into())?;649		// let repeats = match maybe_periodic {650		// 	Some(p) => p.1,651		// 	None => 1,652		// };653		// let _ = T::CallExecutor::reserve_balance(654		// 	id.clone(),655		// 	sender,656		// 	call.as_value().unwrap().clone(),657		// 	repeats,658		// );659660		Agenda::<T>::append(when, Some(s));661		let index = Agenda::<T>::decode_len(when).unwrap_or(1) as u32 - 1;662		let address = (when, index);663		Lookup::<T>::insert(&id, &address);664		Self::deposit_event(Event::Scheduled { when, index });665666		Ok(address)667	}668669	fn do_cancel_named(origin: Option<T::PalletsOrigin>, id: ScheduledId) -> DispatchResult {670		Lookup::<T>::try_mutate_exists(id, |lookup| -> DispatchResult {671			if let Some((when, index)) = lookup.take() {672				let i = index as usize;673				Agenda::<T>::try_mutate(when, |agenda| -> DispatchResult {674					if let Some(s) = agenda.get_mut(i) {675						if let (Some(ref o), Some(ref s)) = (origin.clone(), s.borrow()) {676							if matches!(677								T::OriginPrivilegeCmp::cmp_privilege(o, &s.origin),678								Some(Ordering::Less) | None679							) {680								return Err(BadOrigin.into());681							}682							// release balance reserve683							// let sender = ensure_signed(684							// 	<<T as Config>::Origin as From<T::PalletsOrigin>>::from(685							// 		origin.unwrap(),686							// 	)687							// 	.into(),688							// )?;689							// let _ = T::CallExecutor::cancel_reserve(id, sender);690691							s.call.ensure_unrequested::<T::PreimageProvider>();692						}693						*s = None;694					}695					Ok(())696				})?;697698				Self::deposit_event(Event::Canceled { when, index });699				Ok(())700			} else {701				Err(Error::<T>::NotFound)?702			}703		})704	}705}
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>'),