--- 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", --- a/node/cli/CHANGELOG.md +++ b/node/cli/CHANGELOG.md @@ -1,4 +1,10 @@ + +## [v0.9.27] 2022-09-08 + +### Added +- Support RPC for `AppPromotion` pallet. + ## [v0.9.27] 2022-08-16 ### Other changes --- a/node/rpc/CHANGELOG.md +++ b/node/rpc/CHANGELOG.md @@ -1,4 +1,9 @@ +## [v0.1.2] 2022-09-08 + +### Added +- Support RPC for `AppPromotion` pallet. + ## [v0.1.1] 2022-08-16 ### Other changes --- 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 '] license = 'GPLv3' edition = "2021" --- 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 _ = ::Currency::make_free_balance_be(&caller, Perbill::from_rational(1u32, 2) * BalanceOf::::max_value()); (0..10).map(|_| { + // used to change block number >::finalize(); PromototionPallet::::stake(RawOrigin::Signed(caller.clone()).into(), share * ::Currency::total_balance(&caller)) }).collect::, _>>()?; --- 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 . -//! # 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; - /// ID for treasury + /// `AccountId` for treasury type TreasuryAccountId: Get; - /// 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; @@ -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), /// Unstaking was performed /// /// # Arguments - /// * AccountId: ID of the staker + /// * AccountId: account of the staker /// * Balance : unstaking amount Unstake(T::AccountId, BalanceOf), /// 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 = StorageValue, QueryKind = ValueQuery>; + /// Stores the `admin` account. Some extrinsics can only be executed if they were signed by `admin`. #[pallet::storage] pub type Admin = StorageValue; - /// 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 = StorageNMap< Key = ( @@ -203,11 +231,19 @@ Value = (BalanceOf, 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 = 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 = StorageMap< _, @@ -252,6 +288,15 @@ T::BlockNumber: From + Into, <::Currency as Currency>::Balance: Sum + From, { + /// 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, 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, amount: BalanceOf) -> DispatchResult { let staker_id = ensure_signed(staker)?; @@ -280,6 +332,7 @@ let balance = <::Currency as Currency>::free_balance(&staker_id); + // checks that we can lock `amount` on the `staker` account. <::Currency as Currency>::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) -> DispatchResultWithPostInfo { let staker_id = ensure_signed(staker)?; + + // calculate block number where the sum would be free let block = >::block_number() + T::PendingInterval::get(); + let mut pendings = >::get(block); + // checks that we can do unreserve stakes in the block ensure!(!pendings.is_full(), Error::::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, @@ -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, @@ -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, 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, 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, stakers_number: Option) -> DispatchResult { let admin_id = ensure_signed(admin)?; @@ -446,63 +564,19 @@ Error::::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::::iter(), |key| Staked::::iter_from(key)); NextCalculatedRecord::::set(None); - - // { - // let mut stakers_number = stakers_number.unwrap_or(20); - // let mut last_id = admin_id; - // let mut income_acc = BalanceOf::::default(); - // let mut amount_acc = BalanceOf::::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::::default() { - // >::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::::set(Some((current_id, staked_block))); - // break; - // } - // stakers_number -= 1; - // income_acc = BalanceOf::::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::::default()); let amount_acc = RefCell::new(BalanceOf::::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 Pallet { + /// 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) -> 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) -> DispatchResult { Self::get_locked_balance(staker) .map_or(>::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) { if amount.is_zero() { >::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, ) -> Option>> { @@ -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) -> Option> { let staked = Staked::::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, ) -> Option)>> { @@ -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) -> Option> { staker.map_or(Some(>::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)> { @@ -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> { Self::get_next_calculated_record().map(|key| Staked::::hashed_key_for(key)) @@ -717,6 +822,11 @@ where <::Currency as Currency>::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)> { --- 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: LockableCurrency { + /// Returns lock balance for an account. Allows to determine the cause of the lock. fn locks(who: KArg) -> WeakBoundedVec, Self::MaxLocks> where KArg: EncodeLike; @@ -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, 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, DispatchError>; --- 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 --- a/pallets/evm-contract-helpers/Cargo.toml +++ b/pallets/evm-contract-helpers/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-evm-contract-helpers" -version = "0.2.0" +version = "0.3.0" license = "GPLv3" edition = "2021" --- 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::::remove(contract_address); --- a/pallets/unique/CHANGELOG.md +++ b/pallets/unique/CHANGELOG.md @@ -4,7 +4,7 @@ -## [v0.1.4] 2022-09-5 +## [v0.1.4] 2022-09-05 ### Added --- a/pallets/unique/src/lib.rs +++ b/pallets/unique/src/lib.rs @@ -1105,6 +1105,15 @@ } impl Pallet { + /// 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 = >::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 = >::try_get(collection_id)?; target_collection.check_is_internal()?; --- a/primitives/common/CHANGELOG.md +++ b/primitives/common/CHANGELOG.md @@ -1,4 +1,9 @@ +## [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 --- 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. + +## [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 --- 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. + +## [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 --- 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. + +## [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