Skip to content

Commit

Permalink
Refine mining asset (paritytech#168)
Browse files Browse the repository at this point in the history
* Do not need to exclude PCX in OnAssetChanged methods

* Add referral reward split

* Add GatewayInterface in mining/asset

* Test asset mining reward

* Add some docs
  • Loading branch information
liuchengxu authored Aug 2, 2020
1 parent 7b918e6 commit a03396c
Show file tree
Hide file tree
Showing 6 changed files with 244 additions and 80 deletions.
18 changes: 13 additions & 5 deletions runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -753,14 +753,23 @@ impl xpallet_mining_staking::Trait for Runtime {
type SessionDuration = SessionDuration;
type SessionInterface = Self;
type TreasuryAccount = SimpleTreasuryAccount;
type AssetMining = ();
type AssetMining = XMiningAsset;
type DetermineRewardPotAccount =
xpallet_mining_staking::SimpleValidatorRewardPotAccountDeterminer<Runtime>;
}

pub struct DummyReferralGetter;
impl xpallet_mining_asset::GatewayInterface<AccountId> for DummyReferralGetter {
fn referral_of(_who: &AccountId) -> Option<AccountId> {
// FIXME impl this in gateway
None
}
}

impl xpallet_mining_asset::Trait for Runtime {
type Event = Event;
type StakingInterface = Self;
type GatewayInterface = DummyReferralGetter;
type TreasuryAccount = SimpleTreasuryAccount;
type DetermineRewardPotAccount =
xpallet_mining_asset::SimpleAssetRewardPotAccountDeterminer<Runtime>;
Expand Down Expand Up @@ -1038,10 +1047,9 @@ impl_runtime_apis! {
}

fn trustee_session_info(chain: Chain) -> Option<GenericTrusteeSessionInfo<AccountId>> {
let number = match XGatewayCommon::trustee_session_info_len(chain).checked_sub(1) {
Some(r) => r,
None => u32::max_value(),
};
let number = XGatewayCommon::trustee_session_info_len(chain)
.checked_sub(1)
.unwrap_or_else(u32::max_value);
XGatewayCommon::trustee_session_info_of(chain, number)
}

Expand Down
58 changes: 41 additions & 17 deletions xpallets/mining/asset/src/impls.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use super::*;
use codec::Encode;
use sp_core::crypto::UncheckedFrom;
use sp_runtime::traits::Hash;
use sp_runtime::traits::Saturating;
use sp_runtime::traits::{Hash, Saturating};
use xp_mining_common::{
generic_weight_factors, BaseMiningWeight, Claim, ComputeMiningWeight, WeightFactors, WeightType,
};
Expand Down Expand Up @@ -76,11 +75,20 @@ impl<T: Trait> ComputeMiningWeight<T::AccountId, T::BlockNumber> for Module<T> {
}
}

// ChainX now uses pallet_balances for native coin PCX, therefore we do not
// have to exclude PCX asset in these OnAssetChanged methods:
//
// * `on_issue_pre`
// * `on_issue_post`
// * `on_move_pre`
//
// ```rust
// if xpallet_protocol::PCX == *target {
// return Ok(());
// }
// ```
impl<T: Trait> xpallet_assets::OnAssetChanged<T::AccountId, BalanceOf<T>> for Module<T> {
fn on_issue_pre(target: &AssetId, source: &T::AccountId) {
if xpallet_protocol::PCX == *target {
return;
}
let current_block = <frame_system::Module<T>>::block_number();
Self::init_receiver_mining_ledger(source, target, current_block);

Expand All @@ -92,9 +100,6 @@ impl<T: Trait> xpallet_assets::OnAssetChanged<T::AccountId, BalanceOf<T>> for Mo
source: &T::AccountId,
_value: BalanceOf<T>,
) -> DispatchResult {
if xpallet_protocol::PCX == *target {
return Ok(());
}
Self::issue_deposit_reward(source, target)
}

Expand All @@ -106,9 +111,10 @@ impl<T: Trait> xpallet_assets::OnAssetChanged<T::AccountId, BalanceOf<T>> for Mo
_: AssetType,
_: BalanceOf<T>,
) {
if xpallet_protocol::PCX == *asset_id || from == to {
if from == to {
return;
}

let current_block = <frame_system::Module<T>>::block_number();
Self::init_receiver_mining_ledger(to, asset_id, current_block);

Expand All @@ -123,15 +129,32 @@ impl<T: Trait> xpallet_assets::OnAssetChanged<T::AccountId, BalanceOf<T>> for Mo
}

impl<T: Trait> Module<T> {
/// Allocates the dividend to claimer and referral(treasury) accordingly.
///
/// Each asset miner can have a referral, which splits the 10% of
/// of total asset mining dividend. The 10% split will be transferred
/// to the treasury account if the claimer does not have a referral.
///
/// total_asset_miner_dividend
/// ├──> referral(treasury) 10%
/// └──> claimer 90%
fn allocate_dividend(
claimee_reward_pot: &T::AccountId,
claimer: &T::AccountId,
_claimee: &AssetId,
_claimee_reward_pot: &T::AccountId,
dividend: BalanceOf<T>,
) -> Result<(), Error<T>> {
// todo!("referral_or_treasury 10%, claimer 90%")
// FIXME
// let _ = xpallet_assets::Module::<T>::pcx_issue(claimer, dividend);
let to_referral_or_treasury = dividend / 10.saturated_into();
let reward_splitter = T::GatewayInterface::referral_of(claimer)
.unwrap_or_else(|| T::TreasuryAccount::treasury_account());
Self::transfer(
claimee_reward_pot,
&reward_splitter,
to_referral_or_treasury,
)?;

let to_claimer = dividend - to_referral_or_treasury;
Self::transfer(claimee_reward_pot, claimer, to_claimer)?;

Ok(())
}
}
Expand All @@ -151,8 +174,7 @@ impl<T: Trait> Claim<T::AccountId> for Module<T> {
Self::passed_enough_interval(claimer, claimee, frequency_limit, current_block)?;

let claimee_reward_pot = T::DetermineRewardPotAccount::reward_pot_account_for(claimee);
let reward_pot_balance =
<T as xpallet_assets::Trait>::Currency::free_balance(&claimee_reward_pot);
let reward_pot_balance = Self::free_balance(&claimee_reward_pot);

let (dividend, source_weight, target_weight) =
<Self as ComputeMiningWeight<T::AccountId, _>>::compute_dividend(
Expand All @@ -164,7 +186,7 @@ impl<T: Trait> Claim<T::AccountId> for Module<T> {

Self::has_enough_staking(claimer, dividend, staking_requirement)?;

Self::allocate_dividend(claimer, claimee, &claimee_reward_pot, dividend)?;
Self::allocate_dividend(&claimee_reward_pot, claimer, dividend)?;

Self::apply_update_miner_mining_weight(claimer, claimee, 0, current_block);
Self::apply_update_asset_mining_weight(
Expand All @@ -177,6 +199,8 @@ impl<T: Trait> Claim<T::AccountId> for Module<T> {
miner_ledger.last_claim = Some(current_block);
});

Self::deposit_event(RawEvent::Claim(claimer.clone(), claimee.clone(), dividend));

Ok(())
}
}
Expand Down
89 changes: 66 additions & 23 deletions xpallets/mining/asset/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ mod tests;

use frame_support::{
decl_error, decl_event, decl_module, decl_storage,
dispatch::DispatchResult,
dispatch::{DispatchError, DispatchResult},
ensure,
storage::IterableStorageMap,
traits::{Currency, ExistenceRequirement},
Expand Down Expand Up @@ -42,20 +42,29 @@ pub trait Trait: xpallet_assets::Trait {
/// The overarching event type.
type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;

///
/// Get the staked balances of asset miner.
type StakingInterface: StakingInterface<Self::AccountId, u128>;

///
/// Get the possible referral of asset miner.
type GatewayInterface: GatewayInterface<Self::AccountId>;

/// Get the treasury account.
type TreasuryAccount: TreasuryAccount<Self::AccountId>;

///
/// Generate the reward pot account for mining asset.
type DetermineRewardPotAccount: RewardPotAccountFor<Self::AccountId, AssetId>;
}

pub trait StakingInterface<AccountId, Balance> {
fn staked_of(who: &AccountId) -> Balance;
}

impl<AccountId, Balance: Default> StakingInterface<AccountId, Balance> for () {
fn staked_of(_: &AccountId) -> Balance {
Default::default()
}
}

impl<T: Trait> StakingInterface<<T as frame_system::Trait>::AccountId, u128> for T
where
T: xpallet_mining_staking::Trait,
Expand All @@ -65,23 +74,33 @@ where
}
}

pub trait GatewayInterface<AccountId> {
fn referral_of(who: &AccountId) -> Option<AccountId>;
}

impl<AccountId> GatewayInterface<AccountId> for () {
fn referral_of(_: &AccountId) -> Option<AccountId> {
None
}
}

decl_storage! {
trait Store for Module<T: Trait> as XMiningAsset {
///
/// Possible reward for the new asset owners that does not have native coins yet.
pub DepositReward get(fn deposit_reward): BalanceOf<T> = 100_000.into();

///
/// Can not claim if the claimer violates the restriction.
pub ClaimRestrictionOf get(fn claim_restriction_of):
map hasher(twox_64_concat) AssetId => ClaimRestriction<T::BlockNumber>;

/// External Assets that have the mining rights.
pub MiningPrevilegedAssets get(fn mining_previleged_assets): Vec<AssetId>;

/// Mining weight information of the asset.
/// Mining weight information of the mining assets.
pub AssetLedgers get(fn asset_ledgers):
map hasher(twox_64_concat) AssetId => AssetLedger<T::BlockNumber>;

/// The map from nominator to the vote weight ledger of all nominees.
/// The map from nominator to the vote weight ledger of all mining assets.
pub MinerLedgers get(fn miner_ledgers):
double_map hasher(twox_64_concat) T::AccountId, hasher(twox_64_concat) AssetId
=> MinerLedger<T::BlockNumber>;
Expand Down Expand Up @@ -113,24 +132,24 @@ decl_event!(
Balance = BalanceOf<T>,
<T as frame_system::Trait>::AccountId,
{
///
Claim(AccountId, AccountId, Balance),
/// Claimed the asset mining rewards. [claimer, asset_id, amount]
Claim(AccountId, AssetId, Balance),
}
);

decl_error! {
/// Error for the staking module.
pub enum Error for Module<T: Trait> {
/// The asset does not have the mining rights.
UnprevilegedAsset,
NotPrevilegedAsset,
/// Claimer does not have enough Staking locked balance.
InsufficientStaking,
/// Claimer just did a claim recently, the next frequency limit is not expired.
UnexpiredFrequencyLimit,
/// Asset error.
AssetError,
/// Zero mining weight.
ZeroMiningWeight
ZeroMiningWeight,
/// Balances error.
DispatchError
}
}

Expand All @@ -140,6 +159,12 @@ impl<T: Trait> From<ZeroMiningWeightError> for Error<T> {
}
}

impl<T: Trait> From<DispatchError> for Error<T> {
fn from(_: DispatchError) -> Self {
Self::DispatchError
}
}

decl_module! {
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
type Error = Error<T>;
Expand All @@ -153,7 +178,7 @@ decl_module! {

ensure!(
Self::mining_previleged_assets().contains(&target),
Error::<T>::UnprevilegedAsset
Error::<T>::NotPrevilegedAsset
);

<Self as Claim<T::AccountId>>::claim(&sender, &target)?;
Expand All @@ -174,6 +199,12 @@ decl_module! {
restriction.frequency_limit = new;
});
}

#[weight = 10]
fn set_x_asset_power(origin, asset_id: AssetId, new: FixedAssetPower) {
ensure_root(origin)?;
XTypeAssetPowerMap::insert(asset_id, new);
}
}
}

Expand All @@ -183,6 +214,21 @@ impl<T: Trait> Module<T> {
MinerLedgers::<T>::get(who, asset_id).last_claim
}

#[inline]
fn free_balance(who: &T::AccountId) -> BalanceOf<T> {
<T as xpallet_assets::Trait>::Currency::free_balance(who)
}

#[inline]
fn transfer(from: &T::AccountId, to: &T::AccountId, value: BalanceOf<T>) -> DispatchResult {
<T as xpallet_assets::Trait>::Currency::transfer(
from,
to,
value,
ExistenceRequirement::KeepAlive,
)
}

/// This rule doesn't take effect if the interval is zero.
fn passed_enough_interval(
who: &T::AccountId,
Expand Down Expand Up @@ -302,17 +348,14 @@ impl<T: Trait> Module<T> {
Self::update_asset_mining_weight(target, current_block);
}

/// Gives a tiny reward to the depositor in case of it
/// does not have enough balances to claim the mining reward.
fn issue_deposit_reward(depositor: &T::AccountId, target: &AssetId) -> DispatchResult {
let deposit_reward = Self::deposit_reward();
let reward_pot = T::DetermineRewardPotAccount::reward_pot_account_for(target);
let reward_pot_balance = <T as xpallet_assets::Trait>::Currency::free_balance(&reward_pot);
if reward_pot_balance >= deposit_reward {
<T as xpallet_assets::Trait>::Currency::transfer(
&reward_pot,
depositor,
deposit_reward,
ExistenceRequirement::KeepAlive,
)?;
let reward_pot_balance = Self::free_balance(&reward_pot);
if reward_pot_balance >= deposit_reward && Self::free_balance(depositor) <= deposit_reward {
Self::transfer(&reward_pot, depositor, deposit_reward)?;
} else {
warn!("asset {}'s reward pot has only {:?}, skipped issuing deposit reward for depositor {:?}", target, reward_pot_balance, depositor);
}
Expand Down
Loading

0 comments on commit a03396c

Please sign in to comment.