difftreelog
Merge pull request #579 from UniqueNetwork/doc/app-promotion
in: master
added `doc` to app promotion
16 files changed
Cargo.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",
node/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
node/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
node/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"
pallets/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<_>, _>>()?;
pallets/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>)> {
pallets/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>;
pallets/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
pallets/evm-contract-helpers/Cargo.tomldiffbeforeafterboth--- 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"
pallets/evm-contract-helpers/src/lib.rsdiffbeforeafterboth1// 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#![doc = include_str!("../README.md")]18#![cfg_attr(not(feature = "std"), no_std)]19#![warn(missing_docs)]2021use codec::{Decode, Encode, MaxEncodedLen};22pub use pallet::*;23pub use eth::*;24use scale_info::TypeInfo;25pub mod eth;2627#[frame_support::pallet]28pub mod pallet {29 pub use super::*;30 use crate::eth::ContractHelpersEvents;31 use frame_support::pallet_prelude::*;32 use pallet_evm_coder_substrate::DispatchResult;33 use sp_core::H160;34 use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};35 use up_data_structs::SponsorshipState;36 use evm_coder::ToLog;3738 #[pallet::config]39 pub trait Config:40 frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::account::Config41 {42 /// Overarching event type.43 type Event: IsType<<Self as frame_system::Config>::Event> + From<Event<Self>>;4445 /// Address, under which magic contract will be available46 type ContractAddress: Get<H160>;4748 /// In case of enabled sponsoring, but no sponsoring rate limit set,49 /// this value will be used implicitly50 type DefaultSponsoringRateLimit: Get<Self::BlockNumber>;51 }5253 #[pallet::error]54 pub enum Error<T> {55 /// This method is only executable by contract owner56 NoPermission,5758 /// No pending sponsor for contract.59 NoPendingSponsor,60 }6162 #[pallet::pallet]63 #[pallet::generate_store(pub(super) trait Store)]64 pub struct Pallet<T>(_);6566 /// Store owner for contract.67 ///68 /// * **Key** - contract address.69 /// * **Value** - owner for contract.70 #[pallet::storage]71 pub(super) type Owner<T: Config> =72 StorageMap<Hasher = Twox128, Key = H160, Value = H160, QueryKind = ValueQuery>;7374 #[pallet::storage]75 #[deprecated]76 pub(super) type SelfSponsoring<T: Config> =77 StorageMap<Hasher = Twox128, Key = H160, Value = bool, QueryKind = ValueQuery>;7879 /// Store for contract sponsorship state.80 ///81 /// * **Key** - contract address.82 /// * **Value** - sponsorship state.83 #[pallet::storage]84 pub(super) type Sponsoring<T: Config> = StorageMap<85 Hasher = Twox64Concat,86 Key = H160,87 Value = SponsorshipState<T::CrossAccountId>,88 QueryKind = ValueQuery,89 >;9091 /// Store for sponsoring mode.92 ///93 /// ### Usage94 /// Prefer to delete collection from storage if mode chaged to [`Disabled`](SponsoringModeT::Disabled).95 ///96 /// * **Key** - contract address.97 /// * **Value** - [`sponsoring mode`](SponsoringModeT).98 #[pallet::storage]99 pub(super) type SponsoringMode<T: Config> =100 StorageMap<Hasher = Twox128, Key = H160, Value = SponsoringModeT, QueryKind = OptionQuery>;101102 /// Storage for sponsoring rate limit in blocks.103 ///104 /// * **Key** - contract address.105 /// * **Value** - amount of sponsored blocks.106 #[pallet::storage]107 pub(super) type SponsoringRateLimit<T: Config> = StorageMap<108 Hasher = Twox128,109 Key = H160,110 Value = T::BlockNumber,111 QueryKind = ValueQuery,112 OnEmpty = T::DefaultSponsoringRateLimit,113 >;114115 /// Storage for last sponsored block.116 ///117 /// * **Key1** - contract address.118 /// * **Key2** - sponsored user address.119 /// * **Value** - last sponsored block number.120 #[pallet::storage]121 pub(super) type SponsorBasket<T: Config> = StorageDoubleMap<122 Hasher1 = Twox128,123 Key1 = H160,124 Hasher2 = Twox128,125 Key2 = H160,126 Value = T::BlockNumber,127 QueryKind = OptionQuery,128 >;129130 /// Storege for contracts with [`Allowlisted`](SponsoringModeT::Allowlisted) sponsoring mode.131 ///132 /// ### Usage133 /// Prefer to delete collection from storage if mode chaged to non `Allowlisted`, than set **Value** to **false**.134 ///135 /// * **Key** - contract address.136 /// * **Value** - is contract in [`Allowlisted`](SponsoringModeT::Allowlisted) mode.137 #[pallet::storage]138 pub(super) type AllowlistEnabled<T: Config> =139 StorageMap<Hasher = Twox128, Key = H160, Value = bool, QueryKind = ValueQuery>;140141 /// Storage for users that allowed for sponsorship.142 ///143 /// ### Usage144 /// Prefer to delete record from storage if user no more allowed for sponsorship.145 ///146 /// * **Key1** - contract address.147 /// * **Key2** - user that allowed for sponsorship.148 /// * **Value** - allowance for sponsorship.149 #[pallet::storage]150 pub(super) type Allowlist<T: Config> = StorageDoubleMap<151 Hasher1 = Twox128,152 Key1 = H160,153 Hasher2 = Twox128,154 Key2 = H160,155 Value = bool,156 QueryKind = ValueQuery,157 >;158159 #[pallet::event]160 #[pallet::generate_deposit(pub fn deposit_event)]161 pub enum Event<T: Config> {162 /// Contract sponsor was set.163 ContractSponsorSet(164 /// Contract address of the affected collection.165 H160,166 /// New sponsor address.167 T::AccountId,168 ),169170 /// New sponsor was confirm.171 ContractSponsorshipConfirmed(172 /// Contract address of the affected collection.173 H160,174 /// New sponsor address.175 T::AccountId,176 ),177178 /// Collection sponsor was removed.179 ContractSponsorRemoved(180 /// Contract address of the affected collection.181 H160,182 ),183 }184185 impl<T: Config> Pallet<T> {186 /// Get contract owner.187 pub fn contract_owner(contract: H160) -> H160 {188 <Owner<T>>::get(contract)189 }190191 /// Set `sponsor` for `contract`.192 ///193 /// `sender` must be owner of contract.194 pub fn set_sponsor(195 sender: &T::CrossAccountId,196 contract: H160,197 sponsor: &T::CrossAccountId,198 ) -> DispatchResult {199 Pallet::<T>::ensure_owner(contract, *sender.as_eth())?;200 Sponsoring::<T>::insert(201 contract,202 SponsorshipState::<T::CrossAccountId>::Unconfirmed(sponsor.clone()),203 );204205 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorSet(206 contract,207 sponsor.as_sub().clone(),208 ));209 <PalletEvm<T>>::deposit_log(210 ContractHelpersEvents::ContractSponsorSet {211 contract_address: contract,212 sponsor: *sponsor.as_eth(),213 }214 .to_log(contract),215 );216 Ok(())217 }218219 /// TO-DO220 ///221 ///222 pub fn force_set_sponsor(223 contract_address: H160,224 sponsor: &T::CrossAccountId,225 ) -> DispatchResult {226 Sponsoring::<T>::insert(227 contract_address,228 SponsorshipState::<T::CrossAccountId>::Confirmed(sponsor.clone()),229 );230231 let eth_sponsor = *sponsor.as_eth();232 let sub_sponsor = sponsor.as_sub().clone();233234 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorSet(235 contract_address,236 sub_sponsor.clone(),237 ));238 <PalletEvm<T>>::deposit_log(239 ContractHelpersEvents::ContractSponsorSet {240 contract_address,241 sponsor: eth_sponsor,242 }243 .to_log(contract_address),244 );245246 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorshipConfirmed(247 contract_address,248 sub_sponsor,249 ));250 <PalletEvm<T>>::deposit_log(251 ContractHelpersEvents::ContractSponsorshipConfirmed {252 contract_address,253 sponsor: eth_sponsor,254 }255 .to_log(contract_address),256 );257258 Ok(())259 }260261 /// Remove sponsor for `contract`.262 ///263 /// `sender` must be owner of contract.264 pub fn remove_sponsor(265 sender: &T::CrossAccountId,266 contract_address: H160,267 ) -> DispatchResult {268 Self::ensure_owner(contract_address, *sender.as_eth())?;269 Self::force_remove_sponsor(contract_address)270 }271272 /// TO-DO273 ///274 ///275 pub fn force_remove_sponsor(contract_address: H160) -> DispatchResult {276 Sponsoring::<T>::remove(contract_address);277278 Self::deposit_event(Event::<T>::ContractSponsorRemoved(contract_address));279 <PalletEvm<T>>::deposit_log(280 ContractHelpersEvents::ContractSponsorRemoved { contract_address }281 .to_log(contract_address),282 );283284 Ok(())285 }286287 /// Confirm sponsorship.288 ///289 /// `sender` must be same that set via [`set_sponsor`].290 pub fn confirm_sponsorship(291 sender: &T::CrossAccountId,292 contract_address: H160,293 ) -> DispatchResult {294 match Sponsoring::<T>::get(contract_address) {295 SponsorshipState::Unconfirmed(sponsor) => {296 ensure!(sponsor == *sender, Error::<T>::NoPermission);297 let eth_sponsor = *sponsor.as_eth();298 let sub_sponsor = sponsor.as_sub().clone();299 Sponsoring::<T>::insert(300 contract_address,301 SponsorshipState::<T::CrossAccountId>::Confirmed(sponsor),302 );303304 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorshipConfirmed(305 contract_address,306 sub_sponsor,307 ));308 <PalletEvm<T>>::deposit_log(309 ContractHelpersEvents::ContractSponsorshipConfirmed {310 contract_address,311 sponsor: eth_sponsor,312 }313 .to_log(contract_address),314 );315316 Ok(())317 }318 SponsorshipState::Disabled | SponsorshipState::Confirmed(_) => {319 Err(Error::<T>::NoPendingSponsor.into())320 }321 }322 }323324 /// Get sponsor.325 pub fn get_sponsor(contract: H160) -> Option<T::CrossAccountId> {326 match Sponsoring::<T>::get(contract) {327 SponsorshipState::Disabled | SponsorshipState::Unconfirmed(_) => None,328 SponsorshipState::Confirmed(sponsor) => Some(sponsor),329 }330 }331332 /// Get current sponsoring mode, performing lazy migration from legacy storage333 pub fn sponsoring_mode(contract: H160) -> SponsoringModeT {334 <SponsoringMode<T>>::get(contract)335 .or_else(|| {336 <SelfSponsoring<T>>::get(contract).then(|| SponsoringModeT::Allowlisted)337 })338 .unwrap_or_default()339 }340341 /// Reconfigure contract sponsoring mode342 pub fn set_sponsoring_mode(contract: H160, mode: SponsoringModeT) {343 if mode == SponsoringModeT::Disabled {344 <SponsoringMode<T>>::remove(contract);345 } else {346 <SponsoringMode<T>>::insert(contract, mode);347 }348 <SelfSponsoring<T>>::remove(contract)349 }350351 /// Set duration between two sponsored contract calls352 pub fn set_sponsoring_rate_limit(contract: H160, rate_limit: T::BlockNumber) {353 <SponsoringRateLimit<T>>::insert(contract, rate_limit);354 }355356 /// Is user added to allowlist, or he is owner of specified contract357 pub fn allowed(contract: H160, user: H160) -> bool {358 <Allowlist<T>>::get(&contract, &user) || <Owner<T>>::get(&contract) == user359 }360361 /// Toggle contract allowlist access362 pub fn toggle_allowlist(contract: H160, enabled: bool) {363 <AllowlistEnabled<T>>::insert(contract, enabled)364 }365366 /// Toggle user presence in contract's allowlist367 pub fn toggle_allowed(contract: H160, user: H160, allowed: bool) {368 <Allowlist<T>>::insert(contract, user, allowed);369 }370371 /// Throw error if user is not allowed to reconfigure target contract372 pub fn ensure_owner(contract: H160, user: H160) -> DispatchResult {373 ensure!(<Owner<T>>::get(&contract) == user, Error::<T>::NoPermission);374 Ok(())375 }376 }377}378379/// Available contract sponsoring modes380#[derive(Encode, Decode, PartialEq, TypeInfo, MaxEncodedLen, Default)]381pub enum SponsoringModeT {382 /// Sponsoring is disabled383 #[default]384 Disabled,385 /// Only users from allowlist will be sponsored386 Allowlisted,387 /// All users will be sponsored388 Generous,389}390391impl SponsoringModeT {392 fn from_eth(v: u8) -> Option<Self> {393 Some(match v {394 0 => Self::Disabled,395 1 => Self::Allowlisted,396 2 => Self::Generous,397 _ => return None,398 })399 }400 fn to_eth(self) -> u8 {401 match self {402 SponsoringModeT::Disabled => 0,403 SponsoringModeT::Allowlisted => 1,404 SponsoringModeT::Generous => 2,405 }406 }407}pallets/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
pallets/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()?;
primitives/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
runtime/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
runtime/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
runtime/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