frame_suite/
commitment.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// ``````````````````````````````` COMMITMENT SUITE ``````````````````````````````
14// ===============================================================================
15
16//! A composable framework for expressing financial intent as **immutable commitments**.
17//!
18//! ## Overview
19//!
20//! This module defines a comprehensive set of traits for building financial
21//! commitment systems that are **semantic, composable, transparent, and consistent**.
22//!
23//! At its core, a commitment represents **bonding value to a specific digest under a reason**.
24//!
25//! Instead of treating value as passive balance, this framework models value as
26//! something actively **bound with purpose and context**.
27//!
28//! Every commitment binds an asset to:
29//! - a **Reason** - why the value is committed
30//! - a **Digest** - the exact terms or context the value is bonded to
31//!
32//! Together, they form a **verifiable, immutable agreement**.
33//!
34//! Commitments can carry meaning through variants (e.g., long/short,
35//! for/against, positive/negative), support grouping via indexes and pools,
36//! and allow real-time inspection of state.
37//!
38//! The **reason provider (runtime logic)** governs how committed value evolves:
39//! it can update digest-level values, effectively managing all bonded funds
40//! under that reason. Individual commitments are **resolved lazily**, meaning
41//! adjustments are realized only when commitments are settled.
42//!
43//! ## Mental Model
44//!
45//! A commitment can be understood as:
46//!
47//! "Bond this value, for this purpose, to this exact context."
48//!
49//! - Reason provides intent (staking, escrow, trade, vote)
50//! - Digest defines the binding target (hash, identifier, agreement)
51//! - Asset represents the value being bonded
52//!
53//! This abstraction unifies patterns across financial systems such as
54//! staking, escrow, betting, trading, and governance.
55//!
56//! ## Trait Architecture
57//!
58//! The framework is layered:
59//!
60//! ### Foundation
61//! - [`Commitment`] - atomic bonding and lifecycle of commitments
62//! - [`InspectAsset`] - available funds introspection
63//! - [`DigestModel`] - digest classification
64//!
65//! ### Grouping
66//! - [`CommitIndex`] - a single commitment distributed across multiple digests
67//!   under a reason, as if placed individually
68//!
69//! - [`CommitPool`] - manager-controlled allocation of bonded funds across
70//!   multiple digests under a reason, without custodial ownership
71//!
72//! ### Semantics
73//! - [`CommitVariant`] - directional meaning (long/short, for/against)
74//! - [`IndexVariant`] - variants applied to indexed commitments
75//! - [`PoolVariant`] - variants applied to pooled commitments
76//!
77//! These traits compose into a layered system:
78//! - Base layer: [`Commitment`], [`InspectAsset`], [`DigestModel`]
79//! - Grouping layer: [`CommitIndex`], [`CommitPool`]
80//! - Variant layer: [`CommitVariant`], [`IndexVariant`], [`PoolVariant`]
81//!
82//! ## Core Properties
83//!
84//! - **Atomic** - bonded commitments cannot be partially altered  
85//! - **Immutable** - [`Commitment::Digest`] and reason define permanent binding  
86//! - **Value-bound** - always tied to quantifiable assets  
87//! - **Composable** - higher-level abstractions reuse the same rules  
88//! - **Inspectable** - real-time bonded state is always queryable  
89//!
90//! ## Why This Exists
91//!
92//! Many financial systems repeatedly implement the same primitives:
93//! - locking or bonding funds  
94//! - enforcing rules  
95//! - tracking intent  
96//! - preventing duplication  
97//!
98//! This framework provides a unified abstraction to express all of them
99//! consistently and safely.
100//!
101//! ## Invariants
102//!
103//! - One commitment per `(proprietor, reason)`  
104//! - Commitments are immutable (value may only increase via
105//!   [`Commitment::raise_commit`])  
106//! - [`Commitment::Digest`] defines the binding target and scope  
107//! - State reflects current values via [`Commitment::get_digest_value`],
108//!   not historical deposits  
109//!
110//! ## Use Cases
111//!
112//! This framework is applicable across domains such as:
113//!
114//! - Financial systems (bets, trades, hedges)  
115//! - Prediction markets (affirmative/negative stakes)  
116//! - Voting protocols (for/against commitments)  
117//! - Portfolio management (indexed and pooled commitments)  
118//! - Escrow and contract systems  
119//!
120//! ## Summary
121//!
122//! **Commitment = bonding value to a digest under a reason**
123//!
124//! This module transforms raw assets into structured, meaningful agreements
125//! that can be composed into complex financial systems.
126
127// ===============================================================================
128// ``````````````````````````````````` IMPORTS ```````````````````````````````````
129// ===============================================================================
130
131// --- Core ---
132use core::cmp::Ordering;
133
134// --- Local crate imports ---
135use crate::{
136    base::{
137        Asset, Countable, Delimited, Elastic, Keyed, Percentage, RuntimeEnum, RuntimeError,
138        Storable,
139    },
140    misc::{Directive, Extent},
141};
142
143// --- FRAME Support ----
144use frame_support::ensure;
145
146// --- Substrate primitives ---
147use sp_runtime::{
148    traits::{Saturating, Zero},
149    DispatchError, DispatchResult, Vec,
150};
151
152// ===============================================================================
153// ```````````````````````````````` INSPECT ASSET ````````````````````````````````
154// ===============================================================================
155
156/// A trait for inspecting the total available funds of an proprietor.
157///
158/// This allows querying all funds that are available to a proprietor,
159/// including liquid balances and any amounts held for specific purposes
160/// that the system considers available.
161///
162/// In this trait's context, "available funds" means those balances
163/// that can be used for operations in context.
164///
165/// Generics:
166/// - **Proprietor** - the entity that owns the asset and can make commitments.
167pub trait InspectAsset<Proprietor> {
168    /// Representing the Quantifiable Asset Type.
169    type Asset: Asset;
170
171    /// Retrieves the total available funds for a given proprietor.
172    ///
173    /// This must include all balances that can be utilized for the
174    /// implementation purposes.
175    ///
176    /// ## Returns
177    /// - [`Self::Asset`] containing the total available funds for the proprietor
178    fn available_funds(who: &Proprietor) -> Self::Asset;
179}
180
181// ===============================================================================
182// ````````````````````````````````` DIGEST MODEL ````````````````````````````````
183// ===============================================================================
184
185/// A trait for determining the model variant of a digest.
186///
187/// In this system, a "digest" represents a compact identifier for
188/// a commitment or resource.
189///
190/// Since all digests share a common base type, this trait provides a
191/// way to classify or wrap them into distinct model variants
192/// (e.g., Direct, Index, Pools) to improve clarity and enforce type safety.
193pub trait DigestModel<Proprietor>: Commitment<Proprietor> {
194    /// Wraps [`Commitment::Digest`] to reduce ambiguity.
195    type Model: Delimited + RuntimeEnum + Storable;
196
197    /// Determines the appropriate model variant for the given digest and reason.
198    ///
199    /// ## Returns
200    /// - `Ok(Model)` if the digest is recognized.
201    /// - `Err(DispatchError)` if the digest cannot be determined.
202    fn determine_digest(
203        digest: &Self::Digest,
204        reason: &Self::Reason,
205    ) -> Result<Self::Model, DispatchError>;
206}
207
208// ===============================================================================
209// `````````````````````````````` COMMITMENT ERRORS ``````````````````````````````
210// ===============================================================================
211
212/// Commitment-related error type used in trait defaults.
213pub enum CommitError {
214    /// Insufficient funds to perform the requested
215    /// commitment operation.
216    InsufficientFunds,
217
218    /// A commitment already exists for the given proprietor
219    /// and commitment reason.
220    CommitAlreadyExists,
221
222    /// Minting is not permitted under the current constraints
223    /// or exceeds allowed limits.
224    MintingOffLimits,
225
226    /// Reaping (burning) is not permitted under the current
227    /// constraints or exceeds allowed limits.
228    ReapingOffLimits,
229
230    /// Placing a new commitment is not permitted under the current constraints
231    /// or exceeds allowed limits.
232    PlacingOffLimits,
233
234    /// Increasing (raising) an existing commitment is not permitted under the
235    /// current constraints or exceeds allowed limits.
236    RaisingOffLimits,
237}
238
239/// A trait for mapping **domain-level Commitment errors** into
240/// **caller- or pallet-specific error types**.
241///
242/// This trait acts as a bridge between the generic, FRAME-agnostic
243/// [`CommitError`] enum and the concrete error type expected by the
244/// execution context.
245pub trait CommitErrorHandler {
246    /// Concrete error type produced by the handler.
247    ///
248    /// Implements conversion to [`DispatchError`].
249    type Error: RuntimeError;
250
251    /// Converts a generic [`CommitError`] into the handler's
252    /// concrete error type which implements `Into<DispatchError>`.
253    ///
254    /// This function centralizes error translation logic and ensures
255    /// that all balance-related failures are surfaced consistently
256    /// according to the caller's error domain.
257    fn from_commit_error(e: CommitError) -> Self::Error;
258}
259
260// ===============================================================================
261// `````````````````````````````````` COMMITMENT `````````````````````````````````
262// ===============================================================================
263
264/// Represents an **atomic, immutable financial agreement** between parties.
265///
266/// It is anchored in:
267/// - a **Reason** (category/purpose),
268/// - a **Digest** (commitment context),
269/// - and the act of committing a value.
270///
271/// ## Key Concepts
272///
273/// - **Commitment**: Locking or assigning an asset under agreed terms.
274/// - **Reason**: Purpose/category (e.g., staking, escrow, collateral,
275/// investment, betting).
276/// - **Digest**: Immutable reference to the commitment's context or terms
277/// (often a cryptographic hash or a contextual identifier).
278/// - **Proprietor**: Entity responsible for holding/committing the asset.
279/// - **Asset**: Value being committed.
280///
281/// ## Why Commitment?
282///
283/// Many financial systems - staking, escrow, lending, betting, auctions - share
284/// a common pattern: one party proposes terms (digest), another commits a value
285/// under those terms, and both agree on the purpose (reason).
286///
287/// Without a unifying abstraction, each system must reimplement logic for:
288/// - Locking assets
289/// - Ensuring immutability of terms
290/// - Preventing double commitments
291/// - Categorizing commitments by purpose
292///
293/// The `Commitment` trait standardizes this pattern, enabling:
294/// - **Consistency**: Same behavior across different financial constructs.
295/// - **Composability**: Higher-level constructs (pools, indexes, markets) reuse
296/// the same rules.
297/// - **Auditability**: Commitments can be inspected and verified.
298///
299/// ## Properties
300///
301/// - **Atomic**: Commitments cannot be partially altered.
302/// - **Immutable**: Digest + Reason permanently define the commitment.
303/// - **Category-driven**: Reason defines interpretation.
304/// - **Value-based**: Always tied to a quantifiable asset.
305///
306/// ## Real-World Analogies
307///
308/// | Use Case       | Reason             | Digest              | Commitment    |
309/// |----------------|--------------------|---------------------|---------------|
310/// | Stake          | `"staking"`        | Conditions hash     | Staked amount |
311/// | Escrow         | `"escrow"`         | Contract terms hash | Locked funds  |
312/// | Collateral     | `"loan collateral"`| Loan agreement hash | Pledged asset |
313/// | Betting        | `"bet"`            | Bet terms           | Wagered asset |
314/// | Market position| `"market trade"`   | Order terms         | Locked funds  |
315/// | Auction        | `"auction bid"`    | Bid terms           | Bid amount    |
316///
317///
318/// ## Examples
319///
320/// - **Staking**: Alice stakes 100 tokens for reason `"staking"` with digest `"ant_pool_hash"`.
321///   - Proprietor: Alice
322///   - Reason: `"staking"`
323///   - Digest: `"ant_pool_hash"`
324///   - Asset: 100 tokens.
325///
326/// - **Betting**: Bob places a bet of 50 tokens for reason `"bet_games"` with digest
327/// `"nfl_betting"`.
328///   - Locks Bob's stake until event outcome.
329///
330/// - **Escrow**: Carol commits 1000 tokens into escrow for reason `"escrows"` with
331/// digest `"freelance"`.
332///   - Funds locked until conditions are fulfilled.
333///
334/// - **Market Order**: Dave places a buy order worth 500 tokens for reason
335/// `"market_orders"` with digest `"cryptocurrencies"`.
336///   - Funds locked until order executes or cancels.
337///
338/// ## Invariants
339///
340/// - Proprietor can commit to **only one digest per reason**.
341/// - Commitments are **atomic** and **immutable**.
342/// - Commitment value may increase via [`Commitment::raise_commit`] but cannot
343/// be decreased arbitrarily.
344/// - Commitment values are dynamic, reflecting real-time state.
345/// - Digest values may be updated by the reason provider via
346/// [`Commitment::set_digest_value`] and must propagate to all related commitments.
347///
348/// **Summary:**  
349/// Commitment = locked asset,  
350/// Reason = purpose/category,  
351/// Digest = agreement terms hash,  
352/// Act of commitment = immutable financial agreement.
353///
354/// Generics:
355/// - **Proprietor** - the entity that owns the asset and can make commitments.
356pub trait Commitment<Proprietor>: InspectAsset<Proprietor> + CommitErrorHandler {
357    /// The source used to generate the digest.
358    type DigestSource: Elastic;
359
360    /// The immutable substance proposed by the first party - the "sealed deed content".
361    /// It represents the details of the deed or offer that expects a commitment.
362    ///
363    /// Digest is the core of a commitment: it proves what the commitment is tied to,
364    /// and ensures it is fixed and immutable.
365    type Digest: Keyed;
366
367    /// The category or type of the commitment.
368    /// Provides meaning to the digest by placing it in context.
369    type Reason: Elastic + Storable + RuntimeEnum;
370
371    /// Represents the qualitative configuration of a commit operation.
372    ///
373    /// Encodes behavioral characteristics such as precision and fortitude,
374    /// influencing how commitments are evaluated and executed. (e.g. exact
375    /// vs approximate, polite vs forceful execution).
376    ///
377    /// Default is provided if in case no preference is provided.
378    type Intent: Delimited + Directive + Default;
379
380    /// Provides optional advisory bounds for commitment and digest operations.
381    ///
382    /// These limits act as an additional validation layer on top of core
383    /// invariants, helping prevent premature or extreme values without
384    /// affecting correctness.
385    ///
386    /// Uses [`Extent`] to express minimum, maximum, or optimal values.
387    type Limits: Extent<Scalar = Self::Asset>;
388
389    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
390    // ``````````````````````````````````` CHECKERS ``````````````````````````````````
391    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
392
393    /// Checks whether a commitment exists for the given proprietor and reason.
394    ///
395    /// ### Returns
396    /// - `Ok(())` if the commitment exists
397    /// - `Err(DispatchError)` if no commitment is found
398    fn commit_exists(who: &Proprietor, reason: &Self::Reason) -> DispatchResult;
399
400    /// Checks whether a specific digest exists for the given reason.
401    ///
402    /// ### Returns
403    /// - `Ok(())` if the digest exists
404    /// - `Err(DispatchError)` if the digest is not found
405    fn digest_exists(reason: &Self::Reason, digest: &Self::Digest) -> DispatchResult;
406
407    /// Validates whether a new commitment can be placed.
408    ///
409    /// Ensures that no existing commitment exists for the given reason (enforcing
410    /// the "one digest per reason" invariant) and that the proprietor has sufficient
411    /// available funds to cover the commitment value.
412    ///
413    /// ## Returns
414    /// - `Ok(())` if validation succeeds
415    /// - `Err(DispatchError)` if a commitment already exists or insufficient funds are available
416    fn can_place_commit(
417        who: &Proprietor,
418        reason: &Self::Reason,
419        digest: &Self::Digest,
420        value: Self::Asset,
421        qualifier: &Self::Intent,
422    ) -> DispatchResult {
423        ensure!(
424            Self::commit_exists(who, reason).is_err(),
425            Self::from_commit_error(CommitError::CommitAlreadyExists)
426        );
427        let max = Self::available_funds(who);
428        ensure!(
429            max >= value,
430            Self::from_commit_error(CommitError::InsufficientFunds)
431        );
432        let limits = Self::place_commit_limits(who, reason, digest, qualifier)?;
433        ensure!(
434            limits.contains(value),
435            Self::from_commit_error(CommitError::PlacingOffLimits)
436        );
437        Ok(())
438    }
439
440    /// Validates whether an existing commitment can be increased.
441    ///
442    /// Ensures that a commitment for the given reason already exists and that
443    /// the proprietor has sufficient available funds to increase the commitment
444    /// by the specified value.
445    ///
446    /// ## Returns
447    /// - `Ok(())` if validation succeeds
448    /// - `Err(DispatchError)` if any of the condition fails.
449    fn can_raise_commit(
450        who: &Proprietor,
451        reason: &Self::Reason,
452        value: Self::Asset,
453        qualifier: &Self::Intent,
454    ) -> DispatchResult {
455        Self::commit_exists(who, reason)?;
456        let max = Self::available_funds(who);
457        ensure!(
458            max >= value,
459            Self::from_commit_error(CommitError::InsufficientFunds)
460        );
461        let limits = Self::raise_commit_limits(who, reason, qualifier)?;
462        ensure!(
463            limits.contains(value),
464            Self::from_commit_error(CommitError::RaisingOffLimits)
465        );
466        Ok(())
467    }
468
469    /// Validates whether an existing commitment can be resolved.
470    ///
471    /// Ensures that a commitment for the given reason exists before
472    /// allowing resolution/closing.
473    ///
474    /// ## Returns
475    /// - `Ok(())` if validation succeeds
476    /// - `Err(DispatchError)` if no such commitment exists
477    fn can_resolve_commit(who: &Proprietor, reason: &Self::Reason) -> DispatchResult {
478        Self::commit_exists(who, reason)?;
479        Ok(())
480    }
481
482    /// Validates whether a digest's value can be set or updated.
483    ///
484    /// Ensures that the specified digest exists under the given reason,
485    /// and that the intended value update respects advisory limits
486    /// (mint or reap depending on direction).
487    ///
488    /// The `qualifier` may influence how limits are interpreted.
489    ///
490    /// ## Returns
491    /// - `Ok(())` if validation succeeds
492    /// - `Err(DispatchError)` if the digest does not exist or limits are violated
493    fn can_set_digest_value(
494        reason: &Self::Reason,
495        digest: &Self::Digest,
496        value: Self::Asset,
497        qualifier: &Self::Intent,
498    ) -> DispatchResult {
499        // Get current value
500        let current = Self::get_digest_value(reason, digest)?;
501        match current.cmp(&value) {
502            Ordering::Less => {
503                // Mint path (increase)
504                let limits = Self::digest_mint_limits(digest, reason, qualifier)?;
505                let mintable = value.saturating_sub(current);
506                ensure!(
507                    limits.contains(mintable),
508                    Self::from_commit_error(CommitError::MintingOffLimits)
509                )
510            }
511            Ordering::Greater => {
512                // Reap path (decrease)
513                let limits = Self::digest_reap_limits(digest, reason, qualifier)?;
514                let reapable = current.saturating_sub(value);
515                ensure!(
516                    limits.contains(reapable),
517                    Self::from_commit_error(CommitError::ReapingOffLimits)
518                )
519            }
520            Ordering::Equal => {
521                // No-op, always valid
522            }
523        }
524        Ok(())
525    }
526
527    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
528    // ``````````````````````````````````` GETTERS ```````````````````````````````````
529    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
530
531    /// Retrieves the digest tied to a proprietor's commitment for the given reason.
532    ///
533    /// Since the system enforces only one digest per reason, its expected to be
534    /// only a single digest or none.
535    ///
536    /// ### Returns
537    /// - `Ok(Digest)` containing the commitment's digest
538    /// - `Err(DispatchError)` if no commitment exists for the reason
539    fn get_commit_digest(
540        who: &Proprietor,
541        reason: &Self::Reason,
542    ) -> Result<Self::Digest, DispatchError>;
543
544    /// Retrieves the total aggregated value for all commitments under the given reason.
545    ///
546    /// ### Returns
547    /// - `Self::Asset` containing the total value across all commitments for the reason
548    fn get_total_value(reason: &Self::Reason) -> Self::Asset;
549
550    /// Retrieves the current real-time value of a proprietor's commitment for the given reason.
551    ///
552    /// Returns the live committed value at the moment of calling, reflecting any
553    /// changes to the underlying digest value since the commitment was initially
554    /// placed. This is not a historical value but the actual current state, as
555    /// digest values can be updated via [`set_digest_value`](Self::set_digest_value).
556    ///
557    /// ### Returns
558    /// - `Ok(Asset)` containing the current commitment value
559    /// - `Err(DispatchError)` if the commitment does not exist
560    fn get_commit_value(
561        who: &Proprietor,
562        reason: &Self::Reason,
563    ) -> Result<Self::Asset, DispatchError>;
564
565    /// Retrieves the current aggregated value for the given digest under the specified reason.
566    ///
567    /// Returns the real-time sum of all commitments tied to the digest.
568    /// Useful for evaluating the full scope of commitments to a specific digest.
569    ///
570    /// ### Returns
571    /// - `Ok(Asset)` containing the aggregated digest value
572    /// - `Err(DispatchError)` if the digest does not exist or lookup fails
573    fn get_digest_value(
574        reason: &Self::Reason,
575        digest: &Self::Digest,
576    ) -> Result<Self::Asset, DispatchError>;
577
578    /// Provides advisory bounds for increasing (minting) a digest's value.
579    ///
580    /// Acts as an optional guard to prevent abrupt or excessive growth,
581    /// complementing core digest update logic.
582    fn digest_mint_limits(
583        _digest: &Self::Digest,
584        _reason: &Self::Reason,
585        _qualifier: &Self::Intent,
586    ) -> Result<Self::Limits, DispatchError> {
587        // By default, this returns a baseline limit,
588        // representing a static or fallback bound when no dynamic limits are applied.
589        //
590        // Implementors may override this to provide context-specific limits.
591        Ok(Self::Limits::none())
592    }
593
594    /// Provides advisory bounds for placing a new commitment.
595    ///
596    /// Acts as an optional pre-validation layer to prevent premature or
597    /// undesirable commits by constraining value ranges.
598    fn place_commit_limits(
599        _who: &Proprietor,
600        _reason: &Self::Reason,
601        _digest: &Self::Digest,
602        _qualifier: &Self::Intent,
603    ) -> Result<Self::Limits, DispatchError> {
604        // By default, this returns an unbounded extent,
605        // meaning no additional constraints are applied unless overridden.
606        //
607        // Implementors may override this to provide context-specific limits.
608        Ok(Self::Limits::none())
609    }
610
611    /// Provides advisory bounds for increasing an existing commitment.
612    ///
613    /// Acts as an optional guard to prevent excessive or premature raises,
614    /// complementing core checks like existence and available funds.
615    fn raise_commit_limits(
616        _who: &Proprietor,
617        _reason: &Self::Reason,
618        _qualifier: &Self::Intent,
619    ) -> Result<Self::Limits, DispatchError> {
620        // By default, this returns an unbounded extent,
621        // meaning no additional constraints are applied unless overridden.
622        //
623        // Implementors may override this to provide context-specific limits.
624        Ok(Self::Limits::none())
625    }
626
627    /// Provides advisory bounds for decreasing (reaping) a digest's value.
628    ///
629    /// Acts as an optional guard to prevent aggressive or premature reductions,
630    /// complementing core invariants that ensure correctness.
631    fn digest_reap_limits(
632        _digest: &Self::Digest,
633        _reason: &Self::Reason,
634        _qualifier: &Self::Intent,
635    ) -> Result<Self::Limits, DispatchError> {
636        // By default, this returns a baseline limit,
637        // representing a static or fallback bound when no dynamic limits are applied.
638        // Implementors may override this to provide context-specific limits.
639        Ok(Self::Limits::none())
640    }
641
642    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
643    // ````````````````````````````````` CONSTRUCTORS ````````````````````````````````
644    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
645
646    /// Generates a unique digest from the provided source.
647    ///
648    /// Creates an immutable identifier that represents the commitment's terms,
649    /// ensuring collision-free digest generation across the system.
650    ///
651    /// ## Returns
652    /// - `Ok(Digest)` containing the generated unique digest
653    /// - `Err(DispatchError)` if digest generation fails
654    fn gen_digest(via: &Self::DigestSource) -> Result<Self::Digest, DispatchError>;
655
656    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
657    // ``````````````````````````````````` MUTATORS ``````````````````````````````````
658    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
659
660    /// Places an immutable commitment for the specified digest and reason.
661    ///
662    /// Locks the specified value under the given digest and reason, creating
663    /// an atomic commitment that cannot be reduced or partially withdrawn.
664    ///
665    /// The qualifier parameter enforces exactness or best-effort semantics,
666    /// while also specifying whether the system must force achieving the
667    /// commitment conditions.
668    ///
669    /// ## Returns
670    /// - `Ok(Asset)` containing the actual value committed to the digest
671    /// - `Err(DispatchError)` if placement fails due to insufficient funds or
672    /// validation errors
673    fn place_commit(
674        who: &Proprietor,
675        reason: &Self::Reason,
676        digest: &Self::Digest,
677        value: Self::Asset,
678        qualifier: &Self::Intent,
679    ) -> Result<Self::Asset, DispatchError>;
680
681    /// Resolves and releases the commitment tied to the specified reason.
682    ///
683    /// Finalizes the commitment for the given proprietor and reason, releasing
684    /// the locked value.
685    ///
686    /// Proprietors are enforced to commit to a **single digest per reason**,
687    /// ensuring unambiguous resolution. Higher-level traits such as [`CommitIndex`]
688    /// or [`CommitPool`] can be built to allow indexed or pooled commitments on top of
689    /// this invariant.
690    ///    
691    /// The returned value may differ from the original deposit depending on the reason's
692    /// context and any adjustments made during the commitment's lifetime possibly
693    /// via [`Commitment::set_digest_value`].
694    ///
695    /// ## Returns
696    /// - `Ok(Asset)` containing the fully resolved commitment value
697    /// - `Err(DispatchError)` if the commitment does not exist or resolution fails
698    fn resolve_commit(
699        who: &Proprietor,
700        reason: &Self::Reason,
701    ) -> Result<Self::Asset, DispatchError>;
702
703    /// Increases the value of an existing commitment.
704    ///
705    /// Adds the specified value to an existing commitment for the given reason.
706    /// This operation can only be performed if a commitment already exists.
707    ///
708    /// The qualifier parameter control how the increase is applied.
709    ///
710    /// ## Returns
711    /// - `Ok(Asset)` containing the increment actually added (not the total value)
712    /// - `Err(DispatchError)` if the commitment does not exist or if the increase fails
713    fn raise_commit(
714        who: &Proprietor,
715        reason: &Self::Reason,
716        value: Self::Asset,
717        qualifier: &Self::Intent,
718    ) -> Result<Self::Asset, DispatchError>;
719
720    /// Sets or updates the aggregated value of the specified digest.
721    ///
722    /// Allows the reason provider (implementer) to adjust the digest's value,
723    /// which automatically propagates to all individual commitments tied to that
724    /// digest.
725    ///
726    /// The `qualifier` defines how the update is applied (e.g., exact vs best-effort,
727    /// forceful vs relaxed), and determines the execution semantics for the direction
728    /// of change (increase or decrease relative to the current value).
729    ///
730    /// ### Returns
731    /// - `Ok(Asset)` containing the actual value applied to the digest
732    /// - `Err(DispatchError)` if the digest does not exist or update fails
733    fn set_digest_value(
734        reason: &Self::Reason,
735        digest: &Self::Digest,
736        value: Self::Asset,
737        qualifier: &Self::Intent,
738    ) -> Result<Self::Asset, DispatchError>;
739
740    /// Removes a digest if it has no associated commitments.
741    ///
742    /// Permanently deletes the specified digest from the system if its aggregated
743    /// value is zero, freeing resources and cleaning up unused entries.
744    /// This is a maintenance operation and it should ensures no commitments exist
745    /// for the digest before reaping.
746    ///
747    /// ### Returns
748    /// - `Ok(())` if the digest is successfully removed
749    /// - `Err(DispatchError)` if the digest does not exist or still has active commitments
750    fn reap_digest(digest: &Self::Digest, reason: &Self::Reason) -> DispatchResult;
751
752    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
753    // ```````````````````````````````````` HOOKS ````````````````````````````````````
754    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
755
756    /// Hook called after a commitment is successfully placed.
757    ///
758    /// Provides an extension point for triggering side-effects such as events,
759    /// logging, external state updates, or notifications when a new commitment
760    /// is established.
761    ///
762    /// Default implementation is a no-op.
763    fn on_commit_place(
764        _who: &Proprietor,
765        _reason: &Self::Reason,
766        _digest: &Self::Digest,
767        _value: Self::Asset,
768    ) {
769    }
770
771    /// Hook called after a commitment is successfully raised (increased).
772    ///
773    /// Provides an extension point for triggering side-effects such as recalculations,
774    /// notifications, event emissions, or external state synchronization when an
775    /// existing commitment's value is increased.
776    ///
777    /// Default implementation is a no-op.
778    fn on_commit_raise(
779        _who: &Proprietor,
780        _reason: &Self::Reason,
781        _digest: &Self::Digest,
782        _value: Self::Asset,
783    ) {
784    }
785
786    /// Hook called after a commitment is successfully resolved.
787    ///
788    /// Provides an extension point for performing cleanup, distributing rewards or
789    /// penalties, audit logging, or triggering any finalization logic when a
790    /// commitment is released.
791    ///
792    /// Default implementation is a no-op.
793    fn on_commit_resolve(
794        _who: &Proprietor,
795        _reason: &Self::Reason,
796        _digest: &Self::Digest,
797        _value: Self::Asset,
798    ) {
799    }
800
801    /// Hook called after a digest's value is updated.
802    ///
803    /// Provides an extension point for triggering recalculation of dependent
804    /// commitments, propagating changes to related state, or emitting events
805    /// when a digest's aggregated value changes.
806    ///
807    /// Default implementation is a no-op.
808    fn on_digest_update(_digest: &Self::Digest, _reason: &Self::Reason, _value: Self::Asset) {}
809
810    /// Hook called after a digest is reaped (removed).
811    ///
812    /// Provides an extension point for cleanup tasks, logging, freeing
813    /// related resources, or triggering external notifications when a digest
814    /// is permanently removed from the system.
815    ///
816    /// `dust` represents any residual value that was unclaimable and
817    /// effectively considered dead.
818    ///
819    /// Default implementation is a no-op.
820    fn on_reap_digest(_digest: &Self::Digest, _reason: &Self::Reason, _dust: Self::Asset) {}
821}
822
823// ===============================================================================
824// ````````````````````````````````` COMMIT INDEX ````````````````````````````````
825// ===============================================================================
826
827/// `CommitIndex` extends [`Commitment`] by providing a **higher-level financial abstraction**
828/// that groups multiple commitments under a single "index".
829///
830/// This enables proprietors to manage related commitments collectively,
831/// while preserving the **atomicity** and **immutability** guarantees of `Commitment`.
832///
833/// ## Use Cases
834///
835/// - Portfolio management: Aggregate commitments.
836/// - Betting pools: Collective tracking and settlement.
837/// - Multi-asset contracts: Manage grouped assets with shares.
838/// - Escrow pools: Efficient tracking of grouped escrows.
839/// - Market positions: Aggregate for easier tracking.
840///
841/// ## Why `CommitIndex`?
842///
843/// The base `Commitment` trait enforces:
844/// > "One reason -> one digest".
845///
846/// This is restrictive when:
847/// - A proprietor needs to manage **multiple related commitments under one reason**.
848/// - Aggregation of commitments is required.
849/// - Ownership of grouped commitments must be tracked proportionally.
850/// - Trustless, composable structures are desired.
851///
852/// `CommitIndex` solves this by introducing **indexes**:
853/// - An **index digest** references multiple committed digests (entries).
854/// - Each entry remains an independent `Commitment`.
855/// - The index enables aggregation, share-tracking, and collective management
856/// without breaking core commitment rules.
857///
858/// ## Core Principles
859///
860/// 1. **Wrapper over commitments**
861///    - Index digest groups multiple committed digests.
862///    - Entries retain individual commitment properties.
863///
864/// 2. **Management layer**
865///    - Tracks shares and values for each entry.
866///    - Aggregates entry values into a single index value.
867///
868/// 3. **Integrity**
869///    - Entries remain independent commitments.
870///    - Index creation does not alter entry commitments.
871///
872/// 4. **Trustless design**
873///    - Anyone can interact with indexes without requiring creator consent,
874///      provided the share structure allows it.
875///
876/// ## Examples
877///
878/// ### Example 1: Portfolio Staking
879///
880/// Alice stakes multiple digests under a single reason to manage risk:
881///
882/// | Reason     | Digest        | Value |
883/// |------------|---------------|-------|
884/// | "staking"  | "digest_a123" | 100   |
885/// | "staking"  | "digest_b456" | 200   |
886/// | "staking"  | "digest_c789" | 300   |
887///
888/// Each row is an independent commitment.
889/// Alice creates a `CommitIndex`:
890/// - Reason = `"staking"`
891/// - Digest = `"index_digest_xyz"`
892/// - Entries = `[digest_a123, digest_b456, digest_c789]`
893/// - Shares = `[1, 2, 3]`
894///
895/// Total value = `600`, with proportional share tracking.
896///
897/// ### Example 2: Betting Pool
898///
899/// Bettors commit value for different bets under the same reason:
900///
901/// | Reason         | Digest           | Value |
902/// |----------------|------------------|-------|
903/// | "betting_pool" | "digest_bet101" | 50    |
904/// | "betting_pool" | "digest_bet102" | 80    |
905///
906/// CommitIndex:
907/// - Reason = `"betting_pool"`
908/// - Digest = `"index_digest_betting"`
909/// - Entries = `[digest_bet101, digest_bet102]`
910/// - Shares = `[2, 3]`
911///
912/// Total value = `130`, with proportional share tracking.
913///
914/// ### Example 3: Market Position Index
915///
916/// Dave aggregates multiple market positions:
917///
918/// | Reason            | Digest         | Value |
919/// |-------------------|----------------|-------|
920/// | "market_positions"| "digest_order1"| 500   |
921/// | "market_positions"| "digest_order2"| 300   |
922///
923/// CommitIndex:
924/// - Reason = `"market_positions"`
925/// - Digest = `"index_digest_market"`
926/// - Entries = `[digest_order1, digest_order2]`
927/// - Shares = `[5, 3]`
928///
929/// Aggregates positions without altering atomic commitments.
930///
931/// ### Example 4: Escrow Pool
932///
933/// Multiple escrows under one reason:
934///
935/// | Reason       | Digest          | Value |
936/// |--------------|-----------------|-------|
937/// | "escrow_pool"| "digest_escrow1"| 1000  |
938/// | "escrow_pool"| "digest_escrow2"| 1500  |
939///
940/// CommitIndex:
941/// - Reason = `"escrow_pool"`
942/// - Digest = `"index_digest_escrow"`
943/// - Entries = `[digest_escrow1, digest_escrow2]`
944/// - Shares = `[1, 1.5]`
945///
946/// Tracks total escrow commitments (`2500`) and proportional ownership.
947///
948/// ## Invariants
949///
950/// - An index digest is a managed wrapper over multiple committed digests.
951/// - Each entry is an independent `Commitment`.
952/// - Index creation preserves atomicity, immutability, and reason-digest invariants.
953/// - Shares define proportional ownership of aggregated commitments.
954/// - Must maintain the base-invariant "One Reason -> One Digest"
955///
956/// Generics:
957/// - **Proprietor** - the entity that owns the asset and can make commitments.
958pub trait CommitIndex<Proprietor>: Commitment<Proprietor> {
959    /// The type representing an index.
960    /// This could be a struct containing entries and shares.
961    type Index: Elastic + Storable;
962
963    /// The type representing shares for an entry.
964    ///
965    /// Should be a simple unsigned numeric type.
966    type Shares: Countable;
967
968    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
969    // ``````````````````````````````````` CHECKERS ``````````````````````````````````
970    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
971
972    /// Checks whether an index exists for the given reason and index digest.
973    ///
974    /// ## Returns
975    /// - `Ok(())` if the index exists
976    /// - `Err(DispatchError)` if the index is not found
977    fn index_exists(reason: &Self::Reason, index_of: &Self::Digest) -> DispatchResult;
978
979    /// Checks whether a specific entry exists within the given index.
980    ///
981    /// Verifies that the specified entry digest is part of the index's
982    /// entry list under the given reason.
983    ///
984    /// ## Returns
985    /// - `Ok(())` if the entry exists within the index
986    /// - `Err(DispatchError)` if the entry is not found in the index
987    fn entry_exists(
988        reason: &Self::Reason,
989        index_of: &Self::Digest,
990        entry_of: &Self::Digest,
991    ) -> DispatchResult;
992
993    /// Checks whether any index exists for the given reason.
994    ///
995    /// Verifies that at least one index has been created under the
996    /// specified reason across all proprietors.
997    ///
998    /// ## Returns
999    /// - `Ok(())` if at least one index exists for the reason
1000    /// - `Err(DispatchError)` if no indexes are found for the reason
1001    fn has_index(reason: &Self::Reason) -> DispatchResult;
1002
1003    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1004    // ``````````````````````````````````` GETTERS ```````````````````````````````````
1005    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1006
1007    /// Retrieves the complete index structure for the given reason and index digest.
1008    ///
1009    /// Returns the full index object containing all entries, shares, and
1010    /// associated metadata for inspection or processing.
1011    ///
1012    /// ## Returns
1013    /// - `Ok(Index)` containing the complete index structure i.e.,
1014    /// [`CommitIndex::Index`]
1015    /// - `Err(DispatchError)` if the index does not exist
1016    fn get_index(
1017        reason: &Self::Reason,
1018        index_of: &Self::Digest,
1019    ) -> Result<Self::Index, DispatchError>;
1020
1021    /// Retrieves the real-time aggregated value of the specified index.
1022    ///
1023    /// Computes the total value by summing the current values of all entry digests
1024    /// under the index. Since the supertrait [`Commitment`] allows digest values
1025    /// to be updated via [`Commitment::set_digest_value`], this reflects the
1026    /// **live state** rather than historical deposits.
1027    ///
1028    /// Ensures each entry's digest value is current before aggregation.
1029    ///
1030    /// ## Returns
1031    /// - `Ok(Asset)` containing the total aggregated value of the index
1032    /// - `Err(DispatchError)` if the index does not exist or value computation fails
1033    fn get_index_value(
1034        reason: &Self::Reason,
1035        index_of: &Self::Digest,
1036    ) -> Result<Self::Asset, DispatchError> {
1037        Self::index_exists(reason, index_of)?;
1038        let entries_values = Self::get_entries_value(reason, index_of)?;
1039        let mut value = Self::Asset::zero();
1040        for (_, val) in entries_values {
1041            value = value.saturating_add(val);
1042        }
1043        Ok(value)
1044    }
1045
1046    /// Retrieves the shares of each entry in the given index.
1047    ///
1048    /// Returns a list of entry digests paired with their corresponding shares,
1049    /// representing each entry's proportional weight within the index.
1050    ///
1051    /// ## Returns
1052    /// - `Ok(Vec<(Self::Digest, Self::Shares)>)` containing entry-share pairs
1053    /// - `Err(DispatchError)` if the index does not exist or retrieval fails
1054    fn get_entries_shares(
1055        reason: &Self::Reason,
1056        index_of: &Self::Digest,
1057    ) -> Result<Vec<(Self::Digest, Self::Shares)>, DispatchError>;
1058
1059    /// Retrieves the real-time values of all entries within the specified index.
1060    ///
1061    /// Each entry's value is fetched individually and reflects any changes since
1062    /// the commitment was created, as the supertrait [`Commitment`] allows digest
1063    /// values to be updated via [`Commitment::set_digest_value`].
1064    ///
1065    /// Returns a list of entry digests paired with their current committed values.
1066    ///
1067    /// ## Returns
1068    /// - `Ok(Vec<(Self::Digest, Self::Asset)>)` containing entry-value pairs
1069    /// - `Err(DispatchError)` if the index does not exist or value retrieval fails
1070    fn get_entries_value(
1071        reason: &Self::Reason,
1072        index_of: &Self::Digest,
1073    ) -> Result<Vec<(Self::Digest, Self::Asset)>, DispatchError> {
1074        Self::index_exists(reason, index_of)?;
1075        let entries = Self::get_entries_shares(reason, index_of)?;
1076        let mut vec = Vec::new();
1077        for (entry_of, _) in entries {
1078            let value = Self::get_entry_value(reason, index_of, &entry_of)?;
1079            vec.push((entry_of, value))
1080        }
1081        Ok(vec)
1082    }
1083
1084    /// Retrieves the real-time committed value of a specific entry within an index.
1085    ///
1086    /// Returns the current value for the given entry digest under the specified
1087    /// reason and index. Since the supertrait [`Commitment`] allows digest values
1088    /// to be updated via [`Commitment::set_digest_value`], this reflects the
1089    /// **live state** rather than the original deposit.
1090    ///
1091    /// ## Returns
1092    /// - `Ok(Asset)` containing the entry's current committed value
1093    /// - `Err(DispatchError)` if the index or entry does not exist
1094    fn get_entry_value(
1095        reason: &Self::Reason,
1096        index_of: &Self::Digest,
1097        entry_of: &Self::Digest,
1098    ) -> Result<Self::Asset, DispatchError>;
1099
1100    /// Retrieves the real-time values of a proprietor's commitments to all entries
1101    /// within the specified index.
1102    ///
1103    /// Each entry's value is computed individually for the given proprietor and
1104    /// reflects any changes since commitment, as the supertrait [`Commitment`]
1105    /// allows digest values to be updated via [`Commitment::set_digest_value`].
1106    ///
1107    /// Returns a list of entry digests paired with their current committed values
1108    /// for the specified proprietor.
1109    ///
1110    /// ## Returns
1111    /// - `Ok(Vec<(Self::Digest, Self::Asset)>)` containing entry-value pairs for the proprietor
1112    /// - `Err(DispatchError)` if the index does not exist or value retrieval fails
1113    fn get_entries_value_for(
1114        who: &Proprietor,
1115        reason: &Self::Reason,
1116        index_of: &Self::Digest,
1117    ) -> Result<Vec<(Self::Digest, Self::Asset)>, DispatchError> {
1118        Self::index_exists(reason, index_of)?;
1119        let entries = Self::get_entries_shares(reason, index_of)?;
1120        let mut vec = Vec::new();
1121        for (entry_of, _) in entries {
1122            let value = Self::get_entry_value_for(who, reason, index_of, &entry_of)?;
1123            vec.push((entry_of, value))
1124        }
1125        Ok(vec)
1126    }
1127
1128    /// Retrieves the real-time value of a proprietor's commitment to a specific
1129    /// entry within an index.
1130    ///
1131    /// Returns the live committed value for the given proprietor and entry digest.
1132    /// Since the supertrait [`Commitment`] allows digest values to be updated via
1133    /// [`Commitment::set_digest_value`], this reflects the **current state** rather
1134    /// than the deposited total.
1135    ///
1136    /// ## Returns
1137    /// - `Ok(Asset)` containing the proprietor's current commitment to the entry
1138    /// - `Err(DispatchError)` if the index or entry does not exist
1139    fn get_entry_value_for(
1140        who: &Proprietor,
1141        reason: &Self::Reason,
1142        index_of: &Self::Digest,
1143        entry_of: &Self::Digest,
1144    ) -> Result<Self::Asset, DispatchError>;
1145
1146    /// Retrieves the real-time total value of a proprietor's commitment to an index.
1147    ///
1148    /// Aggregates the live committed values of all entry digests within the index
1149    /// for the given proprietor. Since the supertrait [`Commitment`] allows digest
1150    /// values to be updated via [`Commitment::set_digest_value`], this reflects
1151    /// the **current total** rather than historical deposits.
1152    ///
1153    /// ## Returns
1154    /// - `Ok(Asset)` containing the proprietor's total commitment to the index
1155    /// - `Err(DispatchError)` if the index does not exist or computation fails
1156    fn get_index_value_for(
1157        who: &Proprietor,
1158        reason: &Self::Reason,
1159        index_of: &Self::Digest,
1160    ) -> Result<Self::Asset, DispatchError> {
1161        Self::index_exists(reason, index_of)?;
1162        let mut value = Self::Asset::zero();
1163        let entries = Self::get_entries_value_for(who, reason, index_of)?;
1164        for (_, val) in entries {
1165            value = value.saturating_add(val)
1166        }
1167        Ok(value)
1168    }
1169
1170    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1171    // ````````````````````````````````` CONSTRUCTORS ````````````````````````````````
1172    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1173
1174    /// Generates a unique digest for the given index object.
1175    ///
1176    /// Creates a distinct identifier derived from the proprietor, reason, and
1177    /// index structure (including entries and shares). The generated digest
1178    /// must be collision-resistant and unique across all indexes of reason.
1179    ///
1180    /// ### Returns
1181    /// - `Ok(Digest)` containing the generated unique index digest
1182    /// - `Err(DispatchError)` if digest generation fails due to invalid inputs
1183    fn gen_index_digest(
1184        from: &Proprietor,
1185        reason: &Self::Reason,
1186        index: &Self::Index,
1187    ) -> Result<Self::Digest, DispatchError>;
1188
1189    /// Prepares an index object from the provided entry data.
1190    ///
1191    /// Constructs a valid index structure from a list of `(Digest, Shares)` pairs,
1192    /// where each pair represents:
1193    /// - **Digest**: The unique identifier of an entry within the index
1194    /// - **Shares**: The proportional weight or ownership of that entry
1195    ///
1196    /// This method:
1197    /// - Validates entry data for consistency and integrity
1198    /// - Ensures no duplicate digests exist
1199    /// - Rejects entries with nil/empty digests or zero shares
1200    /// - Creates an immutable, atomic index object
1201    ///
1202    /// ## Returns
1203    /// - `Ok(Index)` containing the prepared index structure
1204    /// - `Err(DispatchError)` if preparation fails due to invalid data or validation errors
1205    fn prepare_index(
1206        who: &Proprietor,
1207        reason: &Self::Reason,
1208        entries: &[(Self::Digest, Self::Shares)],
1209    ) -> Result<Self::Index, DispatchError>;
1210
1211    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1212    // ``````````````````````````````````` MUTATORS ``````````````````````````````````
1213    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1214
1215    /// Binds the prepared index to the specified digest under the given reason.
1216    ///
1217    /// Associates the provided digest with the index structure, making it
1218    /// queryable and usable within the commitment system. The index object
1219    /// should be safely prepared via [`CommitIndex::prepare_index`] before
1220    /// calling this method.
1221    ///
1222    /// This operation ensures:
1223    /// - The digest uniquely identifies the index
1224    /// - All entries and shares are preserved with integrity
1225    /// - The index conforms to [`CommitIndex`] invariants
1226    ///
1227    /// ## Returns
1228    /// - `Ok(())` if the index is successfully set
1229    /// - `Err(DispatchError)` if the operation fails due to invalid data or conflicts
1230    fn set_index(
1231        who: &Proprietor,
1232        reason: &Self::Reason,
1233        index: &Self::Index,
1234        digest: &Self::Digest,
1235    ) -> DispatchResult;
1236
1237    /// Updates the shares of a single entry within an existing index.
1238    ///
1239    /// Since indexes are immutable once created, this method internally:
1240    /// 1. Prepares a new index with the updated share for the specified entry
1241    /// 2. Generates a new digest for the modified index
1242    /// 3. Returns the new index digest
1243    ///
1244    /// Since no explicit entry-removal methods are provided, this method may be
1245    /// used to remove an entry when its share is set to zero.
1246    ///
1247    /// By invariant, commitment indexes must not contain entries with zero shares.
1248    ///
1249    /// For updating multiple entries, use [`CommitIndex::prepare_index`] and
1250    /// [`CommitIndex::set_index`] to create a completely new index structure.
1251    ///
1252    /// ## Returns
1253    /// - `Ok(Digest)` containing the new index digest after share update
1254    /// - `Err(DispatchError)` if the index or entry does not exist, or update fails
1255    fn set_entry_shares(
1256        who: &Proprietor,
1257        reason: &Self::Reason,
1258        index_of: &Self::Digest,
1259        entry_of: &Self::Digest,
1260        shares: Self::Shares,
1261    ) -> Result<Self::Digest, DispatchError>;
1262
1263    /// Removes an index if it contains no active commitments.
1264    ///
1265    /// Permanently deletes the specified index digest under the given reason,
1266    /// freeing associated resources. Indexes can only be reaped when they have
1267    /// no committed entries or remaining balances.
1268    ///
1269    /// ## Returns
1270    /// - `Ok(())` if the index is successfully removed
1271    /// - `Err(DispatchError)` if the index does not exist or contains active commitments
1272    fn reap_index(reason: &Self::Reason, index_of: &Self::Digest) -> DispatchResult;
1273
1274    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1275    // ```````````````````````````````````` HOOKS ````````````````````````````````````
1276    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1277
1278    /// Hook called after an index is created and its digest is set by a proprietor.
1279    ///
1280    /// Provides an extension point for triggering side-effects such as events,
1281    /// logging, recalculations, or external state updates when a new index is
1282    /// established.
1283    ///
1284    /// Default implementation is a no-op.
1285    fn on_set_index(
1286        _who: &Proprietor,
1287        _index_of: &Self::Digest,
1288        _reason: &Self::Reason,
1289        _index: &Self::Index,
1290    ) {
1291    }
1292
1293    /// Hook called after an index is reaped (removed).
1294    ///
1295    /// Provides an extension point for cleanup tasks, logging, freeing
1296    /// related resources, or triggering external notifications when an index
1297    /// is permanently removed from the system.
1298    ///
1299    /// `dust` represents any residual value that was unclaimable and
1300    /// effectively considered dead.
1301    ///
1302    /// Default implementation is a no-op.
1303    fn on_reap_index(_index_of: &Self::Digest, _reason: &Self::Reason, _dust: Self::Asset) {}
1304}
1305
1306// ===============================================================================
1307// ````````````````````````````````` COMMIT POOL `````````````````````````````````
1308// ===============================================================================
1309
1310/// `CommitPool` extends [`Commitment`] and [`CommitIndex`] to provide a **managed,
1311/// dynamic commitment abstraction**.
1312///
1313/// It enables a trusted manager to actively manage where deposited assets are committed
1314/// - effectively controlling how and where value is allocated across different digests
1315/// within a pool.
1316///
1317/// While `CommitIndex` aggregates multiple commitments under a single digest,
1318/// `CommitPool` introduces **live management**:
1319/// - Proprietors deposit assets into a managed pool.  
1320/// - The pool manager determines how these assets are allocated across underlying
1321///   commitments to optimize yield, diversify risk, or execute specific strategies.  
1322/// - Proprietors trust the manager to act within agreed rules and immutable commission terms.
1323///
1324/// In short, Pool digests are stable identities; index digests are content hashes.
1325///
1326/// ## Purpose
1327///
1328/// Pools are useful for:
1329/// - Managed staking or investment
1330/// - Delegated liquidity provision
1331/// - Escrow pools with oversight
1332/// - Collective asset management with dynamic strategy
1333///
1334/// They preserve the atomicity and immutability of individual commitments while enabling
1335/// flexible allocation.
1336///
1337/// ## Core Principles
1338///
1339/// 1. **Managed aggregation**
1340///    - Pools group commitments under a single digest for a given reason.
1341///    - Slots remain immutable commitments.
1342///
1343/// 2. **Dynamic shares**
1344///    - Managers adjust shares to change exposure or strategy.
1345///    - Share changes require releasing and recovering the pool.
1346///
1347/// 3. **Semi-trusted management**
1348///    - Managers cannot withdraw deposits directly.
1349///    - Operations are restricted by pool rules and fixed commission rates.
1350///
1351/// 4. **Immutable commission**
1352///    - Commission rates are fixed at pool creation for transparency and trust.
1353///
1354/// ## Examples
1355///
1356/// ### Example 1: Managed Staking Pool
1357///
1358/// Manager Dave creates a managed pool:
1359/// - Reason = `"managed_staking"`
1360/// - Digest = `"dave_pool"`
1361/// - Entries = `[digest_a123, digest_b456, digest_c789]`
1362/// - Shares = `[1, 2, 3]`
1363/// - Commission = `2%`
1364///
1365/// Alice, Bob, and Carol deposit tokens into a staking pool managed by Dave:
1366///
1367/// | Proprietor | Reason    | Digest        | Value |
1368/// |------------|-----------|---------------|-------|
1369/// | Alice      | staking   | dave_pool     | 100   |
1370/// | Bob        | staking   | dave_pool     | 200   |
1371/// | Carol      | staking   | dave_pool     | 300   |
1372///
1373/// The pool holds a total value of `600`, managed dynamically without altering deposits.  
1374///
1375/// Dave can rebalance the pool or add new staking positions (new entry digests)  
1376/// as opportunities arise, while depositors retain proportional ownership.
1377///
1378/// This makes `CommitPool` distinct from `CommitIndex`,  
1379/// which has **static entries and immutable share structures**.
1380///
1381/// #### Dynamic Reallocation and Expansion
1382///
1383/// After creation, the manager can **update both shares and entries**:
1384///
1385/// - Before:
1386///   - Entries = `[digest_a123, digest_b456, digest_c789]`
1387///   - Shares  = `[1, 2, 3]`
1388///
1389/// - After:
1390///   - Entries = `[digest_a123, digest_b456, digest_c789, digest_d999]`
1391///   - Shares  = `[2, 1, 4, 2]`
1392///
1393/// This allows the pool to grow by adding new slots (digests)  
1394/// and to rebalance proportional exposure.  
1395/// Proprietors can view current allocations at any time,  
1396/// but allocations and entries may change as part of active management.
1397///
1398/// ### Example 2: Managed Liquidity Pool
1399///
1400/// Manager creates:
1401/// - Reason = `"liquidity"`
1402/// - Digest = `"pool_digest_lp"`
1403/// - Entries = `[digest_lp_001, digest_lp_002]`
1404/// - Shares = `[5, 7]`
1405/// - Commission = `1%`
1406///
1407/// The pool manager may later add new liquidity positions (e.g., `digest_lp_003`)  
1408/// or adjust existing shares to respond to market demand.  
1409/// Depositors continue to share in the aggregated performance of the evolving pool.
1410///
1411/// ### Example 3: Escrow Pool with Commission
1412///
1413/// Carol and Bob deposit into a managed escrow pool:
1414///
1415/// | Proprietor | Reason        | Digest              | Value |
1416/// |------------|---------------|---------------------|-------|
1417/// | Carol      | escrow        | pool_digest_escrow  | 1000  |
1418/// | Bob        | escrow        | pool_digest_escrow  | 1500  |
1419///
1420/// Manager creates:
1421/// - Reason = `"escrow"`
1422/// - Digest = `"pool_digest_escrow"`
1423/// - Shares = `[1, 1.5]`
1424/// - Commission = `0.5%`
1425///
1426/// The manager can later add new escrow digests or rebalance shares  
1427/// to adjust allocations, while commission is paid automatically upon resolution.
1428///
1429/// ### Example 4: Delegated Investment Pool
1430///
1431/// Investors deposit:
1432///
1433/// | Proprietor | Reason           | Digest         | Value |
1434/// |------------|------------------|----------------|-------|
1435/// | A          | investment       | inv_pool       | 1000  |
1436/// | B          | investment       | inv_pool       | 2000  |
1437///
1438/// Manager creates:
1439/// - Reason = `"investment"`
1440/// - Digest = `"inv_pool"`
1441/// - Shares = `[1, 2]`
1442/// - Commission = `1.5%`
1443///
1444/// After creation:
1445/// - The manager may add new investment digests or update existing ones.  
1446/// - Proprietors can see current allocations, but these can change over time.  
1447///
1448/// ## Invariants
1449///
1450/// - A pool digest wraps multiple committed entries.
1451/// - Pools are created from indexes.
1452/// - Mutations require full release and recovering.
1453/// - Managers cannot withdraw deposits, only adjust shares.
1454/// - Commission rate is fixed at creation.
1455/// - Commission is paid upon commitment resolution.
1456/// - Share adjustments must preserve proportional commitments.
1457///
1458/// ## Summary
1459///
1460/// `CommitPool` enables managed aggregation of commitments with dynamic share adjustment,
1461/// preserving safety, immutability, and fixed commission rates.
1462///
1463/// Ideal for managed staking, liquidity provision, delegated investment, escrow pools,
1464/// and collective asset management where live management is required.
1465///
1466/// Generics:
1467/// - **Proprietor** - the entity that owns the asset and can make commitments.
1468pub trait CommitPool<Proprietor>: Commitment<Proprietor> + CommitIndex<Proprietor> {
1469    /// The type representing a managed pool.
1470    ///
1471    /// Typically a struct that includes:
1472    /// - The pool's manager (of type `Proprietor`)
1473    /// - A list of slot digests and their shares
1474    /// - The commission rate
1475    /// - Metadata
1476    type Pool: Elastic + Storable;
1477
1478    /// The type representing a pool's commission.
1479    ///
1480    /// Can be a numeric percentage, ratio, or more complex type
1481    /// encoding the manager's commission or performance fee model.
1482    type Commission: Percentage;
1483
1484    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1485    // ``````````````````````````````````` CHECKERS ``````````````````````````````````
1486    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1487
1488    /// Checks whether a pool exists for the given reason and pool digest.
1489    ///
1490    /// ## Returns
1491    /// - `Ok(())` if the pool exists
1492    /// - `Err(DispatchError)` if the pool is not found
1493    fn pool_exists(reason: &Self::Reason, pool_of: &Self::Digest) -> DispatchResult;
1494
1495    /// Checks whether a specific slot exists within the given pool.
1496    ///
1497    /// Verifies that the specified slot digest is part of the pool's
1498    /// slot list under the given reason.
1499    ///
1500    /// ## Returns
1501    /// - `Ok(())` if the slot exists within the pool
1502    /// - `Err(DispatchError)` if the slot is not found in the pool
1503    fn slot_exists(
1504        reason: &Self::Reason,
1505        pool_of: &Self::Digest,
1506        slot_of: &Self::Digest,
1507    ) -> DispatchResult;
1508
1509    /// Checks whether any pool exists for the given reason.
1510    ///
1511    /// Verifies that at least one pool has been created under the
1512    /// specified reason across all managers.
1513    ///
1514    /// ## Returns
1515    /// - `Ok(())` if at least one pool exists for the reason
1516    /// - `Err(DispatchError)` if no pools are found for the reason
1517    fn has_pool(reason: &Self::Reason) -> DispatchResult;
1518
1519    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1520    // ``````````````````````````````````` GETTERS ```````````````````````````````````
1521    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1522
1523    /// Retrieves the manager (controller) of the specified pool.
1524    ///
1525    /// Returns the proprietor who is responsible for managing the pool's
1526    /// allocations, slot configurations, and commission distribution.
1527    ///
1528    /// ## Returns
1529    /// - `Ok(Proprietor)` containing the pool's manager
1530    /// - `Err(DispatchError)` if the pool does not exist
1531    fn get_manager(
1532        reason: &Self::Reason,
1533        pool_of: &Self::Digest,
1534    ) -> Result<Proprietor, DispatchError>;
1535
1536    /// Retrieves the commission rate associated with the specified pool.
1537    ///
1538    /// Returns the fixed commission structure that determines what portion
1539    /// of the pool's yield or value the manager is entitled to receive.
1540    ///
1541    /// ## Returns
1542    /// - `Ok(Commission)` containing the pool's commission structure
1543    /// - `Err(DispatchError)` if the pool does not exist
1544    fn get_commission(
1545        reason: &Self::Reason,
1546        pool_of: &Self::Digest,
1547    ) -> Result<Self::Commission, DispatchError>;
1548
1549    /// Retrieves the complete pool structure for the given reason and pool digest.
1550    ///
1551    /// Returns the full pool object containing all slots, shares, manager identity,
1552    /// commission details, and associated metadata for inspection or processing.
1553    ///
1554    /// ## Returns
1555    /// - `Ok(Pool)` containing the complete pool structure
1556    /// - `Err(DispatchError)` if the pool does not exist
1557    fn get_pool(reason: &Self::Reason, pool_of: &Self::Digest)
1558        -> Result<Self::Pool, DispatchError>;
1559
1560    /// Retrieves the real-time aggregated value of the specified pool.
1561    ///
1562    /// Computes the total value by summing the current values of all slot digests
1563    /// under the pool. Since the supertrait [`Commitment`] allows digest values
1564    /// to be updated via [`Commitment::set_digest_value`], this reflects the
1565    /// **live state** rather than historical deposits.
1566    ///
1567    /// The aggregated value represents the total exposure of all depositors
1568    /// across all slots managed by the pool.
1569    ///
1570    /// ## Returns
1571    /// - `Ok(Asset)` containing the total aggregated value of the pool
1572    /// - `Err(DispatchError)` if the pool does not exist or value computation fails
1573    fn get_pool_value(
1574        reason: &Self::Reason,
1575        pool_of: &Self::Digest,
1576    ) -> Result<Self::Asset, DispatchError> {
1577        Self::pool_exists(reason, pool_of)?;
1578        let slots_values = Self::get_slots_value(reason, pool_of)?;
1579        let mut value: <Self as InspectAsset<Proprietor>>::Asset = Self::Asset::zero();
1580        for (_, val) in slots_values {
1581            value = value.saturating_add(val);
1582        }
1583        Ok(value)
1584    }
1585
1586    /// Retrieves the shares of all slots within the specified pool.
1587    ///
1588    /// Returns a list of slot digests paired with their corresponding shares,
1589    /// representing each slot's proportional weight within the pool's
1590    /// managed allocation strategy.
1591    ///
1592    /// ## Returns
1593    /// - `Ok(Vec<(Self::Digest, Self::Shares)>)` containing slot-share pairs
1594    /// - `Err(DispatchError)` if the pool does not exist or retrieval fails
1595    fn get_slots_shares(
1596        reason: &Self::Reason,
1597        pool_of: &Self::Digest,
1598    ) -> Result<Vec<(Self::Digest, Self::Shares)>, DispatchError>;
1599
1600    /// Retrieves the real-time values of all slots within the specified pool.
1601    ///
1602    /// Each slot's value is fetched individually and reflects any changes since
1603    /// the commitment was created, as the supertrait [`Commitment`] allows digest
1604    /// values to be updated via [`Commitment::set_digest_value`].
1605    ///
1606    /// Returns a list of slot digests paired with their current committed values,
1607    /// representing the live state of the pool's allocation.
1608    ///
1609    /// ## Returns
1610    /// - `Ok(Vec<(Self::Digest, Self::Asset)>)` containing slot-value pairs
1611    /// - `Err(DispatchError)` if the pool does not exist or value retrieval fails
1612    fn get_slots_value(
1613        reason: &Self::Reason,
1614        pool_of: &Self::Digest,
1615    ) -> Result<Vec<(Self::Digest, Self::Asset)>, DispatchError> {
1616        Self::pool_exists(reason, pool_of)?;
1617        let slots = Self::get_slots_shares(reason, pool_of)?;
1618        let mut vec = Vec::new();
1619        for (slot_of, _) in slots {
1620            let value = Self::get_slot_value(reason, pool_of, &slot_of)?;
1621            vec.push((slot_of, value))
1622        }
1623        Ok(vec)
1624    }
1625
1626    /// Retrieves the real-time committed value of a specific slot within a pool.
1627    ///
1628    /// Returns the current value for the given slot digest under the specified
1629    /// reason and pool. Since the supertrait [`Commitment`] allows digest values
1630    /// to be updated via [`Commitment::set_digest_value`], this reflects the
1631    /// **live state** rather than the original allocation.
1632    ///
1633    /// If no funds are available in a valid slot, it is expected to return zero.
1634    ///
1635    /// ## Returns
1636    /// - `Ok(Asset)` containing the slot's current committed value
1637    /// - `Err(DispatchError)` if the pool or slot does not exist
1638    fn get_slot_value(
1639        reason: &Self::Reason,
1640        pool_of: &Self::Digest,
1641        slot_of: &Self::Digest,
1642    ) -> Result<Self::Asset, DispatchError>;
1643
1644    /// Retrieves the real-time values of a proprietor's commitments to all slots
1645    /// within the specified pool.
1646    ///
1647    /// Each slot's value is computed individually for the given proprietor and
1648    /// reflects any changes since commitment, as the supertrait [`Commitment`]
1649    /// allows digest values to be updated via [`Commitment::set_digest_value`].
1650    ///
1651    /// Returns a list of slot digests paired with their current committed values
1652    /// for the specified proprietor, showing their proportional exposure across
1653    /// the pool's managed allocation.
1654    ///
1655    /// ### Returns
1656    /// - `Ok(Vec<(Self::Digest, Self::Asset)>)` containing slot-value pairs for the proprietor
1657    /// - `Err(DispatchError)` if the pool does not exist or value retrieval fails
1658    fn get_slots_value_for(
1659        who: &Proprietor,
1660        reason: &Self::Reason,
1661        pool_of: &Self::Digest,
1662    ) -> Result<Vec<(Self::Digest, Self::Asset)>, DispatchError> {
1663        Self::pool_exists(reason, pool_of)?;
1664        let slots = Self::get_slots_shares(reason, pool_of)?;
1665        let mut vec = Vec::new();
1666        for (slot_of, _) in slots {
1667            let value = Self::get_slot_value_for(who, reason, pool_of, &slot_of)?;
1668            vec.push((slot_of, value))
1669        }
1670        Ok(vec)
1671    }
1672
1673    /// Retrieves the real-time value of a proprietor's commitment to a specific
1674    /// slot within a pool.
1675    ///
1676    /// Returns the live committed value for the given proprietor and slot digest.
1677    /// Since the supertrait [`Commitment`] allows digest values to be updated via
1678    /// [`Commitment::set_digest_value`], this reflects the **current state** rather
1679    /// than the deposited total.
1680    ///
1681    /// ### Returns
1682    /// - `Ok(Asset)` containing the proprietor's current commitment to the slot
1683    /// - `Err(DispatchError)` if the pool or slot does not exist
1684    fn get_slot_value_for(
1685        who: &Proprietor,
1686        reason: &Self::Reason,
1687        pool_of: &Self::Digest,
1688        slot_of: &Self::Digest,
1689    ) -> Result<Self::Asset, DispatchError>;
1690
1691    /// Retrieves the real-time total value of a proprietor's commitment to a pool.
1692    ///
1693    /// Aggregates the live committed values of all slot digests within the pool
1694    /// for the given proprietor. Since the supertrait [`Commitment`] allows digest
1695    /// values to be updated via [`Commitment::set_digest_value`], this reflects
1696    /// the **current total** rather than historical deposits.
1697    ///
1698    /// Represents the proprietor's total exposure to the pool's managed allocation
1699    /// strategy.
1700    ///
1701    /// ### Returns
1702    /// - `Ok(Asset)` containing the proprietor's total commitment to the pool
1703    /// - `Err(DispatchError)` if the pool does not exist or computation fails
1704    fn get_pool_value_for(
1705        who: &Proprietor,
1706        reason: &Self::Reason,
1707        pool_of: &Self::Digest,
1708    ) -> Result<Self::Asset, DispatchError> {
1709        Self::pool_exists(reason, pool_of)?;
1710        let mut value = Self::Asset::zero();
1711        let slots = Self::get_slots_value_for(who, reason, pool_of)?;
1712        for (_, val) in slots {
1713            value = value.saturating_add(val)
1714        }
1715        Ok(value)
1716    }
1717
1718    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1719    // ````````````````````````````````` CONSTRUCTORS ````````````````````````````````
1720    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1721
1722    /// Generates a unique digest that serves as the identifier for a pool configuration.
1723    ///
1724    /// Creates a collision-resistant identifier derived from:
1725    /// - The manager's identity
1726    /// - The reason (category/purpose)
1727    /// - The underlying index digest
1728    /// - The commission structure
1729    ///
1730    /// The generated digest acts as a **globally unique identifier** across all pools,
1731    /// ensuring that even similar configurations or identical indexes result in
1732    /// distinct pool identities when managed by different entities or with different
1733    /// commission rates.
1734    ///
1735    /// ## Returns
1736    /// - `Ok(Digest)` containing the generated unique pool digest
1737    /// - `Err(DispatchError)` if digest generation fails due to invalid inputs
1738    fn gen_pool_digest(
1739        manager: &Proprietor,
1740        reason: &Self::Reason,
1741        index_of: &Self::Digest,
1742        commission: Self::Commission,
1743    ) -> Result<Self::Digest, DispatchError>;
1744
1745    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1746    // ``````````````````````````````````` MUTATORS ``````````````````````````````````
1747    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1748
1749    /// Creates a managed pool from an existing index with a fixed commission rate.
1750    ///
1751    /// Links the pool to an existing index structure under the specified reason
1752    /// and pool digest, establishing a managed aggregation layer with dynamic
1753    /// allocation capabilities.
1754    ///
1755    /// This method:
1756    /// - Validates that the base index exists and is accessible
1757    /// - Ensures the pool digest is unique and available
1758    /// - Records the manager (who) and immutable commission rate
1759    /// - Enables trustless participation for depositors who rely on the index structure
1760    ///
1761    /// **Pools can be created from any valid index**, regardless of who created it,
1762    /// enabling permissionless pool creation while maintaining security.
1763    ///
1764    /// Once created, the pool introduces **managed control and commission logic** over
1765    /// the linked index, allowing the manager to dynamically update slots, adjust slot shares
1766    /// and allocations.
1767    ///
1768    /// ## Returns
1769    /// - `Ok(())` if the pool is successfully created
1770    /// - `Err(DispatchError)` if the index does not exist, pool digest conflicts, or validation fails
1771    fn set_pool(
1772        who: &Proprietor,
1773        reason: &Self::Reason,
1774        pool_of: &Self::Digest,
1775        index_of: &Self::Digest,
1776        commission: Self::Commission,
1777    ) -> DispatchResult;
1778
1779    /// Assigns or updates the manager of an existing pool.
1780    ///
1781    /// Transfers management control of the pool to a new proprietor, who then
1782    /// assumes responsibility for slot configuration, share adjustments, and commission control.
1783    ///
1784    /// This operation preserves all existing commitments, shares, and commission
1785    /// structures while changing only the entity authorized to manage the pool.
1786    ///
1787    /// ### Returns
1788    /// - `Ok(())` if the manager is successfully updated
1789    /// - `Err(DispatchError)` if the pool does not exist or authorization fails
1790    fn set_pool_manager(
1791        reason: &Self::Reason,
1792        pool_of: &Self::Digest,
1793        manager: &Proprietor,
1794    ) -> DispatchResult;
1795
1796    /// Updates the shares of a specific slot within an existing pool.
1797    ///
1798    /// Allows the pool manager to dynamically reallocate exposure between slots
1799    /// without recreating the entire pool structure. This enables responsive
1800    /// strategy adjustments based on market conditions, performance metrics,
1801    /// or risk management requirements.
1802    ///
1803    /// The operation preserves all existing commitments while adjusting the
1804    /// proportional weight of the specified slot within the pool's allocation.
1805    ///
1806    /// ### Returns
1807    /// - `Ok(())` if the slot shares are successfully updated
1808    /// - `Err(DispatchError)` if the pool or slot does not exist, or authorization fails
1809    fn set_slot_shares(
1810        who: &Proprietor,
1811        reason: &Self::Reason,
1812        pool_of: &Self::Digest,
1813        slot_of: &Self::Digest,
1814        shares: Self::Shares,
1815    ) -> DispatchResult;
1816
1817    /// Creates a new pool digest from an index with a specified commission rate.
1818    ///
1819    /// Generates a unique pool identifier by combining the index structure with
1820    /// a commission rate, enabling multiple pools with different commission
1821    /// structures to be created from the same underlying index.
1822    ///
1823    /// Since commission rates are **immutable** after pool creation to protect
1824    /// depositors, this method allows participants to create or select pools
1825    /// with their preferred commission terms without altering existing pools.
1826    ///
1827    /// The generated digest serves as the unique identifier for the new pool
1828    /// configuration and can be used for subsequent pool operations.
1829    ///
1830    /// ## Returns
1831    /// - `Ok(Digest)` containing the newly generated pool digest
1832    /// - `Err(DispatchError)` if pool creation fails due to invalid parameters or conflicts
1833    fn set_commission(
1834        who: &Proprietor,
1835        reason: &Self::Reason,
1836        index_of: &Self::Digest,
1837        commission: Self::Commission,
1838    ) -> Result<Self::Digest, DispatchError> {
1839        let pool_of = Self::gen_pool_digest(who, reason, index_of, commission)?;
1840        Self::set_pool(who, reason, &pool_of, index_of, commission)?;
1841        Ok(pool_of)
1842    }
1843
1844    /// Removes a pool if it contains no active commitments.
1845    ///
1846    /// Permanently deletes the specified pool digest under the given reason,
1847    /// freeing associated resources and references. Pools can only be reaped when
1848    /// **no proprietors have active commitments** to any of its slots.
1849    ///
1850    /// Attempting to reap a non-empty pool must return an error.
1851    ///
1852    /// ## Returns
1853    /// - `Ok(())` if the pool is successfully removed
1854    /// - `Err(DispatchError)` if the pool does not exist or contains active commitments
1855    fn reap_pool(reason: &Self::Reason, pool_of: &Self::Digest) -> DispatchResult;
1856
1857    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1858    // ```````````````````````````````````` HOOKS ````````````````````````````````````
1859    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1860
1861    /// Hook called after a pool is successfully created by a proprietor (assumed to be
1862    /// the current pool-manager).
1863    ///
1864    /// Provides an extension point for triggering side-effects such as events,
1865    /// logging, recalculations, or external state updates when a pool is
1866    /// established or modified.
1867    ///
1868    /// Default implementation is a no-op.
1869    fn on_set_pool(
1870        _who: &Proprietor,
1871        _pool_of: &Self::Digest,
1872        _reason: &Self::Reason,
1873        _pool: &Self::Pool,
1874    ) {
1875    }
1876
1877    /// Hook called after slot shares are updated within a pool.
1878    ///
1879    /// Provides an extension point for triggering recalculations, propagating
1880    /// changes to related state, or emitting events when a slot's shares are
1881    /// adjusted as part of the pool's dynamic allocation strategy.
1882    ///
1883    /// Default implementation is a no-op.
1884    fn on_set_slot_shares(
1885        _pool_of: &Self::Digest,
1886        _reason: &Self::Reason,
1887        _slot_of: &Self::Digest,
1888        _shares: Self::Shares,
1889    ) {
1890    }
1891
1892    /// Hook called after a pool's manager is changed.
1893    ///
1894    /// Provides an extension point for triggering governance events, audit logging,
1895    /// or external notifications when management control of a pool is transferred
1896    /// to a new proprietor.
1897    ///
1898    /// Default implementation is a no-op.
1899    fn on_set_manager(_pool_of: &Self::Digest, _reason: &Self::Reason, _manager: &Proprietor) {}
1900
1901    /// Hook called after a pool is reaped (removed).
1902    ///
1903    /// Provides an extension point for cleanup tasks, audit logging, freeing
1904    /// related resources, or triggering external notifications when a pool
1905    /// is permanently removed from the system.
1906    ///
1907    /// `dust` represents any residual value that was unclaimable and
1908    /// effectively considered dead.
1909    ///
1910    /// Default implementation is a no-op.
1911    fn on_reap_pool(_pool_of: &Self::Digest, _reason: &Self::Reason, _dust: Self::Asset) {}
1912}
1913
1914// ===============================================================================
1915// ``````````````````````````````` COMMIT VARIANT ````````````````````````````````
1916// ===============================================================================
1917
1918/// `CommitVariant` extends [`Commitment`] by introducing **variant-based commitments** -
1919/// allowing each commitment to represent a specific *position* or *type*,
1920/// such as positive/negative, long/short, or other directional states.
1921///
1922/// This abstraction is useful in financial systems where commitments
1923/// can have opposing or complementary meanings (e.g., bets, trades, or hedges),
1924/// and where such **variants** must be consistently classified and tracked
1925/// under the same digest or reason.
1926///
1927/// ## Purpose
1928///
1929/// In standard [`Commitment`], each digest represents a single locked or committed value.
1930/// However, many financial instruments carry a **semantic variant**, such as:
1931/// - Positive / Negative exposure
1932/// - Long / Short position
1933/// - Buy / Sell order
1934/// - For / Against vote
1935///
1936/// `CommitVariant` provides an abstract way to encode and manage such distinctions
1937/// without altering the base commitment model.
1938///
1939/// ## Core Principles
1940///
1941/// 1. **Variant as classification**
1942///    - Each commitment may declare a variant indicating its nature or direction.
1943///    - Variants are accounted for directly under the commitment's digest.
1944///
1945/// 2. **Digest-level association**
1946///    - A commitment's digest uniquely identifies both its value and variant.
1947///    - Variants must be recorded or derivable from the digest state.
1948///
1949/// 3. **Financial semantics**
1950///    - Designed for use in scenarios like betting, derivatives, prediction markets,
1951///      and financial trilemmas where commitments can oppose or complement each other.
1952///
1953/// ## Example Use Cases
1954///
1955/// - **Betting systems**: Represent "for" and "against" commitments.
1956/// - **Trading platforms**: Track "long" and "short" positions.
1957/// - **Hedging mechanisms**: Manage opposing commitments under one reason.
1958/// - **Voting protocols**: Represent affirmative and negative stake commitments.
1959///
1960/// ## Summary
1961///
1962/// `CommitVariant` offers an abstract and extensible layer over [`Commitment`],
1963/// enabling richer semantics and directionality (positive/negative or other variants)
1964/// to be embedded into financial commitment logic in a consistent, trustless manner.
1965///
1966/// Generics:
1967/// - **Proprietor** - the entity that owns the asset and can make commitments.
1968pub trait CommitVariant<Proprietor>: Commitment<Proprietor> {
1969    /// The type representing a commitment's position or variant.
1970    type Position: RuntimeEnum + Delimited;
1971
1972    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1973    // ``````````````````````````````````` CHECKERS ``````````````````````````````````
1974    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1975
1976    /// Validates whether a specific digest variant's value can be set or updated.
1977    ///
1978    /// Ensures that:
1979    /// - The variant is initialized (has a balance entry)
1980    /// - The intended update respects mint/reap advisory limits
1981    ///
1982    /// ## Returns
1983    /// - `Ok(())` if validation succeeds
1984    /// - `Err(DispatchError)` if the digest/variant is invalid or limits are violated
1985    fn can_set_digest_variant_value(
1986        reason: &Self::Reason,
1987        digest: &Self::Digest,
1988        value: Self::Asset,
1989        variant: &Self::Position,
1990        qualifier: &Self::Intent,
1991    ) -> DispatchResult {
1992        let current = Self::get_digest_variant_value(reason, digest, variant)?;
1993        match current.cmp(&value) {
1994            Ordering::Less => {
1995                // Mint path (increase)
1996                let limits = Self::digest_mint_limits(digest, reason, qualifier)?;
1997                let mintable = value.saturating_sub(current);
1998                ensure!(
1999                    limits.contains(mintable),
2000                    Self::from_commit_error(CommitError::MintingOffLimits)
2001                )
2002            }
2003            Ordering::Greater => {
2004                // Reap path (decrease)
2005                let limits = Self::digest_reap_limits(digest, reason, qualifier)?;
2006                let reapable = current.saturating_sub(value);
2007                ensure!(
2008                    limits.contains(reapable),
2009                    Self::from_commit_error(CommitError::ReapingOffLimits)
2010                )
2011            }
2012            Ordering::Equal => {
2013                // No-op
2014            }
2015        }
2016
2017        Ok(())
2018    }
2019
2020    /// Validates whether a new commitment can be placed for a specific variant.
2021    ///
2022    /// Ensures that no existing commitment exists for the given reason (enforcing
2023    /// the "one digest per reason" invariant) and that the proprietor has sufficient
2024    /// available funds to cover the commitment value.
2025    ///
2026    /// Additionally validates that the value satisfies variant-specific placement
2027    /// limits derived from the lazy balance model.
2028    ///
2029    /// ## Returns
2030    /// - `Ok(())` if validation succeeds
2031    /// - `Err(DispatchError)` if a commitment already exists, insufficient funds are available,
2032    ///   or the value violates variant-specific limits
2033    fn can_place_commit_of_variant(
2034        who: &Proprietor,
2035        reason: &Self::Reason,
2036        digest: &Self::Digest,
2037        variant: &Self::Position,
2038        value: Self::Asset,
2039        qualifier: &Self::Intent,
2040    ) -> DispatchResult {
2041        ensure!(
2042            Self::commit_exists(who, reason).is_err(),
2043            Self::from_commit_error(CommitError::CommitAlreadyExists)
2044        );
2045        let max = Self::available_funds(who);
2046        ensure!(
2047            max >= value,
2048            Self::from_commit_error(CommitError::InsufficientFunds)
2049        );
2050        let limits = Self::place_commit_limits_of_variant(who, reason, digest, variant, qualifier)?;
2051        ensure!(
2052            limits.contains(value),
2053            Self::from_commit_error(CommitError::PlacingOffLimits)
2054        );
2055
2056        Ok(())
2057    }
2058
2059    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2060    // ``````````````````````````````````` GETTERS ```````````````````````````````````
2061    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2062
2063    /// Retrieves the position/variant of a proprietor's commitment for
2064    /// the given reason.
2065    ///
2066    /// Returns the active variant under which the proprietor's commitment
2067    /// is currently classified. Since each proprietor can have only **one
2068    /// commitment per reason** (base trait invariant), they also have only
2069    /// **one active variant per reason**.
2070    ///
2071    /// ## Returns
2072    /// - `Ok(Position)` containing the commitment's current variant
2073    /// - `Err(DispatchError)` if no commitment exists for the reason
2074    fn get_commit_variant(
2075        who: &Proprietor,
2076        reason: &Self::Reason,
2077    ) -> Result<Self::Position, DispatchError>;
2078
2079    /// Retrieves the aggregated value for a specific digest and variant combination.
2080    ///
2081    /// Returns the real-time sum of all commitments to the given digest that are
2082    /// classified under the specified variant. This enables querying variant-specific
2083    /// exposure across all proprietors.
2084    ///
2085    /// When both [`Commitment`] and `CommitVariant` are implemented:
2086    /// - [`Commitment::get_digest_value`] returns the aggregate across **all variants**
2087    /// - This method returns the aggregate for **a specific variant**
2088    ///
2089    /// ## Returns
2090    /// - `Ok(Asset)` containing the variant-specific aggregated value
2091    /// - `Err(DispatchError)` if the digest or variant does not exist
2092    fn get_digest_variant_value(
2093        reason: &Self::Reason,
2094        digest: &Self::Digest,
2095        variant: &Self::Position,
2096    ) -> Result<Self::Asset, DispatchError>;
2097
2098    /// Provides advisory bounds for increasing (minting) a digest's
2099    /// specific variant-balance.
2100    ///
2101    /// Acts as an optional guard to prevent abrupt or excessive growth,
2102    /// complementing core digest update logic.
2103    fn digest_mint_limits_of_variant(
2104        _digest: &Self::Digest,
2105        _reason: &Self::Reason,
2106        _variant: &Self::Position,
2107        _qualifier: &Self::Intent,
2108    ) -> Result<Self::Limits, DispatchError> {
2109        // By default, this returns a baseline limit,
2110        // representing a static or fallback bound when no dynamic limits are applied.
2111        //
2112        // Implementors may override this to provide context-specific limits.
2113        Ok(Self::Limits::none())
2114    }
2115
2116    /// Provides advisory bounds for decreasing (reaping) a digest's
2117    /// specific variant-balance.
2118    ///
2119    /// Acts as an optional guard to prevent aggressive or premature reductions,
2120    /// complementing core invariants that ensure correctness.
2121    fn digest_reap_limits_of_variant(
2122        _digest: &Self::Digest,
2123        _reason: &Self::Reason,
2124        _variant: &Self::Position,
2125        _qualifier: &Self::Intent,
2126    ) -> Result<Self::Limits, DispatchError> {
2127        // By default, this returns a baseline limit,
2128        // representing a static or fallback bound when no dynamic limits are applied.
2129        // Implementors may override this to provide context-specific limits.
2130        Ok(Self::Limits::none())
2131    }
2132
2133    /// Provides advisory bounds for placing a new commitment
2134    /// under a given variant.
2135    ///
2136    /// Acts as an optional pre-validation layer to prevent premature or
2137    /// undesirable commits by constraining value ranges.
2138    fn place_commit_limits_of_variant(
2139        _who: &Proprietor,
2140        _reason: &Self::Reason,
2141        _digest: &Self::Digest,
2142        _variant: &Self::Position,
2143        _qualifier: &Self::Intent,
2144    ) -> Result<Self::Limits, DispatchError> {
2145        // By default, this returns an unbounded extent,
2146        // meaning no additional constraints are applied unless overridden.
2147        //
2148        // Implementors may override this to provide context-specific limits.
2149        Ok(Self::Limits::none())
2150    }
2151
2152    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2153    // ``````````````````````````````````` MUTATORS ``````````````````````````````````
2154    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2155
2156    /// Sets or updates the aggregated value for a specific digest and variant.
2157    ///
2158    /// Updates the value associated with a particular variant of a digest,
2159    /// propagating changes to all commitments tied to that digest-variant pair.
2160    ///
2161    /// When both [`Commitment`] and [`CommitVariant`] are utilized in a system:
2162    /// - [`Commitment::set_digest_value`] may set the **default variant**.
2163    /// - This method updates **the specified variant**.
2164    ///
2165    /// The `qualifier` defines how the update is applied (e.g., exact vs best-effort,
2166    /// forceful vs bounded), and may influence the final value that is actually set.
2167    ///
2168    /// ## Returns
2169    /// - `Ok(Asset)` containing the actual value applied to the specified digest-variant
2170    /// - `Err(DispatchError)` if the digest, variant, or reason is invalid, or update fails
2171    fn set_digest_variant_value(
2172        reason: &Self::Reason,
2173        digest: &Self::Digest,
2174        value: Self::Asset,
2175        variant: &Self::Position,
2176        qualifier: &Self::Intent,
2177    ) -> Result<Self::Asset, DispatchError>;
2178
2179    /// Changes the variant of an existing commitment.
2180    ///
2181    /// Transitions a commitment from its current variant to a new variant while
2182    /// preserving the committed value. Since commitments are immutable, this
2183    /// operation uses the **resolve-and-replace pattern**.
2184    ///
2185    /// Ensures that variant changes maintain consistency with the underlying
2186    /// commitment state and respect the semantics of the commitment system.
2187    ///
2188    /// In case if the variant already exists, the function safely returns.
2189    ///
2190    /// ## Returns
2191    /// - `Ok(())` if the variant is successfully changed
2192    /// - `Err(DispatchError)` if:
2193    ///   - No commitment exists for the reason
2194    ///   - New variant matches current variant
2195    ///   - Commitment resolution or placement fails
2196    fn set_commit_variant(
2197        who: &Proprietor,
2198        reason: &Self::Reason,
2199        variant: &Self::Position,
2200        qualifier: &Self::Intent,
2201    ) -> DispatchResult {
2202        Self::commit_exists(who, reason)?;
2203        let current_variant = Self::get_commit_variant(who, reason)?;
2204        if current_variant == *variant {
2205            return Ok(());
2206        }
2207        let digest = Self::get_commit_digest(who, reason)?;
2208        let re_deposit = Self::resolve_commit(who, reason)?;
2209        Self::place_commit_of_variant(who, reason, &digest, re_deposit, variant, qualifier)?;
2210        Ok(())
2211    }
2212
2213    /// Places a commitment under a specific variant.
2214    ///
2215    /// Creates a new commitment with explicit variant classification, enabling
2216    /// directional or positional semantics from the moment of placement.
2217    ///
2218    /// This extends [`Commitment::place_commit`] by adding variant specification.
2219    /// When both traits are implemented:
2220    /// - [`Commitment::place_commit`] may use a **default variant**
2221    /// - This method allows **explicit variant selection**
2222    ///
2223    /// The method must:
2224    /// - Record the commitment under the specified variant
2225    /// - Associate the variant with the digest at the commitment level
2226    /// - Respect qualifier parameters
2227    /// - Return the actual committed value after any adjustments
2228    ///
2229    /// ## Returns
2230    /// - `Ok(Asset)` containing the actual value committed under the variant
2231    /// - `Err(DispatchError)` if placement fails.
2232    fn place_commit_of_variant(
2233        who: &Proprietor,
2234        reason: &Self::Reason,
2235        digest: &Self::Digest,
2236        value: Self::Asset,
2237        variant: &Self::Position,
2238        qualifier: &Self::Intent,
2239    ) -> Result<Self::Asset, DispatchError>;
2240
2241    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2242    // ```````````````````````````````````` HOOKS ````````````````````````````````````
2243    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2244
2245    /// Hook called after placing a commitment under a specific variant.
2246    ///
2247    /// Provides an extension point for triggering custom side-effects when a new
2248    /// variant-based commitment is placed.
2249    ///
2250    /// Default implementation is a no-op.
2251    fn on_place_commit_on_variant(
2252        _who: &Proprietor,
2253        _reason: &Self::Reason,
2254        _digest: &Self::Digest,
2255        _value: Self::Asset,
2256        _variant: &Self::Position,
2257    ) {
2258    }
2259
2260    /// Hook called after a commitment's variant is changed.
2261    ///
2262    /// Provides an extension point for responding to variant transitions.
2263    /// Receives both the digest and value to enable comprehensive
2264    /// state management and analytics.
2265    ///
2266    /// Default implementation is a no-op.
2267    fn on_set_commit_variant(
2268        _who: &Proprietor,
2269        _reason: &Self::Reason,
2270        _digest: &Self::Digest,
2271        _value: Self::Asset,
2272        _variant: &Self::Position,
2273    ) {
2274    }
2275
2276    /// Hook called after a digest's variant value is updated.
2277    ///
2278    /// Provides an extension point for reacting to variant-specific value changes.
2279    ///
2280    /// Default implementation is a no-op.
2281    fn on_set_digest_variant(
2282        _digest: &Self::Digest,
2283        _reason: &Self::Reason,
2284        _value: Self::Asset,
2285        _variant: &Self::Position,
2286    ) {
2287    }
2288}
2289
2290// ===============================================================================
2291// ``````````````````````````````` INDEX VARIANT `````````````````````````````````
2292// ===============================================================================
2293
2294/// A trait for managing and querying **variants** of index entries and its commitments.
2295///
2296/// Extends [`CommitVariant`] and [`CommitIndex`], allowing indexed commitments
2297/// to carry additional positional/variant metadata.  
2298///
2299/// This trait enables tracking, setting, and preparing variant entries in an index context.
2300///
2301/// Generics:
2302/// - **Proprietor** - the entity that owns the asset and can make commitments.
2303pub trait IndexVariant<Proprietor>: CommitVariant<Proprietor> + CommitIndex<Proprietor> {
2304    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2305    // ````````````````````````````````` CONSTRUCTORS ````````````````````````````````
2306    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2307
2308    /// Prepares an index from entry data with explicit variant classification.
2309    ///
2310    /// Constructs a variant-aware index structure from a list of `(Digest, Shares, Position)` tuples,
2311    /// where each tuple represents:
2312    /// - **Digest**: The unique identifier of an entry within the index
2313    /// - **Shares**: The proportional weight or ownership of that entry
2314    /// - **Position**: The variant classification for that entry
2315    ///
2316    /// This extends [`CommitIndex::prepare_index`] by adding variant specification for each entry.
2317    /// When both traits are implemented:
2318    /// - [`CommitIndex::prepare_index`] may assign a **default variant** to all entries
2319    /// - This method allows **explicit variant specification** per entry
2320    ///
2321    /// ## Returns
2322    /// - `Ok(Index)` containing the prepared variant-aware index structure
2323    /// - `Err(DispatchError)` if preparation fails
2324    fn prepare_index_of_variants(
2325        who: &Proprietor,
2326        reason: &Self::Reason,
2327        entries: Vec<(Self::Digest, Self::Shares, Self::Position)>,
2328    ) -> Result<Self::Index, DispatchError>;
2329
2330    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2331    // ``````````````````````````````````` GETTERS ```````````````````````````````````
2332    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2333
2334    /// Retrieves the variant associated with a specific entry within an index.
2335    ///
2336    /// Returns the position classification for the given entry digest under the
2337    /// specified reason and index digest. This enables querying the directional
2338    /// or positional semantics of individual entries within a mixed-variant index.
2339    ///
2340    /// ## Returns
2341    /// - `Ok(Position)` containing the entry's variant
2342    /// - `Err(DispatchError)` if the index or entry does not exist
2343    fn get_entry_variant(
2344        reason: &Self::Reason,
2345        index_of: &Self::Digest,
2346        entry_of: &Self::Digest,
2347    ) -> Result<Self::Position, DispatchError>;
2348
2349    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2350    // ``````````````````````````````````` MUTATORS ``````````````````````````````````
2351    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2352
2353    /// Associates or updates a variant for a specific entry within an index.
2354    ///
2355    /// Records a position (variant) for an entry digest within the context of a given
2356    /// index digest and reason. If shares are provided, they are updated alongside the variant.
2357    ///
2358    /// Since indexes are **immutable** once created, this method internally
2359    /// generates a new digest for the modified index and returns the new index digest.
2360    ///
2361    /// For updating multiple entry variants simultaneously, use
2362    /// [`IndexVariant::prepare_index_of_variants`] to construct a completely new index structure.
2363    ///
2364    /// This behavior partly mirrors [`CommitIndex::set_index`], and can be
2365    /// considered a higher-level wrapper around index reconstruction and binding.
2366    ///
2367    /// As such, it is assumed that any side-effects or lifecycle handling are
2368    /// delegated through the underlying [`CommitIndex::set_index`] call, and
2369    /// therefore this method does **not** introduce a separate hook.
2370    ///
2371    /// ## Returns
2372    /// - `Ok(Digest)` containing the new index digest after variant update
2373    /// - `Err(DispatchError)` if the index or entry does not exist, or update fails
2374    fn set_entry_of_variant(
2375        who: &Proprietor,
2376        reason: &Self::Reason,
2377        index_of: &Self::Digest,
2378        entry_of: &Self::Digest,
2379        variant: Self::Position,
2380        shares: Option<Self::Shares>,
2381    ) -> Result<Self::Digest, DispatchError>;
2382}
2383
2384// ===============================================================================
2385// ```````````````````````````````` POOL VARIANT `````````````````````````````````
2386// ===============================================================================
2387
2388/// A trait for managing and querying **variants** of pool slots and their commitments.
2389///
2390/// Extends [`CommitPool`] to allow pool commitments to carry additional positional
2391/// or variant metadata.
2392///
2393/// Provides the ability to retrieve, set, and react to variant changes within a pool context.
2394///
2395/// Generics:
2396/// - **Proprietor** - the entity that owns the asset and can make commitments.
2397pub trait PoolVariant<Proprietor>:
2398    CommitVariant<Proprietor> + CommitPool<Proprietor> + CommitIndex<Proprietor>
2399{
2400    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2401    // ``````````````````````````````````` GETTERS ```````````````````````````````````
2402    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2403
2404    /// Retrieves the variant (position) associated with a specific slot within a pool.
2405    ///
2406    /// Returns the position classification for the given slot digest under the
2407    /// specified reason and pool digest. This enables querying the directional
2408    /// or positional semantics of individual slots within a managed variant-aware pool.
2409    ///
2410    /// ## Returns
2411    /// - `Ok(Position)` containing the slot's variant
2412    /// - `Err(DispatchError)` if the pool or slot does not exist
2413    fn get_slot_variant(
2414        reason: &Self::Reason,
2415        pool_of: &Self::Digest,
2416        slot_of: &Self::Digest,
2417    ) -> Result<Self::Position, DispatchError>;
2418
2419    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2420    // ``````````````````````````````````` MUTATORS ``````````````````````````````````
2421    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2422
2423    /// Associates or updates a variant for a specific slot within a pool.
2424    ///
2425    /// Records a position (variant) for a slot digest within the context of a given
2426    /// pool digest and reason. If shares are provided, they are updated alongside the variant.
2427    ///
2428    /// Since pools are **mutable** (unlike indexes), this method directly updates
2429    /// the slot's variant without creating a new pool digest.
2430    ///
2431    /// This is a **managed operation** - typically restricted to the pool manager
2432    /// or authorized entities, ensuring controlled strategy execution while
2433    /// protecting depositor interests.
2434    ///
2435    /// ## Returns
2436    /// - `Ok(())` if the variant is successfully updated
2437    /// - `Err(DispatchError)` if fails
2438    fn set_slot_of_variant(
2439        who: &Proprietor,
2440        reason: &Self::Reason,
2441        pool_of: &Self::Digest,
2442        slot_of: &Self::Digest,
2443        variant: Self::Position,
2444        shares: Option<Self::Shares>,
2445    ) -> DispatchResult;
2446
2447    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2448    // ```````````````````````````````````` HOOKS ````````````````````````````````````
2449    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2450
2451    /// Hook called after a slot variant is updated within a pool.
2452    ///
2453    /// Provides an extension point for triggering side-effects when a slot's
2454    /// variant is associated or changed within a managed pool.
2455    ///
2456    /// Receives both the slot digest and optional shares to enable comprehensive
2457    /// state management and analytics when managed slot configurations change.
2458    ///
2459    /// Default implementation is a no-op.
2460    fn on_set_slot_of_variant(
2461        _pool_of: &Self::Digest,
2462        _reason: &Self::Reason,
2463        _slot_of: &Self::Digest,
2464        _shares: Option<Self::Shares>,
2465        _variant: &Self::Position,
2466    ) {
2467    }
2468}