mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 18:41:05 +00:00
decouple balances from some modules (#1641)
* decouple balances from some module by creating a new traits in support/traits * improve decl_event
This commit is contained in:
BIN
Binary file not shown.
@@ -32,7 +32,7 @@ mod tests {
|
||||
use substrate_executor::{WasmExecutor, NativeExecutionDispatch};
|
||||
use parity_codec::{Encode, Decode, Joiner};
|
||||
use keyring::Keyring;
|
||||
use runtime_support::{Hashable, StorageValue, StorageMap};
|
||||
use runtime_support::{Hashable, StorageValue, StorageMap, traits::Currency};
|
||||
use state_machine::{CodeExecutor, Externalities, TestExternalities};
|
||||
use primitives::{
|
||||
twox_128, Blake2Hasher, ChangesTrieConfiguration, ed25519::{Public, Pair}, NeverNativeValue
|
||||
|
||||
@@ -65,8 +65,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
|
||||
spec_name: create_runtime_str!("node"),
|
||||
impl_name: create_runtime_str!("substrate-node"),
|
||||
authoring_version: 10,
|
||||
spec_version: 24,
|
||||
impl_version: 24,
|
||||
spec_version: 25,
|
||||
impl_version: 25,
|
||||
apis: RUNTIME_API_VERSIONS,
|
||||
};
|
||||
|
||||
@@ -141,11 +141,13 @@ impl session::Trait for Runtime {
|
||||
}
|
||||
|
||||
impl staking::Trait for Runtime {
|
||||
type Currency = balances::Module<Self>;
|
||||
type OnRewardMinted = Treasury;
|
||||
type Event = Event;
|
||||
}
|
||||
|
||||
impl democracy::Trait for Runtime {
|
||||
type Currency = balances::Module<Self>;
|
||||
type Proposal = Call;
|
||||
type Event = Event;
|
||||
}
|
||||
@@ -165,6 +167,7 @@ impl council::motions::Trait for Runtime {
|
||||
}
|
||||
|
||||
impl treasury::Trait for Runtime {
|
||||
type Currency = balances::Module<Self>;
|
||||
type ApproveOrigin = council_motions::EnsureMembers<_4>;
|
||||
type RejectOrigin = council_motions::EnsureMembers<_2>;
|
||||
type Event = Event;
|
||||
|
||||
BIN
Binary file not shown.
+121
-187
@@ -31,65 +31,15 @@ use rstd::prelude::*;
|
||||
use rstd::{cmp, result};
|
||||
use parity_codec::Codec;
|
||||
use runtime_support::{StorageValue, StorageMap, Parameter};
|
||||
use runtime_support::traits::{UpdateBalanceOutcome, Currency, EnsureAccountLiquid, OnFreeBalanceZero};
|
||||
use runtime_support::dispatch::Result;
|
||||
use primitives::traits::{Zero, SimpleArithmetic, MakePayment,
|
||||
As, StaticLookup, Member, CheckedAdd, CheckedSub};
|
||||
As, StaticLookup, Member, CheckedAdd, CheckedSub, MaybeSerializeDebug};
|
||||
use system::{IsDeadAccount, OnNewAccount, ensure_signed};
|
||||
|
||||
mod mock;
|
||||
mod tests;
|
||||
|
||||
/// The account with the given id was killed.
|
||||
pub trait OnFreeBalanceZero<AccountId> {
|
||||
/// The account was the given id was killed.
|
||||
fn on_free_balance_zero(who: &AccountId);
|
||||
}
|
||||
|
||||
impl<AccountId> OnFreeBalanceZero<AccountId> for () {
|
||||
fn on_free_balance_zero(_who: &AccountId) {}
|
||||
}
|
||||
impl<
|
||||
AccountId,
|
||||
X: OnFreeBalanceZero<AccountId>,
|
||||
Y: OnFreeBalanceZero<AccountId>,
|
||||
> OnFreeBalanceZero<AccountId> for (X, Y) {
|
||||
fn on_free_balance_zero(who: &AccountId) {
|
||||
X::on_free_balance_zero(who);
|
||||
Y::on_free_balance_zero(who);
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for a hook to get called when some balance has been minted, causing dilution.
|
||||
pub trait OnDilution<Balance> {
|
||||
/// Some `portion` of the total balance just "grew" by `minted`. `portion` is the pre-growth
|
||||
/// amount (it doesn't take account of the recent growth).
|
||||
fn on_dilution(minted: Balance, portion: Balance);
|
||||
}
|
||||
|
||||
impl<Balance> OnDilution<Balance> for () {
|
||||
fn on_dilution(_minted: Balance, _portion: Balance) {}
|
||||
}
|
||||
|
||||
/// Determinator for whether a given account is able to transfer balance.
|
||||
pub trait EnsureAccountLiquid<AccountId> {
|
||||
/// Returns `Ok` iff the account is able to transfer funds normally. `Err(...)`
|
||||
/// with the reason why not otherwise.
|
||||
fn ensure_account_liquid(who: &AccountId) -> Result;
|
||||
}
|
||||
impl<
|
||||
AccountId,
|
||||
X: EnsureAccountLiquid<AccountId>,
|
||||
Y: EnsureAccountLiquid<AccountId>,
|
||||
> EnsureAccountLiquid<AccountId> for (X, Y) {
|
||||
fn ensure_account_liquid(who: &AccountId) -> Result {
|
||||
X::ensure_account_liquid(who)?;
|
||||
Y::ensure_account_liquid(who)
|
||||
}
|
||||
}
|
||||
impl<AccountId> EnsureAccountLiquid<AccountId> for () {
|
||||
fn ensure_account_liquid(_who: &AccountId) -> Result { Ok(()) }
|
||||
}
|
||||
|
||||
pub trait Trait: system::Trait {
|
||||
/// The balance of an account.
|
||||
type Balance: Parameter + Member + SimpleArithmetic + Codec + Default + Copy + As<usize> + As<u64>;
|
||||
@@ -205,40 +155,8 @@ decl_storage! {
|
||||
}
|
||||
}
|
||||
|
||||
/// Outcome of a balance update.
|
||||
pub enum UpdateBalanceOutcome {
|
||||
/// Account balance was simply updated.
|
||||
Updated,
|
||||
/// The update has led to killing of the account.
|
||||
AccountKilled,
|
||||
}
|
||||
|
||||
// For funding methods, see Currency trait
|
||||
impl<T: Trait> Module<T> {
|
||||
// PUBLIC IMMUTABLES
|
||||
|
||||
/// The combined balance of `who`.
|
||||
pub fn total_balance(who: &T::AccountId) -> T::Balance {
|
||||
Self::free_balance(who) + Self::reserved_balance(who)
|
||||
}
|
||||
|
||||
/// Some result as `slash(who, value)` (but without the side-effects) assuming there are no
|
||||
/// balance changes in the meantime and only the reserved balance is not taken into account.
|
||||
pub fn can_slash(who: &T::AccountId, value: T::Balance) -> bool {
|
||||
Self::free_balance(who) >= value
|
||||
}
|
||||
|
||||
/// Same result as `reserve(who, value)` (but without the side-effects) assuming there
|
||||
/// are no balance changes in the meantime.
|
||||
pub fn can_reserve(who: &T::AccountId, value: T::Balance) -> bool {
|
||||
if T::EnsureAccountLiquid::ensure_account_liquid(who).is_ok() {
|
||||
Self::free_balance(who) >= value
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
//PUBLIC MUTABLES (DANGEROUS)
|
||||
|
||||
/// Set the free balance of an account to some new value.
|
||||
///
|
||||
/// Will enforce ExistentialDeposit law, anulling the account as needed.
|
||||
@@ -334,67 +252,6 @@ impl<T: Trait> Module<T> {
|
||||
Ok(Self::set_free_balance(who, b - value))
|
||||
}
|
||||
|
||||
/// Deducts up to `value` from the combined balance of `who`, preferring to deduct from the
|
||||
/// free balance. This function cannot fail.
|
||||
///
|
||||
/// As much funds up to `value` will be deducted as possible. If this is less than `value`,
|
||||
/// then `Some(remaining)` will be returned. Full completion is given by `None`.
|
||||
pub fn slash(who: &T::AccountId, value: T::Balance) -> Option<T::Balance> {
|
||||
let free_balance = Self::free_balance(who);
|
||||
let free_slash = cmp::min(free_balance, value);
|
||||
Self::set_free_balance(who, free_balance - free_slash);
|
||||
Self::decrease_total_stake_by(free_slash);
|
||||
if free_slash < value {
|
||||
Self::slash_reserved(who, value - free_slash)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds up to `value` to the free balance of `who`.
|
||||
///
|
||||
/// If `who` doesn't exist, nothing is done and an Err returned.
|
||||
pub fn reward(who: &T::AccountId, value: T::Balance) -> Result {
|
||||
if Self::total_balance(who).is_zero() {
|
||||
return Err("beneficiary account must pre-exist");
|
||||
}
|
||||
Self::set_free_balance(who, Self::free_balance(who) + value);
|
||||
Self::increase_total_stake_by(value);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Moves `value` from balance to reserved balance.
|
||||
///
|
||||
/// If the free balance is lower than `value`, then no funds will be moved and an `Err` will
|
||||
/// be returned to notify of this. This is different behaviour to `unreserve`.
|
||||
pub fn reserve(who: &T::AccountId, value: T::Balance) -> Result {
|
||||
let b = Self::free_balance(who);
|
||||
if b < value {
|
||||
return Err("not enough free funds")
|
||||
}
|
||||
T::EnsureAccountLiquid::ensure_account_liquid(who)?;
|
||||
Self::set_reserved_balance(who, Self::reserved_balance(who) + value);
|
||||
Self::set_free_balance(who, b - value);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Moves up to `value` from reserved balance to balance. This function cannot fail.
|
||||
///
|
||||
/// As much funds up to `value` will be deducted as possible. If this is less than `value`,
|
||||
/// then `Some(remaining)` will be returned. Full completion is given by `None`.
|
||||
/// NOTE: This is different to `reserve`.
|
||||
pub fn unreserve(who: &T::AccountId, value: T::Balance) -> Option<T::Balance> {
|
||||
let b = Self::reserved_balance(who);
|
||||
let actual = cmp::min(b, value);
|
||||
Self::set_free_balance(who, Self::free_balance(who) + actual);
|
||||
Self::set_reserved_balance(who, b - actual);
|
||||
if actual == value {
|
||||
None
|
||||
} else {
|
||||
Some(value - actual)
|
||||
}
|
||||
}
|
||||
|
||||
/// Transfer some liquid free balance to another staker.
|
||||
pub fn make_transfer(transactor: &T::AccountId, dest: &T::AccountId, value: T::Balance) -> Result {
|
||||
let from_balance = Self::free_balance(transactor);
|
||||
@@ -432,46 +289,6 @@ impl<T: Trait> Module<T> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Deducts up to `value` from reserved balance of `who`. This function cannot fail.
|
||||
///
|
||||
/// As much funds up to `value` will be deducted as possible. If this is less than `value`,
|
||||
/// then `Some(remaining)` will be returned. Full completion is given by `None`.
|
||||
pub fn slash_reserved(who: &T::AccountId, value: T::Balance) -> Option<T::Balance> {
|
||||
let b = Self::reserved_balance(who);
|
||||
let slash = cmp::min(b, value);
|
||||
Self::set_reserved_balance(who, b - slash);
|
||||
Self::decrease_total_stake_by(slash);
|
||||
if value == slash {
|
||||
None
|
||||
} else {
|
||||
Some(value - slash)
|
||||
}
|
||||
}
|
||||
|
||||
/// Moves up to `value` from reserved balance of account `slashed` to free balance of account
|
||||
/// `beneficiary`. `beneficiary` must exist for this to succeed. If it does not, `Err` will be
|
||||
/// returned.
|
||||
///
|
||||
/// As much funds up to `value` will be moved as possible. If this is less than `value`, then
|
||||
/// `Ok(Some(remaining))` will be returned. Full completion is given by `Ok(None)`.
|
||||
pub fn repatriate_reserved(
|
||||
slashed: &T::AccountId,
|
||||
beneficiary: &T::AccountId,
|
||||
value: T::Balance
|
||||
) -> result::Result<Option<T::Balance>, &'static str> {
|
||||
if Self::total_balance(beneficiary).is_zero() {
|
||||
return Err("beneficiary account must pre-exist");
|
||||
}
|
||||
let b = Self::reserved_balance(slashed);
|
||||
let slash = cmp::min(b, value);
|
||||
Self::set_free_balance(beneficiary, Self::free_balance(beneficiary) + slash);
|
||||
Self::set_reserved_balance(slashed, b - slash);
|
||||
if value == slash {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(value - slash))
|
||||
}
|
||||
}
|
||||
|
||||
/// Register a new account (with existential balance).
|
||||
fn new_account(who: &T::AccountId, balance: T::Balance) {
|
||||
@@ -520,6 +337,120 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> Currency<T::AccountId> for Module<T>
|
||||
where
|
||||
T::Balance: MaybeSerializeDebug
|
||||
{
|
||||
type Balance = T::Balance;
|
||||
|
||||
fn total_balance(who: &T::AccountId) -> Self::Balance {
|
||||
Self::free_balance(who) + Self::reserved_balance(who)
|
||||
}
|
||||
|
||||
fn can_slash(who: &T::AccountId, value: Self::Balance) -> bool {
|
||||
Self::free_balance(who) >= value
|
||||
}
|
||||
|
||||
fn can_reserve(who: &T::AccountId, value: Self::Balance) -> bool {
|
||||
if T::EnsureAccountLiquid::ensure_account_liquid(who).is_ok() {
|
||||
Self::free_balance(who) >= value
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn total_issuance() -> Self:: Balance {
|
||||
Self::total_issuance()
|
||||
}
|
||||
|
||||
fn free_balance(who: &T::AccountId) -> Self::Balance {
|
||||
Self::free_balance(who)
|
||||
}
|
||||
|
||||
fn reserved_balance(who: &T::AccountId) -> Self::Balance {
|
||||
Self::reserved_balance(who)
|
||||
}
|
||||
|
||||
fn slash(who: &T::AccountId, value: Self::Balance) -> Option<Self::Balance> {
|
||||
let free_balance = Self::free_balance(who);
|
||||
let free_slash = cmp::min(free_balance, value);
|
||||
Self::set_free_balance(who, free_balance - free_slash);
|
||||
Self::decrease_total_stake_by(free_slash);
|
||||
if free_slash < value {
|
||||
Self::slash_reserved(who, value - free_slash)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn reward(who: &T::AccountId, value: Self::Balance) -> result::Result<(), &'static str> {
|
||||
if Self::total_balance(who).is_zero() {
|
||||
return Err("beneficiary account must pre-exist");
|
||||
}
|
||||
Self::set_free_balance(who, Self::free_balance(who) + value);
|
||||
Self::increase_total_stake_by(value);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn increase_free_balance_creating(who: &T::AccountId, value: Self::Balance) -> UpdateBalanceOutcome {
|
||||
Self::set_free_balance_creating(who, Self::free_balance(who) + value)
|
||||
}
|
||||
|
||||
fn reserve(who: &T::AccountId, value: Self::Balance) -> result::Result<(), &'static str> {
|
||||
let b = Self::free_balance(who);
|
||||
if b < value {
|
||||
return Err("not enough free funds")
|
||||
}
|
||||
T::EnsureAccountLiquid::ensure_account_liquid(who)?;
|
||||
Self::set_reserved_balance(who, Self::reserved_balance(who) + value);
|
||||
Self::set_free_balance(who, b - value);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unreserve(who: &T::AccountId, value: Self::Balance) -> Option<Self::Balance> {
|
||||
let b = Self::reserved_balance(who);
|
||||
let actual = cmp::min(b, value);
|
||||
Self::set_free_balance(who, Self::free_balance(who) + actual);
|
||||
Self::set_reserved_balance(who, b - actual);
|
||||
if actual == value {
|
||||
None
|
||||
} else {
|
||||
Some(value - actual)
|
||||
}
|
||||
}
|
||||
|
||||
fn slash_reserved(who: &T::AccountId, value: Self::Balance) -> Option<Self::Balance> {
|
||||
let b = Self::reserved_balance(who);
|
||||
let slash = cmp::min(b, value);
|
||||
Self::set_reserved_balance(who, b - slash);
|
||||
Self::decrease_total_stake_by(slash);
|
||||
if value == slash {
|
||||
None
|
||||
} else {
|
||||
Some(value - slash)
|
||||
}
|
||||
}
|
||||
|
||||
fn repatriate_reserved(
|
||||
slashed: &T::AccountId,
|
||||
beneficiary: &T::AccountId,
|
||||
value: Self::Balance
|
||||
) -> result::Result<Option<Self::Balance>, &'static str> {
|
||||
if Self::total_balance(beneficiary).is_zero() {
|
||||
return Err("beneficiary account must pre-exist");
|
||||
}
|
||||
let b = Self::reserved_balance(slashed);
|
||||
let slash = cmp::min(b, value);
|
||||
Self::set_free_balance(beneficiary, Self::free_balance(beneficiary) + slash);
|
||||
Self::set_reserved_balance(slashed, b - slash);
|
||||
if value == slash {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(value - slash))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> MakePayment<T::AccountId> for Module<T> {
|
||||
fn make_payment(transactor: &T::AccountId, encoded_len: usize) -> Result {
|
||||
let b = Self::free_balance(transactor);
|
||||
@@ -533,7 +464,10 @@ impl<T: Trait> MakePayment<T::AccountId> for Module<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> IsDeadAccount<T::AccountId> for Module<T> {
|
||||
impl<T: Trait> IsDeadAccount<T::AccountId> for Module<T>
|
||||
where
|
||||
T::Balance: MaybeSerializeDebug
|
||||
{
|
||||
fn is_dead_account(who: &T::AccountId) -> bool {
|
||||
Self::total_balance(who).is_zero()
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ use {balances, system};
|
||||
use rstd::cell::RefCell;
|
||||
use rstd::collections::btree_map::{BTreeMap, Entry};
|
||||
use rstd::prelude::*;
|
||||
use runtime_support::{StorageMap, StorageDoubleMap};
|
||||
use runtime_support::{StorageMap, StorageDoubleMap, traits::UpdateBalanceOutcome};
|
||||
|
||||
pub struct ChangeEntry<T: Trait> {
|
||||
balance: Option<T::Balance>,
|
||||
@@ -65,7 +65,7 @@ impl<T: Trait> AccountDb<T> for DirectAccountDb {
|
||||
fn commit(&mut self, s: ChangeSet<T>) {
|
||||
for (address, changed) in s.into_iter() {
|
||||
if let Some(balance) = changed.balance {
|
||||
if let balances::UpdateBalanceOutcome::AccountKilled =
|
||||
if let UpdateBalanceOutcome::AccountKilled =
|
||||
balances::Module::<T>::set_free_balance_creating(&address, balance)
|
||||
{
|
||||
// Account killed. This will ultimately lead to calling `OnFreeBalanceZero` callback
|
||||
|
||||
@@ -18,10 +18,10 @@ use super::{CodeHash, Config, ContractAddressFor, Event, RawEvent, Trait};
|
||||
use crate::account_db::{AccountDb, DirectAccountDb, OverlayAccountDb};
|
||||
use crate::gas::{GasMeter, Token, approx_gas_for_balance};
|
||||
|
||||
use balances::{self, EnsureAccountLiquid};
|
||||
use rstd::prelude::*;
|
||||
use runtime_primitives::traits::{CheckedAdd, CheckedSub, Zero};
|
||||
use timestamp;
|
||||
use runtime_support::traits::EnsureAccountLiquid;
|
||||
|
||||
pub type BalanceOf<T> = <T as balances::Trait>::Balance;
|
||||
pub type AccountIdOf<T> = <T as system::Trait>::AccountId;
|
||||
|
||||
@@ -79,6 +79,7 @@ use codec::Codec;
|
||||
use runtime_primitives::traits::{Hash, As, SimpleArithmetic,Bounded, StaticLookup};
|
||||
use runtime_support::dispatch::{Result, Dispatchable};
|
||||
use runtime_support::{Parameter, StorageMap, StorageValue, StorageDoubleMap};
|
||||
use runtime_support::traits::OnFreeBalanceZero;
|
||||
use system::{ensure_signed, RawOrigin};
|
||||
use runtime_io::{blake2_256, twox_128};
|
||||
use timestamp;
|
||||
@@ -369,7 +370,7 @@ impl<T: Trait> StorageDoubleMap for StorageOf<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> balances::OnFreeBalanceZero<T::AccountId> for Module<T> {
|
||||
impl<T: Trait> OnFreeBalanceZero<T::AccountId> for Module<T> {
|
||||
fn on_free_balance_zero(who: &T::AccountId) {
|
||||
<CodeHashOf<T>>::remove(who);
|
||||
<StorageOf<T>>::remove_prefix(who.clone());
|
||||
|
||||
@@ -24,6 +24,8 @@ extern crate serde;
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate hex_literal;
|
||||
#[cfg(test)]
|
||||
extern crate srml_balances as balances;
|
||||
|
||||
extern crate parity_codec as codec;
|
||||
extern crate parity_codec_derive;
|
||||
@@ -32,7 +34,6 @@ extern crate sr_std as rstd;
|
||||
extern crate sr_io as runtime_io;
|
||||
#[macro_use] extern crate srml_support;
|
||||
extern crate sr_primitives as primitives;
|
||||
extern crate srml_balances as balances;
|
||||
extern crate srml_democracy as democracy;
|
||||
extern crate srml_system as system;
|
||||
|
||||
@@ -97,6 +98,7 @@ mod tests {
|
||||
type Event = Event;
|
||||
}
|
||||
impl democracy::Trait for Test {
|
||||
type Currency = balances::Module<Self>;
|
||||
type Proposal = Call;
|
||||
type Event = Event;
|
||||
}
|
||||
|
||||
@@ -19,9 +19,8 @@
|
||||
use rstd::prelude::*;
|
||||
use primitives::traits::{Zero, One, As, StaticLookup};
|
||||
use runtime_io::print;
|
||||
use srml_support::{StorageValue, StorageMap, dispatch::Result};
|
||||
use srml_support::{StorageValue, StorageMap, dispatch::Result, traits::Currency};
|
||||
use democracy;
|
||||
use balances;
|
||||
use system::{self, ensure_signed};
|
||||
|
||||
// no polynomial attacks:
|
||||
@@ -80,6 +79,8 @@ use system::{self, ensure_signed};
|
||||
|
||||
pub type VoteIndex = u32;
|
||||
|
||||
type BalanceOf<T> = <<T as democracy::Trait>::Currency as Currency<<T as system::Trait>::AccountId>>::Balance;
|
||||
|
||||
pub trait Trait: democracy::Trait {
|
||||
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
|
||||
}
|
||||
@@ -105,7 +106,7 @@ decl_module! {
|
||||
if !<LastActiveOf<T>>::exists(&who) {
|
||||
// not yet a voter - deduct bond.
|
||||
// NOTE: this must be the last potential bailer, since it changes state.
|
||||
<balances::Module<T>>::reserve(&who, Self::voting_bond())?;
|
||||
T::Currency::reserve(&who, Self::voting_bond())?;
|
||||
|
||||
<Voters<T>>::put({
|
||||
let mut v = Self::voters();
|
||||
@@ -161,10 +162,10 @@ decl_module! {
|
||||
if valid {
|
||||
// This only fails if `reporter` doesn't exist, which it clearly must do since its the origin.
|
||||
// Still, it's no more harmful to propagate any error at this point.
|
||||
<balances::Module<T>>::repatriate_reserved(&who, &reporter, Self::voting_bond())?;
|
||||
T::Currency::repatriate_reserved(&who, &reporter, Self::voting_bond())?;
|
||||
Self::deposit_event(RawEvent::VoterReaped(who, reporter));
|
||||
} else {
|
||||
<balances::Module<T>>::slash_reserved(&reporter, Self::voting_bond());
|
||||
T::Currency::slash_reserved(&reporter, Self::voting_bond());
|
||||
Self::deposit_event(RawEvent::BadReaperSlashed(reporter));
|
||||
}
|
||||
}
|
||||
@@ -181,7 +182,7 @@ decl_module! {
|
||||
ensure!(voters[index] == who, "retraction index mismatch");
|
||||
|
||||
Self::remove_voter(&who, index, voters);
|
||||
<balances::Module<T>>::unreserve(&who, Self::voting_bond());
|
||||
T::Currency::unreserve(&who, Self::voting_bond());
|
||||
}
|
||||
|
||||
/// Submit oneself for candidacy.
|
||||
@@ -200,7 +201,7 @@ decl_module! {
|
||||
"invalid candidate slot"
|
||||
);
|
||||
// NOTE: This must be last as it has side-effects.
|
||||
<balances::Module<T>>::reserve(&who, Self::candidacy_bond())
|
||||
T::Currency::reserve(&who, Self::candidacy_bond())
|
||||
.map_err(|_| "candidate has not enough funds")?;
|
||||
|
||||
<RegisterInfoOf<T>>::insert(&who, (Self::vote_index(), slot as u32));
|
||||
@@ -220,7 +221,7 @@ decl_module! {
|
||||
fn present_winner(
|
||||
origin,
|
||||
candidate: <T::Lookup as StaticLookup>::Source,
|
||||
#[compact] total: T::Balance,
|
||||
#[compact] total: BalanceOf<T>,
|
||||
#[compact] index: VoteIndex
|
||||
) -> Result {
|
||||
let who = ensure_signed(origin)?;
|
||||
@@ -231,8 +232,8 @@ decl_module! {
|
||||
let (_, _, expiring) = Self::next_finalise().ok_or("cannot present outside of presentation period")?;
|
||||
let stakes = Self::snapshoted_stakes();
|
||||
let voters = Self::voters();
|
||||
let bad_presentation_punishment = Self::present_slash_per_voter() * T::Balance::sa(voters.len() as u64);
|
||||
ensure!(<balances::Module<T>>::can_slash(&who, bad_presentation_punishment), "presenter must have sufficient slashable funds");
|
||||
let bad_presentation_punishment = Self::present_slash_per_voter() * BalanceOf::<T>::sa(voters.len() as u64);
|
||||
ensure!(T::Currency::can_slash(&who, bad_presentation_punishment), "presenter must have sufficient slashable funds");
|
||||
|
||||
let mut leaderboard = Self::leaderboard().ok_or("leaderboard must exist while present phase active")?;
|
||||
ensure!(total > leaderboard[0].0, "candidate not worthy of leaderboard");
|
||||
@@ -263,7 +264,7 @@ decl_module! {
|
||||
} else {
|
||||
// we can rest assured it will be Ok since we checked `can_slash` earlier; still
|
||||
// better safe than sorry.
|
||||
let _ = <balances::Module<T>>::slash(&who, bad_presentation_punishment);
|
||||
let _ = T::Currency::slash(&who, bad_presentation_punishment);
|
||||
Err(if dupe { "duplicate presentation" } else { "incorrect total" })
|
||||
}
|
||||
}
|
||||
@@ -313,11 +314,11 @@ decl_storage! {
|
||||
|
||||
// parameters
|
||||
/// How much should be locked up in order to submit one's candidacy.
|
||||
pub CandidacyBond get(candidacy_bond) config(): T::Balance = T::Balance::sa(9);
|
||||
pub CandidacyBond get(candidacy_bond) config(): BalanceOf<T> = BalanceOf::<T>::sa(9);
|
||||
/// How much should be locked up in order to be able to submit votes.
|
||||
pub VotingBond get(voting_bond) config(voter_bond): T::Balance;
|
||||
pub VotingBond get(voting_bond) config(voter_bond): BalanceOf<T>;
|
||||
/// The punishment, per voter, if you provide an invalid presentation.
|
||||
pub PresentSlashPerVoter get(present_slash_per_voter) config(): T::Balance = T::Balance::sa(1);
|
||||
pub PresentSlashPerVoter get(present_slash_per_voter) config(): BalanceOf<T> = BalanceOf::<T>::sa(1);
|
||||
/// How many runners-up should have their approvals persist until the next vote.
|
||||
pub CarryCount get(carry_count) config(): u32 = 2;
|
||||
/// How long to give each top candidate to present themselves after the vote ends.
|
||||
@@ -360,9 +361,9 @@ decl_storage! {
|
||||
/// The accounts holding the seats that will become free on the next tally.
|
||||
pub NextFinalise get(next_finalise): Option<(T::BlockNumber, u32, Vec<T::AccountId>)>;
|
||||
/// The stakes as they were at the point that the vote ended.
|
||||
pub SnapshotedStakes get(snapshoted_stakes): Vec<T::Balance>;
|
||||
pub SnapshotedStakes get(snapshoted_stakes): Vec<BalanceOf<T>>;
|
||||
/// Get the leaderboard if we;re in the presentation phase.
|
||||
pub Leaderboard get(leaderboard): Option<Vec<(T::Balance, T::AccountId)> >; // ORDERED low -> high
|
||||
pub Leaderboard get(leaderboard): Option<Vec<(BalanceOf<T>, T::AccountId)> >; // ORDERED low -> high
|
||||
}
|
||||
}
|
||||
|
||||
@@ -466,12 +467,12 @@ impl<T: Trait> Module<T> {
|
||||
<NextFinalise<T>>::put((number + Self::presentation_duration(), empty_seats as u32, expiring));
|
||||
|
||||
let voters = Self::voters();
|
||||
let votes = voters.iter().map(<balances::Module<T>>::total_balance).collect::<Vec<_>>();
|
||||
let votes = voters.iter().map(T::Currency::total_balance).collect::<Vec<_>>();
|
||||
<SnapshotedStakes<T>>::put(votes);
|
||||
|
||||
// initialise leaderboard.
|
||||
let leaderboard_size = empty_seats + Self::carry_count() as usize;
|
||||
<Leaderboard<T>>::put(vec![(T::Balance::zero(), T::AccountId::default()); leaderboard_size]);
|
||||
<Leaderboard<T>>::put(vec![(BalanceOf::<T>::zero(), T::AccountId::default()); leaderboard_size]);
|
||||
|
||||
Self::deposit_event(RawEvent::TallyStarted(empty_seats as u32));
|
||||
}
|
||||
@@ -485,7 +486,7 @@ impl<T: Trait> Module<T> {
|
||||
<SnapshotedStakes<T>>::kill();
|
||||
let (_, coming, expiring): (T::BlockNumber, u32, Vec<T::AccountId>) =
|
||||
<NextFinalise<T>>::take().ok_or("finalise can only be called after a tally is started.")?;
|
||||
let leaderboard: Vec<(T::Balance, T::AccountId)> = <Leaderboard<T>>::take().unwrap_or_default();
|
||||
let leaderboard: Vec<(BalanceOf<T>, T::AccountId)> = <Leaderboard<T>>::take().unwrap_or_default();
|
||||
let new_expiry = <system::Module<T>>::block_number() + Self::term_duration();
|
||||
|
||||
// return bond to winners.
|
||||
@@ -496,7 +497,7 @@ impl<T: Trait> Module<T> {
|
||||
.take(coming as usize)
|
||||
.map(|(_, a)| a)
|
||||
.cloned()
|
||||
.inspect(|a| {<balances::Module<T>>::unreserve(a, candidacy_bond);})
|
||||
.inspect(|a| {T::Currency::unreserve(a, candidacy_bond);})
|
||||
.collect();
|
||||
let active_council = Self::active_council();
|
||||
let outgoing = active_council.iter().take(expiring.len()).map(|a| a.0.clone()).collect();
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate substrate_primitives;
|
||||
#[cfg(test)]
|
||||
extern crate srml_balances as balances;
|
||||
|
||||
#[macro_use]
|
||||
extern crate parity_codec_derive;
|
||||
@@ -30,13 +32,13 @@ extern crate srml_support;
|
||||
extern crate parity_codec as codec;
|
||||
extern crate sr_io as runtime_io;
|
||||
extern crate sr_primitives as primitives;
|
||||
extern crate srml_balances as balances;
|
||||
extern crate srml_system as system;
|
||||
|
||||
use rstd::prelude::*;
|
||||
use rstd::result;
|
||||
use primitives::traits::{Zero, As};
|
||||
use srml_support::{StorageValue, StorageMap, Parameter, Dispatchable, IsSubType};
|
||||
use srml_support::traits::{Currency, OnFreeBalanceZero, EnsureAccountLiquid};
|
||||
use srml_support::dispatch::Result;
|
||||
use system::ensure_signed;
|
||||
|
||||
@@ -77,7 +79,11 @@ impl Vote {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Trait: balances::Trait + Sized {
|
||||
type BalanceOf<T> = <<T as Trait>::Currency as Currency<<T as system::Trait>::AccountId>>::Balance;
|
||||
|
||||
pub trait Trait: system::Trait + Sized {
|
||||
type Currency: Currency<<Self as system::Trait>::AccountId>;
|
||||
|
||||
type Proposal: Parameter + Dispatchable<Origin=Self::Origin> + IsSubType<Module<Self>>;
|
||||
|
||||
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
|
||||
@@ -91,12 +97,12 @@ decl_module! {
|
||||
fn propose(
|
||||
origin,
|
||||
proposal: Box<T::Proposal>,
|
||||
#[compact] value: T::Balance
|
||||
#[compact] value: BalanceOf<T>
|
||||
) {
|
||||
let who = ensure_signed(origin)?;
|
||||
|
||||
ensure!(value >= Self::minimum_deposit(), "value too low");
|
||||
<balances::Module<T>>::reserve(&who, value)
|
||||
T::Currency::reserve(&who, value)
|
||||
.map_err(|_| "proposer's balance too low")?;
|
||||
|
||||
let index = Self::public_prop_count();
|
||||
@@ -113,7 +119,7 @@ decl_module! {
|
||||
let who = ensure_signed(origin)?;
|
||||
let mut deposit = Self::deposit_of(proposal)
|
||||
.ok_or("can only second an existing proposal")?;
|
||||
<balances::Module<T>>::reserve(&who, deposit.0)
|
||||
T::Currency::reserve(&who, deposit.0)
|
||||
.map_err(|_| "seconder's balance too low")?;
|
||||
deposit.1.push(who);
|
||||
<DepositOf<T>>::insert(proposal, deposit);
|
||||
@@ -125,7 +131,7 @@ decl_module! {
|
||||
let who = ensure_signed(origin)?;
|
||||
ensure!(vote.multiplier() <= Self::max_lock_periods(), "vote has too great a strength");
|
||||
ensure!(Self::is_active_referendum(ref_index), "vote given for invalid referendum.");
|
||||
ensure!(!<balances::Module<T>>::total_balance(&who).is_zero(),
|
||||
ensure!(!T::Currency::total_balance(&who).is_zero(),
|
||||
"transactor must have balance to signal approval.");
|
||||
if !<VoteOf<T>>::exists(&(ref_index, who.clone())) {
|
||||
<VotersFor<T>>::mutate(ref_index, |voters| voters.push(who.clone()));
|
||||
@@ -192,11 +198,11 @@ decl_storage! {
|
||||
/// The public proposals. Unsorted.
|
||||
pub PublicProps get(public_props): Vec<(PropIndex, T::Proposal, T::AccountId)>;
|
||||
/// Those who have locked a deposit.
|
||||
pub DepositOf get(deposit_of): map PropIndex => Option<(T::Balance, Vec<T::AccountId>)>;
|
||||
pub DepositOf get(deposit_of): map PropIndex => Option<(BalanceOf<T>, Vec<T::AccountId>)>;
|
||||
/// How often (in blocks) new public referenda are launched.
|
||||
pub LaunchPeriod get(launch_period) config(): T::BlockNumber = T::BlockNumber::sa(1000);
|
||||
/// The minimum amount to be used as a deposit for a public referendum proposal.
|
||||
pub MinimumDeposit get(minimum_deposit) config(): T::Balance;
|
||||
pub MinimumDeposit get(minimum_deposit) config(): BalanceOf<T>;
|
||||
/// The delay before enactment for all public referenda.
|
||||
pub PublicDelay get(public_delay) config(): T::BlockNumber;
|
||||
/// The maximum number of additional lock periods a voter may offer to strengthen their vote. Multiples of `PublicDelay`.
|
||||
@@ -229,7 +235,7 @@ decl_storage! {
|
||||
|
||||
decl_event!(
|
||||
/// An event in this module.
|
||||
pub enum Event<T> where <T as balances::Trait>::Balance, <T as system::Trait>::AccountId {
|
||||
pub enum Event<T> where Balance = BalanceOf<T>, <T as system::Trait>::AccountId {
|
||||
Tabled(PropIndex, Balance, Vec<AccountId>),
|
||||
Started(ReferendumIndex, VoteThreshold),
|
||||
Passed(ReferendumIndex),
|
||||
@@ -244,8 +250,8 @@ impl<T: Trait> Module<T> {
|
||||
|
||||
/// Get the amount locked in support of `proposal`; `None` if proposal isn't a valid proposal
|
||||
/// index.
|
||||
pub fn locked_for(proposal: PropIndex) -> Option<T::Balance> {
|
||||
Self::deposit_of(proposal).map(|(d, l)| d * T::Balance::sa(l.len() as u64))
|
||||
pub fn locked_for(proposal: PropIndex) -> Option<BalanceOf<T>> {
|
||||
Self::deposit_of(proposal).map(|(d, l)| d * BalanceOf::<T>::sa(l.len() as u64))
|
||||
}
|
||||
|
||||
/// Return true if `ref_index` is an on-going referendum.
|
||||
@@ -273,17 +279,17 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
|
||||
/// Get the voters for the current proposal.
|
||||
pub fn tally(ref_index: ReferendumIndex) -> (T::Balance, T::Balance, T::Balance) {
|
||||
pub fn tally(ref_index: ReferendumIndex) -> (BalanceOf<T>, BalanceOf<T>, BalanceOf<T>) {
|
||||
Self::voters_for(ref_index).iter()
|
||||
.map(|voter| (
|
||||
<balances::Module<T>>::total_balance(voter),
|
||||
T::Currency::total_balance(voter),
|
||||
Self::vote_of((ref_index, voter.clone())),
|
||||
))
|
||||
.map(|(bal, vote)|
|
||||
if vote.is_aye() {
|
||||
(bal * T::Balance::sa(vote.multiplier() as u64), Zero::zero(), bal)
|
||||
(bal * BalanceOf::<T>::sa(vote.multiplier() as u64), Zero::zero(), bal)
|
||||
} else {
|
||||
(Zero::zero(), bal * T::Balance::sa(vote.multiplier() as u64), bal)
|
||||
(Zero::zero(), bal * BalanceOf::<T>::sa(vote.multiplier() as u64), bal)
|
||||
}
|
||||
).fold((Zero::zero(), Zero::zero(), Zero::zero()), |(a, b, c), (d, e, f)| (a + d, b + e, c + f))
|
||||
}
|
||||
@@ -345,10 +351,10 @@ impl<T: Trait> Module<T> {
|
||||
let (prop_index, proposal, _) = public_props.swap_remove(winner_index);
|
||||
<PublicProps<T>>::put(public_props);
|
||||
|
||||
if let Some((deposit, depositors)) = <DepositOf<T>>::take(prop_index) {//: (T::Balance, Vec<T::AccountId>) =
|
||||
if let Some((deposit, depositors)) = <DepositOf<T>>::take(prop_index) {//: (BalanceOf<T>, Vec<T::AccountId>) =
|
||||
// refund depositors
|
||||
for d in &depositors {
|
||||
<balances::Module<T>>::unreserve(d, deposit);
|
||||
T::Currency::unreserve(d, deposit);
|
||||
}
|
||||
Self::deposit_event(RawEvent::Tabled(prop_index, deposit, depositors));
|
||||
Self::inject_referendum(now + Self::voting_period(), proposal, VoteThreshold::SuperMajorityApprove, Self::public_delay())?;
|
||||
@@ -360,7 +366,7 @@ impl<T: Trait> Module<T> {
|
||||
|
||||
fn bake_referendum(now: T::BlockNumber, index: ReferendumIndex, info: ReferendumInfo<T::BlockNumber, T::Proposal>) -> Result {
|
||||
let (approve, against, capital) = Self::tally(index);
|
||||
let total_issuance = <balances::Module<T>>::total_issuance();
|
||||
let total_issuance = T::Currency::total_issuance();
|
||||
let approved = info.threshold.approved(approve, against, capital, total_issuance);
|
||||
let lock_period = Self::public_delay();
|
||||
|
||||
@@ -414,13 +420,13 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> balances::OnFreeBalanceZero<T::AccountId> for Module<T> {
|
||||
impl<T: Trait> OnFreeBalanceZero<T::AccountId> for Module<T> {
|
||||
fn on_free_balance_zero(who: &T::AccountId) {
|
||||
<Bondage<T>>::remove(who);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> balances::EnsureAccountLiquid<T::AccountId> for Module<T> {
|
||||
impl<T: Trait> EnsureAccountLiquid<T::AccountId> for Module<T> {
|
||||
fn ensure_account_liquid(who: &T::AccountId) -> Result {
|
||||
if Self::bondage(who) <= <system::Module<T>>::block_number() {
|
||||
Ok(())
|
||||
@@ -477,6 +483,7 @@ mod tests {
|
||||
type Event = ();
|
||||
}
|
||||
impl Trait for Test {
|
||||
type Currency = balances::Module<Self>;
|
||||
type Proposal = Call;
|
||||
type Event = ();
|
||||
}
|
||||
|
||||
@@ -304,6 +304,7 @@ mod tests {
|
||||
use primitives::BuildStorage;
|
||||
use primitives::traits::{Header as HeaderT, BlakeTwo256, IdentityLookup};
|
||||
use primitives::testing::{Digest, DigestItem, Header, Block};
|
||||
use runtime_support::traits::Currency;
|
||||
use system;
|
||||
|
||||
impl_outer_origin! {
|
||||
|
||||
@@ -33,7 +33,6 @@ extern crate parity_codec_derive;
|
||||
|
||||
extern crate parity_codec as codec;
|
||||
extern crate sr_primitives as primitives;
|
||||
extern crate srml_balances as balances;
|
||||
extern crate srml_consensus as consensus;
|
||||
extern crate srml_session as session;
|
||||
extern crate srml_system as system;
|
||||
@@ -44,13 +43,16 @@ extern crate substrate_primitives;
|
||||
extern crate sr_io as runtime_io;
|
||||
#[cfg(test)]
|
||||
extern crate srml_timestamp as timestamp;
|
||||
#[cfg(test)]
|
||||
extern crate srml_balances as balances;
|
||||
|
||||
use rstd::{prelude::*, cmp};
|
||||
use codec::HasCompact;
|
||||
use runtime_support::{Parameter, StorageValue, StorageMap, dispatch::Result};
|
||||
use runtime_support::traits::{Currency, OnDilution, EnsureAccountLiquid, OnFreeBalanceZero};
|
||||
use session::OnSessionChange;
|
||||
use primitives::{Perbill, traits::{Zero, One, Bounded, As, StaticLookup}};
|
||||
use balances::OnDilution;
|
||||
use primitives::Perbill;
|
||||
use primitives::traits::{Zero, One, Bounded, As, StaticLookup};
|
||||
use system::ensure_signed;
|
||||
|
||||
mod mock;
|
||||
@@ -89,9 +91,14 @@ impl<B: Default + HasCompact + Copy> Default for ValidatorPrefs<B> {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Trait: balances::Trait + session::Trait {
|
||||
type BalanceOf<T> = <<T as Trait>::Currency as Currency<<T as system::Trait>::AccountId>>::Balance;
|
||||
|
||||
pub trait Trait: system::Trait + session::Trait {
|
||||
/// The staking balance.
|
||||
type Currency: Currency<Self::AccountId>;
|
||||
|
||||
/// Some tokens minted.
|
||||
type OnRewardMinted: OnDilution<<Self as balances::Trait>::Balance>;
|
||||
type OnRewardMinted: OnDilution<BalanceOf<Self>>;
|
||||
|
||||
/// The overarching event type.
|
||||
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
|
||||
@@ -182,7 +189,7 @@ decl_module! {
|
||||
fn register_preferences(
|
||||
origin,
|
||||
#[compact] intentions_index: u32,
|
||||
prefs: ValidatorPrefs<T::Balance>
|
||||
prefs: ValidatorPrefs<BalanceOf<T>>
|
||||
) {
|
||||
let who = ensure_signed(origin)?;
|
||||
|
||||
@@ -228,7 +235,7 @@ decl_module! {
|
||||
|
||||
/// An event in this module.
|
||||
decl_event!(
|
||||
pub enum Event<T> where <T as balances::Trait>::Balance, <T as system::Trait>::AccountId {
|
||||
pub enum Event<T> where Balance = BalanceOf<T>, <T as system::Trait>::AccountId {
|
||||
/// All validators have been rewarded by the given balance.
|
||||
Reward(Balance),
|
||||
/// One validator (and their nominators) has been given a offline-warning (they're still
|
||||
@@ -266,7 +273,7 @@ decl_storage! {
|
||||
/// The current era index.
|
||||
pub CurrentEra get(current_era) config(): T::BlockNumber;
|
||||
/// Preferences that a validator has.
|
||||
pub ValidatorPreferences get(validator_preferences): map T::AccountId => ValidatorPrefs<T::Balance>;
|
||||
pub ValidatorPreferences get(validator_preferences): map T::AccountId => ValidatorPrefs<BalanceOf<T>>;
|
||||
/// All the accounts with a desire to stake.
|
||||
pub Intentions get(intentions) config(): Vec<T::AccountId>;
|
||||
/// All nominator -> nominee relationships.
|
||||
@@ -277,9 +284,9 @@ decl_storage! {
|
||||
pub CurrentNominatorsFor get(current_nominators_for): map T::AccountId => Vec<T::AccountId>;
|
||||
|
||||
/// Maximum reward, per validator, that is provided per acceptable session.
|
||||
pub CurrentSessionReward get(current_session_reward) config(): T::Balance;
|
||||
pub CurrentSessionReward get(current_session_reward) config(): BalanceOf<T>;
|
||||
/// Slash, per validator that is taken for the first time they are found to be offline.
|
||||
pub CurrentOfflineSlash get(current_offline_slash) config(): T::Balance;
|
||||
pub CurrentOfflineSlash get(current_offline_slash) config(): BalanceOf<T>;
|
||||
|
||||
/// The next value of sessions per era.
|
||||
pub NextSessionsPerEra get(next_sessions_per_era): Option<T::BlockNumber>;
|
||||
@@ -287,7 +294,7 @@ decl_storage! {
|
||||
pub LastEraLengthChange get(last_era_length_change): T::BlockNumber;
|
||||
|
||||
/// The highest and lowest staked validator slashable balances.
|
||||
pub StakeRange get(stake_range): PairOf<T::Balance>;
|
||||
pub StakeRange get(stake_range): PairOf<BalanceOf<T>>;
|
||||
|
||||
/// The block at which the `who`'s funds become entirely liquid.
|
||||
pub Bondage get(bondage): map T::AccountId => T::BlockNumber;
|
||||
@@ -317,17 +324,17 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
|
||||
/// Balance of a (potential) validator that includes all nominators.
|
||||
pub fn nomination_balance(who: &T::AccountId) -> T::Balance {
|
||||
pub fn nomination_balance(who: &T::AccountId) -> BalanceOf<T> {
|
||||
Self::nominators_for(who).iter()
|
||||
.map(<balances::Module<T>>::total_balance)
|
||||
.map(T::Currency::total_balance)
|
||||
.fold(Zero::zero(), |acc, x| acc + x)
|
||||
}
|
||||
|
||||
/// The total balance that can be slashed from an account.
|
||||
pub fn slashable_balance(who: &T::AccountId) -> T::Balance {
|
||||
pub fn slashable_balance(who: &T::AccountId) -> BalanceOf<T> {
|
||||
Self::nominators_for(who).iter()
|
||||
.map(<balances::Module<T>>::total_balance)
|
||||
.fold(<balances::Module<T>>::total_balance(who), |acc, x| acc + x)
|
||||
.map(T::Currency::total_balance)
|
||||
.fold(T::Currency::total_balance(who), |acc, x| acc + x)
|
||||
}
|
||||
|
||||
/// The block at which the `who`'s funds become entirely liquid.
|
||||
@@ -348,20 +355,20 @@ impl<T: Trait> Module<T> {
|
||||
|
||||
/// Slash a given validator by a specific amount. Removes the slash from their balance by preference,
|
||||
/// and reduces the nominators' balance if needed.
|
||||
fn slash_validator(v: &T::AccountId, slash: T::Balance) {
|
||||
fn slash_validator(v: &T::AccountId, slash: BalanceOf<T>) {
|
||||
// skip the slash in degenerate case of having only 4 staking participants despite having a larger
|
||||
// desired number of validators (validator_count).
|
||||
if Self::intentions().len() <= Self::minimum_validator_count() as usize {
|
||||
return
|
||||
}
|
||||
|
||||
if let Some(rem) = <balances::Module<T>>::slash(v, slash) {
|
||||
if let Some(rem) = T::Currency::slash(v, slash) {
|
||||
let noms = Self::current_nominators_for(v);
|
||||
let total = noms.iter().map(<balances::Module<T>>::total_balance).fold(T::Balance::zero(), |acc, x| acc + x);
|
||||
let total = noms.iter().map(T::Currency::total_balance).fold(BalanceOf::<T>::zero(), |acc, x| acc + x);
|
||||
if !total.is_zero() {
|
||||
let safe_mul_rational = |b| b * rem / total;// FIXME #1572 avoid overflow
|
||||
for n in noms.iter() {
|
||||
let _ = <balances::Module<T>>::slash(n, safe_mul_rational(<balances::Module<T>>::total_balance(n))); // best effort - not much that can be done on fail.
|
||||
let _ = T::Currency::slash(n, safe_mul_rational(T::Currency::total_balance(n))); // best effort - not much that can be done on fail.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -369,7 +376,7 @@ impl<T: Trait> Module<T> {
|
||||
|
||||
/// Reward a given validator by a specific amount. Add the reward to their, and their nominators'
|
||||
/// balance, pro-rata.
|
||||
fn reward_validator(who: &T::AccountId, reward: T::Balance) {
|
||||
fn reward_validator(who: &T::AccountId, reward: BalanceOf<T>) {
|
||||
let off_the_table = reward.min(Self::validator_preferences(who).validator_payment);
|
||||
let reward = reward - off_the_table;
|
||||
let validator_cut = if reward.is_zero() {
|
||||
@@ -377,16 +384,16 @@ impl<T: Trait> Module<T> {
|
||||
} else {
|
||||
let noms = Self::current_nominators_for(who);
|
||||
let total = noms.iter()
|
||||
.map(<balances::Module<T>>::total_balance)
|
||||
.fold(<balances::Module<T>>::total_balance(who), |acc, x| acc + x)
|
||||
.map(T::Currency::total_balance)
|
||||
.fold(T::Currency::total_balance(who), |acc, x| acc + x)
|
||||
.max(One::one());
|
||||
let safe_mul_rational = |b| b * reward / total;// FIXME #1572: avoid overflow
|
||||
for n in noms.iter() {
|
||||
let _ = <balances::Module<T>>::reward(n, safe_mul_rational(<balances::Module<T>>::total_balance(n)));
|
||||
let _ = T::Currency::reward(n, safe_mul_rational(T::Currency::total_balance(n)));
|
||||
}
|
||||
safe_mul_rational(<balances::Module<T>>::total_balance(who))
|
||||
safe_mul_rational(T::Currency::total_balance(who))
|
||||
};
|
||||
let _ = <balances::Module<T>>::reward(who, validator_cut + off_the_table);
|
||||
let _ = T::Currency::reward(who, validator_cut + off_the_table);
|
||||
}
|
||||
|
||||
/// Actually carry out the unstake operation.
|
||||
@@ -405,13 +412,13 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
|
||||
/// Get the reward for the session, assuming it ends with this block.
|
||||
fn this_session_reward(actual_elapsed: T::Moment) -> T::Balance {
|
||||
fn this_session_reward(actual_elapsed: T::Moment) -> BalanceOf<T> {
|
||||
let ideal_elapsed = <session::Module<T>>::ideal_session_duration();
|
||||
if ideal_elapsed.is_zero() {
|
||||
return Self::current_session_reward();
|
||||
}
|
||||
let per65536: u64 = (T::Moment::sa(65536u64) * ideal_elapsed.clone() / actual_elapsed.max(ideal_elapsed)).as_();
|
||||
Self::current_session_reward() * T::Balance::sa(per65536) / T::Balance::sa(65536u64)
|
||||
Self::current_session_reward() * BalanceOf::<T>::sa(per65536) / BalanceOf::<T>::sa(65536u64)
|
||||
}
|
||||
|
||||
/// Session has just changed. We need to determine whether we pay a reward, slash and/or
|
||||
@@ -425,8 +432,8 @@ impl<T: Trait> Module<T> {
|
||||
Self::reward_validator(v, reward);
|
||||
}
|
||||
Self::deposit_event(RawEvent::Reward(reward));
|
||||
let total_minted = reward * <T::Balance as As<usize>>::sa(validators.len());
|
||||
let total_rewarded_stake = Self::stake_range().1 * <T::Balance as As<usize>>::sa(validators.len());
|
||||
let total_minted = reward * <BalanceOf<T> as As<usize>>::sa(validators.len());
|
||||
let total_rewarded_stake = Self::stake_range().1 * <BalanceOf<T> as As<usize>>::sa(validators.len());
|
||||
T::OnRewardMinted::on_dilution(total_minted, total_rewarded_stake);
|
||||
}
|
||||
|
||||
@@ -536,7 +543,7 @@ impl<T: Trait> Module<T> {
|
||||
let base_slash = Self::current_offline_slash();
|
||||
let instances = slash_count - grace;
|
||||
|
||||
let mut total_slash = T::Balance::default();
|
||||
let mut total_slash = BalanceOf::<T>::default();
|
||||
for i in instances..(instances + count as u32) {
|
||||
if let Some(total) = base_slash.checked_shl(i)
|
||||
.and_then(|slash| total_slash.checked_add(&slash)) {
|
||||
@@ -590,7 +597,7 @@ impl<T: Trait> OnSessionChange<T::Moment> for Module<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> balances::EnsureAccountLiquid<T::AccountId> for Module<T> {
|
||||
impl<T: Trait> EnsureAccountLiquid<T::AccountId> for Module<T> {
|
||||
fn ensure_account_liquid(who: &T::AccountId) -> Result {
|
||||
if Self::bondage(who) <= <system::Module<T>>::block_number() {
|
||||
Ok(())
|
||||
@@ -600,7 +607,7 @@ impl<T: Trait> balances::EnsureAccountLiquid<T::AccountId> for Module<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> balances::OnFreeBalanceZero<T::AccountId> for Module<T> {
|
||||
impl<T: Trait> OnFreeBalanceZero<T::AccountId> for Module<T> {
|
||||
fn on_free_balance_zero(who: &T::AccountId) {
|
||||
<Bondage<T>>::remove(who);
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ impl timestamp::Trait for Test {
|
||||
type OnTimestampSet = ();
|
||||
}
|
||||
impl Trait for Test {
|
||||
type Currency = balances::Module<Self>;
|
||||
type OnRewardMinted = ();
|
||||
type Event = ();
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
use super::*;
|
||||
use runtime_io::with_externalities;
|
||||
use mock::{Balances, Session, Staking, System, Timestamp, Test, new_test_ext, Origin};
|
||||
use runtime_support::traits::Currency;
|
||||
|
||||
#[test]
|
||||
fn note_null_offline_should_work() {
|
||||
|
||||
@@ -94,18 +94,12 @@ macro_rules! decl_event {
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
pub enum Event<$evt_generic_param:ident> where
|
||||
$( $( $generic_rename:ident = )* <$generic:ident as $trait:path>::$trait_type:ident ),*
|
||||
{
|
||||
$(
|
||||
$events:tt
|
||||
)*
|
||||
}
|
||||
$( $tt:tt )*
|
||||
) => {
|
||||
$crate::__decl_generic_event!(
|
||||
$( #[ $attr ] )*;
|
||||
$evt_generic_param;
|
||||
$( $( $generic_rename = )* <$generic as $trait>::$trait_type ),*;
|
||||
Events { $( $events )* };
|
||||
{ $( $tt )* };
|
||||
);
|
||||
};
|
||||
(
|
||||
@@ -139,84 +133,102 @@ macro_rules! decl_event {
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
// This parsing to retrieve last ident on unnamed generic could be improved.
|
||||
// but user can still name it if the parsing fails. And improving parsing seems difficult.
|
||||
macro_rules! __decl_generic_event {
|
||||
(
|
||||
$(#[$attr:meta])*;
|
||||
$event_generic_param:ident;
|
||||
$generic_rename:ident = <$generic:ident as $trait:path>::$trait_type:ident
|
||||
$(, $( $rest_gen_rename:ident = )* <$rest_gen:ident as $rest_trait:path>::$rest_trait_type:ident )*;
|
||||
Events { $( $events:tt )* };
|
||||
{ $( $tt:tt )* };
|
||||
) => {
|
||||
$crate::__decl_generic_event!(
|
||||
$crate::__decl_generic_event!(@format_generic
|
||||
$( #[ $attr ] )*;
|
||||
$event_generic_param;
|
||||
$( $( $rest_gen_rename = )* <$rest_gen as $rest_trait>::$rest_trait_type ),*;
|
||||
Events { $( $events )* };
|
||||
$generic_rename;
|
||||
<$generic as $trait>::$trait_type;
|
||||
{ $( $tt )* };
|
||||
{};
|
||||
);
|
||||
};
|
||||
(
|
||||
// Parse named
|
||||
(@format_generic
|
||||
$(#[$attr:meta])*;
|
||||
$event_generic_param:ident;
|
||||
$generic_rename:ident = <$generic:ident as $trait:path>::$trait_type:ident
|
||||
$(, $( $rest_gen_rename:ident = )* <$rest_gen:ident as $rest_trait:path>::$rest_trait_type:ident )*;
|
||||
Events { $( $events:tt )* };
|
||||
$( $parsed_generic_params:ident ),*;
|
||||
$( <$parsed_generic:ident as $parsed_trait:path>::$parsed_trait_type:ident ),*;
|
||||
{ $generic_rename:ident = $generic_type:ty, $($rest:tt)* };
|
||||
{$( $parsed:tt)*};
|
||||
) => {
|
||||
$crate::__decl_generic_event!(
|
||||
$crate::__decl_generic_event!(@format_generic
|
||||
$( #[ $attr ] )*;
|
||||
$event_generic_param;
|
||||
$( $( $rest_gen_rename = )* <$rest_gen as $rest_trait>::$rest_trait_type ),*;
|
||||
Events { $( $events )* };
|
||||
$( $parsed_generic_params ),*, $generic_rename;
|
||||
$( <$parsed_generic as $parsed_trait>::$parsed_trait_type ),*, <$generic as $trait>::$trait_type;
|
||||
{ $($rest)* };
|
||||
{ $($parsed)*, $generic_rename = $generic_type };
|
||||
);
|
||||
};
|
||||
(
|
||||
// Parse unnamed
|
||||
(@format_generic
|
||||
$(#[$attr:meta])*;
|
||||
$event_generic_param:ident;
|
||||
<$generic:ident as $trait:path>::$trait_type:ident
|
||||
$(, $( $rest_gen_rename:ident = )* <$rest_gen:ident as $rest_trait:path>::$rest_trait_type:ident )*;
|
||||
Events { $( $events:tt )* };
|
||||
{ <$generic:ident as $trait:path>::$trait_type:ident, $($rest:tt)* };
|
||||
{$($parsed:tt)*};
|
||||
) => {
|
||||
$crate::__decl_generic_event!(
|
||||
$crate::__decl_generic_event!(@format_generic
|
||||
$( #[ $attr ] )*;
|
||||
$event_generic_param;
|
||||
$( $( $rest_gen_rename = )* <$rest_gen as $rest_trait>::$rest_trait_type ),*;
|
||||
Events { $( $events )* };
|
||||
$trait_type;
|
||||
<$generic as $trait>::$trait_type;
|
||||
{ $($rest)* };
|
||||
{ $($parsed)*, $trait_type = <$generic as $trait>::$trait_type };
|
||||
);
|
||||
};
|
||||
(
|
||||
// Unnamed type can't be parsed
|
||||
(@format_generic
|
||||
$(#[$attr:meta])*;
|
||||
$event_generic_param:ident;
|
||||
<$generic:ident as $trait:path>::$trait_type:ident
|
||||
$(, $( $rest_gen_rename:ident = )* <$rest_gen:ident as $rest_trait:path>::$rest_trait_type:ident )*;
|
||||
Events { $( $events:tt )* };
|
||||
$( $parsed_generic_params:ident ),*;
|
||||
$( <$parsed_generic:ident as $parsed_trait:path>::$parsed_trait_type:ident ),*;
|
||||
{ $generic_type:ty, $($rest:tt)* };
|
||||
{$($parsed:tt)*};
|
||||
) => {
|
||||
$crate::__decl_generic_event!(
|
||||
$crate::__decl_generic_event!(@cannot_parse $generic_type);
|
||||
};
|
||||
// Finish formatting on an unnamed one
|
||||
(@format_generic
|
||||
$(#[$attr:meta])*;
|
||||
$event_generic_param:ident;
|
||||
{ <$generic:ident as $trait:path>::$trait_type:ident { $( $events:tt )* } };
|
||||
{$( $parsed:tt)*};
|
||||
) => {
|
||||
$crate::__decl_generic_event!(@generate
|
||||
$( #[ $attr ] )*;
|
||||
$event_generic_param;
|
||||
$( $( $rest_gen_rename = )* <$rest_gen as $rest_trait>::$rest_trait_type ),*;
|
||||
Events { $( $events )* };
|
||||
$( $parsed_generic_params ),*, $trait_type;
|
||||
$( <$parsed_generic as $parsed_trait>::$parsed_trait_type ),*, <$generic as $trait>::$trait_type;
|
||||
{ $($events)* };
|
||||
{ $($parsed)*, $trait_type = <$generic as $trait>::$trait_type};
|
||||
);
|
||||
};
|
||||
(
|
||||
// Finish formatting on a named one
|
||||
(@format_generic
|
||||
$(#[$attr:meta])*;
|
||||
$event_generic_param:ident;
|
||||
;
|
||||
Events { $( $events:tt )* };
|
||||
$( $generic_param:ident ),*;
|
||||
$( <$generic:ident as $trait:path>::$trait_type:ident ),*;
|
||||
{ $generic_rename:ident = $generic_type:ty { $( $events:tt )* } };
|
||||
{$( $parsed:tt)*};
|
||||
) => {
|
||||
pub type Event<$event_generic_param> = RawEvent<$( <$generic as $trait>::$trait_type ),*>;
|
||||
$crate::__decl_generic_event!(@generate
|
||||
$(#[$attr])*;
|
||||
$event_generic_param;
|
||||
{ $($events)* };
|
||||
{ $($parsed)*, $generic_rename = $generic_type};
|
||||
);
|
||||
};
|
||||
// Final unnamed type can't be parsed
|
||||
(@format_generic
|
||||
$(#[$attr:meta])*;
|
||||
$event_generic_param:ident;
|
||||
{ $generic_type:ty { $( $events:tt )* } };
|
||||
{$( $parsed:tt)*};
|
||||
) => {
|
||||
$crate::__decl_generic_event!(@cannot_parse $generic_type);
|
||||
};
|
||||
(@generate
|
||||
$(#[$attr:meta])*;
|
||||
$event_generic_param:ident;
|
||||
{ $( $events:tt )* };
|
||||
{ ,$( $generic_param:ident = $generic_type:ty ),* };
|
||||
) => {
|
||||
pub type Event<$event_generic_param> = RawEvent<$( $generic_type ),*>;
|
||||
// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
|
||||
#[derive(Clone, PartialEq, Eq, $crate::parity_codec_derive::Encode, $crate::parity_codec_derive::Decode)]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
@@ -235,6 +247,9 @@ macro_rules! __decl_generic_event {
|
||||
$crate::__events_to_metadata!(; $( $events )* )
|
||||
}
|
||||
}
|
||||
};
|
||||
(@cannot_parse $ty:ty) => {
|
||||
compile_error!(concat!("The type `", stringify!($ty), "` can't be parsed as an unnamed one, please name it `Name = ", stringify!($ty), "`"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ mod runtime;
|
||||
#[macro_use]
|
||||
pub mod inherent;
|
||||
mod double_map;
|
||||
pub mod traits;
|
||||
|
||||
pub use self::storage::{StorageVec, StorageList, StorageValue, StorageMap};
|
||||
pub use self::hashable::Hashable;
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Traits for SRML
|
||||
|
||||
use crate::rstd::result;
|
||||
use crate::codec::Codec;
|
||||
use crate::runtime_primitives::traits::{MaybeSerializeDebug, SimpleArithmetic, As};
|
||||
|
||||
/// The account with the given id was killed.
|
||||
pub trait OnFreeBalanceZero<AccountId> {
|
||||
/// The account was the given id was killed.
|
||||
fn on_free_balance_zero(who: &AccountId);
|
||||
}
|
||||
|
||||
impl<AccountId> OnFreeBalanceZero<AccountId> for () {
|
||||
fn on_free_balance_zero(_who: &AccountId) {}
|
||||
}
|
||||
impl<
|
||||
AccountId,
|
||||
X: OnFreeBalanceZero<AccountId>,
|
||||
Y: OnFreeBalanceZero<AccountId>,
|
||||
> OnFreeBalanceZero<AccountId> for (X, Y) {
|
||||
fn on_free_balance_zero(who: &AccountId) {
|
||||
X::on_free_balance_zero(who);
|
||||
Y::on_free_balance_zero(who);
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for a hook to get called when some balance has been minted, causing dilution.
|
||||
pub trait OnDilution<Balance> {
|
||||
/// Some `portion` of the total balance just "grew" by `minted`. `portion` is the pre-growth
|
||||
/// amount (it doesn't take account of the recent growth).
|
||||
fn on_dilution(minted: Balance, portion: Balance);
|
||||
}
|
||||
|
||||
impl<Balance> OnDilution<Balance> for () {
|
||||
fn on_dilution(_minted: Balance, _portion: Balance) {}
|
||||
}
|
||||
|
||||
/// Determinator for whether a given account is able to transfer balance.
|
||||
pub trait EnsureAccountLiquid<AccountId> {
|
||||
/// Returns `Ok` iff the account is able to transfer funds normally. `Err(...)`
|
||||
/// with the reason why not otherwise.
|
||||
fn ensure_account_liquid(who: &AccountId) -> result::Result<(), &'static str>;
|
||||
}
|
||||
impl<
|
||||
AccountId,
|
||||
X: EnsureAccountLiquid<AccountId>,
|
||||
Y: EnsureAccountLiquid<AccountId>,
|
||||
> EnsureAccountLiquid<AccountId> for (X, Y) {
|
||||
fn ensure_account_liquid(who: &AccountId) -> result::Result<(), &'static str> {
|
||||
X::ensure_account_liquid(who)?;
|
||||
Y::ensure_account_liquid(who)
|
||||
}
|
||||
}
|
||||
impl<AccountId> EnsureAccountLiquid<AccountId> for () {
|
||||
fn ensure_account_liquid(_who: &AccountId) -> result::Result<(), &'static str> { Ok(()) }
|
||||
}
|
||||
|
||||
/// Outcome of a balance update.
|
||||
pub enum UpdateBalanceOutcome {
|
||||
/// Account balance was simply updated.
|
||||
Updated,
|
||||
/// The update has led to killing of the account.
|
||||
AccountKilled,
|
||||
}
|
||||
|
||||
/// Abstraction over a fungible assets system.
|
||||
pub trait Currency<AccountId> {
|
||||
/// The balance of an account.
|
||||
type Balance: SimpleArithmetic + As<usize> + As<u64> + Codec + Copy + MaybeSerializeDebug + Default;
|
||||
|
||||
// PUBLIC IMMUTABLES
|
||||
|
||||
/// The combined balance of `who`.
|
||||
fn total_balance(who: &AccountId) -> Self::Balance;
|
||||
|
||||
/// Some result as `slash(who, value)` (but without the side-effects) assuming there are no
|
||||
/// balance changes in the meantime and only the reserved balance is not taken into account.
|
||||
fn can_slash(who: &AccountId, value: Self::Balance) -> bool;
|
||||
|
||||
/// Same result as `reserve(who, value)` (but without the side-effects) assuming there
|
||||
/// are no balance changes in the meantime.
|
||||
fn can_reserve(who: &AccountId, value: Self::Balance) -> bool;
|
||||
|
||||
/// The total amount of stake on the system.
|
||||
fn total_issuance() -> Self:: Balance;
|
||||
|
||||
/// The 'free' balance of a given account.
|
||||
///
|
||||
/// This is the only balance that matters in terms of most operations on tokens. It is
|
||||
/// alone used to determine the balance when in the contract execution environment. When this
|
||||
/// balance falls below the value of `ExistentialDeposit`, then the 'current account' is
|
||||
/// deleted: specifically `FreeBalance`. Furthermore, `OnFreeBalanceZero` callback
|
||||
/// is invoked, giving a chance to external modules to cleanup data associated with
|
||||
/// the deleted account.
|
||||
///
|
||||
/// `system::AccountNonce` is also deleted if `ReservedBalance` is also zero (it also gets
|
||||
/// collapsed to zero if it ever becomes less than `ExistentialDeposit`.
|
||||
fn free_balance(who: &AccountId) -> Self::Balance;
|
||||
|
||||
/// The amount of the balance of a given account that is externally reserved; this can still get
|
||||
/// slashed, but gets slashed last of all.
|
||||
///
|
||||
/// This balance is a 'reserve' balance that other subsystems use in order to set aside tokens
|
||||
/// that are still 'owned' by the account holder, but which are suspendable. (This is different
|
||||
/// and wholly unrelated to the `Bondage` system used in the staking module.)
|
||||
///
|
||||
/// When this balance falls below the value of `ExistentialDeposit`, then this 'reserve account'
|
||||
/// is deleted: specifically, `ReservedBalance`.
|
||||
///
|
||||
/// `system::AccountNonce` is also deleted if `FreeBalance` is also zero (it also gets
|
||||
/// collapsed to zero if it ever becomes less than `ExistentialDeposit`.
|
||||
fn reserved_balance(who: &AccountId) -> Self::Balance;
|
||||
|
||||
// PUBLIC MUTABLES (DANGEROUS)
|
||||
|
||||
/// Deducts up to `value` from the combined balance of `who`, preferring to deduct from the
|
||||
/// free balance. This function cannot fail.
|
||||
///
|
||||
/// As much funds up to `value` will be deducted as possible. If this is less than `value`,
|
||||
/// then `Some(remaining)` will be returned. Full completion is given by `None`.
|
||||
fn slash(who: &AccountId, value: Self::Balance) -> Option<Self::Balance>;
|
||||
|
||||
/// Adds up to `value` to the free balance of `who`.
|
||||
///
|
||||
/// If `who` doesn't exist, nothing is done and an Err returned.
|
||||
fn reward(who: &AccountId, value: Self::Balance) -> result::Result<(), &'static str>;
|
||||
|
||||
/// Adds up to `value` to the free balance of `who`.
|
||||
///
|
||||
/// If `who` doesn't exist, it is created
|
||||
///
|
||||
/// Returns if the account was successfully updated or update has led to killing of the account.
|
||||
///
|
||||
/// NOTE: This assumes that the total stake remains unchanged after this operation.
|
||||
fn increase_free_balance_creating(who: &AccountId, value: Self::Balance) -> UpdateBalanceOutcome;
|
||||
|
||||
/// Moves `value` from balance to reserved balance.
|
||||
///
|
||||
/// If the free balance is lower than `value`, then no funds will be moved and an `Err` will
|
||||
/// be returned to notify of this. This is different behaviour to `unreserve`.
|
||||
fn reserve(who: &AccountId, value: Self::Balance) -> result::Result<(), &'static str>;
|
||||
|
||||
/// Moves up to `value` from reserved balance to balance. This function cannot fail.
|
||||
///
|
||||
/// As much funds up to `value` will be deducted as possible. If this is less than `value`,
|
||||
/// then `Some(remaining)` will be returned. Full completion is given by `None`.
|
||||
/// NOTE: This is different to `reserve`.
|
||||
fn unreserve(who: &AccountId, value: Self::Balance) -> Option<Self::Balance>;
|
||||
|
||||
/// Deducts up to `value` from reserved balance of `who`. This function cannot fail.
|
||||
///
|
||||
/// As much funds up to `value` will be deducted as possible. If this is less than `value`,
|
||||
/// then `Some(remaining)` will be returned. Full completion is given by `None`.
|
||||
fn slash_reserved(who: &AccountId, value: Self::Balance) -> Option<Self::Balance>;
|
||||
|
||||
/// Moves up to `value` from reserved balance of account `slashed` to free balance of account
|
||||
/// `beneficiary`. `beneficiary` must exist for this to succeed. If it does not, `Err` will be
|
||||
/// returned.
|
||||
///
|
||||
/// As much funds up to `value` will be moved as possible. If this is less than `value`, then
|
||||
/// `Ok(Some(remaining))` will be returned. Full completion is given by `Ok(None)`.
|
||||
fn repatriate_reserved(
|
||||
slashed: &AccountId,
|
||||
beneficiary: &AccountId,
|
||||
value: Self::Balance
|
||||
) -> result::Result<Option<Self::Balance>, &'static str>;
|
||||
}
|
||||
@@ -25,6 +25,11 @@ extern crate srml_support as runtime_support;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate sr_io as runtime_io;
|
||||
#[cfg(test)]
|
||||
extern crate substrate_primitives;
|
||||
#[cfg(test)]
|
||||
extern crate srml_balances as balances;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
extern crate serde;
|
||||
|
||||
@@ -32,24 +37,26 @@ extern crate serde;
|
||||
extern crate parity_codec_derive;
|
||||
|
||||
extern crate parity_codec as codec;
|
||||
#[cfg(test)]
|
||||
extern crate substrate_primitives;
|
||||
extern crate sr_primitives as runtime_primitives;
|
||||
extern crate srml_system as system;
|
||||
extern crate srml_balances as balances;
|
||||
|
||||
use rstd::prelude::*;
|
||||
use runtime_support::{StorageValue, StorageMap};
|
||||
use runtime_support::traits::{Currency, OnDilution};
|
||||
use runtime_primitives::{Permill, traits::{Zero, EnsureOrigin, StaticLookup}};
|
||||
use balances::OnDilution;
|
||||
use system::ensure_signed;
|
||||
|
||||
type BalanceOf<T> = <<T as Trait>::Currency as Currency<<T as system::Trait>::AccountId>>::Balance;
|
||||
|
||||
/// Our module's configuration trait. All our types and consts go in here. If the
|
||||
/// module is dependent on specific other modules, then their configuration traits
|
||||
/// should be added to our implied traits list.
|
||||
///
|
||||
/// `system::Trait` should always be included in our implied traits.
|
||||
pub trait Trait: balances::Trait {
|
||||
pub trait Trait: system::Trait {
|
||||
/// The staking balance.
|
||||
type Currency: Currency<Self::AccountId>;
|
||||
|
||||
/// Origin from which approvals must come.
|
||||
type ApproveOrigin: EnsureOrigin<Self::Origin>;
|
||||
|
||||
@@ -73,14 +80,14 @@ decl_module! {
|
||||
/// proposal is awarded.
|
||||
fn propose_spend(
|
||||
origin,
|
||||
#[compact] value: T::Balance,
|
||||
#[compact] value: BalanceOf<T>,
|
||||
beneficiary: <T::Lookup as StaticLookup>::Source
|
||||
) {
|
||||
let proposer = ensure_signed(origin)?;
|
||||
let beneficiary = T::Lookup::lookup(beneficiary)?;
|
||||
|
||||
let bond = Self::calculate_bond(value);
|
||||
<balances::Module<T>>::reserve(&proposer, bond)
|
||||
T::Currency::reserve(&proposer, bond)
|
||||
.map_err(|_| "Proposer's balance too low")?;
|
||||
|
||||
let c = Self::proposal_count();
|
||||
@@ -91,7 +98,7 @@ decl_module! {
|
||||
}
|
||||
|
||||
/// Set the balance of funds available to spend.
|
||||
fn set_pot(#[compact] new_pot: T::Balance) {
|
||||
fn set_pot(#[compact] new_pot: BalanceOf<T>) {
|
||||
// Put the new value into storage.
|
||||
<Pot<T>>::put(new_pot);
|
||||
}
|
||||
@@ -99,7 +106,7 @@ decl_module! {
|
||||
/// (Re-)configure this module.
|
||||
fn configure(
|
||||
#[compact] proposal_bond: Permill,
|
||||
#[compact] proposal_bond_minimum: T::Balance,
|
||||
#[compact] proposal_bond_minimum: BalanceOf<T>,
|
||||
#[compact] spend_period: T::BlockNumber,
|
||||
#[compact] burn: Permill
|
||||
) {
|
||||
@@ -115,7 +122,7 @@ decl_module! {
|
||||
let proposal = <Proposals<T>>::take(proposal_id).ok_or("No proposal at that index")?;
|
||||
|
||||
let value = proposal.bond;
|
||||
let _ = <balances::Module<T>>::slash_reserved(&proposal.proposer, value);
|
||||
let _ = T::Currency::slash_reserved(&proposal.proposer, value);
|
||||
}
|
||||
|
||||
/// Approve a proposal. At a later time, the proposal will be allocated to the beneficiary
|
||||
@@ -156,7 +163,7 @@ decl_storage! {
|
||||
ProposalBond get(proposal_bond) config(): Permill;
|
||||
|
||||
/// Minimum amount of funds that should be placed in a deposit for making a proposal.
|
||||
ProposalBondMinimum get(proposal_bond_minimum) config(): T::Balance;
|
||||
ProposalBondMinimum get(proposal_bond_minimum) config(): BalanceOf<T>;
|
||||
|
||||
/// Period between successive spends.
|
||||
SpendPeriod get(spend_period) config(): T::BlockNumber = runtime_primitives::traits::One::one();
|
||||
@@ -167,13 +174,13 @@ decl_storage! {
|
||||
// State...
|
||||
|
||||
/// Total funds available to this module for spending.
|
||||
Pot get(pot): T::Balance;
|
||||
Pot get(pot): BalanceOf<T>;
|
||||
|
||||
/// Number of proposals that have been made.
|
||||
ProposalCount get(proposal_count): ProposalIndex;
|
||||
|
||||
/// Proposals that have been made.
|
||||
Proposals get(proposals): map ProposalIndex => Option<Proposal<T::AccountId, T::Balance>>;
|
||||
Proposals get(proposals): map ProposalIndex => Option<Proposal<T::AccountId, BalanceOf<T>>>;
|
||||
|
||||
/// Proposal indices that have been approved but not yet awarded.
|
||||
Approvals get(approvals): Vec<ProposalIndex>;
|
||||
@@ -182,7 +189,11 @@ decl_storage! {
|
||||
|
||||
/// An event in this module.
|
||||
decl_event!(
|
||||
pub enum Event<T> where <T as balances::Trait>::Balance, <T as system::Trait>::AccountId {
|
||||
pub enum Event<T>
|
||||
where
|
||||
Balance = <<T as Trait>::Currency as Currency<<T as system::Trait>::AccountId>>::Balance,
|
||||
<T as system::Trait>::AccountId
|
||||
{
|
||||
/// New proposal.
|
||||
Proposed(ProposalIndex),
|
||||
/// We have ended a spend period and will now allocate funds.
|
||||
@@ -200,7 +211,7 @@ impl<T: Trait> Module<T> {
|
||||
// Add public immutables and private mutables.
|
||||
|
||||
/// The needed bond for a proposal whose spend is `value`.
|
||||
fn calculate_bond(value: T::Balance) -> T::Balance {
|
||||
fn calculate_bond(value: BalanceOf<T>) -> BalanceOf<T> {
|
||||
Self::proposal_bond_minimum().max(Self::proposal_bond() * value)
|
||||
}
|
||||
|
||||
@@ -219,10 +230,10 @@ impl<T: Trait> Module<T> {
|
||||
<Proposals<T>>::remove(index);
|
||||
|
||||
// return their deposit.
|
||||
let _ = <balances::Module<T>>::unreserve(&p.proposer, p.bond);
|
||||
let _ = T::Currency::unreserve(&p.proposer, p.bond);
|
||||
|
||||
// provide the allocation.
|
||||
<balances::Module<T>>::increase_free_balance_creating(&p.beneficiary, p.value);
|
||||
T::Currency::increase_free_balance_creating(&p.beneficiary, p.value);
|
||||
|
||||
Self::deposit_event(RawEvent::Awarded(index, p.value, p.beneficiary));
|
||||
false
|
||||
@@ -249,12 +260,12 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> OnDilution<T::Balance> for Module<T> {
|
||||
fn on_dilution(minted: T::Balance, portion: T::Balance) {
|
||||
impl<T: Trait> OnDilution<BalanceOf<T>> for Module<T> {
|
||||
fn on_dilution(minted: BalanceOf<T>, portion: BalanceOf<T>) {
|
||||
// Mint extra funds for the treasury to keep the ratio of portion to total_issuance equal
|
||||
// pre dilution and post-dilution.
|
||||
if !minted.is_zero() && !portion.is_zero() {
|
||||
let total_issuance = <balances::Module<T>>::total_issuance();
|
||||
let total_issuance = T::Currency::total_issuance();
|
||||
let funding = (total_issuance - portion) / portion * minted;
|
||||
<Pot<T>>::mutate(|x| *x += funding);
|
||||
}
|
||||
@@ -298,6 +309,7 @@ mod tests {
|
||||
type Event = ();
|
||||
}
|
||||
impl Trait for Test {
|
||||
type Currency = balances::Module<Test>;
|
||||
type ApproveOrigin = system::EnsureRoot<u64>;
|
||||
type RejectOrigin = system::EnsureRoot<u64>;
|
||||
type Event = ();
|
||||
|
||||
Reference in New Issue
Block a user