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