|
@@ -132,7 +132,7 @@ mod tests;
|
|
|
use codec::Decode;
|
|
|
use frame_support::dispatch::{DispatchError, DispatchResult, UnfilteredDispatchable};
|
|
|
use frame_support::storage::IterableStorageMap;
|
|
|
-use frame_support::traits::Get;
|
|
|
+use frame_support::traits::{Currency, Get, LockIdentifier, LockableCurrency, WithdrawReasons};
|
|
|
use frame_support::{
|
|
|
decl_error, decl_event, decl_module, decl_storage, ensure, print, Parameter, StorageDoubleMap,
|
|
|
};
|
|
@@ -141,9 +141,12 @@ use sp_std::vec::Vec;
|
|
|
use system::{ensure_root, RawOrigin};
|
|
|
|
|
|
use common::origin::ActorOriginValidator;
|
|
|
+use frame_support::sp_std::marker::PhantomData;
|
|
|
|
|
|
/// Proposals engine trait.
|
|
|
-pub trait Trait: system::Trait + pallet_timestamp::Trait + membership::Trait {
|
|
|
+pub trait Trait:
|
|
|
+ system::Trait + pallet_timestamp::Trait + membership::Trait + balances::Trait
|
|
|
+{
|
|
|
/// Engine event type.
|
|
|
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
|
|
|
|
|
@@ -288,6 +291,9 @@ decl_error! {
|
|
|
|
|
|
/// Exact execution block cannot be less than current_block.
|
|
|
InvalidExactExecutionBlock,
|
|
|
+
|
|
|
+ /// There is not enough balance for a stake.
|
|
|
+ InsufficientBalanceForStake,
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -464,6 +470,14 @@ impl<T: Trait> Module<T> {
|
|
|
let proposal_id = T::ProposalId::from(new_proposal_id);
|
|
|
|
|
|
let stake_data = if let Some(stake_balance) = creation_params.stake_balance {
|
|
|
+ ensure!(
|
|
|
+ T::StakingHandler::is_enough_balance_for_stake(
|
|
|
+ &creation_params.account_id,
|
|
|
+ stake_balance
|
|
|
+ ),
|
|
|
+ Error::<T>::InsufficientBalanceForStake
|
|
|
+ );
|
|
|
+
|
|
|
T::StakingHandler::lock(&creation_params.account_id, stake_balance);
|
|
|
|
|
|
Some(ActiveStake {
|
|
@@ -845,3 +859,96 @@ type ProposalOf<T> = Proposal<
|
|
|
BalanceOfCurrency<T>,
|
|
|
<T as system::Trait>::AccountId,
|
|
|
>;
|
|
|
+
|
|
|
+pub struct StakingManager<T: Trait, LockId: Get<LockIdentifier>> {
|
|
|
+ trait_marker: PhantomData<T>,
|
|
|
+ lock_id_marker: PhantomData<LockId>,
|
|
|
+}
|
|
|
+
|
|
|
+impl<T: Trait, LockId: Get<LockIdentifier>> StakingHandler<T> for StakingManager<T, LockId> {
|
|
|
+ fn lock(account_id: &T::AccountId, amount: BalanceOfCurrency<T>) {
|
|
|
+ <balances::Module<T>>::set_lock(LockId::get(), &account_id, amount, WithdrawReasons::all())
|
|
|
+ }
|
|
|
+
|
|
|
+ fn unlock(account_id: &T::AccountId) {
|
|
|
+ T::Currency::remove_lock(LockId::get(), &account_id);
|
|
|
+ }
|
|
|
+
|
|
|
+ fn slash(
|
|
|
+ account_id: &T::AccountId,
|
|
|
+ amount: Option<BalanceOfCurrency<T>>,
|
|
|
+ ) -> BalanceOfCurrency<T> {
|
|
|
+ let locks = <balances::Module<T>>::locks(&account_id);
|
|
|
+
|
|
|
+ let existing_lock = locks.iter().find(|lock| lock.id == LockId::get());
|
|
|
+
|
|
|
+ let mut actually_slashed_balance = Default::default();
|
|
|
+ if let Some(existing_lock) = existing_lock {
|
|
|
+ Self::unlock(&account_id);
|
|
|
+
|
|
|
+ let mut slashable_amount = existing_lock.amount;
|
|
|
+ if let Some(amount) = amount {
|
|
|
+ if existing_lock.amount > amount {
|
|
|
+ let new_amount = existing_lock.amount - amount;
|
|
|
+ Self::lock(&account_id, new_amount);
|
|
|
+
|
|
|
+ slashable_amount = amount;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ let _ = <balances::Module<T>>::slash(&account_id, slashable_amount);
|
|
|
+
|
|
|
+ actually_slashed_balance = slashable_amount
|
|
|
+ }
|
|
|
+
|
|
|
+ actually_slashed_balance
|
|
|
+ }
|
|
|
+
|
|
|
+ fn set_stake(account_id: &T::AccountId, new_stake: BalanceOfCurrency<T>) -> DispatchResult {
|
|
|
+ let current_stake = Self::current_stake(account_id);
|
|
|
+
|
|
|
+ //Unlock previous stake if its not zero.
|
|
|
+ if current_stake > Zero::zero() {
|
|
|
+ Self::unlock(account_id);
|
|
|
+ }
|
|
|
+
|
|
|
+ if !Self::is_enough_balance_for_stake(account_id, new_stake) {
|
|
|
+ //Restore previous stake if its not zero.
|
|
|
+ if current_stake > Zero::zero() {
|
|
|
+ Self::lock(account_id, current_stake);
|
|
|
+ }
|
|
|
+ return Err(DispatchError::Other("Not enough balance for a new stake."));
|
|
|
+ }
|
|
|
+
|
|
|
+ Self::lock(account_id, new_stake);
|
|
|
+
|
|
|
+ Ok(())
|
|
|
+ }
|
|
|
+
|
|
|
+ fn is_member_staking_account(_member_id: &MemberId<T>, _account_id: &T::AccountId) -> bool {
|
|
|
+ true
|
|
|
+ }
|
|
|
+
|
|
|
+ fn is_account_free_of_conflicting_stakes(account_id: &T::AccountId) -> bool {
|
|
|
+ let locks = <balances::Module<T>>::locks(&account_id);
|
|
|
+
|
|
|
+ let existing_lock = locks.iter().find(|lock| lock.id == LockId::get());
|
|
|
+
|
|
|
+ existing_lock.is_none()
|
|
|
+ }
|
|
|
+
|
|
|
+ fn is_enough_balance_for_stake(
|
|
|
+ account_id: &T::AccountId,
|
|
|
+ amount: BalanceOfCurrency<T>,
|
|
|
+ ) -> bool {
|
|
|
+ <balances::Module<T>>::usable_balance(account_id) >= amount
|
|
|
+ }
|
|
|
+
|
|
|
+ fn current_stake(account_id: &T::AccountId) -> BalanceOfCurrency<T> {
|
|
|
+ let locks = <balances::Module<T>>::locks(&account_id);
|
|
|
+
|
|
|
+ let existing_lock = locks.iter().find(|lock| lock.id == LockId::get());
|
|
|
+
|
|
|
+ existing_lock.map_or(Zero::zero(), |lock| lock.amount)
|
|
|
+ }
|
|
|
+}
|