diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/ambassador/mod.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/ambassador/mod.rs index a052a9d3800c..70c8603a69e8 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/ambassador/mod.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/ambassador/mod.rs @@ -223,6 +223,7 @@ impl pallet_core_fellowship::Config for Runtime { type FastPromoteOrigin = Self::PromoteOrigin; type EvidenceSize = ConstU32<65536>; type MaxRank = ConstU32<9>; + type PayoutPeriodDuration = ConstU32<1>; } pub type AmbassadorSalaryInstance = pallet_salary::Instance2; diff --git a/substrate/frame/core-fellowship/src/lib.rs b/substrate/frame/core-fellowship/src/lib.rs index c61447e36280..fb9f0e23c06e 100644 --- a/substrate/frame/core-fellowship/src/lib.rs +++ b/substrate/frame/core-fellowship/src/lib.rs @@ -119,11 +119,14 @@ pub struct ParamsType< Balance: Clone + Eq + PartialEq + Debug, BlockNumber: Clone + Eq + PartialEq + Debug, Ranks: Get, + PayoutPeriodDuration: Get, > { - /// The amounts to be paid when a member of a given rank (-1) is active. - pub active_salary: BoundedVec, - /// The amounts to be paid when a member of a given rank (-1) is passive. - pub passive_salary: BoundedVec, + /// The amounts to be paid per `PayoutPeriod` when a member of a given rank (-1) is active. + pub active_salary_per_period: BoundedVec, + /// The amounts to be paid per `PayoutPeriod` when a member of a given rank (-1) is passive. + pub passive_salary_per_period: BoundedVec, + /// The number of blocks between two payout periods. + pub payout_period_duration: PayoutPeriodDuration, /// The period between which unproven members become demoted. pub demotion_period: BoundedVec, /// The period between which members must wait before they may proceed to this rank. @@ -140,8 +143,9 @@ impl< { fn default() -> Self { Self { - active_salary: Default::default(), - passive_salary: Default::default(), + active_salary_per_period: Default::default(), + passive_salary_per_period: Default::default(), + payout_period_duration: Default::default(), demotion_period: Default::default(), min_promotion_period: Default::default(), offboard_timeout: BlockNumber::default(), @@ -225,6 +229,12 @@ pub mod pallet { /// Increasing this value is supported, but decreasing it may lead to a broken state. #[pallet::constant] type MaxRank: Get; + + /// The number of blocks between two payout periods. + /// + /// This is used to calculate the payout amount for each period. + #[pallet::constant] + type PayoutPeriodDuration: Get>; } pub type ParamsOf = @@ -629,6 +639,9 @@ pub mod pallet { &mut p.passive_salary, partial_params.passive_salary, ); + if let Some(new_payout_period) = partial_params.payout_period_duration { + p.payout_period = new_payout_period; + } Self::set_partial_params_slice( &mut p.demotion_period, partial_params.demotion_period, @@ -691,9 +704,12 @@ pub mod pallet { None => return Zero::zero(), }; let params = Params::::get(); - let salary = - if member.is_active { params.active_salary } else { params.passive_salary }; - salary[index] + let salary_per_period = if member.is_active { + params.active_salary_per_period + } else { + params.passive_salary_per_period + }; + salary_per_period[index] } } } diff --git a/substrate/frame/core-fellowship/src/migration.rs b/substrate/frame/core-fellowship/src/migration.rs index b1e27d1e7936..f0b7a40cf251 100644 --- a/substrate/frame/core-fellowship/src/migration.rs +++ b/substrate/frame/core-fellowship/src/migration.rs @@ -84,8 +84,13 @@ impl, I: 'static> UncheckedOnRuntimeUpgrade for MigrateToV1 { let old_value = v0::Params::::take(); // Write the new value to storage let new = crate::ParamsType { - active_salary: BoundedVec::defensive_truncate_from(old_value.active_salary.to_vec()), - passive_salary: BoundedVec::defensive_truncate_from(old_value.passive_salary.to_vec()), + active_salary_per_period: BoundedVec::defensive_truncate_from( + old_value.active_salary.to_vec(), + ), + passive_salary_per_period: BoundedVec::defensive_truncate_from( + old_value.passive_salary.to_vec(), + ), + payout_period_duration: old_value.payout_period, demotion_period: BoundedVec::defensive_truncate_from( old_value.demotion_period.to_vec(), ), diff --git a/substrate/frame/salary/src/lib.rs b/substrate/frame/salary/src/lib.rs index efb4f5d3c542..e0b106e2e8ba 100644 --- a/substrate/frame/salary/src/lib.rs +++ b/substrate/frame/salary/src/lib.rs @@ -392,6 +392,12 @@ pub mod pallet { pub fn cycle_period() -> BlockNumberFor { T::RegistrationPeriod::get() + T::PayoutPeriod::get() } + fn calculate_periods_to_pay(last_payout: BlockNumberFor, now: BlockNumberFor) -> u32 { + // TODO: retrieve payout period from core-fellowship config? + let payout_period = T::PayoutPeriod::get(); + let elapsed = now.saturating_sub(last_payout); + (elapsed / payout_period) as u32 + } fn do_payout(who: T::AccountId, beneficiary: T::AccountId) -> DispatchResult { let mut status = Status::::get().ok_or(Error::::NotStarted)?; let mut claimant = Claimant::::get(&who).ok_or(Error::::NotInducted)?; @@ -418,7 +424,9 @@ pub mod pallet { Nothing | Attempted { .. } if claimant.last_active < status.cycle_index => { // Not registered for this cycle. Pay from whatever is left. let rank = T::Members::rank_of(&who).ok_or(Error::::NotMember)?; - let ideal_payout = T::Salary::get_salary(rank, &who); + let salary_per_period = T::Salary::get_salary(rank, &who); + let periods_to_pay = Self::calculate_periods_to_pay(status.cycle_start, now); + let ideal_payout = salary_per_period.saturating_mul(periods_to_pay.into()); let pot = status .budget