Skip to main content

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