git.delta.rocks / unique-network / refs/commits / d23689511691

difftreelog

Merge pull request #579 from UniqueNetwork/doc/app-promotion

Yaroslav Bolyukin2022-09-13parents: #848ef93 #11a57bd.patch.diff
in: master
added `doc` to app promotion

16 files changed

modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5733,7 +5733,7 @@
 
 [[package]]
 name = "pallet-evm-contract-helpers"
-version = "0.2.0"
+version = "0.3.0"
 dependencies = [
  "ethereum",
  "evm-coder",
@@ -12316,7 +12316,7 @@
 
 [[package]]
 name = "unique-rpc"
-version = "0.1.1"
+version = "0.1.2"
 dependencies = [
  "app-promotion-rpc",
  "fc-db",
modifiednode/cli/CHANGELOG.mddiffbeforeafterboth
--- a/node/cli/CHANGELOG.md
+++ b/node/cli/CHANGELOG.md
@@ -1,4 +1,10 @@
 <!-- bureaucrate goes here -->
+
+## [v0.9.27] 2022-09-08
+
+### Added
+-  Support RPC for `AppPromotion` pallet. 
+
 ## [v0.9.27] 2022-08-16
 
 ### Other changes
modifiednode/rpc/CHANGELOG.mddiffbeforeafterboth
--- a/node/rpc/CHANGELOG.md
+++ b/node/rpc/CHANGELOG.md
@@ -1,4 +1,9 @@
 <!-- bureaucrate goes here -->
+## [v0.1.2] 2022-09-08
+
+### Added
+-  Support RPC for `AppPromotion` pallet. 
+
 ## [v0.1.1] 2022-08-16
 
 ### Other changes
modifiednode/rpc/Cargo.tomldiffbeforeafterboth
--- a/node/rpc/Cargo.toml
+++ b/node/rpc/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "unique-rpc"
-version = "0.1.1"
+version = "0.1.2"
 authors = ['Unique Network <support@uniquenetwork.io>']
 license = 'GPLv3'
 edition = "2021"
modifiedpallets/app-promotion/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/app-promotion/src/benchmarking.rs
+++ b/pallets/app-promotion/src/benchmarking.rs
@@ -112,6 +112,7 @@
 		let share = Perbill::from_rational(1u32, 20);
 		let _ = <T as Config>::Currency::make_free_balance_be(&caller,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());
 		(0..10).map(|_| {
+			// used to change block number
 			<frame_system::Pallet<T>>::finalize();
 			PromototionPallet::<T>::stake(RawOrigin::Signed(caller.clone()).into(), share * <T as Config>::Currency::total_balance(&caller))
 		}).collect::<Result<Vec<_>, _>>()?;
modifiedpallets/app-promotion/src/lib.rsdiffbeforeafterboth
--- a/pallets/app-promotion/src/lib.rs
+++ b/pallets/app-promotion/src/lib.rs
@@ -14,13 +14,34 @@
 // You should have received a copy of the GNU General Public License
 // along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
 
-//! # App promotion
+//! # App Promotion pallet
+//!
+//! The pallet implements the mechanics of staking and sponsoring collections/contracts.
 //!
-//! The app promotion pallet is designed to ... .
+//! - [`Config`]
+//! - [`Pallet`]
+//! - [`Error`]
+//! - [`Event`]
+//!
+//! ## Overview
+//! The App Promotion pallet allows fund holders to stake at a certain daily rate of return.
+//! The mechanics implemented in the pallet allow it to act as a sponsor for collections / contracts,
+//! the list of which is set by the pallet administrator.
+//!  
 //!
 //! ## Interface
+//!	The pallet provides interfaces for funds, collection/contract operations (see [types] module).
+
 //!
 //! ### Dispatchable Functions
+//!	- [`set_admin_address`][`Pallet::set_admin_address`] - sets an address as the the admin.
+//! - [`stake`][`Pallet::stake`] - stakes the amount of native tokens.
+//! - [`unstake`][`Pallet::unstake`] - unstakes all stakes.
+//! - [`sponsor_collection`][`Pallet::sponsor_collection`] - sets the pallet to be the sponsor for the collection.
+//! - [`stop_sponsoring_collection`][`Pallet::stop_sponsoring_collection`] - removes the pallet as the sponsor for the collection.
+//! - [`sponsor_contract`][`Pallet::sponsor_contract`] - sets the pallet to be the sponsor for the contract.
+//! - [`stop_sponsoring_contract`][`Pallet::stop_sponsoring_contract`] - removes the pallet as the sponsor for the contract.
+//! - [`payout_stakers`][`Pallet::payout_stakers`] - recalculates interest for the specified number of stakers.
 //!
 
 // #![recursion_limit = "1024"]
@@ -95,10 +116,10 @@
 		/// Type for interacting with conrtacts
 		type ContractHandler: ContractHandler<AccountId = Self::CrossAccountId, ContractId = H160>;
 
-		/// ID for treasury
+		/// `AccountId` for treasury
 		type TreasuryAccountId: Get<Self::AccountId>;
 
-		/// The app's pallet id, used for deriving its sovereign account ID.
+		/// The app's pallet id, used for deriving its sovereign account address.
 		#[pallet::constant]
 		type PalletId: Get<PalletId>;
 
@@ -138,7 +159,7 @@
 		/// Staking recalculation was performed
 		///
 		/// # Arguments
-		/// * AccountId: ID of the staker.
+		/// * AccountId: account of the staker.
 		/// * Balance : recalculation base
 		/// * Balance : total income
 		StakingRecalculation(
@@ -153,21 +174,21 @@
 		/// Staking was performed
 		///
 		/// # Arguments
-		/// * AccountId: ID of the staker
+		/// * AccountId: account of the staker
 		/// * Balance : staking amount
 		Stake(T::AccountId, BalanceOf<T>),
 
 		/// Unstaking was performed
 		///
 		/// # Arguments
-		/// * AccountId: ID of the staker
+		/// * AccountId: account of the staker
 		/// * Balance : unstaking amount
 		Unstake(T::AccountId, BalanceOf<T>),
 
 		/// The admin was set
 		///
 		/// # Arguments
-		/// * AccountId: ID of the admin
+		/// * AccountId: account address of the admin
 		SetAdmin(T::AccountId),
 	}
 
@@ -187,13 +208,20 @@
 		IncorrectLockedBalanceOperation,
 	}
 
+	/// Stores the total staked amount.
 	#[pallet::storage]
 	pub type TotalStaked<T: Config> = StorageValue<Value = BalanceOf<T>, QueryKind = ValueQuery>;
 
+	/// Stores the `admin` account. Some extrinsics can only be executed if they were signed by `admin`.
 	#[pallet::storage]
 	pub type Admin<T: Config> = StorageValue<Value = T::AccountId, QueryKind = OptionQuery>;
 
-	/// Amount of tokens staked by account in the blocknumber.
+	/// Stores the amount of tokens staked by account in the blocknumber.
+	///
+	/// * **Key1** - Staker account.
+	/// * **Key2** - Relay block number when the stake was made.
+	/// * **(Balance, BlockNumber)** - Balance of the stake.
+	/// The number of the relay block in which we must perform the interest recalculation
 	#[pallet::storage]
 	pub type Staked<T: Config> = StorageNMap<
 		Key = (
@@ -203,11 +231,19 @@
 		Value = (BalanceOf<T>, T::BlockNumber),
 		QueryKind = ValueQuery,
 	>;
-	/// Amount of stakes for an Account
+
+	/// Stores amount of stakes for an `Account`.
+	///
+	/// * **Key** - Staker account.
+	/// * **Value** - Amount of stakes.
 	#[pallet::storage]
 	pub type StakesPerAccount<T: Config> =
 		StorageMap<_, Blake2_128Concat, T::AccountId, u8, ValueQuery>;
 
+	/// Stores amount of stakes for an `Account`.
+	///
+	/// * **Key** - Staker account.
+	/// * **Value** - Amount of stakes.
 	#[pallet::storage]
 	pub type PendingUnstake<T: Config> = StorageMap<
 		_,
@@ -252,6 +288,15 @@
 		T::BlockNumber: From<u32> + Into<u32>,
 		<<T as Config>::Currency as Currency<T::AccountId>>::Balance: Sum + From<u128>,
 	{
+		/// Sets an address as the the admin.
+		///
+		/// # Permissions
+		///
+		/// * Sudo
+		///
+		/// # Arguments
+		///
+		/// * `admin`: account of the new admin.
 		#[pallet::weight(T::WeightInfo::set_admin_address())]
 		pub fn set_admin_address(origin: OriginFor<T>, admin: T::CrossAccountId) -> DispatchResult {
 			ensure_root(origin)?;
@@ -263,6 +308,13 @@
 			Ok(())
 		}
 
+		/// Stakes the amount of native tokens.
+		/// Sets `amount` to the locked state.
+		/// The maximum number of stakes for a staker is 10.
+		///
+		/// # Arguments
+		///
+		/// * `amount`: in native tokens.
 		#[pallet::weight(T::WeightInfo::stake())]
 		pub fn stake(staker: OriginFor<T>, amount: BalanceOf<T>) -> DispatchResult {
 			let staker_id = ensure_signed(staker)?;
@@ -280,6 +332,7 @@
 			let balance =
 				<<T as Config>::Currency as Currency<T::AccountId>>::free_balance(&staker_id);
 
+			// checks that we can lock `amount` on the `staker` account.
 			<<T as Config>::Currency as Currency<T::AccountId>>::ensure_can_withdraw(
 				&staker_id,
 				amount,
@@ -293,6 +346,8 @@
 
 			let block_number = T::RelayBlockNumberProvider::current_block_number();
 
+			// Calculation of the number of recalculation periods,
+			// after how much the first interest calculation should be performed for the stake
 			let recalculate_after_interval: T::BlockNumber =
 				if block_number % T::RecalculationInterval::get() == 0u32.into() {
 					1u32.into()
@@ -300,6 +355,8 @@
 					2u32.into()
 				};
 
+			// Сalculation of the number of the relay block
+			// in which it is necessary to accrue remuneration for the stake.
 			let recalc_block = (block_number / T::RecalculationInterval::get()
 				+ recalculate_after_interval)
 				* T::RecalculationInterval::get();
@@ -327,12 +384,20 @@
 			Ok(())
 		}
 
+		/// Unstakes all stakes.
+		/// Moves the sum of all stakes to the `reserved` state.
+		/// After the end of `PendingInterval` this sum becomes completely
+		/// free for further use.
 		#[pallet::weight(T::WeightInfo::unstake())]
 		pub fn unstake(staker: OriginFor<T>) -> DispatchResultWithPostInfo {
 			let staker_id = ensure_signed(staker)?;
+
+			// calculate block number where the sum would be free
 			let block = <frame_system::Pallet<T>>::block_number() + T::PendingInterval::get();
+
 			let mut pendings = <PendingUnstake<T>>::get(block);
 
+			// checks that we can do unreserve stakes in the block
 			ensure!(!pendings.is_full(), Error::<T>::PendingForBlockOverflow);
 
 			let mut total_stakes = 0u64;
@@ -371,6 +436,15 @@
 			Ok(None.into())
 		}
 
+		/// Sets the pallet to be the sponsor for the collection.
+		///
+		/// # Permissions
+		///
+		/// * Pallet admin
+		///
+		/// # Arguments
+		///
+		/// * `collection_id`: ID of the collection that will be sponsored by `pallet_id`
 		#[pallet::weight(T::WeightInfo::sponsor_collection())]
 		pub fn sponsor_collection(
 			admin: OriginFor<T>,
@@ -384,6 +458,18 @@
 
 			T::CollectionHandler::set_sponsor(Self::account_id(), collection_id)
 		}
+
+		/// Removes the pallet as the sponsor for the collection.
+		/// Returns [`NoPermission`][`Error::NoPermission`]
+		/// if the pallet wasn't the sponsor.
+		///
+		/// # Permissions
+		///
+		/// * Pallet admin
+		///
+		/// # Arguments
+		///
+		/// * `collection_id`: ID of the collection that is sponsored by `pallet_id`
 		#[pallet::weight(T::WeightInfo::stop_sponsoring_collection())]
 		pub fn stop_sponsoring_collection(
 			admin: OriginFor<T>,
@@ -404,6 +490,15 @@
 			T::CollectionHandler::remove_collection_sponsor(collection_id)
 		}
 
+		/// Sets the pallet to be the sponsor for the contract.
+		///
+		/// # Permissions
+		///
+		/// * Pallet admin
+		///
+		/// # Arguments
+		///
+		/// * `contract_id`: the contract address that will be sponsored by `pallet_id`
 		#[pallet::weight(T::WeightInfo::sponsor_contract())]
 		pub fn sponsor_contract(admin: OriginFor<T>, contract_id: H160) -> DispatchResult {
 			let admin_id = ensure_signed(admin)?;
@@ -419,6 +514,17 @@
 			)
 		}
 
+		/// Removes the pallet as the sponsor for the contract.
+		/// Returns [`NoPermission`][`Error::NoPermission`]
+		/// if the pallet wasn't the sponsor.
+		///
+		/// # Permissions
+		///
+		/// * Pallet admin
+		///
+		/// # Arguments
+		///
+		/// * `contract_id`: the contract address that is sponsored by `pallet_id`
 		#[pallet::weight(T::WeightInfo::stop_sponsoring_contract())]
 		pub fn stop_sponsoring_contract(admin: OriginFor<T>, contract_id: H160) -> DispatchResult {
 			let admin_id = ensure_signed(admin)?;
@@ -437,6 +543,18 @@
 			T::ContractHandler::remove_contract_sponsor(contract_id)
 		}
 
+		/// Recalculates interest for the specified number of stakers.
+		/// If all stakers are not recalculated, the next call of the extrinsic
+		/// will continue the recalculation, from those stakers for whom this
+		/// was not perform in last call.
+		///
+		/// # Permissions
+		///
+		/// * Pallet admin
+		///
+		/// # Arguments
+		///
+		/// * `stakers_number`: the number of stakers for which recalculation will be performed
 		#[pallet::weight(T::WeightInfo::payout_stakers(stakers_number.unwrap_or(20) as u32))]
 		pub fn payout_stakers(admin: OriginFor<T>, stakers_number: Option<u8>) -> DispatchResult {
 			let admin_id = ensure_signed(admin)?;
@@ -446,63 +564,19 @@
 				Error::<T>::NoPermission
 			);
 
+			// calculate the number of the current recalculation block,
+			// this is necessary in order to understand which stakers we should calculate interest
 			let current_recalc_block =
 				Self::get_current_recalc_block(T::RelayBlockNumberProvider::current_block_number());
+
+			// calculate the number of the next recalculation block,
+			// this value is set for the stakers to whom the recalculation will be performed
 			let next_recalc_block = current_recalc_block + T::RecalculationInterval::get();
 
 			let mut storage_iterator = Self::get_next_calculated_key()
 				.map_or(Staked::<T>::iter(), |key| Staked::<T>::iter_from(key));
 
 			NextCalculatedRecord::<T>::set(None);
-
-			// {
-			// 	let mut stakers_number = stakers_number.unwrap_or(20);
-			// 	let mut last_id = admin_id;
-			// 	let mut income_acc = BalanceOf::<T>::default();
-			// 	let mut amount_acc = BalanceOf::<T>::default();
-
-			// 	while let Some((
-			// 		(current_id, staked_block),
-			// 		(amount, next_recalc_block_for_stake),
-			// 	)) = storage_iterator.next()
-			// 	{
-			// 		if last_id != current_id {
-			// 			if income_acc != BalanceOf::<T>::default() {
-			// 				<T::Currency as Currency<T::AccountId>>::transfer(
-			// 					&T::TreasuryAccountId::get(),
-			// 					&last_id,
-			// 					income_acc,
-			// 					ExistenceRequirement::KeepAlive,
-			// 				)
-			// 				.and_then(|_| Self::add_lock_balance(&last_id, income_acc))?;
-
-			// 				Self::deposit_event(Event::StakingRecalculation(
-			// 					last_id, amount, income_acc,
-			// 				));
-			// 			}
-
-			// 			if stakers_number == 0 {
-			// 				NextCalculatedRecord::<T>::set(Some((current_id, staked_block)));
-			// 				break;
-			// 			}
-			// 			stakers_number -= 1;
-			// 			income_acc = BalanceOf::<T>::default();
-			// 			last_id = current_id;
-			// 		};
-			// 		if current_recalc_block >= next_recalc_block_for_stake {
-			// 			Self::recalculate_and_insert_stake(
-			// 				&last_id,
-			// 				staked_block,
-			// 				next_recalc_block,
-			// 				amount,
-			// 				((current_recalc_block - next_recalc_block_for_stake)
-			// 					/ T::RecalculationInterval::get())
-			// 				.into() + 1,
-			// 				&mut income_acc,
-			// 			);
-			// 		}
-			// 	}
-			// }
 
 			{
 				let mut stakers_number = stakers_number.unwrap_or(20);
@@ -510,6 +584,8 @@
 				let income_acc = RefCell::new(BalanceOf::<T>::default());
 				let amount_acc = RefCell::new(BalanceOf::<T>::default());
 
+				// this closure is used to perform some of the actions if we break the loop because we reached the number of stakers for recalculation,
+				// but there were unrecalculated records in the storage.
 				let flush_stake = || -> DispatchResult {
 					if let Some(last_id) = &*last_id.borrow() {
 						if !income_acc.borrow().is_zero() {
@@ -578,10 +654,18 @@
 }
 
 impl<T: Config> Pallet<T> {
+	/// The account address of the app promotion pot.
+	///
+	/// This actually does computation. If you need to keep using it, then make sure you cache the
+	/// value and only call this once.
 	pub fn account_id() -> T::AccountId {
 		T::PalletId::get().into_account_truncating()
 	}
 
+	/// Unlocks the balance that was locked by the pallet.
+	///
+	/// - `staker`: staker account.
+	/// - `amount`: amount of unlocked funds.
 	fn unlock_balance(staker: &T::AccountId, amount: BalanceOf<T>) -> DispatchResult {
 		let locked_balance = Self::get_locked_balance(staker)
 			.map(|l| l.amount)
@@ -598,6 +682,10 @@
 		Ok(())
 	}
 
+	/// Adds the balance to locked by the pallet.
+	///
+	/// - `staker`: staker account.
+	/// - `amount`: amount of added locked funds.
 	fn add_lock_balance(staker: &T::AccountId, amount: BalanceOf<T>) -> DispatchResult {
 		Self::get_locked_balance(staker)
 			.map_or(<BalanceOf<T>>::default(), |l| l.amount)
@@ -606,6 +694,10 @@
 			.ok_or(ArithmeticError::Overflow.into())
 	}
 
+	/// Sets the new state of a balance locked by the pallet.
+	///
+	/// - `staker`: staker account.
+	/// - `amount`: amount of locked funds.
 	fn set_lock_unchecked(staker: &T::AccountId, amount: BalanceOf<T>) {
 		if amount.is_zero() {
 			<T::Currency as LockableCurrency<T::AccountId>>::remove_lock(LOCK_IDENTIFIER, &staker);
@@ -619,6 +711,9 @@
 		}
 	}
 
+	/// Returns the balance locked by the pallet for the staker.
+	///
+	/// - `staker`: staker account.
 	pub fn get_locked_balance(
 		staker: impl EncodeLike<T::AccountId>,
 	) -> Option<BalanceLock<BalanceOf<T>>> {
@@ -627,6 +722,9 @@
 			.find(|l| l.id == LOCK_IDENTIFIER)
 	}
 
+	/// Returns the total staked balance for the staker.
+	///
+	/// - `staker`: staker account.
 	pub fn total_staked_by_id(staker: impl EncodeLike<T::AccountId>) -> Option<BalanceOf<T>> {
 		let staked = Staked::<T>::iter_prefix((staker,))
 			.into_iter()
@@ -640,6 +738,10 @@
 		}
 	}
 
+	/// Returns all relay block numbers when stake was made,
+	/// the amount of the stake.
+	///
+	/// - `staker`: staker account.
 	pub fn total_staked_by_id_per_block(
 		staker: impl EncodeLike<T::AccountId>,
 	) -> Option<Vec<(T::BlockNumber, BalanceOf<T>)>> {
@@ -655,6 +757,9 @@
 		}
 	}
 
+	/// Returns the total staked balance for the staker.
+	/// If `staker` is `None`, returns the total amount staked.
+	/// - `staker`: staker account.
 	pub fn cross_id_total_staked(staker: Option<T::CrossAccountId>) -> Option<BalanceOf<T>> {
 		staker.map_or(Some(<TotalStaked<T>>::get()), |s| {
 			Self::total_staked_by_id(s.as_sub())
@@ -667,6 +772,10 @@
 	// 		.unwrap_or_default()
 	// }
 
+	/// Returns all relay block numbers when stake was made,
+	/// the amount of the stake.
+	///
+	/// - `staker`: staker account.
 	pub fn cross_id_total_staked_per_block(
 		staker: T::CrossAccountId,
 	) -> Vec<(T::BlockNumber, BalanceOf<T>)> {
@@ -703,10 +812,6 @@
 	fn get_current_recalc_block(current_relay_block: T::BlockNumber) -> T::BlockNumber {
 		(current_relay_block / T::RecalculationInterval::get()) * T::RecalculationInterval::get()
 	}
-
-	// fn get_next_recalc_block(current_relay_block: T::BlockNumber) -> T::BlockNumber {
-	// 	Self::get_current_recalc_block(current_relay_block) + T::RecalculationInterval::get()
-	// }
 
 	fn get_next_calculated_key() -> Option<Vec<u8>> {
 		Self::get_next_calculated_record().map(|key| Staked::<T>::hashed_key_for(key))
@@ -717,6 +822,11 @@
 where
 	<<T as Config>::Currency as Currency<T::AccountId>>::Balance: Sum,
 {
+	/// Returns the amount reserved by the pending.
+	/// If `staker` is `None`, returns the total pending.
+	///
+	/// -`staker`: staker account.
+	///
 	/// Since user funds are not transferred anywhere by staking, overflow protection is provided
 	/// at the level of the associated type `Balance` of `Currency` trait. In order to overflow,
 	/// the staker must have more funds on his account than the maximum set for `Balance` type.
@@ -740,6 +850,10 @@
 		)
 	}
 
+	/// Returns all parachain block numbers when unreserve is expected,
+	/// the amount of the unreserved funds.
+	///
+	/// - `staker`: staker account.
 	pub fn cross_id_pending_unstake_per_block(
 		staker: T::CrossAccountId,
 	) -> Vec<(T::BlockNumber, BalanceOf<T>)> {
modifiedpallets/app-promotion/src/types.rsdiffbeforeafterboth
--- a/pallets/app-promotion/src/types.rs
+++ b/pallets/app-promotion/src/types.rs
@@ -9,7 +9,10 @@
 use sp_std::borrow::ToOwned;
 use pallet_evm_contract_helpers::{Pallet as EvmHelpersPallet, Config as EvmHelpersConfig};
 
+/// This trait was defined because `LockableCurrency`
+/// has no way to know the state of the lock for an account.
 pub trait ExtendedLockableCurrency<AccountId: Parameter>: LockableCurrency<AccountId> {
+	/// Returns lock balance for an account. Allows to determine the cause of the lock.
 	fn locks<KArg>(who: KArg) -> WeakBoundedVec<BalanceLock<Self::Balance>, Self::MaxLocks>
 	where
 		KArg: EncodeLike<AccountId>;
@@ -25,18 +28,28 @@
 		Self::locks(who)
 	}
 }
-
+/// Trait for interacting with collections.
 pub trait CollectionHandler {
 	type CollectionId;
 	type AccountId;
 
+	/// Sets sponsor for a collection.
+	///
+	/// - `sponsor_id`: the account of the sponsor-to-be.
+	/// - `collection_id`: ID of the modified collection.
 	fn set_sponsor(
 		sponsor_id: Self::AccountId,
 		collection_id: Self::CollectionId,
 	) -> DispatchResult;
 
+	/// Removes sponsor for a collection.
+	///
+	/// - `collection_id`: ID of the modified collection.
 	fn remove_collection_sponsor(collection_id: Self::CollectionId) -> DispatchResult;
 
+	/// Retuns the current sponsor for a collection if one is set.
+	///
+	/// - `collection_id`: ID of the collection.
 	fn sponsor(collection_id: Self::CollectionId)
 		-> Result<Option<Self::AccountId>, DispatchError>;
 }
@@ -66,18 +79,28 @@
 			.map(|acc| acc.to_owned()))
 	}
 }
-
+/// Trait for interacting with contracts.
 pub trait ContractHandler {
 	type ContractId;
 	type AccountId;
 
+	/// Sets sponsor for a contract.
+	///
+	/// - `sponsor_id`: the account of the sponsor-to-be.
+	/// - `contract_address`: the address of the modified contract.
 	fn set_sponsor(
 		sponsor_id: Self::AccountId,
 		contract_address: Self::ContractId,
 	) -> DispatchResult;
 
+	/// Removes sponsor for a contract.
+	///
+	/// - `contract_address`: the address of the modified contract.
 	fn remove_contract_sponsor(contract_address: Self::ContractId) -> DispatchResult;
 
+	/// Retuns the current sponsor for a contract if one is set.
+	///
+	/// - `contract_address`: the contract address.
 	fn sponsor(
 		contract_address: Self::ContractId,
 	) -> Result<Option<Self::AccountId>, DispatchError>;
modifiedpallets/evm-contract-helpers/CHANGELOG.mddiffbeforeafterboth
--- a/pallets/evm-contract-helpers/CHANGELOG.md
+++ b/pallets/evm-contract-helpers/CHANGELOG.md
@@ -2,29 +2,35 @@
 
 All notable changes to this project will be documented in this file.
 
+## [v0.3.0] 2022-09-05
+
+### Added
+
+-   Methods `force_set_sponsor` , `force_remove_sponsor` to be able to administer sponsorships with other pallets. Added to implement `AppPromotion` pallet logic.
+
 ## [v0.2.0] - 2022-08-19
 
 ### Added
 
- - Set arbitrary evm address as contract sponsor.
- - Ability to remove current sponsor.
+-   Set arbitrary evm address as contract sponsor.
+-   Ability to remove current sponsor.
 
 ### Removed
- - Remove methods
-   + sponsoring_enabled
-   + toggle_sponsoring
 
- ### Changed
+-   Remove methods
+    -   sponsoring_enabled
+    -   toggle_sponsoring
 
- - Change `toggle_sponsoring` to `self_sponsored_enable`.
+### Changed
 
+-   Change `toggle_sponsoring` to `self_sponsored_enable`.
 
 ## [v0.1.2] 2022-08-16
 
 ### Other changes
 
-- build: Upgrade polkadot to v0.9.27 2c498572636f2b34d53b1c51b7283a761a7dc90a
+-   build: Upgrade polkadot to v0.9.27 2c498572636f2b34d53b1c51b7283a761a7dc90a
 
-- build: Upgrade polkadot to v0.9.26 85515e54c4ca1b82a2630034e55dcc804c643bf8
+-   build: Upgrade polkadot to v0.9.26 85515e54c4ca1b82a2630034e55dcc804c643bf8
 
-- build: Upgrade polkadot to v0.9.25 cdfb9bdc7b205ff1b5134f034ef9973d769e5e6b
\ No newline at end of file
+-   build: Upgrade polkadot to v0.9.25 cdfb9bdc7b205ff1b5134f034ef9973d769e5e6b
modifiedpallets/evm-contract-helpers/Cargo.tomldiffbeforeafterboth
1[package]1[package]
2name = "pallet-evm-contract-helpers"2name = "pallet-evm-contract-helpers"
3version = "0.2.0"3version = "0.3.0"
4license = "GPLv3"4license = "GPLv3"
5edition = "2021"5edition = "2021"
66
modifiedpallets/evm-contract-helpers/src/lib.rsdiffbeforeafterboth
--- a/pallets/evm-contract-helpers/src/lib.rs
+++ b/pallets/evm-contract-helpers/src/lib.rs
@@ -216,9 +216,10 @@
 			Ok(())
 		}
 
-		/// TO-DO
-		///
+		/// Force set `sponsor` for `contract`.
 		///
+		/// Differs from `set_sponsor` in that confirmation
+		/// from the sponsor is not required.
 		pub fn force_set_sponsor(
 			contract_address: H160,
 			sponsor: &T::CrossAccountId,
@@ -269,9 +270,10 @@
 			Self::force_remove_sponsor(contract_address)
 		}
 
-		/// TO-DO
-		///
+		/// Force remove `sponsor` for `contract`.
 		///
+		/// Differs from `remove_sponsor` in that
+		/// it doesn't require consent from the `owner` of the contract.
 		pub fn force_remove_sponsor(contract_address: H160) -> DispatchResult {
 			Sponsoring::<T>::remove(contract_address);
 
modifiedpallets/unique/CHANGELOG.mddiffbeforeafterboth
--- a/pallets/unique/CHANGELOG.md
+++ b/pallets/unique/CHANGELOG.md
@@ -4,7 +4,7 @@
 
 <!-- bureaucrate goes here -->
 
-## [v0.1.4] 2022-09-5
+## [v0.1.4] 2022-09-05
 
 ### Added
 
modifiedpallets/unique/src/lib.rsdiffbeforeafterboth
--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -1105,6 +1105,15 @@
 }
 
 impl<T: Config> Pallet<T> {
+	/// Force set `sponsor` for `collection`.
+	///
+	/// Differs from [`set_collection_sponsor`][`Pallet::set_collection_sponsor`] in that confirmation
+	/// from the `sponsor` is not required.
+	///
+	/// # Arguments
+	///
+	/// * `sponsor`: ID of the account of the sponsor-to-be.
+	/// * `collection_id`: ID of the modified collection.
 	pub fn force_set_sponsor(sponsor: T::AccountId, collection_id: CollectionId) -> DispatchResult {
 		let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;
 		target_collection.check_is_internal()?;
@@ -1125,6 +1134,14 @@
 		target_collection.save()
 	}
 
+	/// Force remove `sponsor` for `collection`.
+	///
+	/// Differs from `remove_sponsor` in that
+	/// it doesn't require consent from the `owner` of the collection.
+	///
+	/// # Arguments
+	///
+	/// * `collection_id`: ID of the modified collection.
 	pub fn force_remove_collection_sponsor(collection_id: CollectionId) -> DispatchResult {
 		let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;
 		target_collection.check_is_internal()?;
modifiedprimitives/common/CHANGELOG.mddiffbeforeafterboth
--- a/primitives/common/CHANGELOG.md
+++ b/primitives/common/CHANGELOG.md
@@ -1,4 +1,9 @@
 <!-- bureaucrate goes here -->
+## [v0.9.27] 2022-09-08
+
+### Added
+-  Relay block constants. In particular, it is necessary to add the `AppPromotion` pallet at runtime.
+
 ## [v0.9.25] 2022-08-16
 
 ### Other changes
modifiedruntime/opal/CHANGELOG.mddiffbeforeafterboth
--- a/runtime/opal/CHANGELOG.md
+++ b/runtime/opal/CHANGELOG.md
@@ -3,16 +3,23 @@
 All notable changes to this project will be documented in this file.
 
 <!-- bureaucrate goes here -->
+
+## [v0.9.27] 2022-09-08
+
+### Added
+
+-   `AppPromotion` pallet to runtime.
+
 ## [v0.9.27] 2022-08-16
 
 ### Bugfixes
 
-- Add missing config keys 74f532ac28dce15c15e7d576c074a58eba658c08
+-   Add missing config keys 74f532ac28dce15c15e7d576c074a58eba658c08
 
 ### Other changes
 
-- build: Upgrade polkadot to v0.9.27 2c498572636f2b34d53b1c51b7283a761a7dc90a
+-   build: Upgrade polkadot to v0.9.27 2c498572636f2b34d53b1c51b7283a761a7dc90a
 
-- build: Upgrade polkadot to v0.9.26 85515e54c4ca1b82a2630034e55dcc804c643bf8
+-   build: Upgrade polkadot to v0.9.26 85515e54c4ca1b82a2630034e55dcc804c643bf8
 
-- build: Upgrade polkadot to v0.9.25 cdfb9bdc7b205ff1b5134f034ef9973d769e5e6b
+-   build: Upgrade polkadot to v0.9.25 cdfb9bdc7b205ff1b5134f034ef9973d769e5e6b
modifiedruntime/quartz/CHANGELOG.mddiffbeforeafterboth
--- a/runtime/quartz/CHANGELOG.md
+++ b/runtime/quartz/CHANGELOG.md
@@ -3,17 +3,23 @@
 All notable changes to this project will be documented in this file.
 
 <!-- bureaucrate goes here -->
+
+## [v0.9.27] 2022-09-08
+
+### Added
+
+-   `AppPromotion` pallet to runtime.
+
 ## [v0.9.27] 2022-08-16
 
 ### Bugfixes
 
-- Add missing config keys 74f532ac28dce15c15e7d576c074a58eba658c08
+-   Add missing config keys 74f532ac28dce15c15e7d576c074a58eba658c08
 
 ### Other changes
 
-- build: Upgrade polkadot to v0.9.27 2c498572636f2b34d53b1c51b7283a761a7dc90a
+-   build: Upgrade polkadot to v0.9.27 2c498572636f2b34d53b1c51b7283a761a7dc90a
 
-- build: Upgrade polkadot to v0.9.26 85515e54c4ca1b82a2630034e55dcc804c643bf8
+-   build: Upgrade polkadot to v0.9.26 85515e54c4ca1b82a2630034e55dcc804c643bf8
 
-- build: Upgrade polkadot to v0.9.25 cdfb9bdc7b205ff1b5134f034ef9973d769e5e6b
-
+-   build: Upgrade polkadot to v0.9.25 cdfb9bdc7b205ff1b5134f034ef9973d769e5e6b
modifiedruntime/unique/CHANGELOG.mddiffbeforeafterboth
--- a/runtime/unique/CHANGELOG.md
+++ b/runtime/unique/CHANGELOG.md
@@ -3,16 +3,23 @@
 All notable changes to this project will be documented in this file.
 
 <!-- bureaucrate goes here -->
+
+## [v0.9.27] 2022-09-08
+
+### Added
+
+-   `AppPromotion` pallet to runtime.
+
 ## [v0.9.27] 2022-08-16
 
 ### Bugfixes
 
-- Add missing config keys 74f532ac28dce15c15e7d576c074a58eba658c08
+-   Add missing config keys 74f532ac28dce15c15e7d576c074a58eba658c08
 
 ### Other changes
 
-- build: Upgrade polkadot to v0.9.27 2c498572636f2b34d53b1c51b7283a761a7dc90a
+-   build: Upgrade polkadot to v0.9.27 2c498572636f2b34d53b1c51b7283a761a7dc90a
 
-- build: Upgrade polkadot to v0.9.26 85515e54c4ca1b82a2630034e55dcc804c643bf8
+-   build: Upgrade polkadot to v0.9.26 85515e54c4ca1b82a2630034e55dcc804c643bf8
 
-- build: Upgrade polkadot to v0.9.25 cdfb9bdc7b205ff1b5134f034ef9973d769e5e6b
+-   build: Upgrade polkadot to v0.9.25 cdfb9bdc7b205ff1b5134f034ef9973d769e5e6b