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