pallet_commitment/
lib.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// ````````````````````````````` PALLET COMMITMENT ```````````````````````````````
14// ===============================================================================
15
16//! Implementation crate for the [`Commitment`](frame_suite::commitment)
17//! family of traits.
18//!
19//! A semantic bonding layer that binds assets to caller-defined purpose and context,
20//! with collective management of bonded value and lazy adjustments.
21//!
22//! Used instead of direct fungible locks to enable richer control semantics and
23//! automated balance management. These are called commitments as they inherently
24//! carry a semantic reason defined by the consuming pallet.
25//!
26//! ## Overview
27//!
28//! - [`Config`] - Runtime configuration
29//! - [`Call`] - Dispatchable extrinsics
30//! - [`Pallet`] - Trait implementation for external modules
31//!
32//! This pallet provides a **generalized bonding (locking) mechanism** for
33//! fungible (quantitative) assets, enabling other pallets to express
34//! **structured financial intent**.
35//!
36//! Instead of treating balances as passive values, this system allows assets to be:
37//!
38//! - **Bonded under a reason** (e.g., `LockReason`)
39//! - **Bound to a digest** (a context-specific identifier defined by the caller pallet)
40//!
41//! Together, this forms a **Commitment**:
42//!
43//! ```text
44//! Commitment = bond(asset) -> (reason, digest)
45//! ```
46//!
47//! ## Key Responsibilities
48//!
49//! This pallet acts as a **shared infrastructure layer** that:
50//!
51//! - Locks assets on behalf of any fungible account
52//! - Groups commitments under **runtime-defined reasons**
53//! - Tracks and manages value at the **digest level**
54//! - Allows controlled updates to aggregated digest values
55//!
56//! The **meaning and context of each digest** are defined by the
57//! caller pallet (e.g., staking, escrow, trading, governance).
58//!
59//! ## Core Features
60//!
61//! - **Commitment** - lock assets under `(reason, digest)`, enforcing one commitment per
62//!   (proprietor, reason). Value can only increase per commitment, while aggregate
63//!   digest values may be adjusted. Supports lazy resolution.
64//!
65//! - **Digest Management** - track and update aggregate value at the digest level,
66//!   propagating changes to all commitments.
67//!
68//! - **Digest Mint / Reap** - increase or decrease the aggregate value of a digest,
69//!   automatically adjusting all associated commitments proportionally.
70//!
71//! - **Indexes** - group multiple digests with share-based ownership
72//!   (see [`CommitIndex`](frame_suite::commitment::CommitIndex)).
73//!
74//! - **Pools** - manager-controlled allocation with dynamic rebalancing and fixed
75//!   commissions (see [`CommitPool`](frame_suite::commitment::CommitPool)).
76//!
77//! - **Variants** - semantic differentiation (e.g., long/short, positive/negative)
78//!   (see [`CommitVariant`](frame_suite::commitment::CommitVariant)).
79//!
80//! - **Lazy Evaluation** - values reflect live digest state and are realized on query
81//!   or resolution.
82//!
83//! - **Asset Agnostic** - works with any fungible asset type implementing required traits.
84//!
85//! Each commitment is scoped by a **Reason** and anchored to a **Digest**.
86//!
87//! ### Design Scope
88//!
89//! The pallet is a **generic implementation** of
90//! [`Commitment`](frame_suite::commitment::Commitment),
91//! designed to be **loosely coupled** with consumer pallets via their `Config` traits.
92//!
93//! ```ignore
94//! // Consumer pallet configuration
95//! pub trait Config {
96//!     /// Commitment adapter providing bonding logic
97//!     type CommitmentAdapter: Commitment<Self::AccountId>;
98//! }
99//! ```
100//!
101//! It handles commitment accounting and lifecycle management, while consumer
102//! pallets define domain-specific logic and user interactions.
103//!
104//! This provides a **bonding abstraction over basic fungible locks**, enabling
105//! rich economic behaviors without duplicating core logic.
106//!
107//! ## Extrinsics Scope
108//!
109//! This pallet exposes **no user-facing extrinsics for commitments**.
110//!
111//! Commitment operations are accessed by consumer pallets through the traits,
112//! ensuring controlled and domain-specific usage.
113//!
114//! Only minimal extrinsics for **basic deposit and withdrawal** for a
115//! default `PrepareForCommit` hold/reserve are provided and some read-only
116//! APIs (see [`Call`]).
117//!
118//! ## Native Commitment Reserve
119//!
120//! This pallet exposes only minimal extrinsics to deposit and withdraw
121//! funds into a native reserve ([`HoldReason::PrepareForCommit`]), which acts
122//! as the default funding source for all commitment operations.
123//!
124//! Commitments consume assets from this reserve under
125//! [`Fortitude::Polite`](frame_support::traits::tokens::Fortitude),
126//! allowing users to pre-fund commitments once and reuse the
127//! same reserve across all consumer pallets, ensuring efficient,
128//! directive-driven execution
129//!
130//! When [`Fortitude::Force`](frame_support::traits::tokens::Fortitude)
131//! is used, operations may fallback to the liquid (free) balance if
132//! reserve funds are insufficient.
133//!
134//! Consumer pallets utilizing commitments should account for this behavior:
135//! - use polite semantics to operate strictly on reserved funds
136//! - use force semantics to allow fallback to liquid balance when required
137//!
138//! ## Instance and Model
139//!
140//! The pallet supports **multiple instances**, each capable of handling
141//! commitments across diverse scenarios.
142//!
143//! - Each instance may define its own configuration via [`Config`],
144//!   allowing independent behavior and specialization.
145//!
146//! - Each instance supports **multiple reasons**, enabling different pallets
147//!   or domains to operate within the same instance.
148//!
149//! - A single instance can be shared across multiple consumer pallets,
150//!   with separation maintained through reasons and digests.
151//!
152//! - Instances may be separated when isolation is required.
153//!
154//! This enables the pallet to act as a **shared economic layer** across use cases.
155//!
156//! ## Terminology
157//!
158//! - **Proprietor** - the account or entity that owns and manages commitments.
159//! - **Reason** - the categorical purpose of a commitment.
160//! - **Digest** - a unique identifier representing commitment context.
161//! - **Direct Commitment** - a commitment to a single digest.
162//! - **Index** - a collection of digests with shares
163//!   (see [`CommitIndex`](frame_suite::commitment::CommitIndex)).
164//! - **Pool** - a managed structure with allocation and commission
165//!   (see [`CommitPool`](frame_suite::commitment::CommitPool)).
166//! - **Entry** - a digest component within an index.
167//! - **Slot** - a digest component within a pool.
168//! - **Commission** - manager's share collected on resolution.
169//! - **Variant** - semantic position (see
170//!   [`CommitVariant`](frame_suite::commitment::CommitVariant)).
171//!
172//! ## Development Feature Gate
173//!
174//! This pallet includes a `dev` feature gate for development and testing.
175//!
176//! Core functionality is exposed via public APIs for RPC and UI usage.
177//! The `dev` feature provides thin wrapper extrinsics and extended
178//! events for direct inspection.
179//!
180//! This feature must be disabled in production runtimes due to additional debugging overhead.
181
182#![cfg_attr(not(feature = "std"), no_std)]
183
184// ===============================================================================
185// `````````````````````````````````` MODULES ````````````````````````````````````
186// ===============================================================================
187
188#[cfg(feature = "runtime-benchmarks")]
189mod benchmarking;
190#[cfg(test)]
191mod mock;
192#[cfg(test)]
193mod tests;
194mod balance;
195mod commitment;
196mod helpers;
197pub mod traits;
198pub mod types;
199pub mod weights;
200
201// ===============================================================================
202// `````````````````````````````` PALLET MODULE ``````````````````````````````````
203// ===============================================================================
204
205pub use pallet::*;
206
207#[frame_support::pallet]
208pub mod pallet {
209
210    // ===============================================================================
211    // ````````````````````````````````` IMPORTS `````````````````````````````````````
212    // ===============================================================================
213
214    // --- Local crate imports ---
215    use crate::{balance::*, types::*, weights::WeightInfo};
216
217    // --- FRAME Support ---
218    use frame_support::{
219        dispatch::DispatchResult,
220        pallet_prelude::*,
221        traits::{
222            fungible::InspectHold,
223            tokens::{fungible::*, Fortitude, Precision, Preservation},
224            BuildGenesisConfig, VariantCount,
225        },
226    };
227
228    // --- FRAME System ---
229    use frame_system::{ensure_signed, pallet_prelude::OriginFor};
230
231    // --- FRAME Suite ---
232    use frame_suite::{
233        assets::*,
234        base::{Countable, Delimited, Fractional, Percentage, RuntimeEnum, Time},
235        commitment::*,
236        misc::PositionIndex,
237        plugin_types,
238    };
239
240    // --- Substrate primitives ---
241    use sp_runtime::{
242        traits::{Debug, MaybeDisplay},
243        DispatchError, FixedPointNumber, PerThing,
244    };
245    use sp_std::vec::Vec;
246
247    // ===============================================================================
248    // `````````````````````````````` PALLET MARKER ``````````````````````````````````
249    // ===============================================================================
250
251    /// Primary Marker type for the **Commitment pallet**.
252    ///
253    /// This pallet provides implementations for traits from
254    /// [`commitment`](frame_suite::xp)
255    ///
256    /// [`Pallet`] implements the core commitment system traits:
257    ///
258    /// - [`InspectAsset`]
259    /// - [`DigestModel`]
260    /// - [`Commitment`]
261    /// - [`CommitIndex`]
262    /// - [`CommitPool`]
263    /// - [`CommitVariant`]
264    /// - [`IndexVariant`]
265    /// - [`PoolVariant`]
266    #[pallet::pallet]
267    pub struct Pallet<T, I = ()>(_);
268
269    // ===============================================================================
270    // ```````````````````````````` INTERNAL PALLET MARKER ```````````````````````````
271    // ===============================================================================
272
273    /// Internal helper struct for implementing low-level commitment trait operations.
274    ///
275    /// This marker type serves as a namespace for trait implementations defined in
276    /// [`crate::traits`], providing internal access to commitment system low-level primitives
277    /// without exposing unchecked-functions as part of the public API via [`Pallet`].
278    ///
279    /// `CommitHelpers` implements the commitment low-level helper traits:
280    ///
281    /// - [`CommitBalance`](crate::traits::CommitBalance)
282    /// - [`CommitDeposit`](crate::traits::CommitDeposit)
283    /// - [`CommitWithdraw`](crate::traits::CommitWithdraw)
284    /// - [`CommitOps`](crate::traits::CommitOps)
285    /// - [`CommitInspect`](crate::traits::CommitInspect)
286    /// - [`PoolOps`](crate::traits::PoolOps)
287    /// - [`IndexOps`](crate::traits::IndexOps)
288    pub(crate) struct CommitHelpers<T: Config<I>, I: 'static = ()>(PhantomData<(T, I)>);
289
290    // ===============================================================================
291    // `````````````````````````````` CONFIG TRAIT ```````````````````````````````````
292    // ===============================================================================
293
294    /// Configuration trait for the Commitment pallet.
295    ///
296    /// This trait defines the types, constants, and dependencies
297    /// that the runtime must provide for this pallet to function.
298    ///
299    /// The generic parameter `I` allows the same pallet to be instantiated
300    /// multiple times within a runtime. Each instance can have its own
301    /// independent storage and configuration.
302    ///
303    /// Example:
304    /// - `I = ()` -> default (single instance)
305    /// - `I = Staking`, `Governance`, etc. -> multiple independent instances
306    #[pallet::config]
307    pub trait Config<I: 'static = ()>: frame_system::Config {
308        // --- Runtime Anchors ---
309
310        /// The overarching event type for the runtime.
311        type RuntimeEvent: From<Event<Self, I>>
312            + IsType<<Self as frame_system::Config>::RuntimeEvent>;
313
314        /// Hold reason type for locking assets.
315        ///
316        /// Utilized to provide a native hold reason for all commitments.
317        type AssetHold: From<HoldReason> + RuntimeEnum + Delimited + Copy + VariantCount;
318
319        /// Freeze reason type for commitment-specific freezes.
320        type AssetFreeze: From<FreezeReason> + RuntimeEnum + Delimited + Copy + VariantCount;
321
322        // --- Scalars ---
323
324        /// Type for representing shares in indexes or pools.
325        ///
326        /// Must implement an unsigned integer and be convertible into the pallet's
327        /// asset type (safe conversion).
328        type Shares: Countable + Into<AssetOf<Self, I>> + MaybeDisplay;
329
330        /// Bias factor used for fixed-point arithmetic for
331        /// direct, index, or pool commitments.
332        ///
333        /// Must be a fixed-point number whose precision is sufficient to
334        /// safely handle division and percentage-based arithmetic operations.
335        type Bias: Fractional;
336
337        /// Time counter used for timestamps.
338        ///
339        /// May be block number, Unix epoch, or an internal counter.
340        type Time: Time;
341
342        /// Commission type used for pool fee calculations.
343        ///
344        /// Represents a percentage or ratio value applied during pool
345        /// resolution to extract commission from committed value.
346        type Commission: Percentage;
347
348        // --- Pallet Adapters ---
349
350        /// The fungible asset type for this pallet instance.
351        ///
352        /// Must support inspection and unbalanced mutation, freezing, and holding operations.
353        type Asset: Inspect<
354                Proprietor<Self>,
355                Balance: MaybeDisplay
356                             + From<<Self::Bias as FixedPointNumber>::Inner>
357                             + From<<Self::Commission as PerThing>::Inner>,
358            > + InspectFreeze<Proprietor<Self>, Id = Self::AssetFreeze>
359            + InspectHold<Proprietor<Self>, Reason = Self::AssetHold>
360            + Mutate<Proprietor<Self>>
361            + UnbalancedHold<Proprietor<Self>>
362            + Unbalanced<Proprietor<Self>>
363            + MutateHold<Proprietor<Self>>
364            + MutateFreeze<Proprietor<Self>>;
365
366        // --- Contexual Enums ---
367
368        /// The set of commitment dispositions supported by this pallet.
369        ///
370        /// A disposition acts as a **meta-identifier** defining the semantic position
371        /// of a commitment (e.g. affirmative, contrary). Commitments are scoped by
372        /// this value.
373        ///
374        /// For plain commitments where no variant is explicitly specified, the
375        /// [`Default`] value of this type is used.
376        ///
377        /// Implementations must ensure that only **semantic variants** participate
378        /// in indexing; marker or non-semantic variants should be excluded in
379        /// [`PositionIndex`].
380        ///
381        /// The [`Ignore`](frame_suite::misc::Ignore) type may be used to represent a
382        /// single, non-variant position. In this configuration, all commitments map
383        /// to index `0`, effectively disabling variant-based semantics.
384        ///
385        /// For optimal storage usage, it is recommended that the default variant
386        /// maps to index `0`, allowing non-varianted commitments to occupy the
387        /// first slot without requiring initialization of higher variant slots.
388        type Position: PositionIndex + RuntimeEnum + Delimited + Default;
389
390        // --- Plugins ---
391
392        plugin_types! {
393            // Input carrier for lazy balance operations.
394            //
395            // Encodes operation-specific arguments used by
396            // `Self::BalanceFamily` when resolving behavior via
397            // `LazyBalanceRoot`.
398            //
399            // Supports both mutable and immutable borrow access patterns,
400            // allowing operations to express validation, mutation,
401            // and query semantics over commitments.
402            input: LazyInput<'a, Self, I>,
403
404            // Output carrier for lazy balance operations.
405            //
406            // Represents the result of executing a plugin model,
407            // including computed values, state transitions,
408            // receipts, and error outcomes.
409            //
410            // Acts as the result boundary for all operations
411            // resolved through `Self::BalanceFamily`.
412            output: LazyOutput<'a, Self, I>,
413
414
415            // Lifetime binding for plugin execution.
416            //
417            // Represents mutable and immutable borrows used by `LazyInput`
418            // and `LazyOutput` during operation execution.
419            //
420            // Ensures safe propagation of references across plugin models.
421            borrow: ['a],
422
423            // Root discriminant for lazy balance operations.
424            //
425            // Defines the complete set of operation identifiers
426            // (e.g. deposit, withdraw, resolve, query), each of
427            // which maps to a concrete plugin model in
428            // `Self::BalanceFamily`.
429            //
430            // Acts as the entry point for compile-time dispatch
431            // of behavior across all lazy balance operations.
432            root: LazyBalanceRoot,
433
434            /// The [`Lazy balance`](frame_suite::assets::LazyBalance)
435            /// [`plugin family`](frame_suite::plugins) anchor type.
436            ///
437            /// Encapsulates all lazy balance operations including validation,
438            /// mutation, resolution, and query semantics over commitments,
439            /// indexes, and pools.
440            ///
441            /// Each operation is selected via a discriminant defined in
442            /// [`LazyBalanceRoot`] and executed using [`LazyInput`] -> [`LazyOutput`]
443            /// transformation.
444            ///
445            /// Conceptually performs:
446            ///
447            /// `Operation(LazyInput) -> LazyOutput`
448            ///
449            /// where the specific behavior is determined by the plugin model
450            /// associated with each operation discriminant.
451            ///
452            /// Designed to be selectable using template plugin family models in
453            /// [`frame_plugins::balances`] or custom model defining
454            /// macros via [`frame_suite::plugins`].
455            ///
456            /// ## Pool Constraints
457            ///
458            /// Pool operations via [`CommitPool`] represent **higher-order balance
459            /// compositions** (balance over balance), and therefore impose stricter
460            /// requirements.
461            ///
462            /// These operations rely on [`Directive`] semantics, where:
463            ///
464            /// - [`Precision::Exact`] must be honored for correct value distribution
465            /// - [`Fortitude::Force`] must be enforced for deterministic execution
466            ///
467            /// These guarantees are essential for correctness of pool allocation,
468            /// redistribution, and resolution.
469            ///
470            /// If [`Config::MaxIndexEntries`] is set to `0`, index and pool
471            /// commitments are disabled, and these requirements do not apply.
472            ///
473            /// When pools are enabled, plugin models must correctly support these
474            /// semantics, otherwise operations may fail internally.
475            family: BalanceFamily,
476
477            /// Plugin family **context** for lazy balance execution.
478            ///
479            /// Supplies the execution environment required by
480            /// [`Self::BalanceFamily`] for all operations over
481            /// [`LazyInput`] -> [`LazyOutput`].
482            ///
483            /// Defines bounds, extension schemas, and unified error handling,
484            /// ensuring consistent and type-safe execution across all operations.
485            ///
486            /// Must produce a context satisfying [`LazyBalanceContext`].
487            context: BalanceContext,
488
489            // Ensures the resolved context
490            // `<BalanceContext as frame_suite::plugins::ModelContext>::Context`
491            // satisfies the required contract for lazy balance execution.
492            //
493            // Guarantees that all plugin models operate within a fully
494            // specified lazy-balance environment.
495            provides: [LazyBalanceContext],
496        }
497
498        // --- Weights ---
499
500        /// Weight information for extrinsics in this pallet.
501        type WeightInfo: WeightInfo;
502
503        // --- Constants ---
504
505        /// Maximum number of entries allowed in an index.
506        ///
507        /// Setting this value to **zero** disables index and pool commitments for this
508        /// pallet instance. A non-zero value enables hosting index and pool commitments;
509        ///
510        /// An index represents an **unmanaged pool** of digests with associated shares.
511        /// A single committed value to the index is proportionally distributed across
512        /// its entries as individual commitments.
513        ///
514        /// Since pools are constructed from indexes, this limit also bounds the maximum
515        /// number of slots a pool may contain.
516        #[pallet::constant]
517        type MaxIndexEntries: Get<u32> + Clone + Debug;
518
519        /// Maximum number of commitments allowed per digest.
520        ///
521        /// **Should be a Non-Zero Value**, else unstable behaviours should be
522        /// expected.
523        ///
524        /// Each commitment represents an individual lock against a digest.
525        /// A digest may therefore have at most this many active commitments.
526        ///
527        /// This limit applies uniformly, regardless of whether commitments
528        /// originate directly, from an index, or from a pool. Commitments
529        /// distributed from an index or pool still count as individual
530        /// commitments to the underlying digest.
531        #[pallet::constant]
532        type MaxCommits: Get<u32> + Clone + Debug;
533
534        /// Controls emission of [`Event`] via `deposit_event`.
535        ///
536        /// Recommended:
537        /// - `false` for production runtimes (to reduce overhead)
538        /// - `true` for development and mock runtimes (for testing and
539        /// observability)
540        #[pallet::constant]
541        type EmitEvents: Get<bool> + Clone + Debug;
542    }
543
544    // ===============================================================================
545    // ``````````````````````````````` COMPOSITE ENUMS ```````````````````````````````
546    // ===============================================================================
547
548    #[pallet::composite_enum]
549    /// Commitment pallet `HoldReason`, merged into the runtime composite enum.
550    pub enum HoldReason {
551        /// Native hold reason used by the Commitment pallet.
552        ///
553        /// Assets held under this reason act as a **pre-reserved balance** from which
554        /// commitments can be created efficiently. This allows frequent bonding users
555        /// to pre-hold assets once and later use them across any consumer pallet that
556        /// shares this Commitment pallet instance.
557        ///
558        /// Using this hold reason reduces repeated balance checks and locking overhead
559        /// when creating commitments.
560        PrepareForCommit,
561    }
562
563    #[pallet::composite_enum]
564    /// Commitment pallet `FreezeReason`, merged into the runtime composite enum.
565    pub enum FreezeReason {
566        /// Placeholder freeze reason used exclusively for benchmarking.
567        ///
568        /// Consumer pallets typically define their own bounded freeze-reason enums
569        /// and may not be able to reuse this type. This pallet itself does not freeze
570        /// any assets using this reason, making it suitable as a no-op placeholder
571        /// for benchmarking purposes.
572        BenchTestReason,
573    }
574
575    // ===============================================================================
576    // ``````````````````````````` GENESIS CONFIG (EMPTY) ````````````````````````````
577    // ===============================================================================
578
579    /// No-BalanceOp Genesis configuration for the pallet.
580    ///
581    /// This pallet does not currently expose any runtime-configurable parameters
582    /// at genesis. Some internal values are initialized automatically during
583    /// genesis execution.
584    ///
585    /// The genesis configuration is retained to allow future or downstream
586    /// initialization extensions without breaking compatibility.
587    #[pallet::genesis_config]
588    pub struct GenesisConfig<T: Config<I>, I: 'static = ()> {
589        /// Phantom data to satisfy generic parameters.
590        /// No user-configurable data is stored.
591        _phantom: PhantomData<(T, I)>,
592    }
593
594    impl<T: Config<I>, I: 'static> Default for GenesisConfig<T, I> {
595        /// Provides a default instance of the genesis config.
596        ///
597        /// Since there are no configurable parameters, this simply
598        /// initializes the `_phantom` field.
599        fn default() -> Self {
600            Self {
601                _phantom: Default::default(),
602            }
603        }
604    }
605
606    #[pallet::genesis_build]
607    impl<T: Config<I>, I: 'static> BuildGenesisConfig for GenesisConfig<T, I> {
608        /// Builds the initial pallet state at genesis.
609        ///
610        /// Currently, the only action is to initialize asset tracking
611        /// storages to zero:
612        /// - [`AssetToIssue`]: Tracks total asset units to be issued.
613        /// - [`AssetToReap`]: Tracks total asset units to be reaped (burned).
614        ///
615        /// No other state (indexes, digests, pools, slots, commits) is
616        /// initialized at genesis.
617        fn build(&self) {
618            let zero = AssetOf::<T, I>::zero();
619            AssetToIssue::<T, I>::put(zero); // Initialize issued assets to zero
620            AssetToReap::<T, I>::put(zero); // Initialize reaped assets to zero
621        }
622    }
623
624    // ===============================================================================
625    // ```````````````````````````````` STORAGE TYPES ````````````````````````````````
626    // ===============================================================================
627
628    /// Tracks the **total committed asset value** for a specific `CommitReason`.
629    ///
630    /// This storage sums up all committed amounts (total asset-circulation) across
631    /// **all digests and variants** for a given reason.
632    ///
633    /// Unlike digest-level or variant-level tracking, `ReasonValue` provides an **aggregated
634    /// view** of total assets committed under a specific reason, which simplifies monitoring,
635    /// accounting, and reporting of the total exposure.
636    ///
637    /// This includes assets scheduled to be issued ([`AssetToIssue`]), as newly minted
638    /// value is reflected within commitments. However, it excludes assets scheduled
639    /// to be reaped ([`AssetToReap`]), as those are pending removal and not considered
640    /// part of the effective committed value.
641    #[pallet::storage]
642    pub type ReasonValue<T: Config<I>, I: 'static = ()> =
643        StorageMap<_, Blake2_128Concat, CommitReason<T, I>, AssetOf<T, I>, OptionQuery>;
644
645    /// Maps each [`CommitReason`] and its associated [`Digest`] to the digest's information.
646    ///
647    /// ### Key Structure:
648    /// `(CommitReason, Digest) -> DigestInfo`
649    ///
650    /// This storage ensures that **every digest is always tied to a reason**. A digest cannot exist
651    /// independently without a reason, enforcing a strict relationship between commitments and their
652    /// context.
653    #[pallet::storage]
654    pub type DigestMap<T: Config<I>, I: 'static = ()> = StorageNMap<
655        _,
656        (
657            NMapKey<Blake2_128Concat, CommitReason<T, I>>,
658            NMapKey<Blake2_128Concat, Digest<T>>,
659        ),
660        DigestInfo<T, I>,
661        OptionQuery,
662    >;
663
664    /// Maps each [`Proprietor`] and the committed reason ([`CommitReason`]) to
665    /// their commitment information.
666    ///
667    /// ### Key Structure:
668    /// `(Proprietor, CommitReason) -> CommitInfo`
669    ///
670    /// This storage enforces the invariant: **one proprietor can have at most one
671    /// commitment per reason**. Each commitment is tied to a single digest, and all
672    /// variant-specific details for that commitment are stored within [`CommitInfo`].
673    ///
674    /// Unlike [`DigestMap`], which allows multiple digests per reason, `CommitMap`
675    /// enforces at most one [`CommitInfo`] per [`CommitReason`] for each proprietor
676    /// through its storage structure.
677    #[pallet::storage]
678    pub type CommitMap<T: Config<I>, I: 'static = ()> = StorageNMap<
679        _,
680        (
681            NMapKey<Blake2_128Concat, Proprietor<T>>,
682            NMapKey<Blake2_128Concat, CommitReason<T, I>>,
683        ),
684        CommitInfo<T, I>,
685        OptionQuery,
686    >;
687
688    /// Stores **index information** for a given [`CommitReason`] additionally keyed
689    /// by [`IndexDigest`].
690    ///
691    /// Each index represents a collection of digest entries, each holding a share and variant,  
692    /// functioning as an aggregation layer between individual digests and higher-level pools.  
693    ///
694    /// ### Key Structure:
695    /// `(CommitReason, IndexDigest) -> IndexInfo`
696    ///
697    /// ### Purpose:
698    /// - Provides a way to group multiple digest entries under a single reason.
699    /// - Simplifies calculations related to composite positions and share-based aggregation.
700    /// - Allows reusability and modularization of commitment logic (e.g., an index can feed
701    /// into a pool).
702    ///
703    /// ### Notes:
704    /// - An index **cannot exist without an associated reason**, enforcing a clear
705    /// ownership hierarchy.
706    /// - Each [`IndexInfo`] tracks its own [`Entries`], total `capital`, and
707    /// current `balance_of`.
708    /// - [`IndexInfo`] is used as a foundational structure for generating higher-order
709    /// constructs like [`PoolInfo`].
710    #[pallet::storage]
711    pub type IndexMap<T: Config<I>, I: 'static = ()> = StorageNMap<
712        _,
713        (
714            NMapKey<Blake2_128Concat, CommitReason<T, I>>,
715            NMapKey<Blake2_128Concat, IndexDigest<T>>,
716        ),
717        IndexInfo<T, I>,
718        OptionQuery,
719    >;
720
721    /// Stores **commit information for individual entries within an index**, scoped by both
722    /// a [`CommitReason`] and the corresponding [`Proprietor`].
723    ///
724    /// ### Key Structure:
725    /// `(CommitReason, IndexDigest, EntryDigest, Proprietor) -> Commits`
726    ///
727    /// ### Purpose:
728    /// - Tracks how much each proprietor ([`Proprietor`]) has committed to a specific entry
729    ///   under a particular index and reason.
730    /// - Enforces the hierarchical structure:
731    ///   - **Reason -> Index -> Entry -> Proprietor -> Commits**
732    /// - Each [`Commits`] encompasses one or more commitment instances made by the same proprietor.
733    ///
734    /// ### Notes:
735    /// - Each proprietor can have **only one active commit per (reason, index, entry)** combination.
736    /// - This mapping is essential for resolving commitment provenance during digest updates
737    ///   or rebalancing operations.
738    /// - Commitments stored here are "lazy" - meaning changes may not be immediately reflected
739    ///   in the underlying asset accounting until resolution occurs.
740    #[pallet::storage]
741    pub type EntryMap<T: Config<I>, I: 'static = ()> = StorageNMap<
742        _,
743        (
744            NMapKey<Blake2_128Concat, CommitReason<T, I>>,
745            NMapKey<Blake2_128Concat, IndexDigest<T>>,
746            NMapKey<Blake2_128Concat, EntryDigest<T>>,
747            NMapKey<Blake2_128Concat, Proprietor<T>>,
748        ),
749        Commits<T, I>,
750        OptionQuery,
751    >;
752
753    /// Stores **pool-level information** associated with a specific [`CommitReason`].
754    ///
755    /// ### Key Structure:
756    /// `(CommitReason, PoolDigest) -> PoolInfo`
757    ///
758    /// ### Purpose:
759    /// - Represents a **composite pool** of multiple slots, each corresponding to one or more
760    ///   underlying commitments, entries, or digests.
761    /// - Provides aggregation of collective commitment state, total capital, and operational
762    ///   parameters such as commission.
763    ///
764    /// ### Notes:
765    /// - Used together with [`PoolManager`] for management and operational control.
766    #[pallet::storage]
767    pub type PoolMap<T: Config<I>, I: 'static = ()> = StorageNMap<
768        _,
769        (
770            NMapKey<Blake2_128Concat, CommitReason<T, I>>,
771            NMapKey<Blake2_128Concat, PoolDigest<T>>,
772        ),
773        PoolInfo<T, I>,
774        OptionQuery,
775    >;
776
777    /// Tracks the manager of each pool for a given reason.
778    ///
779    /// Each pool has a designated manager (proprietor) responsible for slot management.
780    /// Pools are mutable, but their manager can be transfered/updated.
781    ///
782    /// - Keys:
783    ///   1. [`CommitReason`] - the reason/context under which the pool exists.
784    ///   2. [`PoolDigest`] - the unique identifier of the pool.
785    /// - Value: [`Proprietor`] - account managing the pool.
786    /// - Query behavior: `OptionQuery` returns `None` if the pool has no assigned manager,
787    /// effectively stale.
788    #[pallet::storage]
789    pub type PoolManager<T: Config<I>, I: 'static = ()> = StorageNMap<
790        _,
791        (
792            NMapKey<Blake2_128Concat, CommitReason<T, I>>,
793            NMapKey<Blake2_128Concat, PoolDigest<T>>,
794        ),
795        Proprietor<T>,
796        OptionQuery,
797    >;
798
799    /// Tracks the total amount of assets that are scheduled to be issued/minted.
800    ///
801    /// This value is updated whenever commitments increase a digest's balance (reward/inflation).  
802    ///
803    /// Since this pallet uses **lazy on-demand operations**, the underlying base asset pallet may not  
804    /// immediately reflect these changes. `AssetToIssue` provides an **accounting view** of the total  
805    /// assets that will soon be minted and added to the system eventually.
806    ///
807    /// This helps in auditing, ensuring the system knows **how much asset value is pending issuance**.
808    #[pallet::storage]
809    pub type AssetToIssue<T: Config<I>, I: 'static = ()> =
810        StorageValue<_, AssetOf<T, I>, ValueQuery>;
811
812    /// Tracks the total amount of assets that are scheduled to be reaped/burned.
813    ///
814    /// This value is updated whenever commitments decrease a digest's balance (penalty/deflation).  
815    ///
816    /// Similar to `AssetToIssue`, these operations are lazy and may not immediately affect the underlying  
817    /// base asset pallet. `AssetToReap` provides an **accounting view** of the total assets that will  
818    /// soon be removed eventually from circulation, ensuring proper bookkeeping and equilibrium.
819    #[pallet::storage]
820    pub type AssetToReap<T: Config<I>, I: 'static = ()> =
821        StorageValue<_, AssetOf<T, I>, ValueQuery>;
822
823    /// Snapshot storage for [`LazyBalance`] state.
824    ///
825    /// When a balance needs to capture and store its current state for 
826    /// later queries, a snapshot is recorded here.
827    ///
828    /// Used by plugin families implementing [`LazyBalanceRoot`] via
829    /// [`Config::BalanceFamily`] to support snapshot-based, lazy resolution.
830    ///
831    /// This is the concrete storage backing the
832    /// [`VirtualNMap`](frame_suite::virtuals::VirtualNMap) used by [`LazyBalance`].
833    ///
834    /// Each snapshot is indexed by:
835    /// - `Digest`: the balance identifier (linked to a commitment reason)
836    /// - `Position`: the balance position (see [`Config::Position`])
837    /// - `Time`: the snapshot time (typically a counter)
838    #[pallet::storage]
839    pub type BalanceSnapShots<T: Config<I>, I: 'static = ()> = StorageNMap<
840        _,
841        (
842            NMapKey<Blake2_128Concat, Digest<T>>,
843            NMapKey<Blake2_128Concat, T::Position>,
844            NMapKey<Blake2_128Concat, T::Time>,
845        ),
846        VirtualSnapShot<T, I>,
847        OptionQuery,
848    >;
849
850    // ===============================================================================
851    // ```````````````````````````````````` ERROR ````````````````````````````````````
852    // ===============================================================================
853
854    #[pallet::error]
855    /// Commitment Pallet Errors
856    pub enum Error<T, I = ()> {
857        /// Digest not found in the system.
858        /// Cannot determine whether it is a direct digest, index, or pool.
859        DigestNotFoundToDetermine,
860
861        /// Insufficient funds for the requested operation.
862        ///
863        /// Consider forcing the operation or reducing the given
864        /// asset value.
865        InsufficientFunds,
866
867        /// The proprietor already holds a commitment for the reason.
868        ///
869        /// Only a single commit should exist for a reason. Utilize
870        /// indexes and pools in case of distributing multiple commitments.
871        CommitAlreadyExists,
872
873        /// Failed to generate a digest for a given source or
874        /// index/pool structure.
875        CannotGenerateDigest,
876
877        /// A commitment with zero value is invalid.
878        ///
879        /// Commitments are economic in nature and must not be used
880        /// as zero-value markers.
881        MarkerCommitNotAllowed,
882
883        /// The proprietor's commit was not found in the system.
884        CommitNotFound,
885
886        /// The specified direct-digest was not found in the system.
887        DigestNotFound,
888
889        /// The specified index digest was not found in the system.
890        IndexNotFound,
891
892        /// The specified entry digest is not found in the index.
893        EntryOfIndexNotFound,
894
895        /// The specified index digest already exists for the given reason.
896        IndexDigestTaken,
897
898        /// Attempted to remove/reap a direct-digest that still holds funds.
899        DigestHasFunds,
900
901        /// Attempted to remove/reap an index that still holds funds.
902        IndexHasFunds,
903
904        /// The specified pool digest was not found in the system.
905        PoolNotFound,
906
907        /// The specified slot digest is not found in the pool.
908        SlotOfPoolNotFound,
909
910        /// The Manager for the specified Pool is not found.
911        /// A Pool is expected to have a manager at all times.
912        PoolManagerNotFound,
913
914        /// The specified pool digest already exists for the given reason.
915        PoolDigestTaken,
916
917        /// Attempted to remove/reap a pool that still holds funds.
918        PoolHasFunds,
919
920        /// The direct-digest balance specialized for the given commit-variant (position)
921        /// is not initialized or found to be.
922        ///
923        /// It can only be initialized during deposit operations. Not while being queried.
924        DigestVariantBalanceNotFound,
925
926        /// Cannot include more asset value for issuing as the total issue balance exhausted.
927        ///
928        /// - For non-issuance assets: migrate to a larger scalar type immediately.
929        /// - For issuance assets: conduct an internal audit on the asset (unexpected behavior).
930        MaxAssetIssued,
931
932        /// Cannot include more asset value for reaping as the total reapable balance exhausted.
933        ///
934        /// - For non-issuance assets: migrate to a larger scalar type immediately.
935        /// - For issuance assets: conduct an internal audit on the asset (unexpected behavior).
936        MaxAssetReaped,
937
938        /// Shares given for an index's entry or a pool's slot cannot be zero.
939        /// Marker entries or slots are invalid in the system.
940        ShareCannotBeZero,
941
942        /// Capital cannot be zero when creating an index or pool.  
943        ///
944        /// Correct behavior:
945        /// - Index/Pool must have `sum(shares) == capital`.
946        /// - Entries/Slots must not have empty shares.
947        CapitalCannotBeZero,
948
949        /// Share value exceeded capital when creating an index or pool.  
950        ///
951        /// Correct behavior: every entry/slot must satisfy `share <= capital`.
952        ShareGreaterThanCapital,
953
954        /// Asset Issued and Minting to the underlying fungible system
955        /// detected inconsistency.
956        MintingMoreThanIssued,
957
958        /// Asset To Reap and Burning to the underlying fungible system
959        /// detected inconsistency.
960        BurningMoreThanReapable,
961
962        /// Indicates that the operation expects the proprietor's existing reserves
963        /// (held funds) to be released in order to proceed, typically to be
964        /// re-deposited under the commitment hold reason (`PrepareForCommit`).
965        ///
966        /// If this is not possible, the operation may attempt to proceed by forcing
967        /// withdrawal from liquid funds, potentially risking account closure if
968        /// enforced by the asset provider or best-effort methods.
969        ExpectsHoldWithdrawal,
970
971        /// Asset units overflown when commit-reserve balance is added with liquidly held funds.
972        ///
973        /// - For non-issuance assets: migrate to a larger scalar type immediately.
974        /// - For issuance assets: conduct an internal audit on the asset (unexpected behavior).
975        ReserveLiquidOverflow,
976
977        /// Indicates that the operation expects the proprietor's existing reserves
978        /// (held funds) and freezes (locked funds) to be released in order to proceed,
979        /// typically to be re-deposited under the commitment hold reason (`PrepareForCommit`).
980        ///
981        /// Since, the operation cannot attempt to proceed by forcing withdrawal from just
982        /// liquid funds, as its insufficient.
983        ///
984        /// Try to reduce provided asset amount or do operation based on best-effort possible
985        /// only.
986        ExpectsFreezeAndHoldWithdrawal,
987
988        /// The Model of Digest constructed is invalid, since its possibly a
989        /// compile time marker.
990        InvalidDigestModel,
991
992        /// Digest commit-variant (positional) balances are exhausted
993        /// or at maximum capacity.
994        ///
995        /// This reveals that the [`Config::Position`]'s trait `PositionIndex`
996        /// is implemented not as-per its defined invariants.
997        VariantsExhausted,
998
999        /// Share value is too small (underflow) to produce a valid
1000        /// factor when calculating `share/capital`.
1001        ///
1002        /// Possible resolutions:
1003        /// - Increase the fixed-point precision of `Bias` in future upgrades.
1004        /// - Increase the index entry or pool slot's share value and retry.
1005        TooSmallShareValue,
1006
1007        /// Deposit derivation overflowed during index or pool deposit operation.  
1008        ///
1009        /// Occurs when an excessively high `share/capital` ratio, multiplied by
1010        /// a balance value, overflows the scalar.
1011        DepositDeriveOverflowed,
1012
1013        /// `share/capital` ratio produced a factor greater than 1.
1014        ///
1015        /// This results in errors which may indicate invariants are broken.
1016        ///  - `sum(shares) <= capital` or,
1017        ///  - `current_share > capital`
1018        FactorGreaterThanOne,
1019
1020        /// Index total balance (deposits-only) has reached its maximum top-level capacity.
1021        ///
1022        /// - For non-issuance assets: migrate to a larger scalar type immediately.
1023        /// - For issuance assets: conduct an internal audit on the asset (unexpected behavior).
1024        MaxIndexCapacityReached,
1025
1026        /// Proprietor doesn't hold commits for the specified entry of an index.
1027        ///
1028        /// Indicates that the prorprietor haven't committed to the index at all.
1029        CommitNotFoundForEntry,
1030
1031        /// Proprietor doesn't hold commits for the specified slot of a pool.
1032        ///
1033        /// Indicates that the prorprietor haven't committed to the pool at all.
1034        CommitNotFoundForSlot,
1035
1036        /// Proprietor doesn't hold commits for the specified pool.
1037        CommitNotFoundForPool,
1038
1039        /// Accumulating total deposit for direct and indirect digests (index/pools)
1040        /// has overflowed the provided asset type.
1041        ///
1042        /// - For non-issuance assets: migrate to a larger scalar type immediately.
1043        /// - For issuance assets: conduct an internal audit on the asset (unexpected behavior).
1044        DepositAccumulationExhausted,
1045
1046        /// The digest for the specified entry of index was not found in the list of digests.
1047        ///
1048        /// The digest list represents the underlying direct digests for which
1049        /// commitments have been made.
1050        EntryDigestNotFound,
1051
1052        /// Accumulating total withdrawal for direct or indirect digests (pool/index)
1053        /// has overflowed the provided asset type.
1054        ///
1055        /// - For non-issuance assets: migrate to a larger scalar type immediately.
1056        /// - For issuance assets: conduct an internal audit on the asset (unexpected behavior).
1057        WithdrawAccumulationExhausted,
1058
1059        /// Accumulating total real-time values of all commit instances has overflowed the
1060        /// provided asset type.
1061        ///
1062        /// - For non-issuance assets: migrate to a larger scalar type immediately.
1063        /// - For issuance assets: conduct an internal audit on the asset (unexpected behavior).
1064        CommitsAccumulationExhausted,
1065
1066        /// Indicates that a pool was recovered without being released i.e., empty.
1067        ReleasePoolToRecover,
1068
1069        /// The specified pool requires atleast a single slot, with valid shares to carry
1070        /// the operation.
1071        EmptySlotsNotAllowed,
1072
1073        /// Capital shares underflowed during index or pool creation/modification.
1074        CapitalUnderflowed,
1075
1076        /// Capital shares overflowed during index or pool creation/modification.
1077        CapitalOverflowed,
1078
1079        /// Maximum number of slots in a pool is reached.
1080        MaxSlotsReached,
1081
1082        /// The digest for the specified slot of pool was not found in the list of digests.
1083        ///
1084        /// The digest list represents the underlying direct digests for which
1085        /// commitments have been made.
1086        SlotDigestNotFound,
1087
1088        /// Max Commits per reason (as per commitment invariant for a single digest model) is exhausted.
1089        ///
1090        /// Try resolving and committing a new value instead to the same digest model.
1091        MaxCommitsReached,
1092
1093        /// Maximum number of entries in an index is reached.
1094        MaxEntriesReached,
1095
1096        /// Reason exists (as its compile-time proved) but contains no commitments.
1097        CommitsNotFoundForReason,
1098
1099        /// Index logic is broken, since withdrawing index balance is only for higher
1100        /// level queries and deposits (principal) is only withdrawn.
1101        IndexBalanceUnderflow,
1102
1103        /// There exists an invalid commit-variant [`Config::Position`] via invalid
1104        /// trait implementation of [`PositionIndex`].
1105        ///
1106        /// Indicates the position cannot be derived from the positional index or
1107        /// that the index doesn't pertain to the actual position.
1108        InvalidCommitVariantIndex,
1109
1110        /// This pallet instance's maximum commitment per proprietor configuration is
1111        /// set at zero, effectively restricts the commitment-pallet's operations.
1112        ///
1113        /// Require [`Config::MaxCommits`] to be set to more than zero to operate.
1114        ZeroMaxCommits,
1115
1116        /// This pallet instance's index and pool support is halted via setting
1117        /// [`Config::MaxIndexEntries`] to zero and tried attempting to create an
1118        /// index (and in future pools via indexes).
1119        ///
1120        /// If indexes and pools are required [`Config::MaxIndexEntries`] should be set
1121        /// to more than zero.
1122        TriedCreatingHaltedIndexes,
1123
1124        /// Attempted to create index via empty entries. Indexes require
1125        /// valid entries, with non-zero share values.
1126        ///
1127        /// It is to note that pools are created via existing
1128        /// indexes and pool slots are mutated via valid individual entries.
1129        EmptyEntriesNotAllowed,
1130
1131        /// A proprietor's commit is found to be empty without any commit-instance
1132        /// which is an invalid state.
1133        EmptyCommitsNotAllowed,
1134
1135        /// Attempted to insert a duplicate entry into an index.
1136        DuplicateEntry,
1137
1138        /// Attempted to insert a duplicate slot into a pool.
1139        DuplicateSlot,
1140
1141        /// For reasons unknown, the commit-instance construction has failed.
1142        CommitConstructionFailed,
1143
1144        /// The required entry's commit for proprietor is not found while raising
1145        /// commit.
1146        EntryCommitNotFound,
1147
1148        /// The balance plugin was corrupted
1149        CorruptedPlugin,
1150
1151        /// During Division Scaling the value underflowed, which is fallible only
1152        /// if the accuracy (denominator) is invalid.
1153        DerivedLessThanZeroValue,
1154
1155        /// Withdraw derivation overflowed during index or pool deposit operation.  
1156        ///
1157        /// Occurs when an excessively high `share/capital` ratio, multiplied by
1158        /// a balance value, overflows the scalar.
1159        WithdrawalOverflow,
1160
1161        /// Derived Commission Amount Overflowed the Scalar Asset Type.
1162        CommissionOverflow,
1163
1164        /// Pools are unsupported when the underlying balance plugin cannot
1165        /// guarantee precision-exact and forceful (unbounded) operations.
1166        ///
1167        /// Pools maintain their own top-level balance and rely on exact value
1168        /// propagation and forced execution to remain consistent. If the plugin
1169        /// enforces limits even under forced execution, these requirements cannot
1170        /// be satisfied, making pool semantics invalid.
1171        PoolUnsupported,
1172
1173        /// Direct Digest Minting exceeded allowed limits by the underlying
1174        /// Lazy Balance Plugin Family for the current operation.
1175        MintingOffLimits,
1176
1177        /// Direct Digest Reaping (Burning) exceeded allowed limits by the
1178        /// underlying Lazy Balance Plugin Family for the current operation.
1179        ReapingOffLimits,
1180
1181        /// Placing a new commitment has exceeded allowed deposit limits by the
1182        /// underlying Lazy Balance Plugin Family for the current operation.
1183        PlacingOffLimits,
1184
1185        /// Increasing (raising) an existing commitment has exceeded allowed
1186        /// raising (deposit) limits by the underlying Lazy Balance Plugin Family
1187        /// for the current operation.
1188        RaisingOffLimits,
1189    }
1190
1191    // ===============================================================================
1192    // ```````````````````````````````````` EVENTS ```````````````````````````````````
1193    // ===============================================================================
1194
1195    #[pallet::event]
1196    #[pallet::generate_deposit(pub(super) fn deposit_event)]
1197    pub enum Event<T: Config<I>, I: 'static = ()> {
1198        /// Emitted when a proprietor places a new commit on a
1199        /// digest with a specific variant.
1200        CommitPlaced {
1201            who: Proprietor<T>,
1202            reason: CommitReason<T, I>,
1203            #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
1204            model: DigestVariant<T, I>,
1205            #[cfg(not(any(feature = "dev", feature = "runtime-benchmarks")))]
1206            digest: Digest<T>,
1207            value: AssetOf<T, I>,
1208            variant: T::Position,
1209        },
1210
1211        /// Emitted when an existing commit for a digest is
1212        /// increased or raised.
1213        CommitRaised {
1214            who: Proprietor<T>,
1215            reason: CommitReason<T, I>,
1216            #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
1217            model: DigestVariant<T, I>,
1218            #[cfg(not(any(feature = "dev", feature = "runtime-benchmarks")))]
1219            digest: Digest<T>,
1220            value: AssetOf<T, I>,
1221        },
1222
1223        /// Emitted when a commit is resolved (finalized/withdrawn)
1224        /// for a digest.
1225        CommitResolved {
1226            who: Proprietor<T>,
1227            reason: CommitReason<T, I>,
1228            #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
1229            model: DigestVariant<T, I>,
1230            #[cfg(not(any(feature = "dev", feature = "runtime-benchmarks")))]
1231            digest: Digest<T>,
1232            value: AssetOf<T, I>,
1233        },
1234
1235        /// Emitted when querying the current committed value
1236        /// for a specific digest and reason.
1237        CommitValue {
1238            #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
1239            model: DigestVariant<T, I>,
1240            #[cfg(not(any(feature = "dev", feature = "runtime-benchmarks")))]
1241            digest: Digest<T>,
1242            reason: CommitReason<T, I>,
1243            value: AssetOf<T, I>,
1244        },
1245
1246        /// Emitted when the effective value of the digest
1247        /// variant is updated.
1248        DigestInfo {
1249            digest: Digest<T>,
1250            reason: CommitReason<T, I>,
1251            value: AssetOf<T, I>,
1252            variant: T::Position,
1253        },
1254
1255        /// Emitted when a direct-digest is reaped (removed)
1256        /// after all commitments are cleared from it.
1257        ///
1258        /// `dust` represents unclaimable dead asset value.
1259        DigestReaped {
1260            digest: Digest<T>,
1261            reason: CommitReason<T, I>,
1262            dust: AssetOf<T, I>,
1263        },
1264
1265        /// Emitted when a new index of variants is initialized.
1266        /// Contains all entries (digests, sharesa and variant) of the index.
1267        IndexInitialized {
1268            index_of: IndexDigest<T>,
1269            reason: CommitReason<T, I>,
1270            #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
1271            entries: Vec<(EntryDigest<T>, T::Shares, T::Position)>,
1272        },
1273
1274        /// Emitted when querying the total value of an index
1275        /// for a specific proprietor.
1276        IndexValue {
1277            index_of: IndexDigest<T>,
1278            reason: CommitReason<T, I>,
1279            value: AssetOf<T, I>,
1280        },
1281
1282        /// Emitted when querying the value of a specific entry
1283        /// within an index.
1284        IndexEntryValue {
1285            index_of: IndexDigest<T>,
1286            reason: CommitReason<T, I>,
1287            entry_of: Digest<T>,
1288            value: AssetOf<T, I>,
1289        },
1290
1291        /// Emitted when querying the values of all entries
1292        /// within an index.
1293        IndexEntriesValue {
1294            index_of: IndexDigest<T>,
1295            reason: CommitReason<T, I>,
1296            entries: Vec<(EntryDigest<T>, AssetOf<T, I>)>,
1297        },
1298
1299        /// Emitted when a index is reaped (removed)
1300        /// after all entry commitments are cleared from it.
1301        IndexReaped {
1302            index_of: IndexDigest<T>,
1303            reason: CommitReason<T, I>,
1304        },
1305
1306        /// Emitted when a pool's manager is set or updated.
1307        /// The manager is responsible for managing slots
1308        /// and internal pool operations.
1309        PoolManager {
1310            pool_of: PoolDigest<T>,
1311            reason: CommitReason<T, I>,
1312            manager: Proprietor<T>,
1313        },
1314
1315        /// Emitted when a new pool is initialized from an index.
1316        /// Includes the commission rate and initial slots with their
1317        /// associated shares and variants.
1318        PoolInitialized {
1319            pool_of: PoolDigest<T>,
1320            reason: CommitReason<T, I>,
1321            commission: T::Commission,
1322            #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
1323            slots: Vec<(SlotDigest<T>, T::Shares, T::Position)>,
1324        },
1325
1326        /// Emitted when a slot within a pool has its variant updated or a
1327        /// new slot is added.
1328        PoolSlot {
1329            pool_of: PoolDigest<T>,
1330            reason: CommitReason<T, I>,
1331            slot_of: SlotDigest<T>,
1332            variant: T::Position,
1333            shares: T::Shares,
1334        },
1335
1336        /// Emitted when querying the total value of a pool
1337        /// for a specific proprietor.
1338        PoolValue {
1339            pool_of: PoolDigest<T>,
1340            reason: CommitReason<T, I>,
1341            value: AssetOf<T, I>,
1342        },
1343
1344        /// Emitted when querying the value of a specific slot
1345        /// within a pool.
1346        PoolSlotValue {
1347            pool_of: PoolDigest<T>,
1348            reason: CommitReason<T, I>,
1349            slot_of: SlotDigest<T>,
1350            value: AssetOf<T, I>,
1351        },
1352
1353        /// Emitted when querying the values of all slots
1354        /// within a pool.
1355        PoolSlotsValue {
1356            pool_of: PoolDigest<T>,
1357            reason: CommitReason<T, I>,
1358            slots: Vec<(SlotDigest<T>, AssetOf<T, I>)>,
1359        },
1360
1361        /// Emitted when querying or updating the commission
1362        /// rate of a pool.
1363        PoolCommission {
1364            pool_of: PoolDigest<T>,
1365            reason: CommitReason<T, I>,
1366            commission: T::Commission,
1367        },
1368
1369        /// Emitted when funds are deposited into reserve (held balance)
1370        /// under the prepare-for-commit reason.
1371        ReserveDeposited {
1372            amount: AssetOf<T, I>,
1373            total_on_hold: AssetOf<T, I>,
1374        },
1375
1376        /// Emitted when reserved funds are withdrawn back to free balance.
1377        ReserveWithdrawn {
1378            amount: AssetOf<T, I>,
1379            total_on_hold: AssetOf<T, I>,
1380        },
1381
1382        /// Emitted when a pool is reaped (removed)
1383        /// after all slot commitments are cleared from it.
1384        PoolReaped {
1385            pool_of: PoolDigest<T>,
1386            reason: CommitReason<T, I>,
1387        },
1388
1389        /// Emitted when a pool slot is removed due to its shares being zero.
1390        PoolSlotRemoved {
1391            pool_of: PoolDigest<T>,
1392            reason: CommitReason<T, I>,
1393            slot_of: SlotDigest<T>,
1394            variant: T::Position,
1395        },
1396
1397        /// Emitted when determining the digest model
1398        /// (Direct, Index, or Pool) for a given digest.
1399        DigestModel { digest: DigestVariant<T, I> },
1400
1401        /// Emitted when the total assets pending issuance are queried.
1402        AssetIssuable { asset: AssetOf<T, I> },
1403
1404        /// Emitted when the total assets pending reaping are queried.
1405        AssetReapable { asset: AssetOf<T, I> },
1406
1407        /// Emitted when the total committed value for a reason is queried.
1408        ReasonValuation {
1409            reason: CommitReason<T, I>,
1410            value: AssetOf<T, I>,
1411        },
1412    }
1413
1414    // ===============================================================================
1415    // `````````````````````````````````` EXTRINSICS `````````````````````````````````
1416    // ===============================================================================
1417
1418    #[pallet::call]
1419    impl<T: Config<I>, I: 'static> Pallet<T, I> {
1420        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1421        // ```````````````````````````````` DISPATCHABLES ````````````````````````````````
1422        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1423
1424        /// Deposits funds from free balance into reserve for future commitments.
1425        ///
1426        /// Locks the specified amount under the [`HoldReason::PrepareForCommit`] hold reason.
1427        ///
1428        /// These funds remain available for placing or raising commitments until explicitly
1429        /// withdrawn via [`Pallet::withdraw_reserve`].
1430        ///
1431        /// ### Behavior
1432        /// - If `precision` is `BestEffort`, deposits the maximum available balance when insufficient
1433        /// - If `precision` is `Exact`, requires exact amount or fails with [`Error::InsufficientFunds`]
1434        ///
1435        /// ### Emits
1436        /// [`Event::ReserveDeposited`]: Contains the amount deposited and the total balance on hold.
1437        #[pallet::call_index(0)]
1438        #[pallet::weight(T::WeightInfo::deposit_reserve())]
1439        pub fn deposit_reserve(
1440            origin: OriginFor<T>,
1441            amount: AssetOf<T, I>,
1442            precision: PrecisionWrapper,
1443        ) -> DispatchResult {
1444            let caller = ensure_signed(origin)?;
1445            let hold_reason: T::AssetHold = HoldReason::PrepareForCommit.into();
1446            let reducible_balance = <T as Config<I>>::Asset::reducible_balance(
1447                &caller,
1448                Preservation::Preserve,
1449                Fortitude::Polite,
1450            );
1451            if reducible_balance < amount {
1452                if precision == PrecisionWrapper::Exact {
1453                    return Err(Error::<T, I>::InsufficientFunds.into());
1454                }
1455                <T as Config<I>>::Asset::decrease_balance(
1456                    &caller,
1457                    reducible_balance,
1458                    Precision::Exact,
1459                    Preservation::Preserve,
1460                    Fortitude::Polite,
1461                )?;
1462                <T as Config<I>>::Asset::increase_balance_on_hold(
1463                    &hold_reason,
1464                    &caller,
1465                    reducible_balance,
1466                    Precision::Exact,
1467                )?;
1468                let total_on_hold = <T as Config<I>>::Asset::balance_on_hold(&hold_reason, &caller);
1469                Self::deposit_event(Event::<T, I>::ReserveDeposited {
1470                    amount: reducible_balance,
1471                    total_on_hold: total_on_hold,
1472                });
1473                return Ok(());
1474            }
1475            <T as Config<I>>::Asset::decrease_balance(
1476                &caller,
1477                amount,
1478                Precision::Exact,
1479                Preservation::Preserve,
1480                Fortitude::Force,
1481            )?;
1482            <T as Config<I>>::Asset::increase_balance_on_hold(
1483                &hold_reason,
1484                &caller,
1485                amount,
1486                Precision::Exact,
1487            )?;
1488            let total_on_hold = <T as Config<I>>::Asset::balance_on_hold(&hold_reason, &caller);
1489            Self::deposit_event(Event::<T, I>::ReserveDeposited {
1490                amount: amount,
1491                total_on_hold: total_on_hold,
1492            });
1493            Ok(())
1494        }
1495
1496        /// Withdraws reserved funds back to the caller's free balance.
1497        ///
1498        /// Releases funds held under the [`HoldReason::PrepareForCommit`] reason and
1499        /// returns them to the caller's free balance.
1500        ///
1501        /// ### Behavior
1502        /// - If `amount` is `None`, all reserved funds under the hold reason are released.
1503        /// - If `amount` is `Some(value)`, only the specified amount is released,
1504        ///   leaving any remaining reserved funds intact.
1505        ///
1506        /// This call decreases the balance on hold with `Precision::Exact` and
1507        /// increases the caller's free balance by the same amount.
1508        ///
1509        /// Returns [`Error::InsufficientFunds`] if a specific `amount` is provided
1510        /// and the held balance is less than the requested amount.
1511        ///
1512        /// ### Emits
1513        /// [`Event::ReserveWithdrawn`]: Contains the amount withdrawn and the amount balance on hold.
1514        #[pallet::call_index(1)]
1515        #[pallet::weight(T::WeightInfo::withdraw_reserve()
1516            .max(T::WeightInfo::withdraw_reserve_partial())
1517        )]
1518        pub fn withdraw_reserve(
1519            origin: OriginFor<T>,
1520            amount: Option<AssetOf<T, I>>,
1521        ) -> DispatchResult {
1522            let caller = ensure_signed(origin)?;
1523            let hold_reason: T::AssetHold = HoldReason::PrepareForCommit.into();
1524            let hold_balance = <T as Config<I>>::Asset::balance_on_hold(&hold_reason, &caller);
1525            match amount {
1526                None => {
1527                    <T as Config<I>>::Asset::decrease_balance_on_hold(
1528                        &hold_reason,
1529                        &caller,
1530                        hold_balance,
1531                        Precision::Exact,
1532                    )?;
1533                    <T as Config<I>>::Asset::increase_balance(
1534                        &caller,
1535                        hold_balance,
1536                        Precision::Exact,
1537                    )?;
1538                    let total_on_hold =
1539                        <T as Config<I>>::Asset::balance_on_hold(&hold_reason, &caller);
1540                    Self::deposit_event(Event::<T, I>::ReserveWithdrawn {
1541                        amount: hold_balance,
1542                        total_on_hold: total_on_hold,
1543                    });
1544                }
1545                Some(amount) => {
1546                    if hold_balance < amount {
1547                        return Err(Error::<T, I>::InsufficientFunds.into());
1548                    }
1549                    <T as Config<I>>::Asset::decrease_balance_on_hold(
1550                        &hold_reason,
1551                        &caller,
1552                        amount,
1553                        Precision::Exact,
1554                    )?;
1555                    <T as Config<I>>::Asset::increase_balance(&caller, amount, Precision::Exact)?;
1556                    let total_on_hold =
1557                        <T as Config<I>>::Asset::balance_on_hold(&hold_reason, &caller);
1558                    Self::deposit_event(Event::<T, I>::ReserveWithdrawn {
1559                        amount: amount,
1560                        total_on_hold: total_on_hold,
1561                    });
1562                }
1563            }
1564            Ok(())
1565        }
1566
1567        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1568        // ````````````````````````````````` INSPECTORS ``````````````````````````````````
1569        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1570
1571        /// Queries the current value of a proprietor's commitment.
1572        ///
1573        /// Returns the real-time committed amount for the caller's active commitment under
1574        /// the specified reason. This value reflects any changes to the underlying digest
1575        /// value since the commitment was placed, as digest values can be updated.
1576        ///
1577        /// ### Emits
1578        /// [`Event::CommitValue`]: Contains the current commitment value
1579        #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
1580        #[pallet::call_index(2)]
1581        #[pallet::weight(T::WeightInfo::inspect_commit_value())]
1582        pub fn inspect_commit_value(
1583            origin: OriginFor<T>,
1584            reason: CommitReason<T, I>,
1585        ) -> DispatchResult {
1586            let caller = ensure_signed(origin)?;
1587            let commit_value = Self::query_commit_value(caller.clone(), reason)?;
1588            let digest_model = Self::resolve_digest_model_for(caller.clone(), reason)?;
1589            Self::deposit_event(Event::<T, I>::CommitValue {
1590                model: digest_model,
1591                reason: reason,
1592                value: commit_value,
1593            });
1594            Ok(())
1595        }
1596
1597        /// Determines the digest model classification for a given digest.
1598        ///
1599        /// Queries whether the specified digest exists as a direct digest, index, or pool
1600        /// under the given reason. The result is wrapped in a [`DigestVariant`] and emitted
1601        /// via the [`Event::DigestModel`] event.
1602        ///
1603        /// ### Emits
1604        /// [`Event::DigestModel`]: Contains the resolved digest variant
1605        #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
1606        #[pallet::call_index(3)]
1607        #[pallet::weight(T::WeightInfo::inspect_digest_model())]
1608        pub fn inspect_digest_model(
1609            origin: OriginFor<T>,
1610            digest: Digest<T>,
1611            reason: CommitReason<T, I>,
1612        ) -> DispatchResult {
1613            ensure_signed(origin)?;
1614            let digest_variant = Self::resolve_digest_model(digest, reason)?;
1615            Self::deposit_event(Event::<T, I>::DigestModel {
1616                digest: digest_variant,
1617            });
1618            Ok(())
1619        }
1620
1621        /// Queries the total value of a proprietor's commitment to an index.
1622        ///
1623        /// Aggregates all entry values within the index, weighted by their respective shares,
1624        /// to compute the proprietor's total exposure. Each entry's value is computed in
1625        /// real-time, reflecting any changes since commitment.
1626        ///
1627        /// ### Emits
1628        /// [`Event::IndexValue`]: Contains the total index commitment value
1629        #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
1630        #[pallet::call_index(4)]
1631        #[pallet::weight(T::WeightInfo::inspect_index_value())]
1632        pub fn inspect_index_value(
1633            origin: OriginFor<T>,
1634            reason: CommitReason<T, I>,
1635            index_of: IndexDigest<T>,
1636        ) -> DispatchResult {
1637            let caller = ensure_signed(origin)?;
1638            let index_value =
1639                Self::query_index_value_for(caller, reason, index_of.clone())?;
1640            Self::deposit_event(Event::<T, I>::IndexValue {
1641                index_of: index_of,
1642                reason: reason,
1643                value: index_value,
1644            });
1645            Ok(())
1646        }
1647
1648        /// Queries the values of all entries within an index.
1649        ///
1650        /// Returns a vector of (entry_digest, value) pairs showing how the proprietor's
1651        /// total index commitment is distributed across all entries. Each value is weighted
1652        /// by its entry's share and computed in real-time.
1653        ///
1654        /// ### Emits
1655        /// [`Event::IndexEntriesValue`]: Contains the vector of entry-value pairs
1656        #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
1657        #[pallet::call_index(5)]
1658        #[pallet::weight(T::WeightInfo::inspect_entries_value())]
1659        pub fn inspect_entries_value(
1660            origin: OriginFor<T>,
1661            reason: CommitReason<T, I>,
1662            index_of: IndexDigest<T>,
1663        ) -> DispatchResult {
1664            let caller = ensure_signed(origin)?;
1665            let entries_value =
1666                Self::query_entries_value_for(caller, reason, index_of.clone())?;
1667            Self::deposit_event(Event::<T, I>::IndexEntriesValue {
1668                index_of: index_of,
1669                reason: reason,
1670                entries: entries_value,
1671            });
1672            Ok(())
1673        }
1674
1675        /// Queries the value of a specific entry within an index.
1676        ///
1677        /// Returns the portion of the proprietor's index commitment allocated to this
1678        /// particular entry, weighted by its share within the index. The value is computed
1679        /// in real-time, reflecting any changes to the underlying entry digest since commitment.
1680        ///
1681        /// ### Emits
1682        /// [`Event::IndexEntryValue`]: Contains the entry's commitment value
1683        #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
1684        #[pallet::call_index(6)]
1685        #[pallet::weight(T::WeightInfo::inspect_entry_value())]
1686        pub fn inspect_entry_value(
1687            origin: OriginFor<T>,
1688            reason: CommitReason<T, I>,
1689            index_of: IndexDigest<T>,
1690            entry_of: Digest<T>,
1691        ) -> DispatchResult {
1692            let caller = ensure_signed(origin)?;
1693            let entry_value = Self::query_entry_value_for(
1694                caller,
1695                reason,
1696                index_of.clone(),
1697                entry_of.clone(),
1698            )?;
1699            Self::deposit_event(Event::<T, I>::IndexEntryValue {
1700                index_of: index_of,
1701                reason: reason,
1702                entry_of: entry_of,
1703                value: entry_value,
1704            });
1705            Ok(())
1706        }
1707
1708        /// Queries the total value of a proprietor's commitment to a pool.
1709        ///
1710        /// Aggregates all slot values within the pool, weighted by their respective shares
1711        /// and accounting for the pool's commission rate.
1712        ///
1713        /// ### Emits
1714        /// [`Event::PoolValue`]: Contains the total pool commitment value
1715        #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
1716        #[pallet::call_index(7)]
1717        #[pallet::weight(T::WeightInfo::inspect_pool_value())]
1718        pub fn inspect_pool_value(
1719            origin: OriginFor<T>,
1720            reason: CommitReason<T, I>,
1721            pool_of: PoolDigest<T>,
1722        ) -> DispatchResult {
1723            let caller = ensure_signed(origin)?;
1724            let pool_value = Self::query_pool_value_for(caller, reason, pool_of.clone())?;
1725            Self::deposit_event(Event::<T, I>::PoolValue {
1726                pool_of: pool_of,
1727                reason: reason,
1728                value: pool_value,
1729            });
1730            Ok(())
1731        }
1732
1733        /// Query the values of all slots within a pool.
1734        ///
1735        /// Returns a vector of (slot_digest, value) pairs showing how the proprietor's
1736        /// total pool commitment is distributed across all pool slots.
1737        ///
1738        /// ### Emits
1739        /// [`Event::PoolSlotsValue`]: Contains the vector of slot-value pairs
1740        #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
1741        #[pallet::call_index(8)]
1742        #[pallet::weight(T::WeightInfo::inspect_slots_value())]
1743        pub fn inspect_slots_value(
1744            origin: OriginFor<T>,
1745            reason: CommitReason<T, I>,
1746            pool_of: PoolDigest<T>,
1747        ) -> DispatchResult {
1748            let caller = ensure_signed(origin)?;
1749            let slots_value = Self::query_slots_value_for(caller, reason, pool_of.clone())?;
1750            Self::deposit_event(Event::<T, I>::PoolSlotsValue {
1751                pool_of: pool_of,
1752                reason: reason,
1753                slots: slots_value,
1754            });
1755            Ok(())
1756        }
1757
1758        /// Queries the value of a specific slot within a pool.
1759        ///
1760        /// Returns the portion of the proprietor's pool commitment allocated to this
1761        /// particular slot, weighted by its share within the pool.
1762        ///
1763        /// ### Emits
1764        /// [`Event::PoolSlotValue`]: Contains the slot's commitment value
1765        #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
1766        #[pallet::call_index(9)]
1767        #[pallet::weight(T::WeightInfo::inspect_slot_value())]
1768        pub fn inspect_slot_value(
1769            origin: OriginFor<T>,
1770            reason: CommitReason<T, I>,
1771            pool_of: PoolDigest<T>,
1772            slot_of: Digest<T>,
1773        ) -> DispatchResult {
1774            let caller = ensure_signed(origin)?;
1775            let slot_value = Self::query_slot_value_for(
1776                caller,
1777                reason,
1778                pool_of.clone(),
1779                slot_of.clone(),
1780            )?;
1781            Self::deposit_event(Event::<T, I>::PoolSlotValue {
1782                pool_of: pool_of,
1783                reason: reason,
1784                slot_of: slot_of,
1785                value: slot_value,
1786            });
1787            Ok(())
1788        }
1789
1790        /// Queries a pool's commission rate.
1791        ///
1792        /// Returns the percentage of withdrawals that the pool manager
1793        /// collects as commission. Commission rates are immutable after pool creation to
1794        /// protect depositors' economic expectations.
1795        ///
1796        /// ### Emits
1797        /// [`Event::PoolCommission`]: Contains the commission rate.
1798        #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
1799        #[pallet::call_index(10)]
1800        #[pallet::weight(T::WeightInfo::inspect_pool_commission())]
1801        pub fn inspect_pool_commission(
1802            origin: OriginFor<T>,
1803            reason: CommitReason<T, I>,
1804            pool_of: PoolDigest<T>,
1805        ) -> DispatchResult {
1806            ensure_signed(origin)?;
1807            let commission = Self::query_pool_commission(reason, pool_of.clone())?;
1808            Self::deposit_event(Event::<T, I>::PoolCommission {
1809                pool_of: pool_of,
1810                reason: reason,
1811                commission: commission,
1812            });
1813            Ok(())
1814        }
1815
1816        /// Queries a pool's manager account.
1817        ///
1818        /// Returns the account responsible for managing the pool's operations, including
1819        /// slot configuration, share adjustments, and commission collection.
1820        ///
1821        /// ### Emits
1822        /// - [`Event::PoolManager`]: Contains the manager's account identifier
1823        #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
1824        #[pallet::call_index(11)]
1825        #[pallet::weight(T::WeightInfo::inspect_pool_manager())]
1826        pub fn inspect_pool_manager(
1827            origin: OriginFor<T>,
1828            reason: CommitReason<T, I>,
1829            pool_of: PoolDigest<T>,
1830        ) -> DispatchResult {
1831            ensure_signed(origin)?;
1832            let manager = Self::query_pool_manager(reason, pool_of.clone())?;
1833            Self::deposit_event(Event::<T, I>::PoolManager {
1834                pool_of: pool_of,
1835                reason: reason,
1836                manager: manager,
1837            });
1838            Ok(())
1839        }
1840
1841        /// Queries the total amount of assets currently recorded as pending issuance.
1842        ///
1843        /// This value reflects the amount tracked in [`AssetToIssue`], representing
1844        /// assets that have been accounted for as "to be minted" but may not yet be
1845        /// reflected in the underlying asset system due to lazy execution.
1846        ///
1847        /// The returned value is purely an **accounting snapshot** and does not
1848        /// guarantee that minting has already occurred.
1849        ///
1850        /// ### Emits
1851        /// [`Event::AssetIssuable`]: Contains the total pending issuance amount.
1852        #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
1853        #[pallet::call_index(12)]
1854        #[pallet::weight(T::WeightInfo::inspect_asset_to_issue())]
1855        pub fn inspect_asset_to_issue(origin: OriginFor<T>) -> DispatchResult {
1856            ensure_signed(origin)?;
1857            let asset = AssetToIssue::<T, I>::get();
1858            Self::deposit_event(Event::<T, I>::AssetIssuable { asset });
1859            Ok(())
1860        }
1861
1862        /// Queries the total amount of assets currently recorded as pending reaping.
1863        ///
1864        /// This value reflects the amount tracked in [`AssetToReap`], representing
1865        /// assets that have been accounted for as "to be removed" but may not yet be
1866        /// reflected in the underlying asset system due to lazy execution.
1867        ///
1868        /// The returned value is purely an **accounting snapshot** and does not
1869        /// guarantee that reaping (burn/removal) has already occurred.
1870        ///
1871        /// ### Emits
1872        /// [`Event::AssetReapable`]: Contains the total pending reaping amount.
1873        #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
1874        #[pallet::call_index(13)]
1875        #[pallet::weight(T::WeightInfo::inspect_asset_to_reap())]
1876        pub fn inspect_asset_to_reap(origin: OriginFor<T>) -> DispatchResult {
1877            ensure_signed(origin)?;
1878            let asset = AssetToReap::<T, I>::get();
1879            Self::deposit_event(Event::<T, I>::AssetReapable { asset });
1880            Ok(())
1881        }
1882
1883        /// Queries the total committed asset value for the specified [`CommitReason`].
1884        ///
1885        /// This value is read directly from [`ReasonValue`] and represents the
1886        /// aggregated committed amount across all digests and variants associated
1887        /// with the given reason.
1888        ///
1889        /// The returned value:
1890        /// - **Includes** assets that are accounted for in commitments (including those pending issuance)
1891        /// - **Excludes** assets pending reaping, as they are not considered part of active committed value
1892        ///
1893        /// If no value exists for the given reason, the storage returns `Zero`,
1894        /// which is emitted as-is in the event.
1895        ///
1896        /// ### Emits
1897        /// [`Event::ReasonValuation`]: Contains the queried committed value for the reason.
1898        #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
1899        #[pallet::call_index(14)]
1900        #[pallet::weight(T::WeightInfo::inspect_reason_value())]
1901        pub fn inspect_reason_value(
1902            origin: OriginFor<T>,
1903            reason: CommitReason<T, I>,
1904        ) -> DispatchResult {
1905            ensure_signed(origin)?;
1906            let value = ReasonValue::<T, I>::get(reason).unwrap_or(Zero::zero());
1907            Self::deposit_event(Event::<T, I>::ReasonValuation { reason, value });
1908            Ok(())
1909        }
1910    }
1911
1912    // ===============================================================================
1913    // ````````````````````````````````` PUBLIC APIS `````````````````````````````````
1914    // ===============================================================================
1915
1916    impl<T: Config<I>, I: 'static> Pallet<T, I> {
1917        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1918        // ``````````````````````````````````` GENERAL ```````````````````````````````````
1919        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1920
1921        /// Resolves the [`DigestVariant`] for a given digest and commit reason.
1922        ///
1923        /// The returned variant defines how the digest is interpreted
1924        /// within the commitment system (Direct, Index or Pool).
1925        pub fn resolve_digest_model(
1926            digest: Digest<T>,
1927            reason: CommitReason<T, I>,
1928        ) -> Result<DigestVariant<T, I>, DispatchError> {
1929            let digest_variant =
1930                <Pallet<T, I> as DigestModel<Proprietor<T>>>::determine_digest(&digest, &reason)?;
1931            Ok(digest_variant)
1932        }
1933
1934        /// Resolves the digest variant of caller's active commitment under `reason`.
1935        ///
1936        /// Retrieves the commitment digest currently associated with the `caller`
1937        /// for the specified `reason`, then determines its classification within
1938        /// the commitment system.
1939        ///
1940        /// The returned [`DigestVariant`] indicates whether the commitment
1941        /// is a Direct, Index, or Pool type.
1942        pub fn resolve_digest_model_for(
1943            caller: T::AccountId,
1944            reason: CommitReason<T, I>,
1945        ) -> Result<DigestVariant<T, I>, DispatchError> {
1946            let digest = Self::get_commit_digest(&caller, &reason)?;
1947            let digest_variant =
1948                <Pallet<T, I> as DigestModel<Proprietor<T>>>::determine_digest(&digest, &reason)?;
1949            Ok(digest_variant)
1950        }
1951
1952        /// Returns the total value committed by `caller` under
1953        /// a `reason`.
1954        ///
1955        /// This represents the caller's full active commitment
1956        /// for the specified reason.
1957        pub fn query_commit_value(
1958            caller: T::AccountId,
1959            reason: CommitReason<T, I>,
1960        ) -> Result<AssetOf<T, I>, DispatchError> {
1961            let commit_value =
1962                <Pallet<T, I> as Commitment<Proprietor<T>>>::get_commit_value(&caller, &reason)?;
1963            Ok(commit_value)
1964        }
1965
1966        /// Returns the total amount of assets currently recorded as pending issuance.
1967        ///
1968        /// The returned value is an accounting value only. It does not guarantee
1969        /// that the underlying asset system has already minted the assets.
1970        pub fn query_asset_to_issue() -> AssetOf<T, I> {
1971            AssetToIssue::<T, I>::get()
1972        }
1973
1974        /// Returns the total amount of assets currently recorded as pending reaping.
1975        ///
1976        /// The returned value is an accounting value only. It does not guarantee
1977        /// that the underlying asset system has already reaped, burned, or removed
1978        /// the assets.
1979        pub fn query_asset_to_reap() -> AssetOf<T, I> {
1980            AssetToReap::<T, I>::get()
1981        }
1982
1983        /// Returns the total committed asset value recorded for the given `reason`.
1984        ///
1985        /// This value is read directly from [`ReasonValue`] and represents the
1986        /// aggregated committed value across all digests and variants associated
1987        /// with the specified reason.
1988        ///
1989        /// Returns `Zero` if no committed value is currently stored for the reason.
1990        pub fn query_reason_value(reason: CommitReason<T, I>) -> AssetOf<T, I> {
1991            ReasonValue::<T, I>::get(reason).unwrap_or(Zero::zero())
1992        }
1993
1994        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1995        // ```````````````````````````````` INDEX (GLOBAL) ```````````````````````````````
1996        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1997
1998        /// Returns the total value committed to an `index`
1999        /// under a `reason`.
2000        ///
2001        /// The returned value is the sum of all entry
2002        /// commitments from all accounts within the index.
2003        pub fn query_index_value(
2004            reason: CommitReason<T, I>,
2005            index: IndexDigest<T>,
2006        ) -> Result<AssetOf<T, I>, DispatchError> {
2007            let index_value =
2008                <Pallet<T, I> as CommitIndex<Proprietor<T>>>::get_index_value(&reason, &index)?;
2009            Ok(index_value)
2010        }
2011
2012        /// Returns the total committed value of every `entry`
2013        /// within an `index` under a `reason`.
2014        ///
2015        /// Each tuple contains:
2016        /// - Entry digest
2017        /// - Aggregated value committed to that entry
2018        pub fn query_entries_value(
2019            reason: CommitReason<T, I>,
2020            index: IndexDigest<T>,
2021        ) -> Result<Vec<(Digest<T>, AssetOf<T, I>)>, DispatchError> {
2022            let entries_value =
2023                <Pallet<T, I> as CommitIndex<Proprietor<T>>>::get_entries_value(&reason, &index)?;
2024            Ok(entries_value)
2025        }
2026
2027        /// Returns the total value committed to an `entry` digest
2028        /// within an `index` under a `reason`.
2029        ///
2030        /// The returned value is aggregated across all accounts
2031        /// that have committed to the entry.
2032        pub fn query_entry_value(
2033            reason: CommitReason<T, I>,
2034            index: IndexDigest<T>,
2035            entry: EntryDigest<T>,
2036        ) -> Result<AssetOf<T, I>, DispatchError> {
2037            let entry_value = <Pallet<T, I> as CommitIndex<Proprietor<T>>>::get_entry_value(
2038                &reason, &index, &entry,
2039            )?;
2040            Ok(entry_value)
2041        }
2042
2043        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2044        // `````````````````````````````` INDEX (PER CALLER) `````````````````````````````
2045        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2046
2047        /// Returns the total value committed by `caller`
2048        /// to an `index` under a `reason`.
2049        pub fn query_index_value_for(
2050            caller: T::AccountId,
2051            reason: CommitReason<T, I>,
2052            index: IndexDigest<T>,
2053        ) -> Result<AssetOf<T, I>, DispatchError> {
2054            let index_value = <Pallet<T, I> as CommitIndex<Proprietor<T>>>::get_index_value_for(
2055                &caller, &reason, &index,
2056            )?;
2057            Ok(index_value)
2058        }
2059
2060        /// Returns the caller's committed value for each `entry`
2061        /// within an `index` under `reason`.
2062        ///
2063        /// Each tuple contains:
2064        /// - Entry digest
2065        /// - Value committed by the caller to that entry
2066        pub fn query_entries_value_for(
2067            caller: T::AccountId,
2068            reason: CommitReason<T, I>,
2069            index: IndexDigest<T>,
2070        ) -> Result<Vec<(Digest<T>, AssetOf<T, I>)>, DispatchError> {
2071            let entries_value =
2072                <Pallet<T, I> as CommitIndex<Proprietor<T>>>::get_entries_value_for(
2073                    &caller, &reason, &index,
2074                )?;
2075            Ok(entries_value)
2076        }
2077
2078        /// Returns the value committed by `caller`
2079        /// to an `entry` within an `index` under a `reason`.
2080        pub fn query_entry_value_for(
2081            caller: T::AccountId,
2082            reason: CommitReason<T, I>,
2083            index: IndexDigest<T>,
2084            entry: EntryDigest<T>,
2085        ) -> Result<AssetOf<T, I>, DispatchError> {
2086            let entry_value = <Pallet<T, I> as CommitIndex<Proprietor<T>>>::get_entry_value_for(
2087                &caller, &reason, &index, &entry,
2088            )?;
2089            Ok(entry_value)
2090        }
2091
2092        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2093        // ```````````````````````````````` POOL (GLOBAL) ````````````````````````````````
2094        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2095
2096        /// Returns the total value committed to a `pool` under a `reason`.
2097        ///
2098        /// The returned value is the sum of all slot commitments
2099        /// from all accounts within the pool.
2100        pub fn query_pool_value(
2101            reason: CommitReason<T, I>,
2102            pool: PoolDigest<T>,
2103        ) -> Result<AssetOf<T, I>, DispatchError> {
2104            let pool_of =
2105                <Pallet<T, I> as CommitPool<Proprietor<T>>>::get_pool_value(&reason, &pool)?;
2106            Ok(pool_of)
2107        }
2108
2109        /// Returns the total committed value of every `slot`
2110        /// within a `pool` under a `reason`.
2111        ///
2112        /// Each tuple contains:
2113        /// - Slot digest
2114        /// - Aggregated value committed to that slot
2115        pub fn query_slots_value(
2116            reason: CommitReason<T, I>,
2117            pool: PoolDigest<T>,
2118        ) -> Result<Vec<(Digest<T>, AssetOf<T, I>)>, DispatchError> {
2119            let slots_value =
2120                <Pallet<T, I> as CommitPool<Proprietor<T>>>::get_slots_value(&reason, &pool)?;
2121            Ok(slots_value)
2122        }
2123
2124        /// Returns the total value committed to a `slot`
2125        /// within a `pool` under a `reason`.
2126        ///
2127        /// The returned value is aggregated across all accounts
2128        /// that have committed to the slot.
2129        pub fn query_slot_value(
2130            reason: CommitReason<T, I>,
2131            pool: PoolDigest<T>,
2132            slot: SlotDigest<T>,
2133        ) -> Result<AssetOf<T, I>, DispatchError> {
2134            let slot_value =
2135                <Pallet<T, I> as CommitPool<Proprietor<T>>>::get_slot_value(&reason, &pool, &slot)?;
2136            Ok(slot_value)
2137        }
2138
2139        /// Returns the commission rate configured for a `pool` under a `reason`.
2140        pub fn query_pool_commission(
2141            reason: CommitReason<T, I>,
2142            pool: PoolDigest<T>,
2143        ) -> Result<T::Commission, DispatchError> {
2144            let commission =
2145                <Pallet<T, I> as CommitPool<Proprietor<T>>>::get_commission(&reason, &pool)?;
2146            Ok(commission)
2147        }
2148
2149        /// Returns the current manager account for a `pool` under a  `reason`.
2150        ///
2151        /// The returned account is the authority responsible for
2152        /// managing the pool's configuration and slot definitions.
2153        pub fn query_pool_manager(
2154            reason: CommitReason<T, I>,
2155            pool: PoolDigest<T>,
2156        ) -> Result<T::AccountId, DispatchError> {
2157            let manager = <Pallet<T, I> as CommitPool<Proprietor<T>>>::get_manager(&reason, &pool)?;
2158            Ok(manager)
2159        }
2160
2161        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2162        // `````````````````````````````` INDEX (PER CALLER) `````````````````````````````
2163        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2164
2165        /// Returns the total value committed by `caller`
2166        /// to a `pool` under a `reason`.
2167        pub fn query_pool_value_for(
2168            caller: T::AccountId,
2169            reason: CommitReason<T, I>,
2170            pool: PoolDigest<T>,
2171        ) -> Result<AssetOf<T, I>, DispatchError> {
2172            let pool_value = <Pallet<T, I> as CommitPool<Proprietor<T>>>::get_pool_value_for(
2173                &caller, &reason, &pool,
2174            )?;
2175            Ok(pool_value)
2176        }
2177
2178        /// Returns the caller's committed value for each `slot`
2179        /// within a `pool` under a `reason`.
2180        ///
2181        /// Each tuple contains:
2182        /// - Slot digest
2183        /// - Value committed by the caller to that slot
2184        pub fn query_slots_value_for(
2185            caller: T::AccountId,
2186            reason: CommitReason<T, I>,
2187            pool_of: PoolDigest<T>,
2188        ) -> Result<Vec<(Digest<T>, AssetOf<T, I>)>, DispatchError> {
2189            let slots_value = <Pallet<T, I> as CommitPool<Proprietor<T>>>::get_slots_value_for(
2190                &caller, &reason, &pool_of,
2191            )?;
2192            Ok(slots_value)
2193        }
2194
2195        /// Returns the value committed by a `caller`
2196        /// to  a `slot` within a `pool` under a `reason`.
2197        pub fn query_slot_value_for(
2198            caller: T::AccountId,
2199            reason: CommitReason<T, I>,
2200            pool: PoolDigest<T>,
2201            slot: SlotDigest<T>,
2202        ) -> Result<AssetOf<T, I>, DispatchError> {
2203            let slot_value = <Pallet<T, I> as CommitPool<Proprietor<T>>>::get_slot_value_for(
2204                &caller, &reason, &pool, &slot,
2205            )?;
2206            Ok(slot_value)
2207        }
2208    }
2209}
2210
2211// ===============================================================================
2212// `````````````````````````````````` API TESTS ``````````````````````````````````
2213// ===============================================================================
2214
2215#[cfg(test)]
2216/// Unit tests for Extrinsics and Public APIs of [`pallet_commitment`](crate).
2217mod ext_tests {
2218        
2219    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2220    // ``````````````````````````````````` IMPORTS ```````````````````````````````````
2221    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2222
2223    // --- Local crate imports ---
2224    use crate::{mock::*, types::PrecisionWrapper};
2225
2226    // --- FRAME Suite ---
2227    use frame_suite::{commitment::*, misc::Directive};
2228
2229    // --- FRAME Support ---
2230    use frame_support::{
2231        assert_err, assert_ok,
2232        pallet_prelude::DispatchError,
2233        traits::{
2234            fungible::{Inspect, InspectHold},
2235            tokens::{Fortitude, Precision},
2236        },
2237    };
2238
2239    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2240    // ``````````````````````````````` EXTRINSIC TESTS ```````````````````````````````
2241    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2242
2243    #[test]
2244    fn deposit_reserve_success_exact() {
2245        commit_test_ext().execute_with(|| {
2246            System::set_block_number(2);
2247            initiate_key_and_set_balance_and_hold(ALICE, LARGE_VALUE, LARGE_VALUE).unwrap();
2248            Pallet::deposit_reserve(RuntimeOrigin::signed(ALICE), 10, PrecisionWrapper::Exact)
2249                .unwrap();
2250            // balance check
2251            let actual_balance = AssetOf::balance(&ALICE);
2252            let actual_hold_balance = AssetOf::balance_on_hold(&PREPARE_FOR_COMMIT, &ALICE);
2253            let expected_balance = 10;
2254            let expected_hold_balance = 30;
2255            assert_eq!(actual_balance, expected_balance);
2256            assert_eq!(actual_hold_balance, expected_hold_balance);
2257            System::assert_last_event(
2258                Event::ReserveDeposited {
2259                    amount: 10,
2260                    total_on_hold: actual_hold_balance,
2261                }
2262                .into(),
2263            );
2264        })
2265    }
2266
2267    #[test]
2268    fn deposit_reserve_success_best_efforts() {
2269        commit_test_ext().execute_with(|| {
2270            System::set_block_number(2);
2271            initiate_key_and_set_balance_and_hold(ALICE, LARGE_VALUE, LARGE_VALUE).unwrap();
2272            Pallet::deposit_reserve(
2273                RuntimeOrigin::signed(ALICE),
2274                25,
2275                PrecisionWrapper::BestEffort,
2276            )
2277            .unwrap();
2278            // balance check
2279            let actual_balance = AssetOf::balance(&ALICE);
2280            let actual_hold_balance = AssetOf::balance_on_hold(&PREPARE_FOR_COMMIT, &ALICE);
2281            let expected_balance = 0;
2282            let expected_hold_balance = 40;
2283            assert_eq!(actual_balance, expected_balance);
2284            assert_eq!(actual_hold_balance, expected_hold_balance);
2285            System::assert_last_event(
2286                Event::ReserveDeposited {
2287                    amount: 20,
2288                    total_on_hold: actual_hold_balance,
2289                }
2290                .into(),
2291            );
2292        })
2293    }
2294
2295    #[test]
2296    fn deposit_reserve_err_insufficient_funds() {
2297        commit_test_ext().execute_with(|| {
2298            initiate_key_and_set_balance_and_hold(ALICE, LARGE_VALUE, LARGE_VALUE).unwrap();
2299            assert_err!(
2300                Pallet::deposit_reserve(
2301                    RuntimeOrigin::signed(ALICE),
2302                    25,
2303                    PrecisionWrapper::Exact
2304                ),
2305                Error::InsufficientFunds
2306            );
2307        })
2308    }
2309
2310    #[test]
2311    fn withdraw_reserve_success() {
2312        commit_test_ext().execute_with(|| {
2313            System::set_block_number(2);
2314            initiate_key_and_set_balance_and_hold(ALICE, LARGE_VALUE, LARGE_VALUE).unwrap();
2315            Pallet::withdraw_reserve(RuntimeOrigin::signed(ALICE), None).unwrap();
2316            // balance check
2317            let actual_balance = AssetOf::balance(&ALICE);
2318            let actual_hold_balance = AssetOf::balance_on_hold(&PREPARE_FOR_COMMIT, &ALICE);
2319            let expected_balance = 40;
2320            let expected_hold_balance = 0;
2321            assert_eq!(actual_balance, expected_balance);
2322            assert_eq!(actual_hold_balance, expected_hold_balance);
2323            System::assert_last_event(
2324                Event::ReserveWithdrawn {
2325                    amount: 20,
2326                    total_on_hold: 0,
2327                }
2328                .into(),
2329            );
2330        })
2331    }
2332
2333    #[test]
2334    fn withdraw_reserve_err_insufficient_funds() {
2335        commit_test_ext().execute_with(|| {
2336            initiate_key_and_set_balance_and_hold(ALICE, LARGE_VALUE, LARGE_VALUE).unwrap();
2337            assert_err!(
2338                Pallet::withdraw_reserve(RuntimeOrigin::signed(ALICE), Some(25)),
2339                Error::InsufficientFunds
2340            );
2341        })
2342    }
2343
2344    #[test]
2345    fn withdraw_reserve_partial_success() {
2346        commit_test_ext().execute_with(|| {
2347            System::set_block_number(2);
2348            initiate_key_and_set_balance_and_hold(ALICE, LARGE_VALUE, LARGE_VALUE).unwrap();
2349            Pallet::withdraw_reserve(RuntimeOrigin::signed(ALICE), Some(15)).unwrap();
2350            // balance check
2351            let actual_balance = AssetOf::balance(&ALICE);
2352            let actual_hold_balance = AssetOf::balance_on_hold(&PREPARE_FOR_COMMIT, &ALICE);
2353            let expected_balance = 35;
2354            let expected_hold_balance = 5;
2355            assert_eq!(actual_balance, expected_balance);
2356            assert_eq!(actual_hold_balance, expected_hold_balance);
2357            System::assert_last_event(
2358                Event::ReserveWithdrawn {
2359                    amount: 15,
2360                    total_on_hold: 5,
2361                }
2362                .into(),
2363            );
2364        })
2365    }
2366
2367    #[cfg(feature = "dev")]
2368    #[test]
2369    fn inspect_digest_model_direct_success() {
2370        commit_test_ext().execute_with(|| {
2371            initiate_key_and_set_balance_and_hold(ALICE, LARGE_VALUE, LARGE_VALUE).unwrap();
2372            System::set_block_number(2);
2373            Pallet::place_commit(
2374                &ALICE,
2375                &STAKING,
2376                &ALPHA_DIGEST,
2377                15,
2378                &Directive::new(Precision::Exact, Fortitude::Force),
2379            )
2380            .unwrap();
2381            Pallet::inspect_digest_model(RuntimeOrigin::signed(ALICE), ALPHA_DIGEST, STAKING)
2382                .unwrap();
2383            System::assert_last_event(
2384                Event::DigestModel {
2385                    digest: DigestVariant::Direct(ALPHA_DIGEST),
2386                }
2387                .into(),
2388            );
2389        })
2390    }
2391
2392    #[cfg(feature = "dev")]
2393    #[test]
2394    fn inspect_digest_model_index_success() {
2395        commit_test_ext().execute_with(|| {
2396            initiate_key_and_set_balance_and_hold(ALICE, LARGE_VALUE, LARGE_VALUE).unwrap();
2397            System::set_block_number(2);
2398            initiate_digest_with_default_balance(STAKING, ALPHA_ENTRY_DIGEST).unwrap();
2399            System::set_block_number(4);
2400            prepare_and_initiate_index(
2401                ALICE,
2402                STAKING,
2403                &[(ALPHA_ENTRY_DIGEST, 40)],
2404                ALPHA_INDEX_DIGEST,
2405            )
2406            .unwrap();
2407            assert_ok!(Pallet::index_exists(&STAKING, &ALPHA_INDEX_DIGEST));
2408            System::set_block_number(6);
2409            // Place commit to an index
2410            let commit_amount = 20;
2411            Pallet::place_commit(
2412                &ALICE,
2413                &STAKING,
2414                &ALPHA_INDEX_DIGEST,
2415                commit_amount,
2416                &Directive::new(Precision::BestEffort, Fortitude::Polite),
2417            )
2418            .unwrap();
2419            Pallet::inspect_digest_model(
2420                RuntimeOrigin::signed(ALICE),
2421                ALPHA_INDEX_DIGEST,
2422                STAKING,
2423            )
2424            .unwrap();
2425            System::assert_last_event(
2426                Event::DigestModel {
2427                    digest: DigestVariant::Index(ALPHA_INDEX_DIGEST),
2428                }
2429                .into(),
2430            );
2431        })
2432    }
2433
2434    #[cfg(feature = "dev")]
2435    #[test]
2436    fn inspect_digest_model_pool_success() {
2437        commit_test_ext().execute_with(|| {
2438            initiate_key_and_set_balance_and_hold(ALICE, LARGE_VALUE, LARGE_VALUE).unwrap();
2439            initiate_key_and_set_balance_and_hold(BOB, LARGE_VALUE, 30).unwrap();
2440            System::set_block_number(2);
2441            Pallet::place_commit(
2442                &ALICE,
2443                &STAKING,
2444                &ALPHA_ENTRY_DIGEST,
2445                STANDARD_VALUE,
2446                &Directive::new(Precision::BestEffort, Fortitude::Polite),
2447            )
2448            .unwrap();
2449            let entries = vec![(ALPHA_ENTRY_DIGEST, 40)];
2450            prepare_and_initiate_pool(
2451                BOB,
2452                STAKING,
2453                &entries,
2454                ALPHA_INDEX_DIGEST,
2455                ALPHA_POOL_DIGEST,
2456                COMMISSION_ZERO,
2457            )
2458            .unwrap();
2459            let commit_amount = 25;
2460            System::set_block_number(6);
2461            Pallet::place_commit(
2462                &BOB,
2463                &STAKING,
2464                &ALPHA_POOL_DIGEST,
2465                commit_amount,
2466                &Directive::new(Precision::BestEffort, Fortitude::Polite),
2467            )
2468            .unwrap();
2469            Pallet::inspect_digest_model(
2470                RuntimeOrigin::signed(ALICE),
2471                ALPHA_POOL_DIGEST,
2472                STAKING,
2473            )
2474            .unwrap();
2475            System::assert_last_event(
2476                Event::DigestModel {
2477                    digest: DigestVariant::Pool(ALPHA_POOL_DIGEST),
2478                }
2479                .into(),
2480            );
2481        })
2482    }
2483
2484    #[cfg(feature = "dev")]
2485    #[test]
2486    fn inspect_digest_model_err_bad_origin() {
2487        commit_test_ext().execute_with(|| {
2488            initiate_key_and_set_balance_and_hold(ALICE, LARGE_VALUE, LARGE_VALUE).unwrap();
2489            System::set_block_number(2);
2490            Pallet::place_commit(
2491                &ALICE,
2492                &STAKING,
2493                &ALPHA_DIGEST,
2494                15,
2495                &Directive::new(Precision::Exact, Fortitude::Force),
2496            )
2497            .unwrap();
2498            assert_err!(
2499                Pallet::inspect_digest_model(RuntimeOrigin::root(), ALPHA_DIGEST, STAKING,),
2500                DispatchError::BadOrigin
2501            );
2502        })
2503    }
2504
2505    #[cfg(feature = "dev")]
2506    #[test]
2507    fn inspect_commit_value_for_direct_success() {
2508        commit_test_ext().execute_with(|| {
2509            initiate_key_and_set_balance_and_hold(ALICE, LARGE_VALUE, STANDARD_VALUE).unwrap();
2510            System::set_block_number(2);
2511            let commit_amount = 10;
2512            Pallet::place_commit(
2513                &ALICE,
2514                &STAKING,
2515                &ALPHA_DIGEST,
2516                commit_amount,
2517                &Directive::new(Precision::BestEffort, Fortitude::Force),
2518            )
2519            .unwrap();
2520            // fetch the commit value
2521            Pallet::inspect_commit_value(RuntimeOrigin::signed(ALICE), STAKING).unwrap();
2522            // verify if the data in the event emmission is correct
2523            System::assert_last_event(
2524                Event::CommitValue {
2525                    model: DigestVariant::Direct(ALPHA_DIGEST),
2526                    reason: STAKING,
2527                    value: commit_amount,
2528                }
2529                .into(),
2530            );
2531        })
2532    }
2533
2534    #[cfg(feature = "dev")]
2535    #[test]
2536    fn inspect_commit_value_for_index_success() {
2537        commit_test_ext().execute_with(|| {
2538            initiate_key_and_set_balance_and_hold(ALICE, LARGE_VALUE, LARGE_VALUE).unwrap();
2539            System::set_block_number(2);
2540            initiate_digest_with_default_balance(STAKING, ALPHA_ENTRY_DIGEST).unwrap();
2541            System::set_block_number(4);
2542            prepare_and_initiate_index(
2543                ALICE,
2544                STAKING,
2545                &[(ALPHA_ENTRY_DIGEST, 40)],
2546                ALPHA_INDEX_DIGEST,
2547            )
2548            .unwrap();
2549            assert_ok!(Pallet::index_exists(&STAKING, &ALPHA_INDEX_DIGEST));
2550            System::set_block_number(6);
2551            // Place commit to an index
2552            let commit_amount = 20;
2553            Pallet::place_commit(
2554                &ALICE,
2555                &STAKING,
2556                &ALPHA_INDEX_DIGEST,
2557                commit_amount,
2558                &Directive::new(Precision::BestEffort, Fortitude::Polite),
2559            )
2560            .unwrap();
2561            // fetch the commit value
2562            Pallet::inspect_commit_value(RuntimeOrigin::signed(ALICE), STAKING).unwrap();
2563            // verify if the data in the event emmission is correct
2564            System::assert_last_event(
2565                Event::CommitValue {
2566                    model: DigestVariant::Index(ALPHA_INDEX_DIGEST),
2567                    reason: STAKING,
2568                    value: commit_amount,
2569                }
2570                .into(),
2571            );
2572        })
2573    }
2574
2575    #[cfg(feature = "dev")]
2576    #[test]
2577    fn inspect_commit_value_for_pool_success() {
2578        commit_test_ext().execute_with(|| {
2579            initiate_key_and_set_balance_and_hold(ALICE, LARGE_VALUE, LARGE_VALUE).unwrap();
2580            initiate_key_and_set_balance_and_hold(BOB, LARGE_VALUE, 30).unwrap();
2581            System::set_block_number(2);
2582            Pallet::place_commit(
2583                &ALICE,
2584                &STAKING,
2585                &ALPHA_ENTRY_DIGEST,
2586                STANDARD_VALUE,
2587                &Directive::new(Precision::BestEffort, Fortitude::Polite),
2588            )
2589            .unwrap();
2590            let entries = vec![(ALPHA_ENTRY_DIGEST, 40)];
2591            prepare_and_initiate_pool(
2592                BOB,
2593                STAKING,
2594                &entries,
2595                ALPHA_INDEX_DIGEST,
2596                ALPHA_POOL_DIGEST,
2597                COMMISSION_ZERO,
2598            )
2599            .unwrap();
2600            let commit_amount = 25;
2601            System::set_block_number(6);
2602            Pallet::place_commit(
2603                &BOB,
2604                &STAKING,
2605                &ALPHA_POOL_DIGEST,
2606                commit_amount,
2607                &Directive::new(Precision::BestEffort, Fortitude::Polite),
2608            )
2609            .unwrap();
2610            // fetch the commit value
2611            Pallet::inspect_commit_value(RuntimeOrigin::signed(BOB), STAKING).unwrap();
2612            // verify if the data in the event emmission is correct
2613            System::assert_last_event(
2614                Event::CommitValue {
2615                    model: DigestVariant::Pool(ALPHA_POOL_DIGEST),
2616                    reason: STAKING,
2617                    value: commit_amount,
2618                }
2619                .into(),
2620            );
2621        })
2622    }
2623
2624    #[cfg(feature = "dev")]
2625    #[test]
2626    fn inspect_commit_value_err_bad_origin() {
2627        commit_test_ext().execute_with(|| {
2628            initiate_key_and_set_balance_and_hold(ALICE, LARGE_VALUE, LARGE_VALUE).unwrap();
2629            System::set_block_number(2);
2630            Pallet::place_commit(
2631                &ALICE,
2632                &STAKING,
2633                &ALPHA_DIGEST,
2634                15,
2635                &Directive::new(Precision::Exact, Fortitude::Force),
2636            )
2637            .unwrap();
2638            assert_err!(
2639                Pallet::inspect_commit_value(RuntimeOrigin::root(), STAKING),
2640                DispatchError::BadOrigin
2641            );
2642        })
2643    }
2644
2645    #[cfg(feature = "dev")]
2646    #[test]
2647    fn inspect_index_value_success() {
2648        commit_test_ext().execute_with(|| {
2649            initiate_key_and_set_balance_and_hold(ALICE, LARGE_VALUE, 40).unwrap();
2650            initiate_digest_with_default_balance(STAKING, ALPHA_ENTRY_DIGEST).unwrap();
2651            initiate_digest_with_default_balance(STAKING, BETA_ENTRY_DIGEST).unwrap();
2652            prepare_and_initiate_index(
2653                ALICE,
2654                STAKING,
2655                &[(ALPHA_ENTRY_DIGEST, 40), (BETA_ENTRY_DIGEST, 60)],
2656                ALPHA_INDEX_DIGEST,
2657            )
2658            .unwrap();
2659            System::set_block_number(2);
2660            Pallet::place_commit(
2661                &ALICE,
2662                &STAKING,
2663                &ALPHA_INDEX_DIGEST,
2664                35,
2665                &Directive::new(Precision::BestEffort, Fortitude::Polite),
2666            )
2667            .unwrap();
2668
2669            Pallet::inspect_index_value(
2670                RuntimeOrigin::signed(ALICE),
2671                STAKING,
2672                ALPHA_INDEX_DIGEST,
2673            )
2674            .unwrap();
2675
2676            System::assert_last_event(
2677                Event::IndexValue {
2678                    index_of: ALPHA_INDEX_DIGEST,
2679                    reason: STAKING,
2680                    value: 35,
2681                }
2682                .into(),
2683            );
2684        })
2685    }
2686
2687    #[cfg(feature = "dev")]
2688    #[test]
2689    fn inspect_entry_value_success() {
2690        commit_test_ext().execute_with(|| {
2691            initiate_key_and_set_balance_and_hold(ALICE, LARGE_VALUE, 40).unwrap();
2692            initiate_digest_with_default_balance(STAKING, ALPHA_ENTRY_DIGEST).unwrap();
2693            initiate_digest_with_default_balance(STAKING, BETA_ENTRY_DIGEST).unwrap();
2694            prepare_and_initiate_index(
2695                ALICE,
2696                STAKING,
2697                &[(ALPHA_ENTRY_DIGEST, 40), (BETA_ENTRY_DIGEST, 60)],
2698                ALPHA_INDEX_DIGEST,
2699            )
2700            .unwrap();
2701            System::set_block_number(2);
2702            Pallet::place_commit(
2703                &ALICE,
2704                &STAKING,
2705                &ALPHA_INDEX_DIGEST,
2706                35,
2707                &Directive::new(Precision::BestEffort, Fortitude::Polite),
2708            )
2709            .unwrap();
2710
2711            Pallet::inspect_entry_value(
2712                RuntimeOrigin::signed(ALICE),
2713                STAKING,
2714                ALPHA_INDEX_DIGEST,
2715                ALPHA_ENTRY_DIGEST,
2716            )
2717            .unwrap();
2718
2719            System::assert_last_event(
2720                Event::IndexEntryValue {
2721                    index_of: ALPHA_INDEX_DIGEST,
2722                    reason: STAKING,
2723                    entry_of: ALPHA_ENTRY_DIGEST,
2724                    value: 14,
2725                }
2726                .into(),
2727            );
2728
2729            Pallet::inspect_entry_value(
2730                RuntimeOrigin::signed(ALICE),
2731                STAKING,
2732                ALPHA_INDEX_DIGEST,
2733                BETA_ENTRY_DIGEST,
2734            )
2735            .unwrap();
2736
2737            System::assert_last_event(
2738                Event::IndexEntryValue {
2739                    index_of: ALPHA_INDEX_DIGEST,
2740                    reason: STAKING,
2741                    entry_of: BETA_ENTRY_DIGEST,
2742                    value: 21,
2743                }
2744                .into(),
2745            );
2746        })
2747    }
2748
2749    #[cfg(feature = "dev")]
2750    #[test]
2751    fn inspect_entries_value_success() {
2752        commit_test_ext().execute_with(|| {
2753            initiate_key_and_set_balance_and_hold(ALICE, LARGE_VALUE, 40).unwrap();
2754            initiate_digest_with_default_balance(STAKING, ALPHA_ENTRY_DIGEST).unwrap();
2755            initiate_digest_with_default_balance(STAKING, BETA_ENTRY_DIGEST).unwrap();
2756            prepare_and_initiate_index(
2757                ALICE,
2758                STAKING,
2759                &[(ALPHA_ENTRY_DIGEST, 40), (BETA_ENTRY_DIGEST, 60)],
2760                ALPHA_INDEX_DIGEST,
2761            )
2762            .unwrap();
2763            System::set_block_number(2);
2764            Pallet::place_commit(
2765                &ALICE,
2766                &STAKING,
2767                &ALPHA_INDEX_DIGEST,
2768                35,
2769                &Directive::new(Precision::BestEffort, Fortitude::Polite),
2770            )
2771            .unwrap();
2772
2773            Pallet::inspect_entries_value(
2774                RuntimeOrigin::signed(ALICE),
2775                STAKING,
2776                ALPHA_INDEX_DIGEST,
2777            )
2778            .unwrap();
2779
2780            System::assert_last_event(
2781                Event::IndexEntriesValue {
2782                    index_of: ALPHA_INDEX_DIGEST,
2783                    reason: STAKING,
2784                    entries: vec![(ALPHA_ENTRY_DIGEST, 14), (BETA_ENTRY_DIGEST, 21)],
2785                }
2786                .into(),
2787            );
2788        })
2789    }
2790
2791    #[cfg(feature = "dev")]
2792    #[test]
2793    fn inspect_pool_value_success() {
2794        commit_test_ext().execute_with(|| {
2795            initiate_key_and_set_balance_and_hold(ALICE, LARGE_VALUE, LARGE_VALUE).unwrap();
2796            initiate_key_and_set_balance_and_hold(BOB, LARGE_VALUE, LARGE_VALUE).unwrap();
2797            initiate_key_and_set_balance_and_hold(CHARLIE, LARGE_VALUE, LARGE_VALUE).unwrap();
2798            System::set_block_number(2);
2799            Pallet::place_commit(
2800                &BOB,
2801                &STAKING,
2802                &ALPHA_ENTRY_DIGEST,
2803                STANDARD_VALUE,
2804                &Directive::new(Precision::BestEffort, Fortitude::Polite),
2805            )
2806            .unwrap();
2807            System::set_block_number(6);
2808            Pallet::place_commit(
2809                &CHARLIE,
2810                &STAKING,
2811                &BETA_ENTRY_DIGEST,
2812                STANDARD_VALUE,
2813                &Directive::new(Precision::BestEffort, Fortitude::Polite),
2814            )
2815            .unwrap();
2816
2817            let entries = vec![(ALPHA_ENTRY_DIGEST, 60), (BETA_ENTRY_DIGEST, 40)];
2818            prepare_and_initiate_pool(
2819                ALICE,
2820                STAKING,
2821                &entries,
2822                ALPHA_INDEX_DIGEST,
2823                ALPHA_POOL_DIGEST,
2824                COMMISSION_ZERO,
2825            )
2826            .unwrap();
2827
2828            System::set_block_number(10);
2829            Pallet::place_commit(
2830                &ALICE,
2831                &STAKING,
2832                &ALPHA_POOL_DIGEST,
2833                LARGE_VALUE,
2834                &Directive::new(Precision::BestEffort, Fortitude::Polite),
2835            )
2836            .unwrap();
2837
2838            Pallet::inspect_pool_value(
2839                RuntimeOrigin::signed(ALICE),
2840                STAKING,
2841                ALPHA_POOL_DIGEST,
2842            )
2843            .unwrap();
2844
2845            System::assert_last_event(
2846                Event::PoolValue {
2847                    pool_of: ALPHA_POOL_DIGEST,
2848                    reason: STAKING,
2849                    value: 20,
2850                }
2851                .into(),
2852            );
2853        })
2854    }
2855
2856    #[cfg(feature = "dev")]
2857    #[test]
2858    fn inspect_slot_value_success() {
2859        commit_test_ext().execute_with(|| {
2860            initiate_key_and_set_balance_and_hold(ALICE, LARGE_VALUE, LARGE_VALUE).unwrap();
2861            initiate_key_and_set_balance_and_hold(BOB, LARGE_VALUE, LARGE_VALUE).unwrap();
2862            initiate_key_and_set_balance_and_hold(CHARLIE, LARGE_VALUE, LARGE_VALUE).unwrap();
2863            System::set_block_number(2);
2864            Pallet::place_commit(
2865                &BOB,
2866                &STAKING,
2867                &ALPHA_ENTRY_DIGEST,
2868                STANDARD_VALUE,
2869                &Directive::new(Precision::BestEffort, Fortitude::Polite),
2870            )
2871            .unwrap();
2872            System::set_block_number(6);
2873            Pallet::place_commit(
2874                &CHARLIE,
2875                &STAKING,
2876                &BETA_ENTRY_DIGEST,
2877                STANDARD_VALUE,
2878                &Directive::new(Precision::BestEffort, Fortitude::Polite),
2879            )
2880            .unwrap();
2881
2882            let entries = vec![(ALPHA_ENTRY_DIGEST, 60), (BETA_ENTRY_DIGEST, 40)];
2883            prepare_and_initiate_pool(
2884                ALICE,
2885                STAKING,
2886                &entries,
2887                ALPHA_INDEX_DIGEST,
2888                ALPHA_POOL_DIGEST,
2889                COMMISSION_ZERO,
2890            )
2891            .unwrap();
2892
2893            System::set_block_number(10);
2894            Pallet::place_commit(
2895                &ALICE,
2896                &STAKING,
2897                &ALPHA_POOL_DIGEST,
2898                LARGE_VALUE,
2899                &Directive::new(Precision::BestEffort, Fortitude::Polite),
2900            )
2901            .unwrap();
2902
2903            Pallet::inspect_slot_value(
2904                RuntimeOrigin::signed(ALICE),
2905                STAKING,
2906                ALPHA_POOL_DIGEST,
2907                ALPHA_ENTRY_DIGEST,
2908            )
2909            .unwrap();
2910
2911            System::assert_last_event(
2912                Event::PoolSlotValue {
2913                    pool_of: ALPHA_POOL_DIGEST,
2914                    reason: STAKING,
2915                    slot_of: ALPHA_ENTRY_DIGEST,
2916                    value: 12,
2917                }
2918                .into(),
2919            );
2920
2921            Pallet::inspect_slot_value(
2922                RuntimeOrigin::signed(ALICE),
2923                STAKING,
2924                ALPHA_POOL_DIGEST,
2925                BETA_ENTRY_DIGEST,
2926            )
2927            .unwrap();
2928
2929            System::assert_last_event(
2930                Event::PoolSlotValue {
2931                    pool_of: ALPHA_POOL_DIGEST,
2932                    reason: STAKING,
2933                    slot_of: BETA_ENTRY_DIGEST,
2934                    value: 8,
2935                }
2936                .into(),
2937            );
2938        })
2939    }
2940
2941    #[cfg(feature = "dev")]
2942    #[test]
2943    fn inspect_slots_value_success() {
2944        commit_test_ext().execute_with(|| {
2945            initiate_key_and_set_balance_and_hold(ALICE, LARGE_VALUE, LARGE_VALUE).unwrap();
2946            initiate_key_and_set_balance_and_hold(BOB, LARGE_VALUE, LARGE_VALUE).unwrap();
2947            initiate_key_and_set_balance_and_hold(CHARLIE, LARGE_VALUE, LARGE_VALUE).unwrap();
2948            System::set_block_number(2);
2949            Pallet::place_commit(
2950                &BOB,
2951                &STAKING,
2952                &ALPHA_ENTRY_DIGEST,
2953                STANDARD_VALUE,
2954                &Directive::new(Precision::BestEffort, Fortitude::Polite),
2955            )
2956            .unwrap();
2957            System::set_block_number(6);
2958            Pallet::place_commit(
2959                &CHARLIE,
2960                &STAKING,
2961                &BETA_ENTRY_DIGEST,
2962                STANDARD_VALUE,
2963                &Directive::new(Precision::BestEffort, Fortitude::Polite),
2964            )
2965            .unwrap();
2966
2967            let entries = vec![(ALPHA_ENTRY_DIGEST, 60), (BETA_ENTRY_DIGEST, 40)];
2968            prepare_and_initiate_pool(
2969                ALICE,
2970                STAKING,
2971                &entries,
2972                ALPHA_INDEX_DIGEST,
2973                ALPHA_POOL_DIGEST,
2974                COMMISSION_ZERO,
2975            )
2976            .unwrap();
2977
2978            System::set_block_number(10);
2979            Pallet::place_commit(
2980                &ALICE,
2981                &STAKING,
2982                &ALPHA_POOL_DIGEST,
2983                LARGE_VALUE,
2984                &Directive::new(Precision::BestEffort, Fortitude::Polite),
2985            )
2986            .unwrap();
2987
2988            Pallet::inspect_slots_value(
2989                RuntimeOrigin::signed(ALICE),
2990                STAKING,
2991                ALPHA_POOL_DIGEST,
2992            )
2993            .unwrap();
2994
2995            System::assert_last_event(
2996                Event::PoolSlotsValue {
2997                    pool_of: ALPHA_POOL_DIGEST,
2998                    reason: STAKING,
2999                    slots: vec![(ALPHA_ENTRY_DIGEST, 12), (BETA_ENTRY_DIGEST, 8)],
3000                }
3001                .into(),
3002            );
3003        })
3004    }
3005
3006    #[cfg(feature = "dev")]
3007    #[test]
3008    fn inspect_pool_commission_success() {
3009        commit_test_ext().execute_with(|| {
3010            initiate_key_and_set_balance_and_hold(ALICE, LARGE_VALUE, LARGE_VALUE).unwrap();
3011            initiate_key_and_set_balance_and_hold(BOB, LARGE_VALUE, LARGE_VALUE).unwrap();
3012            initiate_key_and_set_balance_and_hold(CHARLIE, LARGE_VALUE, LARGE_VALUE).unwrap();
3013            System::set_block_number(2);
3014            Pallet::place_commit(
3015                &BOB,
3016                &STAKING,
3017                &ALPHA_ENTRY_DIGEST,
3018                STANDARD_VALUE,
3019                &Directive::new(Precision::BestEffort, Fortitude::Polite),
3020            )
3021            .unwrap();
3022            System::set_block_number(6);
3023            Pallet::place_commit(
3024                &CHARLIE,
3025                &STAKING,
3026                &BETA_ENTRY_DIGEST,
3027                STANDARD_VALUE,
3028                &Directive::new(Precision::BestEffort, Fortitude::Polite),
3029            )
3030            .unwrap();
3031
3032            let entries = vec![(ALPHA_ENTRY_DIGEST, 60), (BETA_ENTRY_DIGEST, 40)];
3033            let init_commission = COMMISSION_HIGH;
3034            prepare_and_initiate_pool(
3035                ALICE,
3036                STAKING,
3037                &entries,
3038                ALPHA_INDEX_DIGEST,
3039                ALPHA_POOL_DIGEST,
3040                init_commission,
3041            )
3042            .unwrap();
3043
3044            System::set_block_number(10);
3045            Pallet::place_commit(
3046                &ALICE,
3047                &STAKING,
3048                &ALPHA_POOL_DIGEST,
3049                LARGE_VALUE,
3050                &Directive::new(Precision::BestEffort, Fortitude::Polite),
3051            )
3052            .unwrap();
3053
3054            Pallet::inspect_pool_commission(
3055                RuntimeOrigin::signed(ALICE),
3056                STAKING,
3057                ALPHA_POOL_DIGEST,
3058            )
3059            .unwrap();
3060
3061            System::assert_last_event(
3062                Event::PoolCommission {
3063                    pool_of: ALPHA_POOL_DIGEST,
3064                    reason: STAKING,
3065                    commission: init_commission,
3066                }
3067                .into(),
3068            );
3069        })
3070    }
3071
3072    #[cfg(feature = "dev")]
3073    #[test]
3074    fn inspect_pool_manager_success() {
3075        commit_test_ext().execute_with(|| {
3076            initiate_key_and_set_balance_and_hold(ALICE, LARGE_VALUE, LARGE_VALUE).unwrap();
3077            initiate_key_and_set_balance_and_hold(BOB, LARGE_VALUE, LARGE_VALUE).unwrap();
3078            initiate_key_and_set_balance_and_hold(CHARLIE, LARGE_VALUE, LARGE_VALUE).unwrap();
3079            System::set_block_number(2);
3080            Pallet::place_commit(
3081                &BOB,
3082                &STAKING,
3083                &ALPHA_ENTRY_DIGEST,
3084                STANDARD_VALUE,
3085                &Directive::new(Precision::BestEffort, Fortitude::Polite),
3086            )
3087            .unwrap();
3088            System::set_block_number(6);
3089            Pallet::place_commit(
3090                &CHARLIE,
3091                &STAKING,
3092                &BETA_ENTRY_DIGEST,
3093                STANDARD_VALUE,
3094                &Directive::new(Precision::BestEffort, Fortitude::Polite),
3095            )
3096            .unwrap();
3097
3098            let entries = vec![(ALPHA_ENTRY_DIGEST, 60), (BETA_ENTRY_DIGEST, 40)];
3099            let manager = ALICE;
3100            prepare_and_initiate_pool(
3101                manager.clone(),
3102                STAKING,
3103                &entries,
3104                ALPHA_INDEX_DIGEST,
3105                ALPHA_POOL_DIGEST,
3106                COMMISSION_ZERO,
3107            )
3108            .unwrap();
3109
3110            System::set_block_number(10);
3111            Pallet::place_commit(
3112                &ALICE,
3113                &STAKING,
3114                &ALPHA_POOL_DIGEST,
3115                LARGE_VALUE,
3116                &Directive::new(Precision::BestEffort, Fortitude::Polite),
3117            )
3118            .unwrap();
3119
3120            Pallet::inspect_pool_manager(
3121                RuntimeOrigin::signed(ALICE),
3122                STAKING,
3123                ALPHA_POOL_DIGEST,
3124            )
3125            .unwrap();
3126
3127            System::assert_last_event(
3128                Event::PoolManager {
3129                    pool_of: ALPHA_POOL_DIGEST,
3130                    reason: STAKING,
3131                    manager: manager,
3132                }
3133                .into(),
3134            );
3135        })
3136    }
3137
3138    #[cfg(feature = "dev")]
3139    #[test]
3140    fn inspect_asset_to_mint() {
3141        commit_test_ext().execute_with(|| {
3142            System::set_block_number(10);
3143            initiate_key_and_set_balance_and_hold(ALICE, STANDARD_COMMIT, STANDARD_HOLD)
3144                .unwrap();
3145            Pallet::place_commit(
3146                &ALICE,
3147                &STAKING,
3148                &VALIDATOR_ALPHA,
3149                STANDARD_COMMIT,
3150                &Directive::new(Precision::BestEffort, Fortitude::Polite),
3151            )
3152            .unwrap();
3153            let new_digest_val = 325; // 250 -> 325
3154            Pallet::set_digest_value(
3155                &STAKING,
3156                &VALIDATOR_ALPHA,
3157                new_digest_val,
3158                &Directive::new(Precision::BestEffort, Fortitude::Polite),
3159            )
3160            .unwrap();
3161            let expected_issuable = new_digest_val.saturating_sub(STANDARD_COMMIT); // 75
3162            Pallet::inspect_asset_to_issue(RuntimeOrigin::signed(ALICE)).unwrap();
3163            System::assert_last_event(
3164                Event::AssetIssuable {
3165                    asset: expected_issuable,
3166                }
3167                .into(),
3168            );
3169        })
3170    }
3171
3172    #[cfg(feature = "dev")]
3173    #[test]
3174    fn inspect_asset_to_reap() {
3175        commit_test_ext().execute_with(|| {
3176            System::set_block_number(10);
3177            initiate_key_and_set_balance_and_hold(ALICE, STANDARD_COMMIT, STANDARD_HOLD)
3178                .unwrap();
3179            Pallet::place_commit(
3180                &ALICE,
3181                &STAKING,
3182                &VALIDATOR_ALPHA,
3183                STANDARD_COMMIT,
3184                &Directive::new(Precision::BestEffort, Fortitude::Polite),
3185            )
3186            .unwrap();
3187            let new_digest_val = 215; // 250 -> 215
3188            Pallet::set_digest_value(
3189                &STAKING,
3190                &VALIDATOR_ALPHA,
3191                new_digest_val,
3192                &Directive::new(Precision::BestEffort, Fortitude::Polite),
3193            )
3194            .unwrap();
3195            let expected_reapable = STANDARD_COMMIT.saturating_sub(new_digest_val); // 35
3196            Pallet::inspect_asset_to_reap(RuntimeOrigin::signed(ALICE)).unwrap();
3197            System::assert_last_event(
3198                Event::AssetReapable {
3199                    asset: expected_reapable,
3200                }
3201                .into(),
3202            );
3203        })
3204    }
3205
3206    #[cfg(feature = "dev")]
3207    #[test]
3208    fn inspect_reason_value() {
3209        commit_test_ext().execute_with(|| {
3210            System::set_block_number(10);
3211            initiate_key_and_set_balance_and_hold(ALICE, STANDARD_COMMIT, STANDARD_HOLD)
3212                .unwrap();
3213            initiate_key_and_set_balance_and_hold(BOB, STANDARD_COMMIT, STANDARD_HOLD).unwrap();
3214            initiate_key_and_set_balance_and_hold(ALAN, STANDARD_COMMIT, STANDARD_HOLD)
3215                .unwrap();
3216
3217            Pallet::place_commit(
3218                &ALICE,
3219                &STAKING,
3220                &VALIDATOR_ALPHA,
3221                150,
3222                &Directive::new(Precision::BestEffort, Fortitude::Polite),
3223            )
3224            .unwrap();
3225
3226            Pallet::inspect_reason_value(RuntimeOrigin::signed(ALICE), STAKING).unwrap();
3227            System::assert_last_event(
3228                Event::ReasonValuation {
3229                    reason: STAKING,
3230                    value: 150,
3231                }
3232                .into(),
3233            );
3234
3235            Pallet::place_commit(
3236                &BOB,
3237                &ESCROW,
3238                &CONTRACT_FREELANCE,
3239                STANDARD_COMMIT,
3240                &Directive::new(Precision::BestEffort, Fortitude::Polite),
3241            )
3242            .unwrap();
3243            Pallet::inspect_reason_value(RuntimeOrigin::signed(ALICE), ESCROW).unwrap();
3244            System::assert_last_event(
3245                Event::ReasonValuation {
3246                    reason: ESCROW,
3247                    value: 250,
3248                }
3249                .into(),
3250            );
3251
3252            Pallet::place_commit(
3253                &ALAN,
3254                &STAKING,
3255                &VALIDATOR_BETA,
3256                STANDARD_COMMIT,
3257                &Directive::new(Precision::BestEffort, Fortitude::Polite),
3258            )
3259            .unwrap();
3260            Pallet::inspect_reason_value(RuntimeOrigin::signed(ALICE), STAKING).unwrap();
3261            System::assert_last_event(
3262                Event::ReasonValuation {
3263                    reason: STAKING,
3264                    value: 400,
3265                }
3266                .into(),
3267            );
3268
3269            Pallet::inspect_reason_value(RuntimeOrigin::signed(ALICE), GOVERNANCE).unwrap();
3270            System::assert_last_event(
3271                Event::ReasonValuation {
3272                    reason: GOVERNANCE,
3273                    value: 0,
3274                }
3275                .into(),
3276            );
3277        })
3278    }
3279
3280    #[test]
3281    fn query_asset_to_mint() {
3282        commit_test_ext().execute_with(|| {
3283            System::set_block_number(10);
3284            initiate_key_and_set_balance_and_hold(ALICE, STANDARD_COMMIT, STANDARD_HOLD)
3285                .unwrap();
3286            Pallet::place_commit(
3287                &ALICE,
3288                &STAKING,
3289                &VALIDATOR_ALPHA,
3290                STANDARD_COMMIT,
3291                &Directive::new(Precision::BestEffort, Fortitude::Polite),
3292            )
3293            .unwrap();
3294            let new_digest_val = 325; // 250 -> 325
3295            Pallet::set_digest_value(
3296                &STAKING,
3297                &VALIDATOR_ALPHA,
3298                new_digest_val,
3299                &Directive::new(Precision::BestEffort, Fortitude::Polite),
3300            )
3301            .unwrap();
3302            let expected_issuable = new_digest_val.saturating_sub(STANDARD_COMMIT); // 75
3303            let actual_issuable = Pallet::query_asset_to_issue();
3304            assert_eq!(expected_issuable, actual_issuable);
3305        })
3306    }
3307
3308    #[test]
3309    fn query_asset_to_reap() {
3310        commit_test_ext().execute_with(|| {
3311            System::set_block_number(10);
3312            initiate_key_and_set_balance_and_hold(ALICE, STANDARD_COMMIT, STANDARD_HOLD)
3313                .unwrap();
3314            Pallet::place_commit(
3315                &ALICE,
3316                &STAKING,
3317                &VALIDATOR_ALPHA,
3318                STANDARD_COMMIT,
3319                &Directive::new(Precision::BestEffort, Fortitude::Polite),
3320            )
3321            .unwrap();
3322            let new_digest_val = 215; // 250 -> 215
3323            Pallet::set_digest_value(
3324                &STAKING,
3325                &VALIDATOR_ALPHA,
3326                new_digest_val,
3327                &Directive::new(Precision::BestEffort, Fortitude::Polite),
3328            )
3329            .unwrap();
3330            let expected_reapable = STANDARD_COMMIT.saturating_sub(new_digest_val); // 35
3331            let actual_reapable = Pallet::query_asset_to_reap();
3332            assert_eq!(expected_reapable, actual_reapable);
3333        })
3334    }
3335
3336    #[test]
3337    fn query_reason_value() {
3338        commit_test_ext().execute_with(|| {
3339            System::set_block_number(10);
3340            initiate_key_and_set_balance_and_hold(ALICE, STANDARD_COMMIT, STANDARD_HOLD)
3341                .unwrap();
3342            initiate_key_and_set_balance_and_hold(BOB, STANDARD_COMMIT, STANDARD_HOLD).unwrap();
3343            initiate_key_and_set_balance_and_hold(ALAN, STANDARD_COMMIT, STANDARD_HOLD)
3344                .unwrap();
3345
3346            Pallet::place_commit(
3347                &ALICE,
3348                &STAKING,
3349                &VALIDATOR_ALPHA,
3350                150,
3351                &Directive::new(Precision::BestEffort, Fortitude::Polite),
3352            )
3353            .unwrap();
3354            let staking_value = Pallet::query_reason_value(STAKING);
3355            assert_eq!(staking_value, 150);
3356
3357            Pallet::place_commit(
3358                &BOB,
3359                &ESCROW,
3360                &CONTRACT_FREELANCE,
3361                STANDARD_COMMIT,
3362                &Directive::new(Precision::BestEffort, Fortitude::Polite),
3363            )
3364            .unwrap();
3365            let escrow_value = Pallet::query_reason_value(ESCROW);
3366            assert_eq!(escrow_value, 250);
3367
3368            Pallet::place_commit(
3369                &ALAN,
3370                &STAKING,
3371                &VALIDATOR_BETA,
3372                STANDARD_COMMIT,
3373                &Directive::new(Precision::BestEffort, Fortitude::Polite),
3374            )
3375            .unwrap();
3376            let staking_value = Pallet::query_reason_value(STAKING);
3377            assert_eq!(staking_value, 400);
3378
3379            let governance_value = Pallet::query_reason_value(GOVERNANCE);
3380            assert_eq!(governance_value, 0);
3381        })
3382    }
3383}