Browse Source

runtime: Add locked balance for an invited member.

Shamil Gadelshin 4 years ago
parent
commit
29c821dee5

+ 3 - 0
Cargo.lock

@@ -3739,6 +3739,7 @@ version = "4.0.0"
 dependencies = [
  "frame-support",
  "frame-system",
+ "pallet-balances",
  "pallet-timestamp",
  "parity-scale-codec",
  "serde",
@@ -3985,6 +3986,7 @@ dependencies = [
  "pallet-balances",
  "pallet-common",
  "pallet-membership",
+ "pallet-staking-handler",
  "pallet-timestamp",
  "parity-scale-codec",
  "serde",
@@ -4040,6 +4042,7 @@ dependencies = [
  "pallet-balances",
  "pallet-common",
  "pallet-membership",
+ "pallet-staking-handler",
  "pallet-timestamp",
  "parity-scale-codec",
  "rand 0.7.3",

+ 2 - 0
runtime-modules/common/Cargo.toml

@@ -14,6 +14,7 @@ frame-support = { package = 'frame-support', default-features = false, git = 'ht
 frame-system = { package = 'frame-system', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
 pallet-timestamp = { package = 'pallet-timestamp', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
 sp-arithmetic = { package = 'sp-arithmetic', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
+balances = { package = 'pallet-balances', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
 
 
 [features]
@@ -28,4 +29,5 @@ std = [
 	'frame-system/std',
 	'pallet-timestamp/std',
 	'sp-arithmetic/std',
+	'balances/std',
 ]

+ 10 - 1
runtime-modules/common/src/working_group.rs

@@ -19,7 +19,7 @@ pub enum WorkingGroup {
     Membership,
 }
 
-/// Working group interface to use in the in the pallets with working groups.
+/// Working group interface to work with its members - workers and leaders.
 pub trait WorkingGroupIntegration<T: crate::Trait> {
     /// Validate origin for the worker.
     fn ensure_worker_origin(origin: T::Origin, worker_id: &T::ActorId) -> DispatchResult;
@@ -36,3 +36,12 @@ pub trait WorkingGroupIntegration<T: crate::Trait> {
     /// Verifies that given account ID and worker ID belong to the working group member.
     fn is_worker_account_id(account_id: &T::AccountId, worker_id: &T::ActorId) -> bool;
 }
+
+/// Working group interface to work with the its budget.
+pub trait WorkingGroupBudgetHandler<T: balances::Trait> {
+    /// Returns current working group balance.
+    fn get_budget() -> T::Balance;
+
+    /// Sets new working broup balance
+    fn set_budget(new_value: T::Balance);
+}

+ 12 - 0
runtime-modules/council/src/mock.rs

@@ -243,6 +243,7 @@ parameter_types! {
     pub const DefaultMembershipPrice: u64 = 100;
     pub const DefaultInitialInvitationBalance: u64 = 100;
     pub const MinimumPeriod: u64 = 5;
+    pub const InvitedMemberLockId: [u8; 8] = [2; 8];
 }
 
 mod balances_mod {
@@ -366,6 +367,17 @@ impl membership::Trait for Runtime {
     type DefaultMembershipPrice = DefaultMembershipPrice;
     type WorkingGroup = ();
     type DefaultInitialInvitationBalance = DefaultInitialInvitationBalance;
+    type InvitedMemberStakingHandler = staking_handler::StakingManager<Self, InvitedMemberLockId>;
+}
+
+impl common::working_group::WorkingGroupBudgetHandler<Runtime> for () {
+    fn get_budget() -> u64 {
+        unimplemented!()
+    }
+
+    fn set_budget(_new_value: u64) {
+        unimplemented!()
+    }
 }
 
 impl common::working_group::WorkingGroupIntegration<Runtime> for () {

+ 2 - 1
runtime-modules/membership/Cargo.toml

@@ -15,11 +15,11 @@ sp-runtime = { package = 'sp-runtime', default-features = false, git = 'https://
 pallet-timestamp = { package = 'pallet-timestamp', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
 balances = { package = 'pallet-balances', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
 common = { package = 'pallet-common', default-features = false, path = '../common'}
+staking-handler = { package = 'pallet-staking-handler', default-features = false, path = '../staking-handler'}
 
 [dev-dependencies]
 sp-io = { package = 'sp-io', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
 sp-core = { package = 'sp-core', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
-staking-handler = { package = 'pallet-staking-handler', default-features = false, path = '../staking-handler'}
 working-group = { package = 'pallet-working-group', default-features = false, path = '../working-group'}
 
 [features]
@@ -35,4 +35,5 @@ std = [
 	'pallet-timestamp/std',
 	'balances/std',
 	'common/std',
+	'staking-handler/std',
 ]

+ 33 - 2
runtime-modules/membership/src/lib.rs

@@ -55,7 +55,8 @@ use sp_arithmetic::traits::{One, Zero};
 use sp_runtime::traits::Hash;
 use sp_std::vec::Vec;
 
-use common::working_group::WorkingGroupIntegration;
+use common::working_group::{WorkingGroupBudgetHandler, WorkingGroupIntegration};
+use staking_handler::StakingHandler;
 
 // Balance type alias
 type BalanceOf<T> = <T as balances::Trait>::Balance;
@@ -70,10 +71,18 @@ pub trait Trait:
     type DefaultMembershipPrice: Get<BalanceOf<Self>>;
 
     /// Working group pallet integration.
-    type WorkingGroup: common::working_group::WorkingGroupIntegration<Self>;
+    type WorkingGroup: common::working_group::WorkingGroupIntegration<Self>
+        + common::working_group::WorkingGroupBudgetHandler<Self>;
 
     /// Defines the default balance for the invited member.
     type DefaultInitialInvitationBalance: Get<BalanceOf<Self>>;
+
+    /// Staking handler used for invited member staking.
+    type InvitedMemberStakingHandler: StakingHandler<
+        Self::AccountId,
+        BalanceOf<Self>,
+        Self::MemberId,
+    >;
 }
 
 pub(crate) const DEFAULT_MEMBER_INVITES_COUNT: u32 = 5;
@@ -210,6 +219,10 @@ decl_error! {
 
         /// Staking account has already been confirmed.
         StakingAccountAlreadyConfirmed,
+
+        /// Cannot invite a member. Working group balance is not sufficient to set the default
+        /// balance.
+        WorkingGroupBudgetIsNotSufficientForInviting,
     }
 }
 
@@ -547,6 +560,14 @@ decl_module! {
                 params.handle,
             )?;
 
+            let current_wg_budget = T::WorkingGroup::get_budget();
+            let default_invitation_balance = T::DefaultInitialInvitationBalance::get();
+
+            ensure!(
+                default_invitation_balance <= current_wg_budget,
+                Error::<T>::WorkingGroupBudgetIsNotSufficientForInviting
+            );
+
             //
             // == MUTATION SAFE ==
             //
@@ -563,6 +584,16 @@ decl_module! {
                 membership.invites = membership.invites.saturating_sub(1);
             });
 
+            // Decrease the working group balance.
+            let new_wg_budget = current_wg_budget - default_invitation_balance;
+            T::WorkingGroup::set_budget(new_wg_budget);
+
+            // Lock invitation balance.
+            T::InvitedMemberStakingHandler::lock(
+                &params.controller_account,
+                default_invitation_balance
+            );
+
             // Fire the event.
             Self::deposit_event(RawEvent::MemberRegistered(member_id));
         }

+ 21 - 0
runtime-modules/membership/src/tests/mock.rs

@@ -6,6 +6,7 @@ use staking_handler::LockComparator;
 
 pub use frame_support::traits::{Currency, LockIdentifier};
 use frame_support::{impl_outer_event, impl_outer_origin, parameter_types};
+use sp_std::cell::RefCell;
 
 use crate::tests::fixtures::ALICE_MEMBER_ID;
 pub use frame_system;
@@ -85,6 +86,7 @@ impl pallet_timestamp::Trait for Test {
 parameter_types! {
     pub const ExistentialDeposit: u32 = 0;
     pub const DefaultMembershipPrice: u64 = 100;
+    pub const InvitedMemberLockId: [u8; 8] = [2; 8];
 }
 
 impl balances::Trait for Test {
@@ -237,6 +239,25 @@ impl Trait for Test {
     type DefaultMembershipPrice = DefaultMembershipPrice;
     type WorkingGroup = ();
     type DefaultInitialInvitationBalance = DefaultInitialInvitationBalance;
+    type InvitedMemberStakingHandler = staking_handler::StakingManager<Self, InvitedMemberLockId>;
+}
+
+pub const WORKING_GROUP_BUDGET: u64 = 100;
+
+thread_local! {
+    pub static WG_BUDGET: RefCell<u64> = RefCell::new(WORKING_GROUP_BUDGET);
+}
+
+impl common::working_group::WorkingGroupBudgetHandler<Test> for () {
+    fn get_budget() -> u64 {
+        WG_BUDGET.with(|val| *val.borrow())
+    }
+
+    fn set_budget(new_value: u64) {
+        WG_BUDGET.with(|val| {
+            *val.borrow_mut() = new_value;
+        });
+    }
 }
 
 impl common::working_group::WorkingGroupIntegration<Test> for () {

+ 24 - 0
runtime-modules/membership/src/tests/mod.rs

@@ -7,6 +7,7 @@ use crate::{Error, Event};
 use fixtures::*;
 use mock::*;
 
+use common::working_group::WorkingGroupBudgetHandler;
 use common::StakingAccountValidator;
 use frame_support::traits::{LockIdentifier, LockableCurrency, WithdrawReasons};
 use frame_support::{assert_ok, StorageMap, StorageValue};
@@ -495,10 +496,33 @@ fn invite_member_succeeds() {
             vec![bob_member_id]
         );
 
+        assert_eq!(
+            WORKING_GROUP_BUDGET - <Test as Trait>::DefaultInitialInvitationBalance::get(),
+            <Test as Trait>::WorkingGroup::get_budget()
+        );
+
         EventFixture::assert_last_crate_event(Event::<Test>::MemberRegistered(bob_member_id));
     });
 }
 
+#[test]
+fn invite_member_fails_with_insufficient_working_group_balance() {
+    build_test_externalities().execute_with(|| {
+        let initial_balance = DefaultMembershipPrice::get();
+        set_alice_free_balance(initial_balance);
+
+        assert_ok!(buy_default_membership_as_alice());
+
+        WG_BUDGET.with(|val| {
+            *val.borrow_mut() = 50;
+        });
+
+        InviteMembershipFixture::default().call_and_assert(Err(
+            Error::<Test>::WorkingGroupBudgetIsNotSufficientForInviting.into(),
+        ));
+    });
+}
+
 #[test]
 fn invite_member_fails_with_bad_origin() {
     build_test_externalities().execute_with(|| {

+ 12 - 0
runtime-modules/proposals/codex/src/tests/mock.rs

@@ -32,6 +32,7 @@ parameter_types! {
     pub const MaximumBlockLength: u32 = 2 * 1024;
     pub const AvailableBlockRatio: Perbill = Perbill::one();
     pub const MinimumPeriod: u64 = 5;
+    pub const InvitedMemberLockId: [u8; 8] = [2; 8];
 }
 
 impl_outer_dispatch! {
@@ -53,6 +54,17 @@ impl membership::Trait for Test {
     type DefaultMembershipPrice = DefaultMembershipPrice;
     type WorkingGroup = ();
     type DefaultInitialInvitationBalance = ();
+    type InvitedMemberStakingHandler = staking_handler::StakingManager<Self, InvitedMemberLockId>;
+}
+
+impl common::working_group::WorkingGroupBudgetHandler<Test> for () {
+    fn get_budget() -> u64 {
+        unimplemented!()
+    }
+
+    fn set_budget(_new_value: u64) {
+        unimplemented!()
+    }
 }
 
 impl common::working_group::WorkingGroupIntegration<Test> for () {

+ 1 - 0
runtime-modules/proposals/discussion/Cargo.toml

@@ -24,6 +24,7 @@ sp-runtime = { package = 'sp-runtime', default-features = false, git = 'https://
 pallet-timestamp = { package = 'pallet-timestamp', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
 balances = { package = 'pallet-balances', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
 membership = { package = 'pallet-membership', default-features = false, path = '../../membership'}
+staking-handler = { package = 'pallet-staking-handler', default-features = false, path = '../../staking-handler'}
 
 [features]
 default = ['std']

+ 24 - 1
runtime-modules/proposals/discussion/src/tests/mock.rs

@@ -2,7 +2,7 @@
 
 pub use frame_system;
 
-use frame_support::traits::{OnFinalize, OnInitialize};
+use frame_support::traits::{LockIdentifier, OnFinalize, OnInitialize};
 use frame_support::{impl_outer_event, impl_outer_origin, parameter_types, weights::Weight};
 use sp_core::H256;
 use sp_runtime::{
@@ -14,6 +14,8 @@ use sp_runtime::{
 use crate::ActorOriginValidator;
 use crate::WeightInfo;
 
+use staking_handler::LockComparator;
+
 impl_outer_origin! {
     pub enum Origin for Test {}
 }
@@ -58,6 +60,7 @@ parameter_types! {
     pub const MaxWhiteListSize: u32 = 4;
     pub const DefaultMembershipPrice: u64 = 100;
     pub const DefaultInitialInvitationBalance: u64 = 100;
+    pub const InvitedMemberLockId: [u8; 8] = [2; 8];
 }
 
 impl balances::Trait for Test {
@@ -80,6 +83,26 @@ impl membership::Trait for Test {
     type DefaultMembershipPrice = DefaultMembershipPrice;
     type WorkingGroup = ();
     type DefaultInitialInvitationBalance = ();
+    type InvitedMemberStakingHandler = staking_handler::StakingManager<Self, InvitedMemberLockId>;
+}
+
+impl LockComparator<<Test as balances::Trait>::Balance> for Test {
+    fn are_locks_conflicting(
+        _new_lock: &LockIdentifier,
+        _existing_locks: &[LockIdentifier],
+    ) -> bool {
+        false
+    }
+}
+
+impl common::working_group::WorkingGroupBudgetHandler<Test> for () {
+    fn get_budget() -> u64 {
+        unimplemented!()
+    }
+
+    fn set_budget(_new_value: u64) {
+        unimplemented!()
+    }
 }
 
 impl common::working_group::WorkingGroupIntegration<Test> for () {

+ 12 - 0
runtime-modules/proposals/engine/src/tests/mock/mod.rs

@@ -183,6 +183,7 @@ parameter_types! {
     pub const LockId: LockIdentifier = [1; 8];
     pub const DefaultMembershipPrice: u64 = 100;
     pub const DefaultInitialInvitationBalance: u64 = 100;
+    pub const InvitedMemberLockId: [u8; 8] = [2; 8];
 }
 
 impl common::Trait for Test {
@@ -195,6 +196,17 @@ impl membership::Trait for Test {
     type DefaultMembershipPrice = DefaultMembershipPrice;
     type WorkingGroup = ();
     type DefaultInitialInvitationBalance = ();
+    type InvitedMemberStakingHandler = staking_handler::StakingManager<Self, InvitedMemberLockId>;
+}
+
+impl common::working_group::WorkingGroupBudgetHandler<Test> for () {
+    fn get_budget() -> u64 {
+        unimplemented!()
+    }
+
+    fn set_budget(_new_value: u64) {
+        unimplemented!()
+    }
 }
 
 impl common::working_group::WorkingGroupIntegration<Test> for () {

+ 1 - 0
runtime-modules/referendum/Cargo.toml

@@ -25,6 +25,7 @@ sp-io = { package = 'sp-io', default-features = false, git = 'https://github.com
 rand = "0.7.3"
 pallet-timestamp = { package = 'pallet-timestamp', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
 membership = { package = 'pallet-membership', default-features = false, path = '../membership' }
+staking-handler = { package = 'pallet-staking-handler', default-features = false, path = '../staking-handler'}
 
 [features]
 default = ['std']

+ 23 - 0
runtime-modules/referendum/src/mock.rs

@@ -29,6 +29,8 @@ use std::collections::BTreeMap;
 use std::iter::FromIterator;
 use std::marker::PhantomData;
 
+use staking_handler::LockComparator;
+
 use crate::GenesisConfig;
 
 pub const USER_ADMIN: u64 = 1;
@@ -162,6 +164,7 @@ impl WeightInfo for () {
 parameter_types! {
     pub const DefaultMembershipPrice: u64 = 100;
     pub const DefaultInitialInvitationBalance: u64 = 100;
+    pub const InvitedMemberLockId: [u8; 8] = [2; 8];
 }
 
 impl membership::Trait for Runtime {
@@ -169,6 +172,7 @@ impl membership::Trait for Runtime {
     type DefaultMembershipPrice = DefaultMembershipPrice;
     type WorkingGroup = ();
     type DefaultInitialInvitationBalance = DefaultInitialInvitationBalance;
+    type InvitedMemberStakingHandler = staking_handler::StakingManager<Self, InvitedMemberLockId>;
 }
 
 impl pallet_timestamp::Trait for Runtime {
@@ -178,6 +182,25 @@ impl pallet_timestamp::Trait for Runtime {
     type WeightInfo = ();
 }
 
+impl LockComparator<<Runtime as pallet_balances::Trait>::Balance> for Runtime {
+    fn are_locks_conflicting(
+        _new_lock: &LockIdentifier,
+        _existing_locks: &[LockIdentifier],
+    ) -> bool {
+        false
+    }
+}
+
+impl common::working_group::WorkingGroupBudgetHandler<Runtime> for () {
+    fn get_budget() -> u64 {
+        unimplemented!()
+    }
+
+    fn set_budget(_new_value: u64) {
+        unimplemented!()
+    }
+}
+
 impl common::working_group::WorkingGroupIntegration<Runtime> for () {
     fn ensure_worker_origin(
         _origin: <Runtime as frame_system::Trait>::Origin,

+ 12 - 0
runtime-modules/service-discovery/src/mock.rs

@@ -97,6 +97,17 @@ impl membership::Trait for Test {
     type DefaultMembershipPrice = DefaultMembershipPrice;
     type WorkingGroup = ();
     type DefaultInitialInvitationBalance = ();
+    type InvitedMemberStakingHandler = staking_handler::StakingManager<Self, InvitedMemberLockId>;
+}
+
+impl common::working_group::WorkingGroupBudgetHandler<Test> for () {
+    fn get_budget() -> u64 {
+        unimplemented!()
+    }
+
+    fn set_budget(_new_value: u64) {
+        unimplemented!()
+    }
 }
 
 impl common::working_group::WorkingGroupIntegration<Test> for () {
@@ -140,6 +151,7 @@ impl balances::Trait for Test {
 parameter_types! {
     pub const MaxWorkerNumberLimit: u32 = 3;
     pub const LockId1: [u8; 8] = [1; 8];
+    pub const InvitedMemberLockId: [u8; 8] = [2; 8];
 }
 
 pub struct WorkingGroupWeightInfo;

+ 12 - 0
runtime-modules/storage/src/tests/mock.rs

@@ -155,6 +155,7 @@ parameter_types! {
     pub const LockId: LockIdentifier = [2; 8];
     pub const DefaultMembershipPrice: u64 = 100;
     pub const DefaultInitialInvitationBalance: u64 = 100;
+    pub const InvitedMemberLockId: [u8; 8] = [2; 8];
 }
 
 pub struct WorkingGroupWeightInfo;
@@ -286,6 +287,17 @@ impl membership::Trait for Test {
     type DefaultMembershipPrice = DefaultMembershipPrice;
     type WorkingGroup = ();
     type DefaultInitialInvitationBalance = ();
+    type InvitedMemberStakingHandler = staking_handler::StakingManager<Self, InvitedMemberLockId>;
+}
+
+impl common::working_group::WorkingGroupBudgetHandler<Test> for () {
+    fn get_budget() -> u64 {
+        unimplemented!()
+    }
+
+    fn set_budget(_new_value: u64) {
+        unimplemented!()
+    }
 }
 
 impl common::working_group::WorkingGroupIntegration<Test> for () {

+ 18 - 1
runtime-modules/working-group/src/lib.rs

@@ -864,7 +864,7 @@ decl_module! {
             //
 
             // Update the budget.
-            <Budget::<T, I>>::put(new_budget);
+            Self::set_working_group_budget(new_budget);
 
             // Trigger event
             Self::deposit_event(RawEvent::BudgetSet(new_budget));
@@ -1331,6 +1331,11 @@ impl<T: Trait<I>, I: Instance> Module<T, I> {
         false
     }
 
+    // Sets the working group budget.
+    fn set_working_group_budget(new_budget: BalanceOf<T>) {
+        <Budget<T, I>>::put(new_budget);
+    }
+
     /// Returns all existing worker id list.
     pub fn get_all_worker_ids() -> Vec<WorkerId<T>> {
         <WorkerById<T, I>>::iter()
@@ -1365,3 +1370,15 @@ impl<T: Trait<I>, I: Instance> common::working_group::WorkingGroupIntegration<T>
             .unwrap_or(false)
     }
 }
+
+impl<T: Trait<I>, I: Instance> common::working_group::WorkingGroupBudgetHandler<T>
+    for Module<T, I>
+{
+    fn get_budget() -> BalanceOf<T> {
+        Self::budget()
+    }
+
+    fn set_budget(new_value: BalanceOf<T>) {
+        Self::set_working_group_budget(new_value);
+    }
+}

+ 2 - 0
runtime-modules/working-group/src/tests/mock.rs

@@ -39,6 +39,7 @@ parameter_types! {
     pub const ExistentialDeposit: u32 = 0;
     pub const DefaultMembershipPrice: u64 = 0;
     pub const DefaultInitialInvitationBalance: u64 = 100;
+    pub const InvitedMemberLockId: [u8; 8] = [2; 8];
 }
 
 // Workaround for https://github.com/rust-lang/rust/issues/26925 - remove when sorted.
@@ -100,6 +101,7 @@ impl membership::Trait for Test {
     type DefaultMembershipPrice = DefaultMembershipPrice;
     type WorkingGroup = Module<Test>;
     type DefaultInitialInvitationBalance = ();
+    type InvitedMemberStakingHandler = staking_handler::StakingManager<Self, InvitedMemberLockId>;
 }
 
 impl LockComparator<<Test as balances::Trait>::Balance> for Test {

+ 1 - 0
runtime/src/constants.rs

@@ -47,6 +47,7 @@ parameter_types! {
     pub const ContentWorkingGroupLockId: LockIdentifier = [7; 8];
     pub const ForumGroupLockId: LockIdentifier = [8; 8];
     pub const MembershipWorkingGroupLockId: LockIdentifier = [9; 8];
+    pub const InvitedMemberLockId: LockIdentifier = [10; 8];
 }
 
 lazy_static! {

+ 3 - 0
runtime/src/lib.rs

@@ -628,6 +628,7 @@ impl membership::Trait for Runtime {
     type DefaultMembershipPrice = DefaultMembershipPrice;
     type WorkingGroup = MembershipWorkingGroup;
     type DefaultInitialInvitationBalance = DefaultInitialInvitationBalance;
+    type InvitedMemberStakingHandler = InvitedMemberStakingManager;
 }
 
 parameter_types! {
@@ -716,6 +717,8 @@ pub type StorageWorkingGroupStakingManager =
     staking_handler::StakingManager<Runtime, StorageWorkingGroupLockId>;
 pub type MembershipWorkingGroupStakingManager =
     staking_handler::StakingManager<Runtime, MembershipWorkingGroupLockId>;
+pub type InvitedMemberStakingManager =
+    staking_handler::StakingManager<Runtime, InvitedMemberLockId>;
 
 impl working_group::Trait<ForumWorkingGroupInstance> for Runtime {
     type Event = Event;