|
@@ -20,12 +20,11 @@
|
|
|
// TODO: Test StakingEventHandler
|
|
|
// TODO: Test refund_proposal_stake()
|
|
|
|
|
|
-pub use types::CouncilManager;
|
|
|
use types::FinalizedProposalData;
|
|
|
use types::ProposalStakeManager;
|
|
|
pub use types::{
|
|
|
- ApprovedProposalStatus, FinalizationData, Proposal, ProposalDecisionStatus, ProposalParameters,
|
|
|
- ProposalStatus, StakeData, StakingEventsHandler, VotingResults,
|
|
|
+ ActiveStake, ApprovedProposalStatus, FinalizationData, Proposal, ProposalDecisionStatus,
|
|
|
+ ProposalParameters, ProposalStatus, VotingResults,
|
|
|
};
|
|
|
pub use types::{BalanceOf, CurrencyOf, NegativeImbalance};
|
|
|
pub use types::{DefaultStakeHandlerProvider, StakeHandler, StakeHandlerProvider};
|
|
@@ -42,21 +41,19 @@ use rstd::prelude::*;
|
|
|
use sr_primitives::traits::{DispatchResult, Zero};
|
|
|
use srml_support::traits::{Currency, Get};
|
|
|
use srml_support::{
|
|
|
- decl_error, decl_event, decl_module, decl_storage, ensure, Parameter, StorageDoubleMap,
|
|
|
+ decl_error, decl_event, decl_module, decl_storage, ensure, print, Parameter, StorageDoubleMap,
|
|
|
};
|
|
|
use system::{ensure_root, RawOrigin};
|
|
|
|
|
|
+use crate::types::ApprovedProposalData;
|
|
|
use common::origin_validator::ActorOriginValidator;
|
|
|
-use membership::origin_validator::MemberId;
|
|
|
use srml_support::dispatch::Dispatchable;
|
|
|
|
|
|
+type MemberId<T> = <T as membership::members::Trait>::MemberId;
|
|
|
+
|
|
|
/// Proposals engine trait.
|
|
|
pub trait Trait:
|
|
|
- system::Trait
|
|
|
- + timestamp::Trait
|
|
|
- + stake::Trait
|
|
|
- + membership::members::Trait
|
|
|
- + governance::council::Trait
|
|
|
+ system::Trait + timestamp::Trait + stake::Trait + membership::members::Trait
|
|
|
{
|
|
|
/// Engine event type.
|
|
|
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
|
|
@@ -96,7 +93,7 @@ pub trait Trait:
|
|
|
type MaxActiveProposalLimit: Get<u32>;
|
|
|
|
|
|
/// Proposals executable code. Can be instantiated by external module Call enum members.
|
|
|
- type ProposalCode: Parameter + Dispatchable<Origin = Self::Origin> + Default;
|
|
|
+ type DispatchableCallCode: Parameter + Dispatchable<Origin = Self::Origin> + Default;
|
|
|
}
|
|
|
|
|
|
decl_event!(
|
|
@@ -106,6 +103,8 @@ decl_event!(
|
|
|
<T as Trait>::ProposalId,
|
|
|
MemberId = MemberId<T>,
|
|
|
<T as system::Trait>::BlockNumber,
|
|
|
+ <T as system::Trait>::AccountId,
|
|
|
+ <T as stake::Trait>::StakeId,
|
|
|
{
|
|
|
/// Emits on proposal creation.
|
|
|
/// Params:
|
|
@@ -117,7 +116,7 @@ decl_event!(
|
|
|
/// Params:
|
|
|
/// - Id of a updated proposal.
|
|
|
/// - New proposal status
|
|
|
- ProposalStatusUpdated(ProposalId, ProposalStatus<BlockNumber>),
|
|
|
+ ProposalStatusUpdated(ProposalId, ProposalStatus<BlockNumber, StakeId, AccountId>),
|
|
|
|
|
|
/// Emits on voting for the proposal
|
|
|
/// Params:
|
|
@@ -191,13 +190,13 @@ impl From<system::Error> for Error {
|
|
|
decl_storage! {
|
|
|
pub trait Store for Module<T: Trait> as ProposalEngine{
|
|
|
/// Map proposal by its id.
|
|
|
- pub Proposals get(fn proposals): map T::ProposalId => ProposalObject<T>;
|
|
|
+ pub Proposals get(fn proposals): map T::ProposalId => ProposalOf<T>;
|
|
|
|
|
|
/// Count of all proposals that have been created.
|
|
|
pub ProposalCount get(fn proposal_count): u32;
|
|
|
|
|
|
/// Map proposal executable code by proposal id.
|
|
|
- pub ProposalCode get(fn proposal_codes): map T::ProposalId => Vec<u8>;
|
|
|
+ pub DispatchableCallCode get(fn proposal_codes): map T::ProposalId => Vec<u8>;
|
|
|
|
|
|
/// Count of active proposals.
|
|
|
pub ActiveProposalCount get(fn active_proposal_count): u32;
|
|
@@ -236,7 +235,7 @@ decl_module! {
|
|
|
ensure!(<Proposals<T>>::exists(proposal_id), Error::ProposalNotFound);
|
|
|
let mut proposal = Self::proposals(proposal_id);
|
|
|
|
|
|
- ensure!(proposal.status == ProposalStatus::Active, Error::ProposalFinalized);
|
|
|
+ ensure!(matches!(proposal.status, ProposalStatus::Active{..}), Error::ProposalFinalized);
|
|
|
|
|
|
let did_not_vote_before = !<VoteExistsByProposalByVoter<T>>::exists(
|
|
|
proposal_id,
|
|
@@ -265,7 +264,7 @@ decl_module! {
|
|
|
let proposal = Self::proposals(proposal_id);
|
|
|
|
|
|
ensure!(proposer_id == proposal.proposer_id, Error::NotAuthor);
|
|
|
- ensure!(proposal.status == ProposalStatus::Active, Error::ProposalFinalized);
|
|
|
+ ensure!(matches!(proposal.status, ProposalStatus::Active{..}), Error::ProposalFinalized);
|
|
|
|
|
|
// mutation
|
|
|
|
|
@@ -279,7 +278,7 @@ decl_module! {
|
|
|
ensure!(<Proposals<T>>::exists(proposal_id), Error::ProposalNotFound);
|
|
|
let proposal = Self::proposals(proposal_id);
|
|
|
|
|
|
- ensure!(proposal.status == ProposalStatus::Active, Error::ProposalFinalized);
|
|
|
+ ensure!(matches!(proposal.status, ProposalStatus::Active{..}), Error::ProposalFinalized);
|
|
|
|
|
|
// mutation
|
|
|
|
|
@@ -300,12 +299,12 @@ decl_module! {
|
|
|
Self::finalize_proposal(proposal_data.proposal_id, proposal_data.status);
|
|
|
}
|
|
|
|
|
|
- let executable_proposal_ids =
|
|
|
- Self::get_approved_proposal_with_expired_grace_period_ids();
|
|
|
+ let executable_proposals =
|
|
|
+ Self::get_approved_proposal_with_expired_grace_period();
|
|
|
|
|
|
// Execute approved proposals with expired grace period
|
|
|
- for proposal_id in executable_proposal_ids {
|
|
|
- Self::execute_proposal(proposal_id);
|
|
|
+ for approved_proosal in executable_proposals {
|
|
|
+ Self::execute_proposal(approved_proosal);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -314,17 +313,14 @@ decl_module! {
|
|
|
impl<T: Trait> Module<T> {
|
|
|
/// Create proposal. Requires 'proposal origin' membership.
|
|
|
pub fn create_proposal(
|
|
|
- origin: T::Origin,
|
|
|
+ account_id: T::AccountId,
|
|
|
proposer_id: MemberId<T>,
|
|
|
parameters: ProposalParameters<T::BlockNumber, types::BalanceOf<T>>,
|
|
|
title: Vec<u8>,
|
|
|
description: Vec<u8>,
|
|
|
stake_balance: Option<types::BalanceOf<T>>,
|
|
|
- proposal_code: Vec<u8>,
|
|
|
+ encoded_dispatchable_call_code: Vec<u8>,
|
|
|
) -> Result<T::ProposalId, Error> {
|
|
|
- let account_id =
|
|
|
- T::ProposerOriginValidator::ensure_actor_origin(origin, proposer_id.clone())?;
|
|
|
-
|
|
|
Self::ensure_create_proposal_parameters_are_valid(
|
|
|
¶meters,
|
|
|
&title,
|
|
@@ -349,7 +345,7 @@ impl<T: Trait> Module<T> {
|
|
|
|
|
|
let mut stake_data = None;
|
|
|
if let Some(stake_id) = stake_id_result {
|
|
|
- stake_data = Some(StakeData {
|
|
|
+ stake_data = Some(ActiveStake {
|
|
|
stake_id,
|
|
|
source_account_id: account_id,
|
|
|
});
|
|
@@ -363,13 +359,12 @@ impl<T: Trait> Module<T> {
|
|
|
title,
|
|
|
description,
|
|
|
proposer_id: proposer_id.clone(),
|
|
|
- status: ProposalStatus::Active,
|
|
|
+ status: ProposalStatus::Active(stake_data),
|
|
|
voting_results: VotingResults::default(),
|
|
|
- stake_data,
|
|
|
};
|
|
|
|
|
|
<Proposals<T>>::insert(proposal_id, new_proposal);
|
|
|
- <ProposalCode<T>>::insert(proposal_id, proposal_code);
|
|
|
+ <DispatchableCallCode<T>>::insert(proposal_id, encoded_dispatchable_call_code);
|
|
|
<ActiveProposalIds<T>>::insert(proposal_id, ());
|
|
|
ProposalCount::put(next_proposal_count_value);
|
|
|
Self::increase_active_proposal_counter();
|
|
@@ -378,6 +373,87 @@ impl<T: Trait> Module<T> {
|
|
|
|
|
|
Ok(proposal_id)
|
|
|
}
|
|
|
+
|
|
|
+ /// Performs all checks for the proposal creation:
|
|
|
+ /// - title, body lengths
|
|
|
+ /// - mac active proposal
|
|
|
+ /// - provided parameters: approval_threshold_percentage and slashing_threshold_percentage > 0
|
|
|
+ /// - provided stake balance and parameters.required_stake are valid
|
|
|
+ pub fn ensure_create_proposal_parameters_are_valid(
|
|
|
+ parameters: &ProposalParameters<T::BlockNumber, types::BalanceOf<T>>,
|
|
|
+ title: &[u8],
|
|
|
+ description: &[u8],
|
|
|
+ stake_balance: Option<types::BalanceOf<T>>,
|
|
|
+ ) -> DispatchResult<Error> {
|
|
|
+ ensure!(!title.is_empty(), Error::EmptyTitleProvided);
|
|
|
+ ensure!(
|
|
|
+ title.len() as u32 <= T::TitleMaxLength::get(),
|
|
|
+ Error::TitleIsTooLong
|
|
|
+ );
|
|
|
+
|
|
|
+ ensure!(!description.is_empty(), Error::EmptyDescriptionProvided);
|
|
|
+ ensure!(
|
|
|
+ description.len() as u32 <= T::DescriptionMaxLength::get(),
|
|
|
+ Error::DescriptionIsTooLong
|
|
|
+ );
|
|
|
+
|
|
|
+ ensure!(
|
|
|
+ (Self::active_proposal_count()) < T::MaxActiveProposalLimit::get(),
|
|
|
+ Error::MaxActiveProposalNumberExceeded
|
|
|
+ );
|
|
|
+
|
|
|
+ ensure!(
|
|
|
+ parameters.approval_threshold_percentage > 0,
|
|
|
+ Error::InvalidParameterApprovalThreshold
|
|
|
+ );
|
|
|
+
|
|
|
+ ensure!(
|
|
|
+ parameters.slashing_threshold_percentage > 0,
|
|
|
+ Error::InvalidParameterSlashingThreshold
|
|
|
+ );
|
|
|
+
|
|
|
+ // check stake parameters
|
|
|
+ if let Some(required_stake) = parameters.required_stake {
|
|
|
+ if let Some(staked_balance) = stake_balance {
|
|
|
+ ensure!(
|
|
|
+ required_stake == staked_balance,
|
|
|
+ Error::StakeDiffersFromRequired
|
|
|
+ );
|
|
|
+ } else {
|
|
|
+ return Err(Error::EmptyStake);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if stake_balance.is_some() && parameters.required_stake.is_none() {
|
|
|
+ return Err(Error::StakeShouldBeEmpty);
|
|
|
+ }
|
|
|
+
|
|
|
+ Ok(())
|
|
|
+ }
|
|
|
+
|
|
|
+ //TODO: candidate for invariant break or error saving to the state
|
|
|
+ /// Callback from StakingEventsHandler. Refunds unstaked imbalance back to the source account
|
|
|
+ pub fn refund_proposal_stake(stake_id: T::StakeId, imbalance: NegativeImbalance<T>) {
|
|
|
+ if <StakesProposals<T>>::exists(stake_id) {
|
|
|
+ //TODO: handle non existence
|
|
|
+
|
|
|
+ let proposal_id = Self::stakes_proposals(stake_id);
|
|
|
+
|
|
|
+ if <Proposals<T>>::exists(proposal_id) {
|
|
|
+ let proposal = Self::proposals(proposal_id);
|
|
|
+
|
|
|
+ if let ProposalStatus::Active(active_stake_result) = proposal.status {
|
|
|
+ if let Some(active_stake) = active_stake_result {
|
|
|
+ //TODO: handle the result
|
|
|
+ let _ = CurrencyOf::<T>::resolve_into_existing(
|
|
|
+ &active_stake.source_account_id,
|
|
|
+ imbalance,
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
impl<T: Trait> Module<T> {
|
|
@@ -415,43 +491,38 @@ impl<T: Trait> Module<T> {
|
|
|
}
|
|
|
|
|
|
// Executes approved proposal code
|
|
|
- fn execute_proposal(proposal_id: T::ProposalId) {
|
|
|
- let mut proposal = Self::proposals(proposal_id);
|
|
|
+ fn execute_proposal(approved_proposal: ApprovedProposal<T>) {
|
|
|
+ let proposal_code = Self::proposal_codes(approved_proposal.proposal_id);
|
|
|
|
|
|
- // Execute only proposals with correct status
|
|
|
- if let ProposalStatus::Finalized(finalized_status) = proposal.status.clone() {
|
|
|
- let proposal_code = Self::proposal_codes(proposal_id);
|
|
|
+ let proposal_code_result = T::DispatchableCallCode::decode(&mut &proposal_code[..]);
|
|
|
|
|
|
- let proposal_code_result = T::ProposalCode::decode(&mut &proposal_code[..]);
|
|
|
-
|
|
|
- let approved_proposal_status = match proposal_code_result {
|
|
|
- Ok(proposal_code) => {
|
|
|
- if let Err(error) = proposal_code.dispatch(T::Origin::from(RawOrigin::Root)) {
|
|
|
- ApprovedProposalStatus::failed_execution(
|
|
|
- error.into().message.unwrap_or("Dispatch error"),
|
|
|
- )
|
|
|
- } else {
|
|
|
- ApprovedProposalStatus::Executed
|
|
|
- }
|
|
|
+ let approved_proposal_status = match proposal_code_result {
|
|
|
+ Ok(proposal_code) => {
|
|
|
+ if let Err(error) = proposal_code.dispatch(T::Origin::from(RawOrigin::Root)) {
|
|
|
+ ApprovedProposalStatus::failed_execution(
|
|
|
+ error.into().message.unwrap_or("Dispatch error"),
|
|
|
+ )
|
|
|
+ } else {
|
|
|
+ ApprovedProposalStatus::Executed
|
|
|
}
|
|
|
- Err(error) => ApprovedProposalStatus::failed_execution(error.what()),
|
|
|
- };
|
|
|
+ }
|
|
|
+ Err(error) => ApprovedProposalStatus::failed_execution(error.what()),
|
|
|
+ };
|
|
|
|
|
|
- let proposal_execution_status =
|
|
|
- finalized_status.create_approved_proposal_status(approved_proposal_status);
|
|
|
+ let proposal_execution_status = approved_proposal
|
|
|
+ .finalisation_status_data
|
|
|
+ .create_approved_proposal_status(approved_proposal_status);
|
|
|
|
|
|
- proposal.status = proposal_execution_status.clone();
|
|
|
- <Proposals<T>>::insert(proposal_id, proposal);
|
|
|
+ let mut proposal = approved_proposal.proposal;
|
|
|
+ proposal.status = proposal_execution_status.clone();
|
|
|
+ <Proposals<T>>::insert(approved_proposal.proposal_id, proposal);
|
|
|
|
|
|
- Self::deposit_event(RawEvent::ProposalStatusUpdated(
|
|
|
- proposal_id,
|
|
|
- proposal_execution_status,
|
|
|
- ));
|
|
|
- }
|
|
|
+ Self::deposit_event(RawEvent::ProposalStatusUpdated(
|
|
|
+ approved_proposal.proposal_id,
|
|
|
+ proposal_execution_status,
|
|
|
+ ));
|
|
|
|
|
|
- // Remove proposals from the 'pending execution' queue even in case of not finalized status
|
|
|
- // to prevent eternal cycles.
|
|
|
- <PendingExecutionProposalIds<T>>::remove(&proposal_id);
|
|
|
+ <PendingExecutionProposalIds<T>>::remove(&approved_proposal.proposal_id);
|
|
|
}
|
|
|
|
|
|
// Performs all actions on proposal finalization:
|
|
@@ -466,36 +537,36 @@ impl<T: Trait> Module<T> {
|
|
|
|
|
|
let mut proposal = Self::proposals(proposal_id);
|
|
|
|
|
|
- if let ProposalDecisionStatus::Approved { .. } = decision_status {
|
|
|
- <PendingExecutionProposalIds<T>>::insert(proposal_id, ());
|
|
|
- }
|
|
|
-
|
|
|
- // deal with stakes if necessary
|
|
|
- let slash_balance = Self::calculate_slash_balance(&decision_status, &proposal.parameters);
|
|
|
- let slash_and_unstake_result =
|
|
|
- Self::slash_and_unstake(proposal.stake_data.clone(), slash_balance);
|
|
|
+ if let ProposalStatus::Active(active_stake) = proposal.status.clone() {
|
|
|
+ if let ProposalDecisionStatus::Approved { .. } = decision_status {
|
|
|
+ <PendingExecutionProposalIds<T>>::insert(proposal_id, ());
|
|
|
+ }
|
|
|
|
|
|
- //TODO: leave stake data as is?
|
|
|
- if slash_and_unstake_result.is_ok() {
|
|
|
- proposal.stake_data = None;
|
|
|
- }
|
|
|
+ // deal with stakes if necessary
|
|
|
+ let slash_balance =
|
|
|
+ Self::calculate_slash_balance(&decision_status, &proposal.parameters);
|
|
|
+ let slash_and_unstake_result =
|
|
|
+ Self::slash_and_unstake(active_stake.clone(), slash_balance);
|
|
|
|
|
|
- // create finalized proposal status with error if any
|
|
|
- let new_proposal_status = //TODO rename without an error
|
|
|
- ProposalStatus::finalized_with_error(decision_status, slash_and_unstake_result.err(), Self::current_block());
|
|
|
+ // create finalized proposal status with error if any
|
|
|
+ let new_proposal_status = //TODO rename without an error
|
|
|
+ ProposalStatus::finalized_with_error(decision_status, slash_and_unstake_result.err(), active_stake, Self::current_block());
|
|
|
|
|
|
- proposal.status = new_proposal_status.clone();
|
|
|
- <Proposals<T>>::insert(proposal_id, proposal);
|
|
|
+ proposal.status = new_proposal_status.clone();
|
|
|
+ <Proposals<T>>::insert(proposal_id, proposal);
|
|
|
|
|
|
- Self::deposit_event(RawEvent::ProposalStatusUpdated(
|
|
|
- proposal_id,
|
|
|
- new_proposal_status,
|
|
|
- ));
|
|
|
+ Self::deposit_event(RawEvent::ProposalStatusUpdated(
|
|
|
+ proposal_id,
|
|
|
+ new_proposal_status,
|
|
|
+ ));
|
|
|
+ } else {
|
|
|
+ print("Broken invariant: proposal cannot be non-active during the finalisation");
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// Slashes the stake and perform unstake only in case of existing stake
|
|
|
fn slash_and_unstake(
|
|
|
- current_stake_data: Option<StakeData<T::StakeId, T::AccountId>>,
|
|
|
+ current_stake_data: Option<ActiveStake<T::StakeId, T::AccountId>>,
|
|
|
slash_balance: BalanceOf<T>,
|
|
|
) -> Result<(), &'static str> {
|
|
|
// only if stake exists
|
|
@@ -531,13 +602,22 @@ impl<T: Trait> Module<T> {
|
|
|
}
|
|
|
|
|
|
// Enumerates approved proposals and checks their grace period expiration
|
|
|
- fn get_approved_proposal_with_expired_grace_period_ids() -> Vec<T::ProposalId> {
|
|
|
+ fn get_approved_proposal_with_expired_grace_period() -> Vec<ApprovedProposal<T>> {
|
|
|
<PendingExecutionProposalIds<T>>::enumerate()
|
|
|
.filter_map(|(proposal_id, _)| {
|
|
|
let proposal = Self::proposals(proposal_id);
|
|
|
|
|
|
if proposal.is_grace_period_expired(Self::current_block()) {
|
|
|
- Some(proposal_id)
|
|
|
+ // this should be true, because it was tested inside is_grace_period_expired()
|
|
|
+ if let ProposalStatus::Finalized(finalisation_data) = proposal.status.clone() {
|
|
|
+ Some(ApprovedProposalData {
|
|
|
+ proposal_id,
|
|
|
+ proposal,
|
|
|
+ finalisation_status_data: finalisation_data,
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ None
|
|
|
+ }
|
|
|
} else {
|
|
|
None
|
|
|
}
|
|
@@ -560,85 +640,6 @@ impl<T: Trait> Module<T> {
|
|
|
ActiveProposalCount::put(next_active_proposal_count_value);
|
|
|
};
|
|
|
}
|
|
|
-
|
|
|
- // Performs all checks for the proposal creation:
|
|
|
- // - title, body lengths
|
|
|
- // - mac active proposal
|
|
|
- // - provided parameters: approval_threshold_percentage and slashing_threshold_percentage > 0
|
|
|
- // - provided stake balance and parameters.required_stake are valid
|
|
|
- fn ensure_create_proposal_parameters_are_valid(
|
|
|
- parameters: &ProposalParameters<T::BlockNumber, types::BalanceOf<T>>,
|
|
|
- title: &[u8],
|
|
|
- description: &[u8],
|
|
|
- stake_balance: Option<types::BalanceOf<T>>,
|
|
|
- ) -> DispatchResult<Error> {
|
|
|
- ensure!(!title.is_empty(), Error::EmptyTitleProvided);
|
|
|
- ensure!(
|
|
|
- title.len() as u32 <= T::TitleMaxLength::get(),
|
|
|
- Error::TitleIsTooLong
|
|
|
- );
|
|
|
-
|
|
|
- ensure!(!description.is_empty(), Error::EmptyDescriptionProvided);
|
|
|
- ensure!(
|
|
|
- description.len() as u32 <= T::DescriptionMaxLength::get(),
|
|
|
- Error::DescriptionIsTooLong
|
|
|
- );
|
|
|
-
|
|
|
- ensure!(
|
|
|
- (Self::active_proposal_count()) < T::MaxActiveProposalLimit::get(),
|
|
|
- Error::MaxActiveProposalNumberExceeded
|
|
|
- );
|
|
|
-
|
|
|
- ensure!(
|
|
|
- parameters.approval_threshold_percentage > 0,
|
|
|
- Error::InvalidParameterApprovalThreshold
|
|
|
- );
|
|
|
-
|
|
|
- ensure!(
|
|
|
- parameters.slashing_threshold_percentage > 0,
|
|
|
- Error::InvalidParameterSlashingThreshold
|
|
|
- );
|
|
|
-
|
|
|
- // check stake parameters
|
|
|
- if let Some(required_stake) = parameters.required_stake {
|
|
|
- if let Some(staked_balance) = stake_balance {
|
|
|
- ensure!(
|
|
|
- required_stake == staked_balance,
|
|
|
- Error::StakeDiffersFromRequired
|
|
|
- );
|
|
|
- } else {
|
|
|
- return Err(Error::EmptyStake);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if stake_balance.is_some() && parameters.required_stake.is_none() {
|
|
|
- return Err(Error::StakeShouldBeEmpty);
|
|
|
- }
|
|
|
-
|
|
|
- Ok(())
|
|
|
- }
|
|
|
-
|
|
|
- //TODO: candidate for invariant break or error saving to the state
|
|
|
- /// Callback from StakingEventsHandler. Refunds unstaked imbalance back to the source account
|
|
|
- pub(crate) fn refund_proposal_stake(stake_id: T::StakeId, imbalance: NegativeImbalance<T>) {
|
|
|
- if <StakesProposals<T>>::exists(stake_id) {
|
|
|
- //TODO: handle non existence
|
|
|
-
|
|
|
- let proposal_id = Self::stakes_proposals(stake_id);
|
|
|
-
|
|
|
- if <Proposals<T>>::exists(proposal_id) {
|
|
|
- let proposal = Self::proposals(proposal_id);
|
|
|
-
|
|
|
- if let Some(stake_data) = proposal.stake_data {
|
|
|
- //TODO: handle the result
|
|
|
- let _ = CurrencyOf::<T>::resolve_into_existing(
|
|
|
- &stake_data.source_account_id,
|
|
|
- imbalance,
|
|
|
- );
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
// Simplification of the 'FinalizedProposalData' type
|
|
@@ -651,8 +652,18 @@ type FinalizedProposal<T> = FinalizedProposalData<
|
|
|
<T as system::Trait>::AccountId,
|
|
|
>;
|
|
|
|
|
|
+// Simplification of the 'ApprovedProposalData' type
|
|
|
+type ApprovedProposal<T> = ApprovedProposalData<
|
|
|
+ <T as Trait>::ProposalId,
|
|
|
+ <T as system::Trait>::BlockNumber,
|
|
|
+ MemberId<T>,
|
|
|
+ types::BalanceOf<T>,
|
|
|
+ <T as stake::Trait>::StakeId,
|
|
|
+ <T as system::Trait>::AccountId,
|
|
|
+>;
|
|
|
+
|
|
|
// Simplification of the 'Proposal' type
|
|
|
-type ProposalObject<T> = Proposal<
|
|
|
+type ProposalOf<T> = Proposal<
|
|
|
<T as system::Trait>::BlockNumber,
|
|
|
MemberId<T>,
|
|
|
types::BalanceOf<T>,
|