frame_suite/
roles.rs

1// SPDX-License-Identifier: MPL-2.0
2//
3// Part of Auguth Labs open-source softwares.
4// Built for the Substrate framework.
5//
6// This Source Code Form is subject to the terms of the Mozilla Public
7// License, v. 2.0. If a copy of the MPL was not distributed with this
8// file, You can obtain one at https://mozilla.org/MPL/2.0/.
9//
10// Copyright (c) 2026 Auguth Labs (OPC) Pvt Ltd, India
11
12// ===============================================================================
13// ```````````````````````````` ROLE MANAGEMENT SUITE ````````````````````````````
14// ===============================================================================
15
16//! Defines a **unified abstraction layer** for managing *role-based logic*
17//! within a runtime system.  
18//!
19//! It provides **trait-based contracts** that describe the lifecycle, funding,
20//! and economic behaviors of entities that assume specific roles, such as:
21//!
22//! - Validators and collators
23//! - Governance council members
24//! - Oracle operators or data feeders
25//! - Curators, auditors, or relayers
26//!
27//! ## Overview
28//!
29//! Each trait in this module represents a **composable building block** for defining
30//! how a role behaves in a decentralized system. These abstractions are designed to be
31//! generic, interoperable, and extensible across multiple pallets or runtime modules.
32//!
33//! ### Currently Included Traits
34//!
35//! - [`RoleManager`] - Core lifecycle and collateral management for role-bearing entities.  
36//! - [`FundRoles`] - Extends `RoleManager` with *backing* and *funding* capabilities.  
37//! - [`CompensateRoles`] - Extends `RoleManager` with *reward* and *penalty* mechanics.  
38//! - [`RoleProbation`] - Extends `RoleManager` with **probation** and **permanence** privileges.  
39//!
40//! ### Future Extensions
41//!
42//! These traits will compose together to form a **role framework** enabling complex
43//! behaviors (like staking, governance, delegation, conflict resolutions, etc)
44//! to be implemented consistently across different runtime modules.
45//!
46//! ## Design Philosophy
47//!
48//! - **Composability:** Each role trait defines minimal, orthogonal functionality that
49//!   can be combined with others to form rich behavior.  
50//! - **Abstraction over implementation:** These traits are *interfaces*, not storage-bound
51//!   logic. Concrete modules implement them.  
52//! - **Interoperability:** Enables higher-level systems (e.g. governance, incentives,
53//!   or auditing) to operate generically across role types.  
54//! - **Auditability:** Built-in temporal tracking (timestamps, holds, etc.) ensures
55//!   transparent lifecycle and accounting for each role.  
56//!
57//! This approach allows shared governance, staking, and incentive systems to operate on
58//! **generic roles** without being tightly coupled to any single pallet or implementation.
59
60// ===============================================================================
61// ``````````````````````````````````` IMPORTS ```````````````````````````````````
62// ===============================================================================
63
64// --- Local crate imports ---
65use crate::{Asset, Delimited, Percentage, RuntimeEnum, Time};
66
67// --- FRAME Support ---
68use frame_support::traits::{
69    tokens::{Fortitude, Precision},
70    VariantCount,
71};
72
73// --- Substrate primitives ---
74use sp_runtime::{DispatchError, DispatchResult, Vec};
75
76// ===============================================================================
77// ```````````````````````````````` ROLE MANAGER `````````````````````````````````
78// ===============================================================================
79
80/// A **universal abstraction** for managing *roles* within a runtime context.
81///
82/// This trait defines the full lifecycle of a role-bearing entity - from enrollment
83/// and collateral management to status transitions and resignation.  
84///
85/// It is intended to be the foundational interface for any module that wishes to
86/// assign and track *roles* such as:
87///
88/// - **Validators** - entities providing consensus participation and staking collateral.
89/// - **Council Members / Governance Actors** - accounts participating in decision-making.
90/// - **Oracle Operators** - off-chain data feeders bonded with collateral.
91/// - **Bounty Curators / Auditors / Relayers** - specialized economic actors.
92///
93/// By providing a unified API for checking role existence, managing enrollment conditions,
94/// and handling collateral and lifecycle events, this trait enables **role composability**
95/// and modular runtime design.
96///
97/// ## Type Parameters
98///
99/// - `Candidate`: The identifier type representing an account or entity that can assume a role.
100///   Typically this is an `AccountId`, but it can also be a multi-entity struct or hash ID.
101///
102/// ## Invariants
103///
104/// - Collateral associated with a `Candidate` **must remain locked** while the
105/// role is active.
106/// - Status transitions must be **atomic and consistent** - i.e.,  
107///   once [`Self::set_status`] succeeds, [`Self::get_status`] should immediately
108/// reflect the new state.
109///
110/// ## Example Implementations
111///
112/// ### Example 1: Validator Role
113///
114/// A validator role might involve participants who are responsible for block production.
115/// In this context:
116/// - `Status` could include `Pending`, `Active`, `Slashed`, or `Resigned`.
117/// - Candidates are required to provide a minimum collateral to enroll.
118/// - The system checks whether a candidate is already enrolled before allowing enrollment.
119/// - The trait implementation manages enrollment, resignation, collateral tracking, and
120/// status updates.
121///
122/// ### Example 2: Governance Council Member
123///
124/// A council member role represents participants in a governance body:
125/// - `Status` could include `Candidate`, `Active`, `Expelled`, or `Retired`.
126/// - Enrollment may not require collateral but must still enforce eligibility rules.
127/// - Resignation or removal is validated against membership in the council.
128/// - The trait implementation allows tracking of active members, status changes, and
129/// role lifecycle events.
130///
131/// These examples illustrate how the `RoleManager` trait can be applied to **different
132/// kinds of roles**, demonstrating the flexibility of the trait without tying it to a
133/// specific runtime storage or pallet.
134///
135/// ## Usage
136///
137/// - This trait is **not tied to a specific module/pallet**. It can be implemented by multiple
138///   role-managing pallets (e.g. `pallet_validators`, `pallet_council`, etc.).
139/// - Generic logic (e.g. in governance or reputation systems) can rely on `RoleManager`
140///   to interact with roles abstractly without hardcoding pallet internals.
141pub trait RoleManager<Candidate> {
142    /// Represents the discrete state (status) of the role (e.g. Active, Pending, Resigned, etc).  
143    /// Must implement [`VariantCount`] for easy introspection of status variants.
144    type Status: RuntimeEnum + Delimited + VariantCount;
145
146    /// Represents the metadata of the candidate.
147    ///
148    /// Could include valuable information pertaining to the role-activity
149    /// of the candidate.
150    type Meta: Delimited;
151
152    /// Represents the collateral or balance type associated with the role.  
153    type Asset: Asset;
154
155    /// Represents a timestamp or block number used to track temporal data
156    /// like enrollment or status changes.
157    type TimeStamp: Time;
158
159    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
160    // ``````````````````````````````````` CHECKERS ``````````````````````````````````
161    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
162
163    /// Checks whether a given `Candidate` currently has a registered role
164    /// in the system.
165    ///
166    /// Returns `Ok(())` if the role exists, otherwise `DispatchError`.
167    fn role_exists(who: &Candidate) -> DispatchResult;
168
169    /// Validates whether the `Candidate` is eligible to enroll for a role,
170    /// given a specific collateral amount.
171    ///
172    /// This function only performs validation and **not mutates state**.
173    ///
174    /// Returns `Ok(())` if the candidate can enroll, or a `DispatchError` if not.
175    fn can_enroll(who: &Candidate, collateral: Self::Asset) -> DispatchResult;
176
177    /// Verifies whether the `Candidate` can safely resign their role.
178    ///
179    /// This typically ensures there are no pending obligations or locked collateral.
180    fn can_resign(who: &Candidate) -> DispatchResult;
181
182    /// Returns `Ok(())` if the `Candidate` is considered not *defaulted* i.e., available.
183    ///
184    /// A **defaulted** state represents a breach of expected behavior such as
185    /// under-collateralization, missed performance targets, or protocol violations.
186    ///
187    /// Depending on implementation, a defaulted candidate may be:
188    /// - **Temporarily suspended** - can later recover or be reinstated once obligations are met.  
189    /// - **Permanently defaulted** - considered resigned, fired, or barred from reactivation.
190    fn is_available(who: &Candidate) -> DispatchResult;
191
192    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
193    // ``````````````````````````````````` GETTERS ```````````````````````````````````
194    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
195
196    /// Retrieves the meta-data of the `Candidate`.
197    ///
198    /// DispatchError otherwise.
199    fn get_meta(who: &Candidate) -> Result<Self::Meta, DispatchError>;
200
201    /// Retrieves the amount of collateral currently locked by the `Candidate`.
202    ///
203    /// Returns a [`Result`] containing the collateral value or a [`DispatchError`].
204    fn get_collateral(who: &Candidate) -> Result<Self::Asset, DispatchError>;
205
206    /// Retrieves the real-time amount of collateral currently locked by all
207    /// the `Candidate`s.
208    fn total_collateral() -> Self::Asset;
209
210    /// Returns the timestamp (or block number) when the `Candidate` enrolled
211    /// in the role.
212    fn enroll_since(who: &Candidate) -> Result<Self::TimeStamp, DispatchError>;
213
214    /// Returns the current `Status` of the `Candidate`.
215    fn get_status(who: &Candidate) -> Result<Self::Status, DispatchError>;
216
217    /// Returns the timestamp of the last status change for the `Candidate`.
218    fn status_since(who: &Candidate) -> Result<Self::TimeStamp, DispatchError>;
219
220    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
221    // ``````````````````````````````````` MUTATORS ``````````````````````````````````
222    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
223
224    /// Updates the role status of the `Candidate` to a new `Status` value.
225    fn set_status(who: &Candidate, status: Self::Status) -> DispatchResult;
226
227    /// Enrolls a new `Candidate` with the specified collateral amount.
228    ///
229    /// Should handle collateral reservation and emit necessary side effects.
230    ///
231    /// The `force` parameter determines the privilege with which the operation is conducted.
232    ///
233    /// Returns the actual amount of collateral accepted or a `DispatchError`.
234    fn enroll(
235        who: &Candidate,
236        collateral: Self::Asset,
237        force: Fortitude,
238    ) -> Result<Self::Asset, DispatchError>;
239
240    /// Resigns the `Candidate` from their role and releases any associated collateral.
241    ///
242    /// Should reverse the effects of `enroll`.
243    ///
244    /// Returns the released collateral amount.
245    fn resign(who: &Candidate) -> Result<Self::Asset, DispatchError>;
246
247    /// Increases a `Candidate`'s collateral. Required if the implementation enforces
248    /// variable collateral.
249    ///
250    /// The `force` parameter determines the privilege with which the operation is conducted.
251    ///
252    /// Returns the actual amount of collateral added or a `DispatchError`.
253    fn add_collateral(
254        who: &Candidate,
255        collateral: Self::Asset,
256        force: Fortitude,
257    ) -> Result<Self::Asset, DispatchError>;
258
259    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
260    // ```````````````````````````````````` HOOKS ````````````````````````````````````
261    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
262
263    /// Hook triggered after a successful enrollment
264    /// along with its collateral.
265    ///
266    /// Used for post-processing actions such as emitting events or
267    /// initializing role data.
268    ///
269    /// Default is no-op.
270    fn on_enroll(_who: &Candidate, _collateral: Self::Asset) {}
271
272    /// Hook triggered after a role resignation along with
273    /// regained (unlocked) collateral.
274    ///
275    /// Typically used to perform cleanup or emit resignation events.
276    ///
277    /// Default is no-op.
278    fn on_resign(_who: &Candidate, _released: Self::Asset) {}
279
280    /// Hook triggered when a `Candidate`'s status is mutated.
281    ///
282    /// Allows implementing pallets to react to state transitions (e.g. Active -> Inactive).
283    ///
284    /// Default is no-op.
285    fn on_status_update(_who: &Candidate, _status: &Self::Status) {}
286
287    /// Hook triggered when the collateral value of a role is incremented.
288    ///
289    /// Can be used to adjust dependent metrics or notify external systems.
290    ///
291    /// Default is no-op.
292    fn on_add_collateral(_who: &Candidate, _raised: Self::Asset) {}
293}
294
295// ===============================================================================
296// ````````````````````````````````` FUND ROLES ``````````````````````````````````
297// ===============================================================================
298
299/// Extends [`RoleManager`] to introduce **funding and backing mechanics**
300/// for role-based systems.
301///
302/// This trait defines a common interface for *roles that require financial
303/// support or collateralization from external entities* (called **backers**).
304/// It models relationships  where one or more backers provide funds to a candidate
305/// in exchange for shared exposure, yield, or delegated influence.
306///
307/// Typical applications include:
308///
309/// - **Validator nomination systems** - nominators/delegators (backers) stake
310/// funds behind validators (candidates).  
311/// - **DAO or governance roles** - community members back representatives or
312/// proposal leaders.  
313/// - **DeFi-style credit systems** - where backers fund loan candidates, or
314/// insurance underwriters provide coverage.  
315/// - **Service networks** - e.g., oracle or relayer pools where reputation and
316/// collateral are pooled.
317///
318/// ## Type Parameters
319/// - `Candidate`: The entity or account being backed.
320///
321/// ## Invariants
322///
323/// - Each backer must fund with at least [`Self::min_fund`] units of asset.  
324/// - When a `Candidate` is **not available** (See [`RoleManager::is_available`]),
325/// backers may withdraw all or some of their stake (context-basis).  
326/// - [`Self::fund`] and [`Self::draw`] operations must maintain **consistent
327/// accounting symmetry** between [`Self::backers_of`] and [`Self::backed_for`].  
328///
329/// ## Example Implementations
330///
331/// ### Example 1: Nominated Validator System
332///
333/// In a nominated staking system:
334/// - Candidates are validators who receive backing from multiple nominators (backers).
335/// - Each backer must meet a minimum funding requirement when supporting a candidate.
336/// - The total funding for a candidate cannot exceed a maximum exposure limit.
337/// - Funding increases the candidate's active collateral, while drawing allows backers
338/// to withdraw.
339/// - This example shows how the trait enforces eligibility, updates backing relationships,
340///   and maintains real-time funded values.
341///
342/// ### Example 2: Governance Role Backing
343///
344/// In a governance system where community members can financially support representatives:
345/// - Candidates are council members or proposal leads.
346/// - Backers provide funds to indicate confidence and to incentivize participation.
347/// - The trait ensures backers cannot overexpose themselves or fund below minimum thresholds.
348/// - Candidates may become defaulted if they violate rules or fail to perform, triggering
349/// potential fund loss.
350/// - Rewards, penalties, or collateral adjustments can be layered on top, creating a
351/// dynamic funding ecosystem.
352///
353/// These examples illustrate how `FundRoles` can handle **different backing scenarios**,
354/// from staking and validator support to governance funding, while maintaining **real-time
355/// tracking** of funds and enforcing role-related invariants.
356///
357/// ## Usage
358///
359/// Implement this trait for any role that allows external capital participation or support.
360/// Higher-level logic such as slashing, reward distribution, or governance power weighting
361/// can be layered on top by combining [`CompensateRoles`] or custom traits.
362pub trait FundRoles<Candidate>: RoleManager<Candidate> {
363    /// Type representing the entity providing funding or backing support.
364    type Backer: Delimited;
365
366    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
367    // ``````````````````````````````````` CHECKERS ``````````````````````````````````
368    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
369
370    /// Checks whether the `Candidate` currently holds any funds or active backing relationships.
371    ///
372    /// Returns `Ok(())` if funds exist, otherwise a `DispatchError`.
373    fn has_funds(who: &Candidate) -> DispatchResult;
374
375    /// Validates whether a `Backer` can fund a `Candidate` with the specified amount.
376    ///
377    /// This performs **only validation**, not mutation.
378    ///
379    /// The `precision` parameter defines proportional allocation or best behavior.
380    /// The `force` parameter defines enforcement principle of this funding operation.
381    ///
382    /// Should verify minimum and maximum exposure constraints possibly
383    /// via [`Self::min_fund`] and [`Self::max_exposure`].
384    fn can_fund(
385        by: &Self::Backer,
386        to: &Candidate,
387        value: Self::Asset,
388        precision: Precision,
389        force: Fortitude,
390    ) -> DispatchResult;
391
392    /// Validates whether a `Backer` can draw its funds backed to a `Candidate`.
393    ///
394    /// This performs **only validation**, not mutation.
395    fn can_draw(by: &Self::Backer, from: &Candidate) -> DispatchResult;
396
397    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
398    // ``````````````````````````````````` GETTERS ```````````````````````````````````
399    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
400
401    /// Returns the **maximum exposure** (total backable amount) allowed for this `Candidate`.
402    ///
403    /// This acts as an upper cap on all active fundings combined.
404    ///
405    /// The `precision` and `force` parameters simulate a funding attempt under
406    /// the given directive, determining the effective exposure limits.
407    fn max_exposure(
408        from: &Self::Backer,
409        towards: &Candidate,
410        precision: Precision,
411        force: Fortitude,
412    ) -> Result<Self::Asset, DispatchError>;
413
414    /// Returns the **minimum funding amount** required for a `Backer` to participate.
415    ///
416    /// The `precision` and `force` parameters simulate a funding attempt under
417    /// the given directive, determining the effective minimum requirement.
418    fn min_fund(
419        from: &Self::Backer,
420        towards: &Candidate,
421        precision: Precision,
422        force: Fortitude,
423    ) -> Result<Self::Asset, DispatchError>;
424
425    /// Returns the real-time total **value of funds currently backing** the given `Candidate`.
426    ///
427    /// This reflects the **latest on-chain state**, meaning the value may vary over time
428    /// due to dynamic adjustments such as:
429    /// - Collateral revaluation or slashing
430    /// - Partial withdrawals or new backings
431    /// - Protocol-driven rewards or penalties
432    ///
433    /// Implementations should compute or retrieve this value *at the moment of call*,
434    /// ensuring it reflects the candidate's **live backing exposure**.
435    fn backed_value(who: &Candidate) -> Result<Self::Asset, DispatchError>;
436
437    /// Returns all `Backer`s currently funding the specified `Candidate`,
438    /// along with each backer's **real-time effective contribution**.
439    ///
440    /// Note that returned contribution values are **not static** - they represent the
441    /// candidate's current funding snapshot and may change due to slashing,
442    /// rebalancing, or collateral mutation.
443    fn backers_of(who: &Candidate) -> Result<Vec<(Self::Backer, Self::Asset)>, DispatchError>;
444
445    /// Returns all `Candidate`s that the given `Backer` is funding,
446    /// along with each **real-time funded amount**.
447    ///
448    /// The returned data represents the backer's **current live exposure**, not a
449    /// historical record. Implementations should resolve these dynamically from
450    /// the latest runtime state.
451    fn backed_for(by: &Self::Backer) -> Result<Vec<(Candidate, Self::Asset)>, DispatchError>;
452
453    /// Retrieves the real-time amount of funds currently funded for all the `Candidate`s.
454    fn total_backing() -> Self::Asset;
455
456    /// Retrieves the **current effective funded amount** that `by` has provided to `who`.
457    ///
458    /// This is a *real-time query* that reflects the live value of the fund relationship,
459    /// factoring in any protocol-driven adjustments (e.g., penalties, rewards,
460    /// partial draws, or slashes).
461    fn get_fund(who: &Candidate, by: &Self::Backer) -> Result<Self::Asset, DispatchError>;
462
463    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
464    // ``````````````````````````````````` MUTATORS ``````````````````````````````````
465    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
466
467    /// Performs the **funding operation**, locking the given value of asset
468    /// from the `Backer` in support of the `Candidate`.
469    ///
470    /// The `precision` parameter defines proportional allocation or best behavior.
471    /// The `force` parameter defines enforcement principle of this funding operation.
472    ///
473    /// Returns the actual funded amount.
474    fn fund(
475        to: &Candidate,
476        by: &Self::Backer,
477        value: Self::Asset,
478        precision: Precision,
479        force: Fortitude,
480    ) -> Result<Self::Asset, DispatchError>;
481
482    /// Allows the `Backer` to **draw back** (withdraw) their previously funded assets
483    /// from a `Candidate`.
484    ///
485    /// Returns the amount successfully withdrawn.
486    fn draw(from: &Candidate, by: &Self::Backer) -> Result<Self::Asset, DispatchError>;
487
488    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
489    // ```````````````````````````````````` HOOKS ````````````````````````````````````
490    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
491
492    /// Hook triggered when a funding action is completed.
493    ///
494    /// Can be used to emit events, update metrics, or notify off-chain logic.
495    ///
496    /// Default is no-op.
497    fn on_funded(_who: &Candidate, _by: &Self::Backer, _value: Self::Asset) {}
498
499    /// Hook triggered when funds are drawn or withdrawn.
500    ///
501    /// Use this for cleanup or balance reallocation tasks.
502    ///
503    /// Default is no-op.
504    fn on_drawn(_who: &Candidate, _by: &Self::Backer, _value: Self::Asset) {}
505}
506
507// ===============================================================================
508// `````````````````````````````` COMPENSATE ROLES ```````````````````````````````
509// ===============================================================================
510
511/// Extends [`RoleManager`] to introduce **reward and penalty mechanics**
512/// for role-based systems.
513///
514/// This trait manages economic incentives and behavioral enforcement for candidates,
515/// tracking rewards, penalties, and temporary holdings. Typical applications include
516/// (but not constrained to):
517///
518/// - **Validators** - rewards for block production, penalties for missed duties.
519/// - **Council members** - incentives for active participation, slashing for misconduct.
520/// - **Oracle operators** - rewards for accurate reporting, penalties for stale or
521/// incorrect data.
522///
523/// ## Concepts
524///
525/// - **Hold**: The total reservation of a candidate's assets, includes rewards and other
526/// backings (collaterals, fundings, etc).
527/// - **Reward**: An addition to a candidate's assets for performing expected duties.
528/// - **Penalty**: A fractional deduction (ratio) applied to assets for misbehavior or
529/// non-performance.
530/// - **Forgive**: Reverses penalties, partially or fully, for rehabilitation or
531/// governance action.
532/// - **Reclaim**: Withdraw previously rewarded assets for redistribution or correction.
533///
534/// All returned values (assets, rewards, penalties) are **real-time**, reflecting the
535/// candidate's current effective state.
536///
537/// ## Example Implementations
538///
539/// ### Example 1: Validator Rewards and Penalties
540///
541/// In a blockchain validator system:
542/// - Validators earn rewards for producing blocks, finalizing chains, or performing
543/// other expected duties.
544/// - Misbehavior, missed blocks, or downtime results in penalties, which reduce the
545/// validator's hold.
546/// - Rewards and penalties are recorded with timestamps, allowing auditability and
547/// historical tracking.
548/// - The system may forgive temporary penalties or reclaim rewards in case of protocol
549/// corrections.
550/// - Real-time queries (get_rewards_of, get_penalties_of) reflect the current effective
551/// state of the validator.
552///
553/// ### Example 2: Council Member Performance Incentives
554///
555/// In a governance council:
556/// - Council members receive rewards for attending sessions, voting, or completing
557/// assigned tasks.
558/// - Failure to participate or violating rules triggers penalties, possibly reducing
559/// influence or rewards.
560/// - Holds represent assets reserved for potential penalties or pending rewards.
561/// - Forgiveness mechanisms allow partial reversal of penalties for rehabilitation or
562/// governance decisions.
563/// - Reclaiming rewards may occur if a proposal is invalidated or performance is reversed.
564/// - Real-time computations ensure all incentives and penalties are up-to-date when queried.
565///
566/// These examples illustrate how `CompensateRoles` can manage **dynamic reward and penalty
567/// systems** across different roles, ensuring fairness, accountability, and flexibility in
568/// role-based operations.
569///
570/// ## Invariants
571///
572/// - Total penalties cannot exceed the candidate's current hold or collateral.
573/// - Implementations should track timestamps accurately to maintain auditability.
574pub trait CompensateRoles<Candidate>: RoleManager<Candidate> {
575    /// The ratio type used to represent **penalty factors** applied to a candidate.
576    ///
577    /// This type defines how much of a candidate's assets should be deducted
578    /// during a penalty event, expressed as a fractional percentage.
579    ///
580    /// - Values should be within `[0, 1]` (0% to 100%)
581    /// - Example: `0.05` -> 5% penalty, `0.25` -> 25% penalty
582    type Ratio: Percentage;
583
584    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
585    // ``````````````````````````````````` CHECKERS ``````````````````````````````````
586    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
587
588    /// Checks whether the candidate has any pending rewards.
589    fn has_reward(who: &Candidate) -> DispatchResult;
590
591    /// Checks whether the candidate has any pending penalties.
592    fn has_penalty(who: &Candidate) -> DispatchResult;
593
594    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
595    // ``````````````````````````````````` GETTERS ```````````````````````````````````
596    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
597
598    /// Returns the current **hold amount** of a candidate.
599    ///
600    /// Holds are total reserved assets (may include rewards) that may be used
601    /// for penalties or temporary constraints.
602    fn get_hold(who: &Candidate) -> Result<Self::Asset, DispatchError>;
603
604    /// Returns all pending rewards for a candidate along with their timestamp of enforcement.
605    ///
606    /// The list reflects **real-time pending rewards**, not historical logs.
607    fn get_rewards_of(
608        who: &Candidate,
609    ) -> Result<Vec<(Self::TimeStamp, Self::Asset)>, DispatchError>;
610
611    /// Returns all rewards of candidates issued at a specific timestamp.
612    ///
613    /// - This is a **snapshot query** for the given timestamp; it does **not mutate state**.
614    /// - Returned values may reflect the **rewards actually enforced or pending** based on
615    ///   the given timestamp, which may be used for auditing, reporting, or batch processing.
616    fn get_rewards_on(
617        time_stamp: Self::TimeStamp,
618    ) -> Result<Vec<(Candidate, Self::Asset)>, DispatchError>;
619
620    /// Returns all pending penalties for a candidate along with their timestamp of enforcement.
621    ///
622    /// The list reflects **real-time pending penalties**, not historical logs.
623    fn get_penalties_of(
624        who: &Candidate,
625    ) -> Result<Vec<(Self::TimeStamp, Self::Ratio)>, DispatchError>;
626
627    /// Returns all penalties of candidates issued at a specific timestamp.
628    ///
629    /// - This is a **snapshot query** for the given timestamp; it does **not mutate state**.
630    /// - Returned values may reflect the **penalties actually enforced or pending** based on
631    ///   the given timestamp, which may be used for auditing, reporting, or batch processing.
632    fn get_penalties_on(
633        time_stamp: Self::TimeStamp,
634    ) -> Result<Vec<(Candidate, Self::Ratio)>, DispatchError>;
635
636    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
637    // ``````````````````````````````````` MUTATORS ``````````````````````````````````
638    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
639
640    /// Updates the hold of a candidate to the specified amount.
641    ///
642    /// This method is utilized to reward or penalize a `Candidate`'s hold which
643    /// reflects on its individual backings.
644    fn set_hold(
645        who: &Candidate,
646        value: Self::Asset,
647        precision: Precision,
648        force: Fortitude,
649    ) -> DispatchResult;
650
651    /// Issues a reward to a candidate, marks it as pending and returning the timestamp.
652    ///
653    /// Once the reward is enforced during the timestamp, it is applied to the
654    /// `Candidate`'s hold.
655    ///
656    /// This is to ensure rewards reversal (regaining) if applied wrongly.
657    fn reward(
658        who: &Candidate,
659        value: Self::Asset,
660        precision: Precision,
661    ) -> Result<Self::TimeStamp, DispatchError>;
662
663    /// Applies a penalty (fractional [`Ratio`](Self::Ratio)) to a candidate, marks it as pending
664    /// and returning the timestamp.
665    ///
666    /// Once the penalty is enforced during the timestamp, it is applied to the
667    /// `Candidate`'s hold.
668    ///
669    /// This is to ensure penalty reversal (forgiving) if applied wrongly.
670    fn penalize(who: &Candidate, factor: Self::Ratio) -> Result<Self::TimeStamp, DispatchError>;
671
672    /// Forgives a pending penalty, returning its factor.
673    ///
674    /// Cannot be utilized for enforced penalties, only pending ones.
675    ///
676    /// `from` specifies the timestamp of the penalty to forgive.
677    fn forgive(who: &Candidate, from: Self::TimeStamp) -> Result<Self::Ratio, DispatchError>;
678
679    /// Reclaims a pending reward from a candidate, returning its reward value.
680    ///
681    /// Cannot be utilized for enforced rewards, only pending ones.
682    ///
683    /// `from` specifies the timestamp of the reward to reclaim.
684    fn reclaim(who: &Candidate, from: Self::TimeStamp) -> Result<Self::Asset, DispatchError>;
685
686    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
687    // ```````````````````````````````````` HOOKS ````````````````````````````````````
688    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
689
690    /// Hook triggered when a reward is issued.
691    ///
692    /// Default is no-op.
693    fn on_reward(_who: &Candidate, _amount: Self::Asset, _at: Self::TimeStamp) {}
694
695    /// Hook triggered when a penalty is applied.
696    ///
697    /// Default is no-op.
698    fn on_penalize(_who: &Candidate, _factor: Self::Ratio, _at: Self::TimeStamp) {}
699
700    /// Hook triggered when a penalty is forgiven.
701    ///
702    /// Default is no-op.
703    fn on_forgive(_who: &Candidate, _factor: Self::Ratio) {}
704
705    /// Hook triggered when a reward is reclaimed.
706    ///
707    /// Default is no-op.
708    fn on_reclaim(_who: &Candidate, _amount: Self::Asset) {}
709
710    /// Hook triggered when an author's total hold is updated.
711    ///
712    /// Signals a mutation maybe due to rewards/penalties or internal
713    /// changes enforced (finalized)
714    ///
715    /// Default is no-op.
716    fn on_set_hold(_who: &Candidate, _value: Self::Asset) {}
717}
718
719// ===============================================================================
720// ``````````````````````````````` ROLE PROBATION ````````````````````````````````
721// ===============================================================================
722
723/// Extends [`RoleManager`] to introduce **probation and permanent status mechanics**
724/// for role-based systems.
725///
726/// This trait manages the probation lifecycle of candidates, tracking their risk,
727/// confirmation, and eligibility for permanent status. Typical applications include:
728/// - **Employees** - probation periods before permanent employment.
729/// - **Validators or council members** - temporary risk periods before confirmed full status.
730/// - **Accounts or participants** - temporary monitoring before achieving full privileges.
731///
732/// ## Concepts
733///
734/// - **Probation / Risk**: Candidate is under evaluation; actions or failures
735/// may have consequences.
736/// - **Permanent / Confirmation**: Candidate has successfully passed evaluation;
737/// fully confirmed.
738/// - **Secure**: Temporarily avoids risk without confirming permanent status.
739///
740/// ## Probation Flow
741/// - A new enrolled candidate starts in **probation** status.
742/// - Upon negative performance, they risk losing permanence.
743/// - Upon meeting required criteria, they are **confirmed permanent**.
744/// - If a permanent candidate violates policies, permanent status may be revoked.
745///
746/// ## Invariants
747/// - Candidates should not simultaneously be in probation and permanent status.
748pub trait RoleProbation<Candidate>: RoleManager<Candidate> {
749
750    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
751    // ``````````````````````````````````` CHECKERS ``````````````````````````````````
752    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
753
754    /// Checks if the candidate is currently under probation.
755    ///
756    /// Returns `DispatchError` if the query fails.
757    fn is_on_probation(who: &Candidate) -> DispatchResult;
758
759    /// Checks if the candidate has secured permanent / confirmed status.
760    ///
761    /// Returns `DispatchError` if the query fails.
762    fn is_permanent(who: &Candidate) -> DispatchResult;
763
764    /// Checks if the candidate is eligible to become permanent.
765    ///
766    /// Returns `DispatchError` if the candidate is not eligible.
767    fn can_be_permanent(who: &Candidate) -> DispatchResult;
768
769    /// Checks if the candidate's permanent status can be revoked,
770    /// returning them to probation.
771    ///
772    /// Returns `DispatchError` if the candidate is not eligible.
773    fn can_revoke_permanence(who: &Candidate) -> DispatchResult;
774
775    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
776    // ``````````````````````````````````` MUTATORS ``````````````````````````````````
777    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
778
779    /// Places the candidate's probation at risk (risking permanence).
780    ///
781    /// Returns `DispatchError` if the candidate is not in probation.
782    fn risk_probation(who: &Candidate) -> DispatchResult;
783
784    /// Places the candidate's permanence at risk (risking probation).
785    ///
786    /// Returns `DispatchError` if the candidate is not in permanence.
787    fn risk_permanence(who: &Candidate) -> DispatchResult;
788
789    /// Marks the candidate as positively progressing toward permanent status.
790    ///
791    /// This indicates that the candidate has demonstrated sufficient performance
792    /// or compliance during probation, making them eligible to become permanent
793    /// once probation concludes.
794    ///
795    /// Returns `DispatchError` if the candidate is not in probation.
796    fn secure_permanence(who: &Candidate) -> DispatchResult;
797
798    /// Marks the candidate as secured / confirmed / permanent.
799    ///
800    /// Returns the new status on success.
801    /// Returns `DispatchError` if the operation fails.
802    fn set_permanence(who: &Candidate) -> Result<Self::Status, DispatchError>;
803
804    /// Revokes permanent status and places the candidate back under probation.
805    ///
806    /// Returns the new status on success.
807    /// Returns `DispatchError` if the operation fails.
808    fn revoke_permanence(who: &Candidate) -> Result<Self::Status, DispatchError>;
809
810    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
811    // ```````````````````````````````````` HOOKS ````````````````````````````````````
812    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
813
814    /// Hook triggered after the candidate under probation
815    /// risks their permanence.
816    ///
817    /// Default implementation is a no-op.
818    fn on_risk_probation(_who: &Candidate) {}
819
820    /// Hook triggered after the permanent candidate
821    /// risks their permanence.
822    ///
823    /// Default implementation is a no-op.
824    fn on_risk_permanence(_who: &Candidate) {}
825
826    /// Hook triggered after a candidate secures being
827    /// permanently promoted soon without negative impacts.
828    ///
829    /// Default implementation is a no-op.
830    fn on_secure_permanence(_who: &Candidate) {}
831
832    /// Hook triggered after a candidate gets permanent status.
833    ///
834    /// Default implementation is a no-op.
835    fn on_set_permance(_who: &Candidate) {}
836
837    /// Hook triggered after a candidate's permanence is
838    /// revoked, placing them back under probation.
839    ///
840    /// Default implementation is a no-op.
841    fn on_revoke_permanence(_who: &Candidate) {}
842}
843
844// ===============================================================================
845// ```````````````````````````````` ROLE ACTIVITY ````````````````````````````````
846// ===============================================================================
847
848/// A lightweight abstraction for determining whether a role-bearing `Candidate` is
849/// currently **idle** or **actively performing duties**.
850///
851/// This trait models **real-time operational activity**, independent of role
852/// lifecycle, status, or eligibility. It is intended to compose with role
853/// management, funding, and compensation logic.
854///
855/// ## Semantics
856///
857/// The core API uses an **inverted pattern which allows activity to be represented
858/// with structured context rather than a lossy boolean.
859///
860/// The associated `Activity` type must represent **non-fatal engagement**
861/// and be convertible into [`DispatchError`].
862pub trait RoleActivity<Candidate, TimeStamp> {
863    /// Describes the duty currently being performed by the candidate.
864    ///
865    /// Returned when the candidate is active. When converted into
866    /// [`DispatchError`], it must clearly indicate **why the operation is blocked**
867    /// and **what must occur to withdraw from or complete the ongoing activity**.
868    ///
869    /// This type represents non-fatal operational engagement only.
870    type Activity: RuntimeEnum + Delimited + Into<DispatchError>;
871
872    /// Returns `Ok(())` if the candidate is currently **idle** (not performing duties).
873    ///
874    /// Returns `Err(Activity)` if the candidate is **active**, where `Activity`
875    /// describes the duty being performed.
876    ///
877    /// This method must not mutate state and should reflect real-time activity.
878    fn is_idle(who: &Candidate) -> Result<(), Self::Activity>;
879
880    /// Returns `Ok(Activity)` if the candidate is **active**, where `Activity`
881    /// describes the duty being performed.
882    ///
883    /// Returns `Err(())` if the candidate is **idle**.
884    fn is_active(who: &Candidate) -> Result<Self::Activity, ()> {
885        match Self::is_idle(who) {
886            Ok(_) => Err(()),
887            Err(a) => Ok(a),
888        }
889    }
890}