Skip to main content

pallet_commitment/
commitment.rs

1// SPDX-License-Identifier: MPL-2.0
2//
3// Part of Auguth Labs open-source softwares.
4// Built for the Substrate framework.
5//
6// This Source Code Form is subject to the terms of the Mozilla Public
7// License, v. 2.0. If a copy of the MPL was not distributed with this
8// file, You can obtain one at https://mozilla.org/MPL/2.0/.
9//
10// Copyright (c) 2026 Auguth Labs (OPC) Pvt Ltd, India
11
12// ===============================================================================
13// ```````````````````````````` COMMITMENT TRAITS IMPL ```````````````````````````
14// ===============================================================================
15
16//! Implementation module of the [`Commitment Family`](frame_suite::commitment)
17//! traits, where we utilize indexes, pools, and variants to create a flexible and
18//! semantic commitment system.
19//!
20//! Low-level helper traits are defined in [`crate::traits`] within this crate. These
21//! helpers provide fundamental functions that can be reused by other implementations
22//! to offer a similar commitment system.
23//!
24//! The asset type is defined as the fungible trait's balance - i.e., a unit with
25//! fungible behaviours. See the required trait bounds of [`Config::Asset`] to
26//! understand which methods this system utilizes.
27//!
28//! Notably, this commitment system does not rely on standard balanced (safe) fungible methods.
29//! Instead, it uses its own safe models via low-level methods provided by fungible traits.
30//! Therefore, it does not query the total asset in circulation, as its scope is limited
31//! to commitments for the particular asset holder.
32//!
33//! [`Pallet`] implements:
34//! - [`InspectAsset`]
35//! - [`DigestModel`]
36//! - [`Commitment`]
37//! - [`CommitIndex`]
38//! - [`CommitPool`]
39//! - [`CommitVariant`]
40//! - [`IndexVariant`]
41//! - [`PoolVariant`]
42//! - and other helper traits include
43//!     - [`CommitErrorHandler`]
44//!
45//! Local Tests for these traits are covered in `tests`.
46
47// ===============================================================================
48// ``````````````````````````````````` IMPORTS ```````````````````````````````````
49// ===============================================================================
50
51// --- Local crate imports ---
52use crate::{
53    balance::*, traits::*, types::*, AssetToIssue, AssetToReap, CommitHelpers, CommitMap, Config,
54    DigestMap, EntryMap, Error, Event, HoldReason, IndexMap, Pallet, PoolManager, PoolMap,
55};
56
57// --- Core ---
58use core::cmp::Ordering;
59
60// --- FRAME Suite ---
61use frame_suite::{commitment::*, keys::*, misc::Extent};
62
63// --- FRAME Support ---
64use frame_support::{
65    ensure,
66    traits::{
67        fungible::{Inspect, InspectHold},
68        tokens::{Fortitude, Preservation},
69    },
70};
71
72// --- Substrate primitives ---
73use sp_core::Get;
74use sp_runtime::{
75    traits::{CheckedAdd, Saturating, Zero},
76    DispatchError, DispatchResult, Vec,
77};
78
79// ===============================================================================
80// ```````````````````````````````` INSPECT ASSET ````````````````````````````````
81// ===============================================================================
82
83/// Implements [`InspectAsset`] for the pallet, allowing the
84/// commitment pallet to inspect a user's available funds
85/// for commitment.
86impl<T: Config<I>, I: 'static> InspectAsset<Proprietor<T>> for Pallet<T, I> {
87    /// The asset type used in commitments, taken from the fungible `Inspect` trait.
88    ///
89    /// This allows any fungible implementation to be used as the commitment asset,
90    /// providing flexibility in the type of value being committed while ensuring
91    /// compatibility with the broader Substrate fungible ecosystem.
92    type Asset = AssetOf<T, I>;
93
94    /// Retrieves the total available funds for commitment for a given proprietor.
95    ///
96    /// Aggregates two balance sources:
97    /// - Funds held under [`HoldReason::PrepareForCommit`] reason
98    /// - Liquid balance reducible under [`Preservation::Preserve`] and
99    /// [`Fortitude::Polite`] rules
100    ///
101    /// This aggregated view is used by commitment validation logic to determine whether
102    /// a proprietor has sufficient funds to place, raise, or modify commitments.
103    ///
104    /// ## Returns
105    /// `Asset` containing the total available balance
106    fn available_funds(who: &Proprietor<T>) -> Self::Asset {
107        let hold_reason: T::AssetHold = HoldReason::PrepareForCommit.into();
108
109        // Funds specifically held for this commitment purpose.
110        let held_balance = T::Asset::balance_on_hold(&hold_reason, who);
111
112        // Liquid balance available for commitment.
113        // We do not want the account to be dusted/killed/reaped.
114        let liquid_balance =
115            T::Asset::reducible_balance(who, Preservation::Preserve, Fortitude::Polite);
116
117        held_balance.saturating_add(liquid_balance)
118    }
119}
120
121// ===============================================================================
122// ```````````````````````````````` DIGEST MODEL `````````````````````````````````
123// ===============================================================================
124
125/// Implements [`DigestModel`] for the pallet, allowing the system to
126/// determine the specific model variant of a given digest.
127///
128/// Since all digests share the same base type, we need a wrapper
129/// i.e., [`DigestVariant`] to distinguish between models such as Direct, Index, and Pool.
130impl<T: Config<I>, I: 'static> DigestModel<Proprietor<T>> for Pallet<T, I> {
131    /// The digest model type, wrapping the digest with its variant classification.
132    ///
133    /// This type distinguishes between three commitment models:
134    /// - **Direct**: A standalone commitment to a specific digest
135    /// - **Index**: A commitment distributed across multiple entries with weighted shares
136    /// - **Pool**: A managed commitment structure with dynamic slot allocation and commission
137    type Model = DigestVariant<T, I>;
138
139    /// Determines which digest model a given digest belongs to.
140    ///
141    /// This method checks if the digest exists in each model variant in order:
142    /// 1. Direct
143    /// 2. Index
144    /// 3. Pool
145    ///
146    /// If a matching model is found, it returns the wrapped variant.
147    /// Otherwise, a DispatchError.
148    ///
149    /// Note: This method is **not suitable** for creating new digests (not
150    /// registered in the system).
151    ///
152    /// New digests should be manually wrapped to avoid incorrect determination.
153    fn determine_digest(
154        digest: &Self::Digest,
155        reason: &Self::Reason,
156    ) -> Result<Self::Model, DispatchError> {
157        if Self::digest_exists(reason, digest).is_ok() {
158            return Ok(DigestVariant::Direct(digest.clone()));
159        }
160
161        if Self::index_exists(reason, digest).is_ok() {
162            return Ok(DigestVariant::Index(digest.clone()));
163        }
164
165        if Self::pool_exists(reason, digest).is_ok() {
166            return Ok(DigestVariant::Pool(digest.clone()));
167        }
168
169        Err(Error::<T, I>::DigestNotFoundToDetermine.into())
170    }
171}
172
173// ===============================================================================
174// ````````````````````````````````` COMMITMENT ``````````````````````````````````
175// ===============================================================================
176
177/// Implements the base [`Commitment`] trait for the pallet.
178impl<T: Config<I>, I: 'static> Commitment<Proprietor<T>> for Pallet<T, I> {
179    /// The source of a digest, used to determine the concrete digest.
180    ///  
181    /// In this implementation, the digest source is the the calling source
182    /// i.e., [`frame_system::Config::AccountId`]. Must be using its account
183    /// nonce for deterministic-randomness.
184    ///
185    /// This means all digests - whether direct, index, or pool - have a
186    /// deterministic account ID generated by [`Commitment::gen_digest`] and similar
187    /// methods.
188    ///
189    /// Internally, the methods enforces generating the [`Commitment::Digest`] same
190    /// as the source type.
191    type DigestSource = DigestSource<T>;
192
193    /// The digest type used in this pallet, based on account ID type
194    /// (from [`frame_system::Config::AccountId`]).
195    ///
196    /// This ensures all digests are tied to a consistent, predictable
197    /// identity, same as every accounts. Much alike contract addresses.
198    type Digest = Digest<T>;
199
200    /// The reason associated with a commitment.
201    ///
202    /// This type is linked to the `Id` type of the `InspectFreeze` fungible trait,
203    /// typically a composite enum constructed at runtime from all pallets
204    /// that use fungible properties.
205    ///
206    /// Declaring `Reason` as the top-level key:
207    /// - Encourages compile-time reasons for commitments.
208    /// - Prevents creating new reasons at runtime without explicit intent.
209    /// - Ensures other pallets adopt this commitment structure for consistent behavior.
210    type Reason = CommitReason<T, I>;
211
212    /// Commitment operation top level configuration.
213    ///
214    /// Enforces exactness and forcefullness towards a commit operation.
215    type Intent = DispatchPolicy;
216
217    /// Type representing the derived limits used for commitment validations.
218    ///
219    /// Encapsulates bounds (e.g. minimum, maximum, optimal) computed by the
220    /// underlying balance model for deposit, mint, and reap operations.
221    type Limits = LimitsProduct<T, I>;
222
223    // -------------------------------------------------------------------------------
224    // ``````````````````````````````````` CHECKERS ``````````````````````````````````
225    // -------------------------------------------------------------------------------
226
227    /// Checks whether a commitment exists for the given proprietor and reason.
228    ///
229    /// ## Returns
230    /// - `Ok(())` if a commitment exists
231    /// - `Err(DispatchError)` if no commitment exists
232    fn commit_exists(who: &Proprietor<T>, reason: &Self::Reason) -> DispatchResult {
233        ensure!(
234            CommitMap::<T, I>::contains_key((who, reason)),
235            Error::<T, I>::CommitNotFound
236        );
237        Ok(())
238    }
239
240    /// Checks whether a direct-digest exists for the given reason.
241    ///
242    /// This doesn't ensures existence for index or pool digests, as callers
243    /// should use [`CommitIndex::index_exists`] or [`CommitPool::pool_exists`]
244    ///
245    /// ## Returns
246    /// - `Ok(())` if the digest exists
247    /// - `Err(DispatchError)` if the digest does not exist
248    fn digest_exists(reason: &Self::Reason, digest: &Self::Digest) -> DispatchResult {
249        ensure!(
250            DigestMap::<T, I>::contains_key((reason, digest)),
251            Error::<T, I>::DigestNotFound
252        );
253        Ok(())
254    }
255
256    /// Validates whether a new commitment can be placed with the
257    /// variant's [`Config::Position`] default.
258    ///
259    /// This is a thin wrapper over [`Self::can_place_commit_of_variant`],
260    /// using the default [`Config::Position`] for non-variant commitments.
261    ///
262    /// ## Returns
263    /// - `Ok(())` if validation succeeds
264    /// - `Err(DispatchError)` if validation fails
265    #[inline]
266    fn can_place_commit(
267        who: &Proprietor<T>,
268        reason: &Self::Reason,
269        digest: &Self::Digest,
270        value: Self::Asset,
271        qualifier: &Self::Intent,
272    ) -> DispatchResult {
273        Self::can_place_commit_of_variant(
274            who,
275            reason,
276            digest,
277            &Default::default(),
278            value,
279            qualifier,
280        )
281    }
282
283    /// Validates whether an existing commitment can be increased (raised).
284    ///
285    /// Same as the default trait validation, but extended with an additional
286    /// check against the underlying balance model to ensure the deposit can
287    /// actually be applied.
288    ///
289    /// In the lazy balance model, raising is equivalent to performing an
290    /// additional deposit and the underlying system does not distinguish between
291    /// placing and raising.
292    ///
293    /// ## Returns
294    /// - `Ok(())` if validation succeeds
295    /// - `Err(DispatchError)` if any constraint is violated
296    fn can_raise_commit(
297        who: &Proprietor<T>,
298        reason: &Self::Reason,
299        value: Self::Asset,
300        qualifier: &Self::Intent,
301    ) -> DispatchResult {
302        let digest = &Self::get_commit_digest(who, reason)?;
303        let max = Self::available_funds(who);
304        ensure!(max >= value, Error::<T, I>::InsufficientFunds);
305        let variant = &Self::get_commit_variant(who, reason)?;
306        // debug_assert!()
307        let balance = DigestMap::<T, I>::get((reason, digest))
308            .and_then(|digest_info| digest_info.get_balance(&variant).cloned())
309            .unwrap_or_default();
310        let limits = deposit_limits_of(&balance, &variant, digest, qualifier)?;
311        ensure!(
312            <Self::Limits as Extent>::contains(&limits, value),
313            Error::<T, I>::PlacingOffLimits
314        );
315        can_deposit(&balance, variant, digest, &value, qualifier)
316    }
317
318    /// Validates whether a commitment can be resolved.
319    ///
320    /// Extends the default trait behavior by additionally validating that all
321    /// underlying balances can support the required withdrawals.
322    ///
323    /// Since each digest model maintains balances differently:
324    /// - **Direct / Index**: withdrawals are validated directly against their balances
325    /// - **Pool**: validation is performed against the pool's current (unadjusted)
326    ///   aggregate balance, without applying intermediate slot updates
327    ///
328    /// This is a lightweight validation step; full consistency (including pool
329    /// rebalancing) is enforced during the actual resolution operation.
330    ///
331    /// ## Returns
332    /// - `Ok(())` if all withdrawals are valid
333    /// - `Err(DispatchError)` if any withdrawal is invalid
334    fn can_resolve_commit(who: &Proprietor<T>, reason: &Self::Reason) -> DispatchResult {
335        let digest = &Self::get_commit_digest(who, reason)?;
336        let digest_model = &Self::determine_digest(digest, reason)?;
337        // debug_assert!()
338        match digest_model {
339            DigestVariant::Direct(direct) => {
340                let variant = &Self::get_commit_variant(who, reason)?;
341                // debug_assert!()
342                let digest_info = DigestMap::<T, I>::get((reason, direct))
343                    .ok_or(Error::<T, I>::DigestNotFound)?;
344                // debug_assert!()
345                let balance = digest_info
346                    .get_balance(variant)
347                    // debug_assert!()
348                    .ok_or(Error::<T, I>::DigestVariantBalanceNotFound)?;
349                let commit_info =
350                    CommitMap::<T, I>::get((who, reason)).ok_or(Error::<T, I>::CommitNotFound)?;
351                // debug_assert!()
352                for commit in commit_info.commits() {
353                    can_withdraw(&balance, variant, digest, &commit)?;
354                }
355            }
356            DigestVariant::Index(index) => {
357                let index_info = Self::get_index(reason, index)?;
358                // debug_assert!()
359                for entry in index_info.entries() {
360                    let digest = &entry.digest();
361                    let Some(commits) = EntryMap::<T, I>::get((reason, index, digest, who)) else {
362                        // If Zero Amount Depositted due to low shares
363                        continue;
364                    };
365                    let variant = &entry.variant();
366                    let digest_info = DigestMap::<T, I>::get((reason, digest))
367                        // debug_assert!()
368                        .ok_or(Error::<T, I>::EntryDigestNotFound)?;
369                    let balance = digest_info
370                        .get_balance(variant)
371                        // debug_assert!()
372                        .ok_or(Error::<T, I>::DigestVariantBalanceNotFound)?;
373                    for commit in commits.commits() {
374                        can_withdraw(&balance, variant, digest, &commit)?;
375                    }
376                }
377            }
378            DigestVariant::Pool(pool) => {
379                let pool_info = Self::get_pool(reason, pool)?;
380                // debug_assert!()
381                let balance = pool_info.balance();
382                let commit_info =
383                    CommitMap::<T, I>::get((who, reason)).ok_or(Error::<T, I>::CommitNotFound)?;
384                // debug_assert!()
385                for commit in commit_info.commits() {
386                    can_withdraw(&balance, &Default::default(), pool, &commit)?;
387                }
388            }
389            _ => {
390                debug_assert!(
391                    false,
392                    "digest-model marker variants {:?} are constructed, 
393                    captured during can withdraw validation proprietor {:?} 
394                    of reason {:?} are explicitly dis-allowed",
395                    digest_model, who, reason
396                );
397                return Err(Error::<T, I>::InvalidDigestModel.into());
398            }
399        }
400        Ok(())
401    }
402
403    /// Validates whether a digest's value can be set using the default variant.
404    ///
405    /// This is a thin wrapper over [`Self::can_set_digest_variant_value`],
406    /// using the default [`Config::Position`] for single non-variant commitments.
407    ///
408    /// ## Returns
409    /// - `Ok(())` if validation succeeds
410    /// - `Err(DispatchError)` if validation fails
411    #[inline]
412    fn can_set_digest_value(
413        reason: &Self::Reason,
414        digest: &Self::Digest,
415        value: Self::Asset,
416        qualifier: &Self::Intent,
417    ) -> DispatchResult {
418        Self::can_set_digest_variant_value(reason, digest, value, &Default::default(), qualifier)
419    }
420
421    // -------------------------------------------------------------------------------
422    // ``````````````````````````````````` GETTERS ```````````````````````````````````
423    // -------------------------------------------------------------------------------
424
425    /// Retrieves the digest associated with a proprietor's commitment.
426    ///
427    /// Since [`Commitment::Digest`] is opaque to identify as direct or index
428    /// or pool. This function can return any digest model which later can be
429    /// determined using [`DigestModel::determine_digest`].
430    ///
431    /// Since each reason can only have one active digest per proprietor,
432    /// this directly returns the commitment's digest.
433    ///
434    /// ## Returns
435    /// - `Ok(Digest)` containing the commitment's digest
436    /// - `Err(DispatchError)` if no commitment exists
437    fn get_commit_digest(
438        who: &Proprietor<T>,
439        reason: &Self::Reason,
440    ) -> Result<Self::Digest, DispatchError> {
441        let commit_info =
442            CommitMap::<T, I>::get((who, reason)).ok_or(Error::<T, I>::CommitNotFound)?;
443        let digest = commit_info.digest();
444        debug_assert!(
445            // cannot do `digest_exists` as this can get called by any digest model
446            // `determine_digest` holds all checks
447            Self::determine_digest(&digest, reason).is_ok(),
448            "commit-exists for reason {:?} of digest {:?} for proprietor {:?}, 
449            but internally digest doesn't really exist",
450            reason,
451            digest,
452            who
453        );
454        Ok(digest)
455    }
456
457    /// Retrieves the total committed value across all proprietors for a reason.
458    ///
459    /// ## Returns
460    /// - `Asset` containing the total committed value, or zero if unavailable
461    fn get_total_value(reason: &Self::Reason) -> Self::Asset {
462        CommitHelpers::<T, I>::value_of(None, reason).unwrap_or(Zero::zero())
463    }
464
465    /// Retrieves the real-time committed value for a specific proprietor and reason.
466    ///
467    /// This value reflects the current state including any applied rewards or penalties.
468    /// Since each proprietor can only have one active digest per reason, this returns
469    /// the aggregate value across all commitment instances for that digest.
470    ///
471    /// Internally, raising commits doesn't mutate an existing commitment-balance but
472    /// instead accmulate new immutable balances as commit-instances which will be
473    /// aggregated.
474    ///
475    /// ## Returns
476    /// - `Ok(Asset)` containing the proprietor's current committed value
477    /// - `Err(DispatchError)` if no commitment exists
478    fn get_commit_value(
479        who: &Proprietor<T>,
480        reason: &Self::Reason,
481    ) -> Result<Self::Asset, DispatchError> {
482        let digest = Self::get_commit_digest(who, reason)?;
483        let digest_model = Self::determine_digest(&digest, reason);
484        debug_assert!(
485            digest_model.is_ok(),
486            "proprietor {:?} commit-exists in digest {:?} for reason {:?}, 
487            but its model cannot be determined",
488            who,
489            digest,
490            reason
491        );
492        let digest_model = digest_model?;
493        CommitHelpers::<T, I>::commit_value_of(who, reason, &digest_model)
494    }
495
496    /// Retrieves the real-time total value of a specific direct-digest's
497    /// default variant of [`Config::Position`] for a given reason.
498    ///
499    /// This doesn't provides value for index or pool digests, as callers
500    /// should use [`CommitIndex::get_index_value`] or [`CommitPool::get_pool_value`]
501    ///
502    /// Aggregates all commitments across all proprietors who committed
503    /// funds to this digest's default variant.
504    ///
505    /// This method delagates itself to [`CommitVariant::get_digest_variant_value`]
506    /// with the default position variant.
507    ///
508    /// ## Returns
509    /// - `Ok(Asset)` containing the digest's total value
510    /// - `Err(DispatchError)` if the digest does not exist
511    #[inline]
512    fn get_digest_value(
513        reason: &Self::Reason,
514        digest: &Self::Digest,
515    ) -> Result<Self::Asset, DispatchError> {
516        Self::get_digest_variant_value(reason, digest, &T::Position::default())
517    }
518
519    /// Derives place commit limits for the given params of the
520    /// default commit-variant.
521    ///
522    /// This is a convenience wrapper over [`Self::place_commit_limits_of_variant`],
523    /// using the default [`Config::Position`] for non-variant commitments.
524    ///
525    /// ## Returns
526    /// - `Ok(Limits)` containing the derived constraints
527    /// - `Err(DispatchError)` if the derivation fails
528    #[inline]
529    fn place_commit_limits(
530        who: &Proprietor<T>,
531        reason: &Self::Reason,
532        digest: &Self::Digest,
533        qualifier: &Self::Intent,
534    ) -> Result<Self::Limits, DispatchError> {
535        Self::place_commit_limits_of_variant(who, reason, digest, &Default::default(), qualifier)
536    }
537
538    /// Derives limits for increasing (raising) an existing commitment under
539    /// the default commit-variant.
540    ///
541    /// Resolves the commit's associated digest and variant for the given
542    /// proprietor and reason, then delegates to
543    /// [`Self::place_commit_limits_of_variant`].
544    ///
545    /// In the lazy balance model, raising is equivalent to performing an
546    /// additional deposit on an existing commitment, so the same limit
547    /// derivation logic applies.
548    ///
549    /// The `qualifier` influences how limits are derived.
550    ///
551    /// ## Returns
552    /// - `Ok(Limits)` containing the derived constraints
553    /// - `Err(DispatchError)` if the commitment does not exist or derivation fails
554    fn raise_commit_limits(
555        who: &Proprietor<T>,
556        reason: &Self::Reason,
557        qualifier: &Self::Intent,
558    ) -> Result<Self::Limits, DispatchError> {
559        let digest = Self::get_commit_digest(who, reason)?;
560        let variant = Self::get_commit_variant(who, reason)?;
561        // debug_assert!()
562        Self::place_commit_limits_of_variant(who, reason, &digest, &variant, qualifier)
563    }
564
565    /// Derives minting limits for a digest using the default variant.
566    ///
567    /// This is a convenience wrapper over [`Self::digest_mint_limits_of_variant`],
568    /// using the default [`Config::Position`] for single non-variant digests.
569    ///
570    /// The `qualifier` influences how limits are derived (e.g. strict vs relaxed).
571    ///
572    /// ## Returns
573    /// - `Ok(Limits)` containing the derived minting constraints
574    /// - `Err(DispatchError)` if the derivation fails
575    #[inline]
576    fn digest_mint_limits(
577        digest: &Self::Digest,
578        reason: &Self::Reason,
579        qualifier: &Self::Intent,
580    ) -> Result<Self::Limits, DispatchError> {
581        Self::digest_mint_limits_of_variant(digest, reason, &Default::default(), qualifier)
582    }
583
584    /// Derives reaping limits for a digest using the default variant.
585    ///
586    /// This is a convenience wrapper over [`Self::digest_reap_limits_of_variant`],
587    /// using the default [`Config::Position`] for single non-variant digests.
588    ///
589    /// The `qualifier` influences how limits are derived (e.g. strict vs relaxed).
590    ///
591    /// ## Returns
592    /// - `Ok(Limits)` containing the derived reaping constraints
593    /// - `Err(DispatchError)` if the derivation fails
594    #[inline]
595    fn digest_reap_limits(
596        digest: &Self::Digest,
597        reason: &Self::Reason,
598        qualifier: &Self::Intent,
599    ) -> Result<Self::Limits, DispatchError> {
600        Self::digest_reap_limits_of_variant(digest, reason, &Default::default(), qualifier)
601    }
602
603    // -------------------------------------------------------------------------------
604    // ````````````````````````````````` CONSTRUCTORS ````````````````````````````````
605    // -------------------------------------------------------------------------------
606
607    /// Generates a unique digest identifier from the given source.
608    ///
609    /// Uses the account nonce as a salt to ensure uniqueness across multiple
610    /// digest generations for the same source.
611    ///
612    /// Utilizes [`KeyGenFor`] trait implementation via [`KeySeedFor`]
613    ///
614    /// ## Returns
615    /// - `Ok(Digest)` containing the generated digest
616    /// - `Err(DispatchError)` if digest generation fails
617    fn gen_digest(source: &DigestSource<T>) -> Result<Self::Digest, DispatchError> {
618        let target = Into::<&Self::Digest>::into(source);
619
620        // Retrieve account nonce from the system pallet as a salt
621        let salt = frame_system::Pallet::<T>::account_nonce(source);
622
623        // Generate a digest key with salt
624        let key =
625            KeySeedFor::<Self::Digest, (), T::Nonce, T::Hashing, T>::gen_key(target, &(), salt)
626                .ok_or(Error::<T, I>::CannotGenerateDigest)?;
627
628        Ok(key)
629    }
630
631    // -------------------------------------------------------------------------------
632    // ``````````````````````````````````` MUTATORS ``````````````````````````````````
633    // -------------------------------------------------------------------------------
634
635    /// Places a commitment with the variant's [`Config::Position`] default.
636    ///
637    /// This method delagates itself to [`CommitVariant::place_commit_of_variant`]
638    /// with the default position variant.
639    ///
640    /// For detailed information on how placing a commitment works, refer to the
641    /// called implemented method [`CommitVariant::place_commit_of_variant`].
642    ///
643    /// ## Returns
644    /// - `Ok(Asset)` containing the actual committed amount
645    /// - `Err(DispatchError)` if placement fails
646    fn place_commit(
647        who: &Proprietor<T>,
648        reason: &Self::Reason,
649        digest: &Self::Digest,
650        value: Self::Asset,
651        qualifier: &Self::Intent,
652    ) -> Result<Self::Asset, DispatchError> {
653        Self::place_commit_of_variant(
654            who,
655            reason,
656            digest,
657            value,
658            &T::Position::default(),
659            qualifier,
660        )
661    }
662
663    /// Resolves and withdraws a commitment for the given proprietor and reason.
664    ///
665    /// Calculates the final value including any rewards or penalties, unfreezes
666    /// the committed assets, and returns them to the owner (authorized-caller).
667    ///
668    /// The commitment record is removed upon successful resolution.
669    ///
670    /// ## Returns
671    /// - `Ok(Asset)` containing the resolved amount returned to the proprietor
672    /// - `Err(DispatchError)` if no commitment exists or resolution fails
673    fn resolve_commit(
674        who: &Proprietor<T>,
675        reason: &Self::Reason,
676    ) -> Result<Self::Asset, DispatchError> {
677        let digest = Self::get_commit_digest(who, reason)?;
678        let digest_model = Self::determine_digest(&digest, reason);
679        debug_assert!(
680            digest_model.is_ok(),
681            "proprietor {:?} commit-exists in digest {:?} of reason {:?}, 
682            but its model cannot be determined",
683            who,
684            digest,
685            reason
686        );
687        let digest_model = digest_model?;
688        let resolved = CommitHelpers::<T, I>::resolve_commit_of(who, reason, &digest_model)?;
689        Self::on_commit_resolve(who, reason, &digest, resolved);
690        Ok(resolved)
691    }
692
693    /// Raises a commitment for the given proprietor and reason.
694    ///
695    /// Enforces that the proprietor must already have an active
696    /// commitment for the given reason.
697    ///
698    /// Does not allow zero-value marker commitments.
699    ///
700    /// ## Returns
701    /// - `Ok(Asset)` containing the raised amount
702    /// - `Err(DispatchError)` if no existing commitment is found or funds are insufficient
703    fn raise_commit(
704        who: &Proprietor<T>,
705        reason: &Self::Reason,
706        value: Self::Asset,
707        qualifier: &Self::Intent,
708    ) -> Result<Self::Asset, DispatchError> {
709        Self::commit_exists(who, reason)?;
710        ensure!(!value.is_zero(), Error::<T, I>::MarkerCommitNotAllowed);
711        let digest = Self::get_commit_digest(who, reason)?;
712        let digest_model = Self::determine_digest(&digest, reason);
713        debug_assert!(
714            digest_model.is_ok(),
715            "proprietor {:?} commit-exists in digest {:?} for reason {:?}, 
716            but its model cannot be determined",
717            who,
718            digest,
719            reason
720        );
721        let digest_model = digest_model?;
722        let raised =
723            CommitHelpers::<T, I>::raise_commit_of(who, reason, &digest_model, value, qualifier)?;
724        Self::on_commit_raise(who, reason, &digest, raised);
725        Ok(raised)
726    }
727
728    /// Sets a direct value on a digest, typically for applying rewards/inflation or
729    /// penalties/deflation.
730    ///
731    /// **Note**: This function operates at the low-level commitment for a direct-digest. It
732    /// cannot be used to directly apply rewards or penalties to indexes or pools, because
733    /// those maintain their balances at a higher level through their entries and slots digests.
734    ///
735    /// Any value adjustment for indexes or pools should propagate via their underlying
736    /// sub-systems if applicable.
737    ///
738    /// Internally calls [`CommitVariant::set_digest_variant_value`] with the default
739    /// variant of [`Config::Position`].
740    ///
741    /// The `qualifier` influences how the adjustment (via the given direction which
742    /// could be increase or decrease from current digest value) is applied and may
743    /// affect the final value that is actually set.
744    ///
745    /// ## Returns
746    /// - `Ok(Asset)` containing the resulting value of the digest after update
747    /// - `Err(DispatchError)` if the operation fails
748    #[inline]
749    fn set_digest_value(
750        reason: &Self::Reason,
751        digest: &Self::Digest,
752        value: Self::Asset,
753        qualifier: &Self::Intent,
754    ) -> Result<Self::Asset, DispatchError> {
755        Self::set_digest_variant_value(reason, digest, value, &T::Position::default(), qualifier)
756    }
757
758    /// Removes a digest from storage after ensuring it contains no active deposits.
759    ///
760    /// This operation is only allowed when the digest's balance has no remaining
761    /// deposits (i.e., no claimable commitments exist).
762    ///
763    /// **Note**: This function operates for direct digests only. It cannot be used
764    /// to reap indexes or pools.
765    ///
766    /// Any residual value ("dust") left after all deposits are withdrawn is treated
767    /// as unclaimable and reaped-accounted in [`AssetToReap`], deducted from total
768    /// committed value [`crate::ReasonValue`], and considered effectively dead.
769    ///
770    /// The digest itself can be recreated later if a new deposit is made via
771    /// [`CommitDeposit::deposit_to_digest`].
772    ///
773    /// ## Returns
774    /// - `Ok(())` if the digest is successfully removed
775    /// - `Err(DispatchError)` with `DigestHasFunds` if active deposits still exist
776    fn reap_digest(digest: &Self::Digest, reason: &Self::Reason) -> DispatchResult {
777        let digest_info =
778            DigestMap::<T, I>::get((reason, digest)).ok_or(Error::<T, I>::DigestNotFound)?;
779        let balances = digest_info.balances()?;
780        let mut reap = true;
781        let mut remaining = Zero::zero();
782        for (variant, balance) in &balances {
783            if has_deposits(balance, variant, digest).is_ok() {
784                reap = false;
785                break;
786            }
787            remaining = balance_total(balance, variant, digest)?;
788        }
789        // Cannot reap a digest with remaining funds
790        ensure!(reap, Error::<T, I>::DigestHasFunds);
791
792        // If unerlying balance system allows residue/dust after
793        // full-withdrawal due to rounding/precision and other drifts.
794        if !remaining.is_zero() {
795            // Residue as it will never be claimed, but maintained for equillibrium
796            // Considered dead inside commitment system, and never will be able to
797            // resolved in the underlying asset (fungible) system
798            AssetToReap::<T, I>::mutate(|total_to_reap| -> DispatchResult {
799                *total_to_reap = total_to_reap
800                    .checked_add(&remaining)
801                    .ok_or(Error::<T, I>::MaxAssetReaped)?;
802                Ok(())
803            })?;
804            // Subtract reason's total committed value since value is deflated
805            CommitHelpers::<T, I>::sub_from_total_value(reason, remaining)?;
806        }
807
808        // Called earlier to determine reaped digest model.
809        Self::on_reap_digest(digest, reason, remaining);
810
811        // Remove the digest from storage
812        DigestMap::<T, I>::remove((reason, digest));
813        Ok(())
814    }
815
816    // -------------------------------------------------------------------------------
817    // ```````````````````````````````````` HOOKS ````````````````````````````````````
818    // -------------------------------------------------------------------------------
819
820    /// Hook called when a commitment is placed.
821    ///
822    /// Delagates itself to [`CommitVariant::on_place_commit_on_variant`]
823    /// with the default position variant.
824    #[inline]
825    fn on_commit_place(
826        who: &Proprietor<T>,
827        reason: &Self::Reason,
828        digest: &Self::Digest,
829        value: Self::Asset,
830    ) {
831        Self::on_place_commit_on_variant(who, reason, digest, value, &Default::default());
832    }
833
834    /// Hook called when a commitment is raised.
835    ///
836    /// The digest is verified and classified using
837    /// [`DigestModel::determine_digest`].
838    ///
839    /// Emits [`Event::CommitRaised`] event if
840    /// [`Config::EmitEvents`] is `true`.
841    fn on_commit_raise(
842        who: &Proprietor<T>,
843        reason: &Self::Reason,
844        digest: &Self::Digest,
845        value: Self::Asset,
846    ) {
847        if T::EmitEvents::get() {
848            #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
849            {
850                let Ok(digest_model) = Self::determine_digest(digest, reason) else {
851                    return;
852                };
853                Self::deposit_event(Event::<T, I>::CommitRaised {
854                    who: who.clone(),
855                    reason: *reason,
856                    model: digest_model,
857                    value,
858                });
859            }
860
861            #[cfg(not(any(feature = "dev", feature = "runtime-benchmarks")))]
862            {
863                Self::deposit_event(Event::<T, I>::CommitRaised {
864                    who: who.clone(),
865                    reason: *reason,
866                    digest: digest.clone(),
867                    value,
868                });
869            }
870        }
871    }
872
873    /// Hook called when a commitment is resolved.
874    ///
875    /// The digest is verified and classified using
876    /// [`DigestModel::determine_digest`].
877    ///
878    /// Emits [`Event::CommitResolved`] event if
879    /// [`Config::EmitEvents`] is `true`.
880    fn on_commit_resolve(
881        who: &Proprietor<T>,
882        reason: &Self::Reason,
883        digest: &Self::Digest,
884        value: Self::Asset,
885    ) {
886        if T::EmitEvents::get() {
887            #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
888            {
889                let Ok(digest_model) = Self::determine_digest(digest, reason) else {
890                    return;
891                };
892                Self::deposit_event(Event::<T, I>::CommitResolved {
893                    who: who.clone(),
894                    reason: *reason,
895                    model: digest_model,
896                    value,
897                });
898            }
899
900            #[cfg(not(any(feature = "dev", feature = "runtime-benchmarks")))]
901            {
902                Self::deposit_event(Event::<T, I>::CommitResolved {
903                    who: who.clone(),
904                    reason: *reason,
905                    digest: digest.clone(),
906                    value,
907                });
908            }
909        }
910    }
911
912    /// Hook called when a digest value is updated.
913    ///
914    /// This method delagates itself to [`CommitVariant::on_set_digest_variant`]
915    /// with the default position variant.
916    #[inline]
917    fn on_digest_update(digest: &Self::Digest, reason: &Self::Reason, value: Self::Asset) {
918        Self::on_set_digest_variant(digest, reason, value, &Default::default());
919    }
920
921    /// Hook called when a digest is successfully reaped.
922    ///
923    /// Emits [`Event::DigestReaped`] event if [`Config::EmitEvents`] is `true`.
924    fn on_reap_digest(digest: &Self::Digest, reason: &Self::Reason, dust: Self::Asset) {
925        if T::EmitEvents::get() {
926            // Emit the DigestReaped event
927            Self::deposit_event(Event::<T, I>::DigestReaped {
928                digest: digest.clone(),
929                reason: *reason,
930                dust,
931            });
932        }
933    }
934}
935
936// ===============================================================================
937// ```````````````````````````````` COMMIT INDEX `````````````````````````````````
938// ===============================================================================
939
940/// Implements [`CommitIndex`] for the pallet
941impl<T: Config<I>, I: 'static> CommitIndex<Proprietor<T>> for Pallet<T, I> {
942    /// Index struct representing an index's internal structure.
943    ///
944    /// This type holds all entries and their associated shares, used for commitment
945    /// calculations and value aggregations within the index.
946    type Index = IndexInfo<T, I>;
947
948    /// Shares unit defining an entry's proportional weight within the index.
949    ///
950    /// Shares represent each entry's relative contribution to the index capital,
951    /// used for calculating value distributions and reward/penalty allocations.
952    type Shares = T::Shares;
953
954    // -------------------------------------------------------------------------------
955    // ``````````````````````````````````` CHECKERS ``````````````````````````````````
956    // -------------------------------------------------------------------------------
957
958    /// Checks whether a specific index exists for the given reason.
959    ///
960    /// ## Returns
961    /// - `Ok(())` if the index exists
962    /// - `Err(DispatchError)` if the index does not exist
963    fn index_exists(reason: &Self::Reason, index_of: &Self::Digest) -> DispatchResult {
964        ensure!(
965            IndexMap::<T, I>::contains_key((reason, index_of)),
966            Error::<T, I>::IndexNotFound
967        );
968        Ok(())
969    }
970
971    /// Checks whether a specific entry exists within a given index.
972    ///
973    /// ## Returns
974    /// - `Ok(())` if the entry exists
975    /// - `Err(DispatchError)` if the entry does not exist
976    fn entry_exists(
977        reason: &Self::Reason,
978        index_of: &Self::Digest,
979        entry_of: &Self::Digest,
980    ) -> DispatchResult {
981        let index = Self::get_index(reason, index_of)?;
982        for entry in index.entries() {
983            if *entry_of == entry.digest() {
984                return Ok(());
985            }
986        }
987        Err(Error::<T, I>::EntryOfIndexNotFound.into())
988    }
989
990    /// Checks whether any index exists for the given reason.
991    ///
992    /// ## Returns
993    /// - `Ok(())` if at least one index exists
994    /// - `Err(DispatchError)` if no indexes exist
995    fn has_index(reason: &Self::Reason) -> DispatchResult {
996        ensure!(
997            IndexMap::<T, I>::iter_prefix((reason,)).next().is_some(),
998            Error::<T, I>::IndexNotFound
999        );
1000        Ok(())
1001    }
1002
1003    // -------------------------------------------------------------------------------
1004    // ``````````````````````````````````` GETTERS ```````````````````````````````````
1005    // -------------------------------------------------------------------------------
1006
1007    /// Retrieves the index information (meta-struct) for a given
1008    /// reason and index digest.
1009    ///
1010    /// ## Returns
1011    /// - `Ok(Index)` containing the index structure
1012    /// - `Err(DispatchError)`if the index does not exist
1013    fn get_index(
1014        reason: &Self::Reason,
1015        index_of: &Self::Digest,
1016    ) -> Result<Self::Index, DispatchError> {
1017        let index =
1018            IndexMap::<T, I>::get((reason, index_of)).ok_or(Error::<T, I>::IndexNotFound)?;
1019        Ok(index)
1020    }
1021
1022    /// Retrieves all entry digests and their shares for a specific index.
1023    ///
1024    /// ## Returns
1025    /// - `Ok(Vec<(Digest, Shares)>)` containing each entry's digest and shares
1026    /// - `Err(DispatchError)` if the index does not exist
1027    fn get_entries_shares(
1028        reason: &Self::Reason,
1029        index_of: &Self::Digest,
1030    ) -> Result<Vec<(Self::Digest, Self::Shares)>, DispatchError> {
1031        let mut vec = Vec::new();
1032        let index = Self::get_index(reason, index_of)?;
1033        for entry in index.entries() {
1034            let shares = entry.shares();
1035            vec.push((entry.digest(), shares));
1036        }
1037        Ok(vec)
1038    }
1039
1040    /// Computes the aggregated real-time value of a specific entry
1041    /// across all proprietors.
1042    ///
1043    /// Only includes commitments made via this index, not direct commitments
1044    /// to the entry digest.
1045    ///
1046    /// ## Returns
1047    /// - `Ok(Asset)` containing the total committed value for the entry
1048    /// - `Err(DispatchError)` if the entry does not exist
1049    fn get_entry_value(
1050        reason: &Self::Reason,
1051        index_of: &Self::Digest,
1052        entry_of: &Self::Digest,
1053    ) -> Result<Self::Asset, DispatchError> {
1054        Self::entry_exists(reason, index_of, entry_of)?;
1055        let iter = EntryMap::<T, I>::iter_prefix((reason, index_of, entry_of));
1056        let mut actual = Self::Asset::zero();
1057        for (who, _) in iter {
1058            let value =
1059                CommitHelpers::<T, I>::index_entry_commit_value(&who, reason, index_of, entry_of)?;
1060            actual = actual.saturating_add(value);
1061        }
1062        Ok(actual)
1063    }
1064
1065    /// Retrieves the real-time committed value of a specific entry
1066    /// for a given proprietor via an index.
1067    ///
1068    /// ## Returns
1069    /// - `Ok(Asset)` containing the proprietor's committed value for the entry
1070    /// - `Err(DispatchError)` if the entry does not exist
1071    fn get_entry_value_for(
1072        who: &Proprietor<T>,
1073        reason: &Self::Reason,
1074        index_of: &Self::Digest,
1075        entry_of: &Self::Digest,
1076    ) -> Result<Self::Asset, DispatchError> {
1077        let digest = Self::get_commit_digest(who, reason)?;
1078        Self::entry_exists(reason, index_of, entry_of)?;
1079        ensure!(digest == *index_of, Error::<T, I>::CommitNotFoundForEntry);
1080        let value =
1081            CommitHelpers::<T, I>::index_entry_commit_value(who, reason, index_of, entry_of)?;
1082        Ok(value)
1083    }
1084
1085    // -------------------------------------------------------------------------------
1086    // ````````````````````````````````` CONSTRUCTORS ````````````````````````````````
1087    // -------------------------------------------------------------------------------
1088
1089    /// Generates a unique digest for the given index using the proprietor and reason.
1090    ///
1091    /// ## Returns
1092    /// - `Ok(Digest)` with the newly generated digest.
1093    /// - `Err(DispatchError)` if digest generation fails
1094    fn gen_index_digest(
1095        from: &Proprietor<T>,
1096        reason: &Self::Reason,
1097        index: &Self::Index,
1098    ) -> Result<Self::Digest, DispatchError> {
1099        let target = from;
1100        let salt = frame_system::Pallet::<T>::account_nonce(from);
1101        let key_gen_item = IndexOfReason::<T, I>::new(*reason, index.clone());
1102
1103        let key =
1104            KeySeedFor::<Self::Digest, IndexOfReason<T, I>, T::Nonce, T::Hashing, T>::gen_key(
1105                target,
1106                &key_gen_item,
1107                salt,
1108            )
1109            .ok_or(Error::<T, I>::CannotGenerateDigest)?;
1110
1111        Ok(key)
1112    }
1113
1114    /// Prepares a new index instance from a list of entry digests and
1115    /// their corresponding shares.
1116    ///
1117    /// This function does **not** associate the index with a specific reason or
1118    /// proprietor internally. The caller is responsible for ensuring the index
1119    /// is correctly attached to a reason and, optionally, the creator.
1120    ///
1121    /// Entries with zero shares are silently ignored, as they carry no
1122    /// semantic contribution to the index.
1123    ///
1124    /// Entry digests are not validated to be direct digests. If a commitment
1125    /// is placed on the index, each entry digest will be funded accordingly
1126    /// through the normal deposit routing.
1127    ///
1128    /// Nested cases-such as index entries referencing other indexes or pools-
1129    /// are not supported and may be treated as new direct digests when routed
1130    /// through [`CommitDeposit::deposit_to_digest`]. Callers are responsible
1131    /// for validating such cases if required.
1132    ///
1133    /// - `who`: The proprietor creating the index.
1134    /// - `reason`: The reason under which the index is being prepared.
1135    ///
1136    /// ## Returns
1137    /// - `Ok(Index)` containing the prepared index
1138    /// - `Err(DispatchError)` if preparation fails
1139    fn prepare_index(
1140        _who: &Proprietor<T>,
1141        _reason: &Self::Reason,
1142        entries: &[(Self::Digest, Self::Shares)],
1143    ) -> Result<Self::Index, DispatchError> {
1144        // Initialize a new Entries collection for the index
1145        let mut entries_of = Vec::new();
1146        for (digest, shares) in entries {
1147            // Silently ignore non-share allocated entries
1148            if shares.is_zero() {
1149                continue;
1150            }
1151            // Create a new entry with the given variant
1152            let entry_info = EntryInfo::<T, I>::new(digest.clone(), *shares, Default::default())?;
1153            // Add entry to the index, checking for maximum capacity
1154            entries_of.push(entry_info);
1155        }
1156        // Construct the final IndexInfo object
1157        let index_info = IndexInfo::<T, I>::new(&mut Entries::<T, I>::new(entries_of)?)?;
1158        Ok(index_info)
1159    }
1160
1161    // -------------------------------------------------------------------------------
1162    // ``````````````````````````````````` MUTATORS ``````````````````````````````````
1163    // -------------------------------------------------------------------------------
1164
1165    /// Sets a new index for the given digest at the given reason.
1166    ///
1167    /// - The caller must ensure that the provided digest is **unique** i.e.,
1168    /// generated via [`CommitIndex::gen_index_digest`] and does not collide
1169    /// with existing indexes in the system.
1170    /// - This function is intended **only** for creating new indexes.  
1171    /// - Mutations to existing indexes are **not supported** here.  
1172    /// - For updating an index, a new index digest should be generated via
1173    /// [`CommitIndex::set_entry_shares`]
1174    ///
1175    /// ## Returns
1176    /// - `Ok(())` if the index was successfully inserted
1177    /// - `Err(DispatchError)` with `IndexDigestTaken` if the digest already exists
1178    fn set_index(
1179        who: &Proprietor<T>,
1180        reason: &Self::Reason,
1181        index: &Self::Index,
1182        digest: &Self::Digest,
1183    ) -> DispatchResult {
1184        // Ensure the digest does not already exist
1185        ensure!(
1186            !Self::index_exists(reason, digest).is_ok(),
1187            Error::<T, I>::IndexDigestTaken
1188        );
1189        // Insert the new index into the storage map
1190        IndexMap::<T, I>::insert((reason, digest), index);
1191        Self::on_set_index(who, digest, reason, index);
1192        Ok(())
1193    }
1194
1195    /// Updates or sets the shares for a specific entry of
1196    /// an index, producing a new index.
1197    ///
1198    /// - If the entry already exists in the index:
1199    ///   - If `shares` is zero, the entry is removed.
1200    ///   - Otherwise, the entry's shares are updated while preserving its existing variant.
1201    /// - If the entry does not exist:
1202    ///   - If `shares` is zero, the operation is a no-op and the original index is returned.
1203    ///   - Otherwise, a new entry is added with the default [`Config::Position`] variant.
1204    ///
1205    /// The newly added entry digest is not validated to be a direct digest and is
1206    /// accepted as provided through this function. If a commitment is placed on the
1207    /// index, it will be funded accordingly through normal deposit routing.
1208    ///
1209    /// Nested cases-such as entries referencing other indexes or pools-
1210    /// are not supported and may be treated as new direct digests when routed
1211    /// through [`CommitDeposit::deposit_to_digest`]. Callers are responsible
1212    /// for validating such cases if required.
1213    ///
1214    /// ## Returns
1215    /// - `Ok(Digest)` containing the resulting index digest (may be unchanged if no-op)
1216    /// - `Err(DispatchError)` if the operation fails
1217    fn set_entry_shares(
1218        who: &Proprietor<T>,
1219        reason: &Self::Reason,
1220        index_of: &Self::Digest,
1221        entry_of: &Self::Digest,
1222        shares: Self::Shares,
1223    ) -> Result<Self::Digest, DispatchError> {
1224        match Self::entry_exists(reason, index_of, entry_of).is_ok() {
1225            true => {
1226                if shares.is_zero() {
1227                    return CommitHelpers::<T, I>::remove_index_entry(
1228                        who, reason, index_of, entry_of,
1229                    );
1230                }
1231                let variant = &Self::get_entry_variant(reason, index_of, entry_of)?;
1232                // debug_assert!()
1233                CommitHelpers::<T, I>::set_index_entry(
1234                    who, reason, index_of, entry_of, shares, variant,
1235                )
1236            }
1237            false => {
1238                if shares.is_zero() {
1239                    return Ok(index_of.clone());
1240                }
1241                CommitHelpers::<T, I>::set_index_entry(
1242                    who,
1243                    reason,
1244                    index_of,
1245                    entry_of,
1246                    shares,
1247                    &Default::default(),
1248                )
1249            }
1250        }
1251    }
1252
1253    /// Removes an index if all entries have no committed funds.
1254    ///
1255    /// ## Returns
1256    /// - `Ok(())` if the index was successfully removed
1257    /// - `Err(DispatchError)` with `IndexHasFunds` if any entry still has commitments
1258    fn reap_index(reason: &Self::Reason, index_of: &Self::Digest) -> DispatchResult {
1259        let index = Self::get_index(reason, index_of)?;
1260
1261        ensure!(index.principal().is_zero(), Error::<T, I>::IndexHasFunds);
1262
1263        // Check that all entries are empty; otherwise, cannot reap
1264        for entry in index.entries() {
1265            let digest = &entry.digest();
1266            let mut iter = EntryMap::<T, I>::iter_prefix((reason, index_of, digest));
1267            if let Some((_, _)) = iter.next() {
1268                debug_assert!(
1269                    false,
1270                    "index {:?} of reason {:?} top-level principal 
1271                    does not have deposits but its entry-map has commits 
1272                    for entry {:?} found during reap-index attempt",
1273                    index_of, reason, digest
1274                );
1275                return Err(Error::<T, I>::IndexHasFunds.into());
1276            }
1277        }
1278
1279        // Remove the index after all checks pass
1280        IndexMap::<T, I>::remove((reason, index_of));
1281
1282        // Clean removal since entry-digests are funded as direct-digests
1283        // Just that commits are stored in a different layout
1284        Self::on_reap_index(index_of, reason, Zero::zero());
1285        Ok(())
1286    }
1287
1288    // -------------------------------------------------------------------------------
1289    // ```````````````````````````````````` HOOKS ````````````````````````````````````
1290    // -------------------------------------------------------------------------------
1291
1292    /// Emits an event when an index is created.
1293    ///
1294    /// - Records the index digest, reason, and entry-share mappings.
1295    /// - Emits [`Event::IndexInitialized`] or [`Event::IndexInitialized`]
1296    ///   depending on whether multiple variants are supported by [`Config::Position`]
1297    /// - Emits these events only if [`Config::EmitEvents`] is `true`.
1298    fn on_set_index(
1299        _who: &Proprietor<T>,
1300        index_of: &Self::Digest,
1301        reason: &Self::Reason,
1302        index: &Self::Index,
1303    ) {
1304        if T::EmitEvents::get() {
1305            #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
1306            {
1307                let mut entries = Vec::new();
1308                for entry in index.entries() {
1309                    let digest = entry.digest().clone();
1310                    let shares = entry.shares();
1311                    let variant = &entry.variant();
1312                    entries.push((digest, shares, variant.clone()));
1313                }
1314
1315                Self::deposit_event(Event::<T, I>::IndexInitialized {
1316                    index_of: index_of.clone(),
1317                    reason: *reason,
1318                    entries,
1319                })
1320            }
1321
1322            #[cfg(not(any(feature = "dev", feature = "runtime-benchmarks")))]
1323            {
1324                Self::deposit_event(Event::<T, I>::IndexInitialized {
1325                    index_of: index_of.clone(),
1326                    reason: *reason,
1327                })
1328            }
1329        }
1330    }
1331
1332    /// Emits an event when an index is successfully reaped.
1333    ///
1334    /// Emits a [`Event::IndexReaped`] event if [`Config::EmitEvents`] is `true`.
1335    fn on_reap_index(index_of: &Self::Digest, reason: &Self::Reason, dust: Self::Asset) {
1336        debug_assert!(
1337            dust.is_zero(),
1338            "index digest {:?} of reason {:?} reaped with non-zero dust {:?}",
1339            index_of,
1340            reason,
1341            dust
1342        );
1343        if T::EmitEvents::get() {
1344            Self::deposit_event(Event::<T, I>::IndexReaped {
1345                index_of: index_of.clone(),
1346                reason: *reason,
1347            });
1348        }
1349    }
1350}
1351
1352// ===============================================================================
1353// ````````````````````````````````` COMMIT POOL `````````````````````````````````
1354// ===============================================================================
1355
1356/// Implements [`CommitPool`] for the pallet
1357impl<T: Config<I>, I: 'static> CommitPool<Proprietor<T>> for Pallet<T, I> {
1358    /// Pool struct representing a pool's internal structure.
1359    ///
1360    /// Contains the pool's balance, capital, slots, commission rate, and other
1361    /// metadata required for pool operations and value calculations.
1362    type Pool = PoolInfo<T, I>;
1363
1364    /// Commission rate for the pool.
1365    ///
1366    /// Represents the fraction of withdrawals collected by the pool manager
1367    /// as compensation for managing the pool.
1368    type Commission = T::Commission;
1369
1370    // -------------------------------------------------------------------------------
1371    // ``````````````````````````````````` CHECKERS ``````````````````````````````````
1372    // -------------------------------------------------------------------------------
1373
1374    /// Checks whether a pool exists for the given reason and digest.
1375    ///
1376    /// ## Returns
1377    /// - `Ok(())` if the pool exists
1378    /// - `Err(DispatchError)` if the pool does not exist
1379    fn pool_exists(reason: &Self::Reason, pool_of: &Self::Digest) -> DispatchResult {
1380        ensure!(
1381            PoolMap::<T, I>::contains_key((reason, pool_of)),
1382            Error::<T, I>::PoolNotFound
1383        );
1384        Ok(())
1385    }
1386
1387    /// Checks whether a specific slot exists within a given pool.
1388    ///
1389    /// ## Returns
1390    /// - `Ok(())` if the slot exists
1391    /// - `Err(DispatchError)` with `SlotOfPoolNotFound` if the slot does not exist
1392    fn slot_exists(
1393        reason: &Self::Reason,
1394        pool_of: &Self::Digest,
1395        slot_of: &Self::Digest,
1396    ) -> DispatchResult {
1397        let pool = Self::get_pool(reason, pool_of)?;
1398        for slot in pool.slots() {
1399            if slot.digest() == *slot_of {
1400                return Ok(());
1401            }
1402        }
1403        Err(Error::<T, I>::SlotOfPoolNotFound.into())
1404    }
1405
1406    /// Checks whether at least one pool exists for the given reason.
1407    ///
1408    /// ## Returns
1409    /// - `Ok(())` if at least one pool exists
1410    /// - `Err(DispatchError)` with `PoolNotFound` if no pools exist
1411    fn has_pool(reason: &Self::Reason) -> DispatchResult {
1412        ensure!(
1413            PoolMap::<T, I>::iter_prefix((reason,)).next().is_some(),
1414            Error::<T, I>::PoolNotFound
1415        );
1416        Ok(())
1417    }
1418
1419    // -------------------------------------------------------------------------------
1420    // ``````````````````````````````````` GETTERS ```````````````````````````````````
1421    // -------------------------------------------------------------------------------
1422
1423    /// Retrieves the manager of a given pool.
1424    ///
1425    /// Invariant enforced that every valid pool, must have a
1426    /// valid manager. For unmanaged distribution of commitments,
1427    /// [`CommitIndex`] exists.
1428    ///
1429    /// ## Returns
1430    /// - `Ok(Proprietor)` containing the pool manager's account id
1431    /// - `Err(DispatchError)` with `PoolManagerNotFound` if no manager is set
1432    fn get_manager(
1433        reason: &Self::Reason,
1434        pool_of: &Self::Digest,
1435    ) -> Result<Proprietor<T>, DispatchError> {
1436        Self::pool_exists(reason, pool_of)?;
1437        let pool_manager = PoolManager::<T, I>::get((reason, pool_of));
1438        debug_assert!(
1439            pool_manager.is_some(),
1440            "pool {:?} of reason {:?} exists but manager is not",
1441            pool_of,
1442            reason
1443        );
1444        let pool_manager = pool_manager.ok_or(Error::<T, I>::PoolManagerNotFound)?;
1445        Ok(pool_manager)
1446    }
1447
1448    /// Retrieves the commission rate of a given pool.
1449    ///
1450    /// ## Returns
1451    /// - `Ok(Commission)` containing the pool's commission rate
1452    /// - `Err(DispatchError)` with `PoolNotFound` if the pool does not exist
1453    fn get_commission(
1454        reason: &Self::Reason,
1455        pool_of: &Self::Digest,
1456    ) -> Result<Self::Commission, DispatchError> {
1457        let pool = Self::get_pool(reason, pool_of)?;
1458        Ok(pool.commission())
1459    }
1460
1461    /// Retrieves the pool information for a given reason and digest.
1462    ///
1463    /// ## Returns
1464    /// - `Ok(Pool)` containing the pool structure
1465    /// - `Err(DispatchError)` with `PoolNotFound` if the pool does not exist
1466    fn get_pool(
1467        reason: &Self::Reason,
1468        pool_of: &Self::Digest,
1469    ) -> Result<Self::Pool, DispatchError> {
1470        let pool = PoolMap::<T, I>::get((reason, pool_of)).ok_or(Error::<T, I>::PoolNotFound)?;
1471        Ok(pool)
1472    }
1473
1474    /// Retrieves all slot digests of a pool along with their respective shares.
1475    ///
1476    /// ## Returns
1477    /// - `Ok(Vec<(Digest, Shares)>)` containing each slot's digest and shares
1478    /// - `Err(DispatchError)` if the pool does not exist
1479    fn get_slots_shares(
1480        reason: &Self::Reason,
1481        pool_of: &Self::Digest,
1482    ) -> Result<Vec<(Self::Digest, Self::Shares)>, DispatchError> {
1483        let pool = Self::get_pool(reason, pool_of)?;
1484        let mut vec = Vec::new();
1485        for slot in pool.slots() {
1486            let slot_digest = &slot.digest();
1487            let shares = slot.shares();
1488            vec.push((slot_digest.clone(), shares))
1489        }
1490        Ok(vec)
1491    }
1492
1493    /// Computes the real-time value of a specific slot digest in a pool across all proprietors.
1494    ///
1495    /// ## Returns
1496    /// - `Ok(Asset)`containing the aggregated slot value
1497    /// - `Err(DispatchError)` if the slot does not exist
1498    fn get_slot_value(
1499        reason: &Self::Reason,
1500        pool_of: &Self::Digest,
1501        slot_of: &Self::Digest,
1502    ) -> Result<Self::Asset, DispatchError> {
1503        // Ensure the slot exists
1504        Pallet::<T, I>::slot_exists(reason, pool_of, slot_of)?;
1505
1506        // Retrieve pool info
1507        let pool_info = Pallet::<T, I>::get_pool(reason, pool_of)?;
1508        let slots = &pool_info.slots();
1509
1510        // Locate the entry within the index
1511        let mut slot_idx = None;
1512        for (i, slot) in slots.iter().enumerate() {
1513            if slot.digest() == *slot_of {
1514                slot_idx = Some(i);
1515            }
1516        }
1517        let Some(slot_idx) = slot_idx else {
1518            return Err(Error::<T, I>::SlotOfPoolNotFound.into());
1519        };
1520
1521        // Get the entry object and its variant
1522        let slot = slots.get(slot_idx);
1523
1524        debug_assert!(
1525            slot.is_some(),
1526            "pool {:?} of reason {:?} slot {:?} is found 
1527            during iteration, but vector get failed",
1528            pool_of,
1529            reason,
1530            slot_of
1531        );
1532        let slot = slot.ok_or(Error::<T, I>::SlotOfPoolNotFound)?;
1533        let digest = &slot.digest();
1534        let variant = &slot.variant();
1535
1536        let digest_info =
1537            DigestMap::<T, I>::get((reason, digest)).ok_or(Error::<T, I>::SlotDigestNotFound)?;
1538
1539        let balance = digest_info.get_balance(variant);
1540        debug_assert!(
1541            balance.is_some(),
1542            "pool-digest {:?} of reason {:?} slot {:?} variant {:?} balance 
1543            was not initiated properly in the balance vector 
1544            properly during slot-value retrieval",
1545            pool_of,
1546            reason,
1547            slot_of,
1548            variant,
1549        );
1550        let balance = balance.ok_or(Error::<T, I>::DigestVariantBalanceNotFound)?;
1551        let slot_commit = &slot.commit();
1552
1553        if *slot_commit == Default::default() {
1554            return Ok(Zero::zero());
1555        }
1556
1557        let take = receipt_active_value(balance, variant, digest, &slot.commit())?;
1558
1559        Ok(take)
1560    }
1561
1562    /// Computes the real-time value of a specific slot digest in a pool for a given proprietor.
1563    ///
1564    /// ## Returns
1565    /// - `Ok(Asset)` containing the proprietor's slot value
1566    /// - `Err(DispatchError)` if the slot does not exist
1567    fn get_slot_value_for(
1568        who: &Proprietor<T>,
1569        reason: &Self::Reason,
1570        pool_of: &Self::Digest,
1571        slot_of: &Self::Digest,
1572    ) -> Result<Self::Asset, DispatchError> {
1573        let digest = Self::get_commit_digest(who, reason)?;
1574        Self::slot_exists(reason, pool_of, slot_of)?;
1575        ensure!(digest == *pool_of, Error::<T, I>::CommitNotFoundForSlot);
1576        CommitHelpers::<T, I>::pool_slot_commit_value(who, reason, pool_of, slot_of)
1577    }
1578
1579    /// Computes the real-time value of a pool-commit for a given proprietor.
1580    ///
1581    /// ## Returns
1582    /// - `Ok(Asset)` containing the proprietor's pool value
1583    /// - `Err(DispatchError)` if the slot does not exist
1584    fn get_pool_value_for(
1585        who: &Proprietor<T>,
1586        reason: &Self::Reason,
1587        pool_of: &Self::Digest,
1588    ) -> Result<Self::Asset, DispatchError> {
1589        let digest = Self::get_commit_digest(who, reason)?;
1590        Self::pool_exists(reason, pool_of)?;
1591        ensure!(digest == *pool_of, Error::<T, I>::CommitNotFoundForSlot);
1592        CommitHelpers::<T, I>::pool_commit_value(who, reason, pool_of)
1593    }
1594
1595    // -------------------------------------------------------------------------------
1596    // ````````````````````````````````` CONSTRUCTORS ````````````````````````````````
1597    // -------------------------------------------------------------------------------
1598
1599    /// Generates a unique digest for a pool derived from a given index.
1600    ///
1601    /// Pools are created from indexes, and each pool digest must be unique. This function
1602    /// deterministically derives a digest using:
1603    /// - The caller (`who`) as the target
1604    /// - The `reason` under which the pool is created
1605    /// - The pool's internal entries and specified `commission`
1606    /// - The caller's account nonce as a salt
1607    ///
1608    /// This digest can then be used to create or reference the pool in the system.
1609    ///
1610    /// ## Returns
1611    /// - `Ok(Digest)` containing the unique pool digest
1612    /// - `Err(DispatchError)` if digest generation fails
1613    fn gen_pool_digest(
1614        who: &Proprietor<T>,
1615        reason: &Self::Reason,
1616        index_of: &Self::Digest,
1617        commission: Self::Commission,
1618    ) -> Result<Self::Digest, DispatchError> {
1619        let pool_index = Self::get_index(reason, index_of)?;
1620        let actual_pool = PoolInfo::<T, I>::new(pool_index.reveal_entries(), commission);
1621        // Since the function only accumulates capital and straight conversion
1622        // It should pass, unless some commission checks are implied inside
1623        debug_assert!(
1624            actual_pool.is_ok(),
1625            "pool-info construction for reason {:?}
1626            from a already valid index {:?} entries and commission {:?} has failed",
1627            reason,
1628            index_of,
1629            commission
1630        );
1631        let actual_pool = actual_pool?;
1632        let target = who;
1633        let salt = frame_system::Pallet::<T>::account_nonce(who);
1634        let key_gen_item = PoolOfReason::<T, I>::new(*reason, actual_pool.clone());
1635        let key = KeySeedFor::<Self::Digest, PoolOfReason<T, I>, T::Nonce, T::Hashing, T>::gen_key(
1636            target,
1637            &key_gen_item,
1638            salt,
1639        )
1640        .ok_or(Error::<T, I>::CannotGenerateDigest)?;
1641        Ok(key)
1642    }
1643
1644    // -------------------------------------------------------------------------------
1645    // ``````````````````````````````````` MUTATORS ``````````````````````````````````
1646    // -------------------------------------------------------------------------------
1647
1648    /// Creates a new pool based on an existing index with a specified commission rate.
1649    ///
1650    /// Pools are immutable with respect to their commission. Any changes to entries
1651    /// require modifying slots directly or creating a new pool.
1652    ///
1653    /// - Retrieves the index information to populate the pool slots.
1654    /// - Converts each index entry into a pool slot, preserving the shares.
1655    /// - Sets the caller as the manager of the new pool.
1656    ///
1657    /// ## Returns
1658    /// - `Ok(())` if the pool was successfully created
1659    /// - `Err(DispatchError)` with `PoolDigestTaken` if a pool with this digest already exists
1660    fn set_pool(
1661        who: &Proprietor<T>,
1662        reason: &Self::Reason,
1663        pool_of: &Self::Digest,
1664        index_of: &Self::Digest,
1665        commission: Self::Commission,
1666    ) -> DispatchResult {
1667        // Check if the pool already exists
1668        ensure!(
1669            Self::pool_exists(reason, pool_of).is_err(),
1670            Error::<T, I>::PoolDigestTaken
1671        );
1672
1673        // Retrieve index information to initialize the pool slots
1674        let index_info = Self::get_index(reason, index_of)?;
1675
1676        // Create a new pool object from index entries and the specified commission
1677        let pool_info = PoolInfo::<T, I>::new(index_info.reveal_entries(), commission);
1678
1679        // Since the function only accumulates capital and straight conversion
1680        // It should pass, unless some commission checks are implied inside
1681        debug_assert!(
1682            pool_info.is_ok(),
1683            "pool-info construction for new pool {:?} of reason {:?}
1684            from a already valid index {:?} entries and commission {:?} has failed",
1685            pool_of,
1686            reason,
1687            index_of,
1688            commission
1689        );
1690
1691        let pool_info = pool_info?;
1692
1693        // Insert the new pool into storage
1694        PoolMap::<T, I>::insert((reason, pool_of), &pool_info);
1695
1696        // Assign the caller as the manager of the pool
1697        let result = Self::set_pool_manager(reason, pool_of, who);
1698
1699        debug_assert!(
1700            result.is_ok(),
1701            "recently created pool {:?} info inserted but 
1702            later logic set poolmanager {:?} failed",
1703            pool_of,
1704            who
1705        );
1706
1707        result?;
1708
1709        Self::on_set_pool(who, pool_of, reason, &pool_info);
1710        Ok(())
1711    }
1712
1713    /// Sets or updates the manager for a specific pool.
1714    ///
1715    /// The manager is responsible for handeling pool operations including risk management,
1716    /// applying strategies on behalf of pool participants, and earning commission
1717    /// based on the pool's rules.
1718    ///
1719    /// ### Returns
1720    /// - `Ok(())` if the manager was successfully set
1721    /// - `Err(DispatchError)` if the pool does not exist
1722    fn set_pool_manager(
1723        reason: &Self::Reason,
1724        pool_of: &Self::Digest,
1725        manager: &Proprietor<T>,
1726    ) -> DispatchResult {
1727        Self::pool_exists(reason, pool_of)?;
1728        PoolManager::<T, I>::insert((reason, pool_of), manager);
1729        Self::on_set_manager(pool_of, reason, manager);
1730        Ok(())
1731    }
1732
1733    /// Sets or updates the shares of a specific slot within a pool.
1734    ///
1735    /// Unlike indexes, pools are mutable and managed internally, so modifying a slot
1736    /// does **not** produce a new digest. Each mutation releases and recovers the pool
1737    /// to maintain real-time balances.
1738    ///
1739    /// If the slot already exists:
1740    /// - A zero share value removes the slot from the pool.
1741    /// - A non-zero share value updates the slot's shares while keeping its variant.
1742    ///
1743    /// Nested cases-such as pool slots referencing other pools or indexes
1744    /// are not supported and may be treated as new direct digests when routed
1745    /// through [`CommitDeposit::deposit_to_digest`]. Callers are responsible for
1746    /// validating such cases if required.
1747    ///
1748    /// If the slot does not exist:
1749    /// - A zero share value returns early without any changes.
1750    /// - A non-zero share value creates a new slot with the default variant.
1751    ///
1752    /// DispatchError if fails
1753    fn set_slot_shares(
1754        who: &Proprietor<T>,
1755        reason: &Self::Reason,
1756        pool_of: &Self::Digest,
1757        slot_of: &Self::Digest,
1758        shares: Self::Shares,
1759    ) -> DispatchResult {
1760        match Self::slot_exists(reason, pool_of, slot_of).is_ok() {
1761            true => {
1762                let variant = Self::get_slot_variant(reason, pool_of, slot_of);
1763                debug_assert!(
1764                    variant.is_ok(),
1765                    "slot {:?} exists for pool {:?} of reason {:?} 
1766                    but its variant (must-required) is unavailable",
1767                    slot_of,
1768                    pool_of,
1769                    reason
1770                );
1771
1772                let variant = variant?;
1773
1774                match shares.is_zero() {
1775                    true => CommitHelpers::<T, I>::remove_pool_slot(who, reason, pool_of, slot_of)?,
1776                    false => CommitHelpers::<T, I>::set_pool_slot(
1777                        who, reason, pool_of, slot_of, shares, &variant,
1778                    )?,
1779                }
1780            }
1781            false => {
1782                if shares.is_zero() {
1783                    return Ok(());
1784                }
1785                CommitHelpers::<T, I>::set_pool_slot(
1786                    who,
1787                    reason,
1788                    pool_of,
1789                    slot_of,
1790                    shares,
1791                    &T::Position::default(),
1792                )?
1793            }
1794        }
1795        Self::on_set_slot_shares(pool_of, reason, slot_of, shares);
1796        Ok(())
1797    }
1798
1799    /// Removes a pool from the system if all deposits have been withdrawn.
1800    ///
1801    /// Any residual funds (dust) remaining in the pool are automatically refunded
1802    /// to the pool manager.
1803    ///
1804    /// This function doesn't ensures the pool's current manager as it should be
1805    /// validated elsewhere if required.
1806    ///
1807    /// ## Returns
1808    /// - `Ok(())` if the pool was successfully removed
1809    /// - `Err(DispatchError)` otherwise
1810    fn reap_pool(reason: &Self::Reason, pool_of: &Self::Digest) -> DispatchResult {
1811        let pool = Self::get_pool(reason, pool_of)?;
1812        let balance = &pool.balance();
1813
1814        // Cannot reap a pool if deposits still exist
1815        if has_deposits(balance, &Default::default(), pool_of).is_ok() {
1816            return Err(Error::<T, I>::PoolHasFunds.into());
1817        }
1818
1819        // Refund any leftover effective balance to the manager
1820        let effective = balance_total(balance, &Default::default(), pool_of)?;
1821        if !effective.is_zero() {
1822            let imbalance = AssetDelta::<T, I> {
1823                deposit: Zero::zero(),
1824                withdraw: effective,
1825            };
1826            let manager = Self::get_manager(reason, pool_of);
1827            debug_assert!(
1828                manager.is_ok(),
1829                "pool {:?} for reason {:?} exists but manager is not",
1830                pool_of,
1831                reason
1832            );
1833            let manager = manager?;
1834            let dust_retn = CommitHelpers::<T, I>::resolve_imbalance(&manager, imbalance)?;
1835            CommitHelpers::<T, I>::sub_from_total_value(reason, dust_retn)?;
1836        }
1837        // Remove pool and its manager from storage
1838        PoolMap::<T, I>::remove((reason, pool_of));
1839        PoolManager::<T, I>::remove((reason, pool_of));
1840
1841        // Managers are redunded with remaining dust unlike digests which
1842        // doesn't have any informal nominee
1843        Self::on_reap_pool(pool_of, reason, Zero::zero());
1844        Ok(())
1845    }
1846
1847    // -------------------------------------------------------------------------------
1848    // ```````````````````````````````````` HOOKS ````````````````````````````````````
1849    // -------------------------------------------------------------------------------
1850
1851    /// Emits an event when a new pool is created or initialized.
1852    ///
1853    /// - Includes all underlying slots and the commission set for the pool manager.
1854    ///
1855    /// - Emits [`Event::PoolInitialized`] or [`Event::PoolInitialized`]
1856    ///   depending on whether multiple variants are supported by [`Config::Position`].
1857    /// - Emits these events only if [`Config::EmitEvents`] is `true`.
1858    fn on_set_pool(
1859        _who: &Proprietor<T>,
1860        pool_of: &Self::Digest,
1861        reason: &Self::Reason,
1862        pool: &Self::Pool,
1863    ) {
1864        if T::EmitEvents::get() {
1865            #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
1866            {
1867                let mut slots = Vec::new();
1868                for slot in pool.slots() {
1869                    let slot_digest = slot.digest().clone();
1870                    let shares = slot.shares();
1871                    let variant = &slot.variant();
1872                    slots.push((slot_digest, shares, variant.clone()));
1873                }
1874                let commission = pool.commission();
1875                Self::deposit_event(Event::<T, I>::PoolInitialized {
1876                    pool_of: pool_of.clone(),
1877                    reason: *reason,
1878                    commission,
1879                    slots,
1880                });
1881            }
1882
1883            #[cfg(not(any(feature = "dev", feature = "runtime-benchmarks")))]
1884            {
1885                let commission = pool.commission();
1886                Self::deposit_event(Event::<T, I>::PoolInitialized {
1887                    pool_of: pool_of.clone(),
1888                    reason: *reason,
1889                    commission,
1890                });
1891            }
1892        }
1893    }
1894
1895    /// Emits an event when the manager of a pool is set or updated.
1896    ///
1897    /// Emits a [`Event::PoolManager`] event if [`Config::EmitEvents`] is `true`.
1898    fn on_set_manager(pool_of: &Self::Digest, reason: &Self::Reason, manager: &Proprietor<T>) {
1899        if T::EmitEvents::get() {
1900            Self::deposit_event(Event::<T, I>::PoolManager {
1901                pool_of: pool_of.clone(),
1902                reason: *reason,
1903                manager: manager.clone(),
1904            });
1905        }
1906    }
1907
1908    /// Emits an event when the shares of a specific slot in a pool are updated.
1909    ///
1910    /// This method delagates itself to [`PoolVariant::on_set_slot_of_variant`]
1911    /// with the default position variant.
1912    #[inline]
1913    fn on_set_slot_shares(
1914        pool_of: &Self::Digest,
1915        reason: &Self::Reason,
1916        slot_of: &Self::Digest,
1917        shares: Self::Shares,
1918    ) {
1919        Self::on_set_slot_of_variant(pool_of, reason, slot_of, Some(shares), &Default::default());
1920    }
1921
1922    /// Emits an event when a pool is reaped (removed).
1923    ///
1924    /// Emits a [`Event::PoolReaped`] event if [`Config::EmitEvents`] is `true`.
1925    fn on_reap_pool(pool_of: &Self::Digest, reason: &Self::Reason, dust: Self::Asset) {
1926        debug_assert!(
1927            dust.is_zero(),
1928            "pool digest {:?} of reason {:?} reaped with non-zero dust {:?}",
1929            pool_of,
1930            reason,
1931            dust
1932        );
1933        if T::EmitEvents::get() {
1934            Self::deposit_event(Event::<T, I>::PoolReaped {
1935                pool_of: pool_of.clone(),
1936                reason: *reason,
1937            });
1938        }
1939    }
1940}
1941
1942// ===============================================================================
1943// ```````````````````````````````` COMMIT VARIANT ```````````````````````````````
1944// ===============================================================================
1945
1946/// Implements [`CommitVariant`] for the pallet
1947impl<T: Config<I>, I: 'static> CommitVariant<Proprietor<T>> for Pallet<T, I> {
1948    /// Defines the commitment position variant type for a proprietor.
1949    ///
1950    /// Acts as the logical "stance" or directional disposition of a commitment.
1951    type Position = T::Position;
1952
1953    // -------------------------------------------------------------------------------
1954    // ``````````````````````````````````` CHECKERS ``````````````````````````````````
1955    // -------------------------------------------------------------------------------
1956
1957    /// Validates whether a digest-variant value can be set.
1958    ///
1959    /// Same as the default trait validation, but extended with additional
1960    /// checks against the underlying balance model to ensure minting or
1961    /// reaping can actually be applied.
1962    ///
1963    /// In the lazy balance model, value updates are interpreted as:
1964    /// - Increase -> mint
1965    /// - Decrease -> reap
1966    ///
1967    /// Missing digest or variant state is treated as a default
1968    /// (fresh) balance, but validation still depends on the underlying
1969    /// lazy balance model and may fail.
1970    ///
1971    /// ## Returns
1972    /// - `Ok(())` if validation succeeds
1973    /// - `Err(DispatchError)` if any constraint is violated
1974    fn can_set_digest_variant_value(
1975        reason: &Self::Reason,
1976        digest: &Self::Digest,
1977        value: Self::Asset,
1978        variant: &Self::Position,
1979        qualifier: &Self::Intent,
1980    ) -> DispatchResult {
1981        let current = Self::get_digest_variant_value(reason, digest, variant)?;
1982        match current.cmp(&value) {
1983            Ordering::Less => {
1984                // Mint path (increase)
1985                let balance = DigestMap::<T, I>::get((reason, digest))
1986                    .and_then(|digest_info| digest_info.get_balance(variant).cloned())
1987                    .unwrap_or_default();
1988                let limits = mint_limits_of(&balance, variant, digest, qualifier)?;
1989                let mintable = value.saturating_sub(current);
1990                ensure!(
1991                    <Self::Limits as Extent>::contains(&limits, mintable),
1992                    Error::<T, I>::MintingOffLimits,
1993                );
1994                can_mint(&balance, variant, digest, &mintable, qualifier)?;
1995            }
1996            Ordering::Greater => {
1997                // Reap path (decrease)
1998                let balance = DigestMap::<T, I>::get((reason, digest))
1999                    .and_then(|digest_info| digest_info.get_balance(variant).cloned())
2000                    .unwrap_or_default();
2001                let limits = reap_limits_of(&balance, variant, digest, qualifier)?;
2002                let reapable = current.saturating_sub(value);
2003                ensure!(
2004                    <Self::Limits as Extent>::contains(&limits, reapable),
2005                    Error::<T, I>::ReapingOffLimits,
2006                );
2007                can_reap(&balance, variant, digest, &reapable, qualifier)?;
2008            }
2009            Ordering::Equal => {
2010                // No-op
2011            }
2012        }
2013        Ok(())
2014    }
2015
2016    /// Validates whether a new commitment can be placed for a specific variant.
2017    ///
2018    /// Same as the default trait validation, but extended with an additional
2019    /// check against the underlying balance model to ensure the deposit can
2020    /// actually be applied.
2021    ///
2022    /// Missing digest or variant state is treated as a fresh balance; in such
2023    /// cases, a default balance is used to derive limits and validate the deposit.
2024    ///
2025    /// ## Returns
2026    /// - `Ok(())` if validation succeeds
2027    /// - `Err(DispatchError)` if any constraint is violated
2028    fn can_place_commit_of_variant(
2029        who: &Proprietor<T>,
2030        reason: &Self::Reason,
2031        digest: &Self::Digest,
2032        variant: &Self::Position,
2033        value: Self::Asset,
2034        qualifier: &Self::Intent,
2035    ) -> DispatchResult {
2036        ensure!(
2037            Self::commit_exists(who, reason).is_err(),
2038            Error::<T, I>::CommitAlreadyExists
2039        );
2040        let max = Self::available_funds(who);
2041        ensure!(max >= value, Error::<T, I>::InsufficientFunds);
2042        let balance = DigestMap::<T, I>::get((reason, digest))
2043            .and_then(|digest_info| digest_info.get_balance(variant).cloned())
2044            .unwrap_or_default();
2045
2046        let limits = deposit_limits_of(&balance, variant, digest, qualifier)?;
2047        ensure!(
2048            <Self::Limits as Extent>::contains(&limits, value),
2049            Error::<T, I>::PlacingOffLimits
2050        );
2051        can_deposit(&balance, variant, digest, &value, qualifier)
2052    }
2053
2054    // -------------------------------------------------------------------------------
2055    // ``````````````````````````````````` GETTERS ```````````````````````````````````
2056    // -------------------------------------------------------------------------------
2057
2058    /// Retrieves the current commitment variant for a
2059    /// proprietor and reason.
2060    ///
2061    /// In-case of indexes and pools its always the default variant of
2062    /// [`Config::Position`] since its entries and slots carry the actual
2063    /// variants for which this commit is distributed.
2064    ///
2065    /// ## Returns
2066    /// - `Ok(Position)` containing the commitment's variant
2067    /// - `Err(DispatchError)`  with `CommitNotFound` if no commitment exists
2068    fn get_commit_variant(
2069        who: &Proprietor<T>,
2070        reason: &Self::Reason,
2071    ) -> Result<Self::Position, DispatchError> {
2072        let Some(commit_info) = CommitMap::<T, I>::get((who, reason)) else {
2073            return Err(Error::<T, I>::CommitNotFound.into());
2074        };
2075        Ok(commit_info.variant())
2076    }
2077
2078    /// Retrieves the real-time effective value of a specific digest
2079    /// variant for a given reason.
2080    ///
2081    /// ## Returns
2082    /// - `Ok(Asset)` containing the variant's effective value,
2083    /// or zero if the variant does not exist
2084    /// - `Err(DispatchError)` with `DigestNotFound` if the digest
2085    /// does not exist
2086    fn get_digest_variant_value(
2087        reason: &Self::Reason,
2088        digest: &Self::Digest,
2089        variant: &Self::Position,
2090    ) -> Result<Self::Asset, DispatchError> {
2091        let digest_info =
2092            DigestMap::<T, I>::get((reason, digest)).ok_or(Error::<T, I>::DigestNotFound)?;
2093        // Return effective balance if present, otherwise zero
2094        let Some(balance) = digest_info.get_balance(variant) else {
2095            return balance_total::<T, I>(&Default::default(), variant, digest);
2096        };
2097
2098        balance_total(balance, variant, digest)
2099    }
2100
2101    /// Derives minting limits for a specific digest variant.
2102    ///
2103    /// Fetches the current lazy balance of the given `(reason, digest, variant)`
2104    /// and computes the applicable minting limits using the underlying balance model.
2105    ///
2106    /// If the digest not exists nor the variant has an initialized balance,
2107    /// the limits are derived using a default (empty) balance.
2108    ///
2109    /// ## Returns
2110    /// - `Ok(Limits)` containing the derived minting constraints
2111    /// - `Err(DispatchError)` if the limit derivation fails
2112    fn digest_mint_limits_of_variant(
2113        digest: &Self::Digest,
2114        reason: &Self::Reason,
2115        variant: &Self::Position,
2116        qualifier: &Self::Intent,
2117    ) -> Result<Self::Limits, DispatchError> {
2118        let balance = DigestMap::<T, I>::get((reason, digest))
2119            .and_then(|digest_info| digest_info.get_balance(variant).cloned())
2120            .unwrap_or_default();
2121        let limits = mint_limits_of(&balance, variant, digest, qualifier)?;
2122        Ok(limits)
2123    }
2124
2125    /// Derives reaping limits for a specific digest variant.
2126    ///
2127    /// Fetches the current lazy balance of the given `(reason, digest, variant)`
2128    /// and computes the applicable reaping limits using the underlying balance model.
2129    ///
2130    /// If the digest does not exists nor the variant has a initialized balance,
2131    /// limits are derived using a default (empty) balance.
2132    ///
2133    /// ## Returns
2134    /// - `Ok(Limits)` containing the derived reaping constraints
2135    /// - `Err(DispatchError)` if the limit derivation fails
2136    fn digest_reap_limits_of_variant(
2137        digest: &Self::Digest,
2138        reason: &Self::Reason,
2139        variant: &Self::Position,
2140        qualifier: &Self::Intent,
2141    ) -> Result<Self::Limits, DispatchError> {
2142        let balance = DigestMap::<T, I>::get((reason, digest))
2143            .and_then(|digest_info| digest_info.get_balance(variant).cloned())
2144            .unwrap_or_default();
2145
2146        let limits = reap_limits_of(&balance, variant, digest, qualifier)?;
2147        Ok(limits)
2148    }
2149
2150    /// Derives the place commit limits for the given commitment-params.
2151    ///
2152    /// Fetches the current lazy balance of the given `(reason, digest, variant)`
2153    /// and computes the applicable deposit limits using the underlying
2154    /// balance model.
2155    ///
2156    /// - For **Direct digests**, limits are derived based on existing digest balance.
2157    /// - For **Index or Pool digests**, no digest-level balance info exists,
2158    ///   hence limits are treated as **unbounded**.
2159    ///
2160    /// If the direct digest does not exist or the variant has no initialized balance,
2161    /// limits are derived using a default (empty) balance.
2162    ///
2163    /// ## Returns
2164    /// - `Ok(Limits)` containing the derived placement constraints
2165    /// - `Err(DispatchError)` if the limit derivation fails
2166    fn place_commit_limits_of_variant(
2167        _who: &Proprietor<T>,
2168        reason: &Self::Reason,
2169        digest: &Self::Digest,
2170        variant: &Self::Position,
2171        qualifier: &Self::Intent,
2172    ) -> Result<Self::Limits, DispatchError> {
2173        let balance = DigestMap::<T, I>::get((reason, digest))
2174            .and_then(|digest_info| digest_info.get_balance(variant).cloned())
2175            .unwrap_or_default();
2176
2177        let limits = deposit_limits_of(&balance, variant, digest, qualifier)?;
2178        Ok(limits)
2179    }
2180
2181    // -------------------------------------------------------------------------------
2182    // ``````````````````````````````````` MUTATORS ``````````````````````````````````
2183    // -------------------------------------------------------------------------------
2184
2185    /// Sets the effective value of a specific digest variant for a given reason.
2186    ///
2187    /// It is expected that the variant is initiated via a commitment before setting it.
2188    ///
2189    /// Automatically handles minting (for increases) or reaping (for decreases) of assets,
2190    /// and updates the total reason value accordingly. This operation directly modifies
2191    /// the low-level digest variant balance without affecting other variants.
2192    ///
2193    /// The `qualifier` influences how minting/reaping is applied and may affect the
2194    /// final value that is actually set.
2195    ///
2196    /// ## Returns
2197    /// - `Ok(Asset)` containing the resulting value of the digest-variant after update
2198    /// - `Err(DispatchError)` if the operation fails
2199    fn set_digest_variant_value(
2200        reason: &Self::Reason,
2201        digest: &Self::Digest,
2202        value: Self::Asset,
2203        variant: &Self::Position,
2204        qualifier: &Self::Intent,
2205    ) -> Result<Self::Asset, DispatchError> {
2206        let new =
2207            DigestMap::<T, I>::mutate((reason, digest), |result| -> Result<_, DispatchError> {
2208                let digest_info = result.as_mut().ok_or(Error::<T, I>::DigestNotFound)?;
2209                // Get the balance of the variant via its index
2210                let digest_of = digest_info
2211                    .mut_balance(variant)
2212                    // A deposit is required for setting, else it will be unfair to reward a digest
2213                    .ok_or(Error::<T, I>::DigestVariantBalanceNotFound)?;
2214                let current = balance_total(digest_of, variant, digest)?;
2215                let new = match current.cmp(&value) {
2216                    Ordering::Less => {
2217                        // Increase value: mint additional assets
2218                        // Determine value to mint accurately
2219                        let try_actual = value.saturating_sub(current);
2220                        // Via the mutable lazy-balance, mint the required value
2221                        let actual = mint(digest_of, variant, digest, &try_actual, qualifier)?;
2222                        // Asset is inflated via commitment-system so reflect via `AssetToIssue`
2223                        // Later when commitments in this inflated variant balance is resolved this will
2224                        // will minted to its underlying fungible system.
2225                        AssetToIssue::<T, I>::mutate(|total_issued| -> DispatchResult {
2226                            *total_issued = total_issued
2227                                .checked_add(&actual)
2228                                .ok_or(Error::<T, I>::MaxAssetIssued)?;
2229                            Ok(())
2230                        })?;
2231                        // Add to reason's total committed value since value is inflated
2232                        CommitHelpers::<T, I>::add_to_total_value(reason, actual)?;
2233                        current.saturating_add(actual)
2234                    }
2235                    Ordering::Greater => {
2236                        // Decrease value: reap excess assets
2237                        // Determine value to reap accurately
2238                        let try_actual = current.saturating_sub(value);
2239                        // Via the mutable lazy-balance, reap the required value
2240                        let actual = reap(digest_of, variant, digest, &try_actual, qualifier)?;
2241                        // Asset is deflated (burned) via commitment-system so reflect via `AssetToReap`
2242                        // Later when commitments in this deflated variant balance is resolved this will
2243                        // will reap the burned balance from its underlying fungible system.
2244                        AssetToReap::<T, I>::mutate(|total_to_reap| -> DispatchResult {
2245                            *total_to_reap = total_to_reap
2246                                .checked_add(&actual)
2247                                .ok_or(Error::<T, I>::MaxAssetReaped)?;
2248                            Ok(())
2249                        })?;
2250                        // Subtract reason's total committed value since value is deflated
2251                        CommitHelpers::<T, I>::sub_from_total_value(reason, actual)?;
2252                        current.saturating_sub(actual)
2253                    }
2254                    core::cmp::Ordering::Equal => {
2255                        // No change needed
2256                        current
2257                    }
2258                };
2259                Self::on_set_digest_variant(digest, reason, new, variant);
2260                Ok(new)
2261            })?;
2262        Ok(new)
2263    }
2264
2265    /// Places a commitment with a specific variant to a digest.
2266    ///
2267    /// This function validates the commitment eligibility, determines the digest model
2268    /// (Direct, Index, or Pool), and registers the commitment with the specified variant
2269    /// and amount according to the provided precision and fortitude parameters.
2270    ///
2271    /// Zero-value (marker) commitments are not allowed and will be rejected.
2272    ///
2273    /// If the digest does not yet exist in the system, this function assumes it to
2274    /// be a **direct digest** (since indexes and pools require prior initialization)
2275    /// and initializes the commitment accordingly.
2276    ///
2277    /// Callers must ensure that such digests are valid within their intended
2278    /// commitment context and agreement.
2279    ///
2280    /// ## Returns
2281    /// - `Ok(Asset)` containing the actual committed amount
2282    /// - `Err(DispatchError)` if placement fails
2283    fn place_commit_of_variant(
2284        who: &Proprietor<T>,
2285        reason: &Self::Reason,
2286        digest: &Self::Digest,
2287        value: Self::Asset,
2288        variant: &Self::Position,
2289        qualifier: &Self::Intent,
2290    ) -> Result<Self::Asset, DispatchError> {
2291        ensure!(
2292            Self::commit_exists(who, reason).is_err(),
2293            Error::<T, I>::CommitAlreadyExists
2294        );
2295        ensure!(!value.is_zero(), Error::<T, I>::MarkerCommitNotAllowed);
2296        let digest_model =
2297            Self::determine_digest(digest, reason).unwrap_or(DigestVariant::Direct(digest.clone()));
2298        let actual = CommitHelpers::<T, I>::place_commit_of(
2299            who,
2300            reason,
2301            &digest_model,
2302            value,
2303            variant,
2304            qualifier,
2305        )?;
2306        Self::on_place_commit_on_variant(who, reason, digest, actual, variant);
2307        Ok(actual)
2308    }
2309
2310    // -------------------------------------------------------------------------------
2311    // ```````````````````````````````````` HOOKS ````````````````````````````````````
2312    // -------------------------------------------------------------------------------
2313
2314    /// Emits an event indicating that a commitment has been placed
2315    /// for a specific digest variant and proprietor.
2316    ///
2317    /// The digest is verified and classified using
2318    /// [`DigestModel::determine_digest`].
2319    ///
2320    /// Emits [`Event::CommitPlaced`] event if [`Config::EmitEvents`] is `true`.
2321    fn on_place_commit_on_variant(
2322        who: &Proprietor<T>,
2323        reason: &Self::Reason,
2324        digest: &Self::Digest,
2325        value: Self::Asset,
2326        variant: &Self::Position,
2327    ) {
2328        if T::EmitEvents::get() {
2329            #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
2330            {
2331                let Ok(digest_model) = Self::determine_digest(digest, reason) else {
2332                    return;
2333                };
2334                Self::deposit_event(Event::<T, I>::CommitPlaced {
2335                    who: who.clone(),
2336                    reason: *reason,
2337                    model: digest_model,
2338                    value: value,
2339                    variant: variant.clone(),
2340                })
2341            }
2342
2343            #[cfg(not(any(feature = "dev", feature = "runtime-benchmarks")))]
2344            {
2345                Self::deposit_event(Event::<T, I>::CommitPlaced {
2346                    who: who.clone(),
2347                    reason: *reason,
2348                    digest: digest.clone(),
2349                    value: value,
2350                    variant: variant.clone(),
2351                })
2352            }
2353        }
2354    }
2355
2356    /// Emits an event when a commit for a specific digest
2357    /// variant is updated.
2358    ///
2359    /// This method delagates itself to [`CommitVariant::on_place_commit_on_variant`]
2360    /// with the default position variant.
2361    ///
2362    /// [`Self::set_commit_variant`] is semantically similar to resolving and
2363    /// placing a new-commitment on a different variant internally.
2364    #[inline]
2365    fn on_set_commit_variant(
2366        who: &Proprietor<T>,
2367        reason: &Self::Reason,
2368        digest: &Self::Digest,
2369        value: Self::Asset,
2370        variant: &Self::Position,
2371    ) {
2372        Self::on_place_commit_on_variant(who, reason, digest, value, variant)
2373    }
2374
2375    /// Emits an event when a digest variant's effective value is updated.
2376    ///
2377    /// This is typically triggered after a reward, penalty, or manual
2378    /// adjustment applied directly to a digest variant.
2379    ///
2380    /// Emits [`Event::DigestInfo`] event if [`Config::EmitEvents`] is `true`.
2381    fn on_set_digest_variant(
2382        digest: &Self::Digest,
2383        reason: &Self::Reason,
2384        value: Self::Asset,
2385        variant: &Self::Position,
2386    ) {
2387        if T::EmitEvents::get() {
2388            Self::deposit_event(Event::<T, I>::DigestInfo {
2389                digest: digest.clone(),
2390                reason: *reason,
2391                value: value,
2392                variant: variant.clone(),
2393            });
2394        }
2395    }
2396}
2397
2398// ===============================================================================
2399// ```````````````````````````````` INDEX VARIANT ````````````````````````````````
2400// ===============================================================================
2401
2402/// Implements [`IndexVariant`] for the pallet
2403impl<T: Config<I>, I: 'static> IndexVariant<Proprietor<T>> for Pallet<T, I> {
2404    // -------------------------------------------------------------------------------
2405    // ````````````````````````````````` CONSTRUCTORS ````````````````````````````````
2406    // -------------------------------------------------------------------------------
2407
2408    /// Prepares a new index object from a list of entry digests, their shares,
2409    /// and variants.
2410    ///
2411    /// This function does **not** associate the index with a specific reason or
2412    /// proprietor internally. The caller is responsible for ensuring the index
2413    /// is correctly attached to a reason via [`CommitIndex::set_index`] and,
2414    /// optionally, the creator (for reap reasons since index should not have a
2415    /// manager).
2416    ///
2417    /// Entries with zero shares are silently ignored, as they carry no
2418    /// semantic contribution to the index.
2419    ///
2420    /// - `who`: The proprietor creating the index.
2421    /// - `reason`: The reason under which the index is being prepared (not used internally).
2422    /// - `entries`: A vector of tuples containing:
2423    ///     - `Digest`: The entry digest
2424    ///     - `Shares`: The number of shares assigned to the entry
2425    ///     - `Position`: The variant/disposition of the entry
2426    ///
2427    /// ## Returns
2428    /// - `Ok(Index)` containing the prepared index
2429    /// - `Err(DispatchError)` if preparation fails
2430    fn prepare_index_of_variants(
2431        _who: &Proprietor<T>,
2432        _reason: &Self::Reason,
2433        entries: Vec<(Self::Digest, Self::Shares, Self::Position)>,
2434    ) -> Result<Self::Index, DispatchError> {
2435        // Initialize a new Entries collection for the index
2436        let mut entries_of = Vec::new();
2437        for (digest, shares, variant) in entries {
2438            // Silently ignore non-share allocated entries
2439            if shares.is_zero() {
2440                continue;
2441            }
2442            // Create a new entry with the given variant
2443            let entry_info = EntryInfo::<T, I>::new(digest, shares, variant)?;
2444            // Add entry to the index, checking for maximum capacity
2445            entries_of.push(entry_info);
2446        }
2447        // Construct the final IndexInfo object
2448        let index_info = IndexInfo::<T, I>::new(&mut Entries::<T, I>::new(entries_of)?)?;
2449
2450        Ok(index_info)
2451    }
2452
2453    // -------------------------------------------------------------------------------
2454    // ``````````````````````````````````` GETTERS ```````````````````````````````````
2455    // -------------------------------------------------------------------------------
2456
2457    /// Retrieves the variant associated with a specific entry in an index.
2458    ///
2459    /// ## Returns
2460    /// - `Ok(Position)` containing the entry's variant
2461    /// - `Err(DispatchError)` otherwise
2462    fn get_entry_variant(
2463        reason: &Self::Reason,
2464        index_of: &Self::Digest,
2465        entry_of: &Self::Digest,
2466    ) -> Result<Self::Position, DispatchError> {
2467        let index_info = Self::get_index(reason, index_of)?;
2468        let entries = &index_info.entries();
2469
2470        let mut idx = None;
2471
2472        // Locate the entry by digest
2473        for (i, entry) in entries.iter().enumerate() {
2474            if entry.digest() == *entry_of {
2475                idx = Some(i);
2476            }
2477        }
2478
2479        let entry_info = match idx {
2480            Some(i) => {
2481                let entry = entries.get(i);
2482                debug_assert!(
2483                    entry.is_some(),
2484                    "entry {:?} of index {:?} found by iterating over index, 
2485                    but retrieval via vector index {:?} get failed",
2486                    entry_of,
2487                    index_of,
2488                    i
2489                );
2490                entry.ok_or(Error::<T, I>::EntryOfIndexNotFound)?
2491            }
2492            None => return Err(Error::<T, I>::EntryOfIndexNotFound.into()),
2493        };
2494        let variant = entry_info.variant().clone();
2495
2496        Ok(variant)
2497    }
2498
2499    // -------------------------------------------------------------------------------
2500    // ``````````````````````````````````` MUTATORS ``````````````````````````````````
2501    // -------------------------------------------------------------------------------
2502
2503    /// Sets or updates the variant (position/disposition) of a specific entry
2504    /// of an index, producing a new index.
2505    ///
2506    /// This function handles both existing and new entries:
2507    /// - If the entry exists:
2508    ///   - If `shares` is `Some(zero)`, the entry is removed.
2509    ///   - If `shares` is `Some`, both shares and variant are updated.
2510    ///   - If `shares` is `None`, only the variant is updated while retaining existing shares.
2511    /// - If the entry does not exist:
2512    ///   - If `shares` is `Some(zero)` or `None`, the operation is a no-op and the original index is returned.
2513    ///   - Otherwise, a new entry is added with the provided variant and shares.
2514    ///
2515    /// The entry digest is not validated to be a direct digest and is accepted as provided.
2516    /// If a commitment is placed on the index, the entry will be funded through normal deposit routing.
2517    ///
2518    /// ## Returns
2519    /// - `Ok(Digest)` containing the resulting index digest (may be unchanged if no-op)
2520    /// - `Err(DispatchError)` if the operation fails
2521    fn set_entry_of_variant(
2522        who: &Proprietor<T>,
2523        reason: &Self::Reason,
2524        index_of: &Self::Digest,
2525        entry_of: &Self::Digest,
2526        variant: Self::Position,
2527        shares: Option<Self::Shares>,
2528    ) -> Result<Self::Digest, DispatchError> {
2529        match Self::entry_exists(reason, index_of, entry_of).is_ok() {
2530            true => {
2531                // Entry exists
2532                match shares {
2533                    Some(s) => {
2534                        if s.is_zero() {
2535                            return CommitHelpers::<T, I>::remove_index_entry(
2536                                who, reason, index_of, entry_of,
2537                            );
2538                        }
2539                        // Update entry with new shares and variant
2540                        CommitHelpers::<T, I>::set_index_entry(
2541                            who, reason, index_of, entry_of, s, &variant,
2542                        )
2543                    }
2544                    None => {
2545                        // Retain existing shares
2546                        let entries = Self::get_entries_shares(reason, index_of);
2547                        debug_assert!(
2548                            entries.is_ok(),
2549                            "entry {:?} of index {:?} of reason {:?} exists but cannot get 
2550                            all entries shares of the index",
2551                            entry_of,
2552                            index_of,
2553                            reason
2554                        );
2555                        let entries = entries?;
2556                        let mut current_shares = None;
2557                        for (entry_digest, share) in entries {
2558                            if entry_digest == *entry_of {
2559                                current_shares = Some(share);
2560                            }
2561                        }
2562                        let current_shares =
2563                            current_shares.ok_or(Error::<T, I>::EntryOfIndexNotFound)?;
2564                        CommitHelpers::<T, I>::set_index_entry(
2565                            who,
2566                            reason,
2567                            index_of,
2568                            entry_of,
2569                            current_shares,
2570                            &variant,
2571                        )
2572                    }
2573                }
2574            }
2575            false => {
2576                // Entry does not exist
2577                if let Some(s) = shares {
2578                    if s.is_zero() {
2579                        return Ok(index_of.clone());
2580                    }
2581                    CommitHelpers::<T, I>::set_index_entry(
2582                        who, reason, index_of, entry_of, s, &variant,
2583                    )
2584                } else {
2585                    // Cannot create a new entry without shares
2586                    Ok(index_of.clone())
2587                }
2588            }
2589        }
2590    }
2591}
2592
2593// ===============================================================================
2594// ````````````````````````````````` POOL VARIANT ````````````````````````````````
2595// ===============================================================================
2596
2597/// Implements [`PoolVariant`] for the pallet
2598impl<T: Config<I>, I: 'static> PoolVariant<Proprietor<T>> for Pallet<T, I> {
2599    // -------------------------------------------------------------------------------
2600    // ``````````````````````````````````` GETTERS ```````````````````````````````````
2601    // -------------------------------------------------------------------------------
2602
2603    /// Retrieves the variant associated with a specific slot in a pool.
2604    ///
2605    /// ## Returns
2606    /// - `Ok(Position)` containing the slot's variant
2607    /// - `Err(DispatchError)`if the slot does not exist
2608    fn get_slot_variant(
2609        reason: &Self::Reason,
2610        pool_of: &Self::Digest,
2611        slot_of: &Self::Digest,
2612    ) -> Result<Self::Position, DispatchError> {
2613        // Fetch the pool information
2614        let pool_info = Self::get_pool(reason, pool_of)?;
2615        let slots = &pool_info.slots();
2616
2617        // Find the index of the requested slot
2618        let mut idx = None;
2619        for (i, slot) in slots.iter().enumerate() {
2620            if slot.digest() == *slot_of {
2621                idx = Some(i);
2622            }
2623        }
2624
2625        let slot_info = match idx {
2626            Some(i) => {
2627                let slot = slots.get(i);
2628                debug_assert!(
2629                    slot.is_some(),
2630                    "slot {:?} of pool {:?} of reason {:?} found by iterating over 
2631                    pool, but later retrieval via vector index {:?} get failed",
2632                    slot_of,
2633                    pool_of,
2634                    reason,
2635                    i
2636                );
2637                slot.ok_or(Error::<T, I>::SlotOfPoolNotFound)?
2638            }
2639            None => return Err(Error::<T, I>::SlotOfPoolNotFound.into()),
2640        };
2641
2642        let variant = slot_info.variant().clone();
2643        Ok(variant)
2644    }
2645
2646    // -------------------------------------------------------------------------------
2647    // ``````````````````````````````````` MUTATORS ``````````````````````````````````
2648    // -------------------------------------------------------------------------------
2649
2650    /// Sets or updates the variant of a specific slot within a pool.
2651    ///
2652    /// Unlike indexes, pools are mutable, so this operation modifies the
2653    /// pool in place without creating a new pool digest. The pool is released
2654    /// and recovered to maintain real-time balances after the mutation.
2655    ///
2656    /// This function handles both existing and new slots:
2657    /// - If the slot exists:
2658    ///   - Updates its variant.
2659    ///   - Updates shares if provided.
2660    ///   - Removes the slot if provided shares are zero.
2661    ///   - Retains existing shares if `shares` is `None`.
2662    /// - If the slot does not exist:
2663    ///   - Creates a new slot if non-zero `shares` is provided.
2664    ///   - Does nothing if `shares` is zero.
2665    ///   - Returns an error if `shares` is `None` (no slot to update and no data to create).
2666    ///
2667    /// ## Returns
2668    /// - `Ok(())` if the operation completes successfully
2669    /// - `Err(DispatchError)` if the slot is not found and cannot be created,
2670    ///   or if the operation fails
2671    fn set_slot_of_variant(
2672        who: &Proprietor<T>,
2673        reason: &Self::Reason,
2674        pool_of: &Self::Digest,
2675        slot_of: &Self::Digest,
2676        variant: Self::Position,
2677        shares: Option<Self::Shares>,
2678    ) -> DispatchResult {
2679        match Self::slot_exists(reason, pool_of, slot_of).is_ok() {
2680            true => {
2681                // Slot exists, determine shares to use
2682                let actual_shares = if let Some(shares) = shares {
2683                    if shares.is_zero() {
2684                        return CommitHelpers::<T, I>::remove_pool_slot(
2685                            who, reason, pool_of, slot_of,
2686                        );
2687                    }
2688                    shares
2689                } else {
2690                    let slots = Self::get_slots_shares(reason, pool_of);
2691                    debug_assert!(
2692                        slots.is_ok(),
2693                        "slot {:?} of pool {:?} of reason {:?} exists 
2694                        but cannot get all slots shares of the pool",
2695                        slot_of,
2696                        pool_of,
2697                        reason
2698                    );
2699                    let slots = slots?;
2700                    let mut found_shares = None;
2701                    for (slot_digest, share) in slots {
2702                        if slot_digest == *slot_of {
2703                            found_shares = Some(share);
2704                        }
2705                    }
2706                    found_shares.ok_or(Error::<T, I>::SlotOfPoolNotFound)?
2707                };
2708                CommitHelpers::<T, I>::set_pool_slot(
2709                    who,
2710                    reason,
2711                    pool_of,
2712                    slot_of,
2713                    actual_shares,
2714                    &variant,
2715                )
2716            }
2717            false => {
2718                // Slot does not exist
2719                if let Some(shares) = shares {
2720                    if shares.is_zero() {
2721                        return Ok(());
2722                    }
2723                    CommitHelpers::<T, I>::set_pool_slot(
2724                        who, reason, pool_of, slot_of, shares, &variant,
2725                    )
2726                } else {
2727                    // No shares provided, No Slot Found, No Variant to Set.
2728                    return Err(Error::<T, I>::SlotOfPoolNotFound.into());
2729                }
2730            }
2731        }
2732    }
2733
2734    // -------------------------------------------------------------------------------
2735    // ```````````````````````````````````` HOOKS ````````````````````````````````````
2736    // -------------------------------------------------------------------------------
2737
2738    /// Emits an event when a slot's variant or shares within a pool is updated.
2739    ///
2740    /// Records the pool digest, reason, slot digest, new variant, and shares (either
2741    /// provided or retrieved from current state).
2742    ///
2743    /// If the slot cannot be found, the function silently returns.
2744    ///
2745    /// Emits:
2746    /// - [`Event::PoolSlot`] if `shares > 0`
2747    /// - [`Event::PoolSlotRemoved`] if `shares == 0`
2748    ///
2749    /// Events are emitted only if [`Config::EmitEvents`] is `true`.
2750    fn on_set_slot_of_variant(
2751        pool_of: &Self::Digest,
2752        reason: &Self::Reason,
2753        slot_of: &Self::Digest,
2754        shares: Option<Self::Shares>,
2755        variant: &Self::Position,
2756    ) {
2757        if T::EmitEvents::get() {
2758            let shares = match shares {
2759                Some(shares) => shares,
2760                None => {
2761                    // Fallback: look up existing slot shares from the pool
2762                    let slots = match Self::get_slots_shares(reason, pool_of) {
2763                        Ok(slots) => slots,
2764                        Err(_) => return,
2765                    };
2766
2767                    match slots
2768                        .into_iter()
2769                        .find(|(slot_digest, _)| slot_digest == slot_of)
2770                    {
2771                        Some((_, share)) => share,
2772                        None => return,
2773                    }
2774                }
2775            };
2776
2777            match shares.is_zero() {
2778                true => Self::deposit_event(Event::<T, I>::PoolSlotRemoved {
2779                    pool_of: pool_of.clone(),
2780                    reason: *reason,
2781                    slot_of: slot_of.clone(),
2782                    variant: variant.clone(),
2783                }),
2784                false => Self::deposit_event(Event::<T, I>::PoolSlot {
2785                    pool_of: pool_of.clone(),
2786                    reason: *reason,
2787                    slot_of: slot_of.clone(),
2788                    variant: variant.clone(),
2789                    shares,
2790                }),
2791            }
2792        }
2793    }
2794}
2795
2796// ===============================================================================
2797// ```````````````````````````` COMMIT ERROR HANDLER `````````````````````````````
2798// ===============================================================================
2799
2800impl<T: Config<I>, I: 'static> CommitErrorHandler for Pallet<T, I> {
2801    type Error = Error<T, I>;
2802
2803    fn from_commit_error(e: CommitError) -> Self::Error {
2804        match e {
2805            CommitError::CommitAlreadyExists => Error::<T, I>::CommitAlreadyExists,
2806            CommitError::InsufficientFunds => Error::<T, I>::InsufficientFunds,
2807            CommitError::MintingOffLimits => Error::<T, I>::MintingOffLimits,
2808            CommitError::ReapingOffLimits => Error::<T, I>::ReapingOffLimits,
2809            CommitError::PlacingOffLimits => Error::<T, I>::PlacingOffLimits,
2810            CommitError::RaisingOffLimits => Error::<T, I>::RaisingOffLimits,
2811        }
2812    }
2813}
2814
2815// ===============================================================================
2816// `````````````````````````````` COMMITMENT TESTS ```````````````````````````````
2817// ===============================================================================
2818
2819/// Unit tests for [`commitment`](frame_suite::commitment) trait
2820/// implementations over [`Pallet`].
2821#[cfg(test)]
2822mod tests {
2823
2824    // -------------------------------------------------------------------------------
2825    // ```````````````````````````````````` IMPORTS ``````````````````````````````````
2826    // -------------------------------------------------------------------------------
2827
2828    // --- Local crate imports ---
2829    use crate::{balance::*, mock::*};
2830
2831    // --- FRAME Suite ---
2832    use frame_suite::{
2833        commitment::*,
2834        misc::{Directive, PositionIndex, Disposition},
2835    };
2836
2837    // --- FRAME Support ---
2838    use frame_support::{
2839        assert_err, assert_ok,
2840        traits::{
2841            fungible::{Inspect, InspectFreeze, InspectHold},
2842            tokens::{Fortitude, Precision},
2843        },
2844    };
2845
2846    // --- Substrate Primitives ---
2847    use sp_runtime::traits::Zero;
2848
2849    // -------------------------------------------------------------------------------
2850    // ```````````````````````````````` INSPECT ASSET ````````````````````````````````
2851    // -------------------------------------------------------------------------------
2852
2853    #[test]
2854    fn available_funds_success() {
2855        commit_test_ext().execute_with(|| {
2856            set_default_user_balance_and_standard_hold(ALICE).unwrap();
2857            let liquid_balance = AssetOf::balance(&ALICE);
2858            let hold_balance = AssetOf::balance_on_hold(&PREPARE_FOR_COMMIT, &ALICE);
2859            assert_eq!(liquid_balance, INITIAL_BALANCE);
2860            assert_eq!(hold_balance, STANDARD_HOLD);
2861            let expected_available_funds = liquid_balance + hold_balance;
2862            let actual_available_funds = Pallet::available_funds(&ALICE);
2863            assert_eq!(actual_available_funds, expected_available_funds);
2864        })
2865    }
2866
2867    #[test]
2868    fn available_funds_success_with_zero_for_uninitialized_user() {
2869        commit_test_ext().execute_with(|| {
2870            // since, NIX is uninitialized expected funds is 0
2871            let expected_available_funds = ZERO_VALUE;
2872            let actual_available_funds = Pallet::available_funds(&AMY);
2873            assert_eq!(actual_available_funds, expected_available_funds);
2874        })
2875    }
2876
2877    // -------------------------------------------------------------------------------
2878    // ```````````````````````````````` DIGEST MODEL `````````````````````````````````
2879    // -------------------------------------------------------------------------------
2880
2881    #[test]
2882    fn determine_digest_success_for_direct_digest_model() {
2883        commit_test_ext().execute_with(|| {
2884            set_default_user_balance_and_standard_hold(ALICE).unwrap();
2885            Pallet::place_commit(
2886                &ALICE,
2887                &ESCROW,
2888                &CONTRACT_FREELANCE,
2889                STANDARD_COMMIT,
2890                &Directive::new(Precision::Exact, Fortitude::Force),
2891            )
2892            .unwrap();
2893            assert_eq!(
2894                Pallet::determine_digest(&CONTRACT_FREELANCE, &ESCROW),
2895                Ok(DigestVariant::Direct(CONTRACT_FREELANCE))
2896            );
2897        })
2898    }
2899
2900    #[test]
2901    fn determine_digest_success_for_index_model() {
2902        commit_test_ext().execute_with(|| {
2903            set_default_user_balance_and_standard_hold(ALICE).unwrap();
2904            set_default_user_balance_and_standard_hold(BOB).unwrap();
2905            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
2906            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
2907            let entries = vec![
2908                (VALIDATOR_ALPHA, SHARE_EQUAL),
2909                (VALIDATOR_BETA, SHARE_EQUAL),
2910            ];
2911            prepare_and_initiate_index(BOB, STAKING, &entries, INDEX_BALANCED_STAKING).unwrap();
2912            Pallet::place_commit(
2913                &ALICE,
2914                &STAKING,
2915                &INDEX_BALANCED_STAKING,
2916                STANDARD_COMMIT,
2917                &Directive::new(Precision::Exact, Fortitude::Force),
2918            )
2919            .unwrap();
2920            assert_eq!(
2921                Pallet::determine_digest(&INDEX_BALANCED_STAKING, &STAKING),
2922                Ok(DigestVariant::Index(INDEX_BALANCED_STAKING))
2923            );
2924        })
2925    }
2926
2927    #[test]
2928    fn determine_digest_success_for_pool_model() {
2929        commit_test_ext().execute_with(|| {
2930            set_default_user_balance_and_standard_hold(ALICE).unwrap();
2931            set_default_user_balance_and_standard_hold(BOB).unwrap();
2932            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
2933            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
2934            let entries = vec![
2935                (VALIDATOR_ALPHA, SHARE_DOMINANT),
2936                (VALIDATOR_BETA, SHARE_MAJOR),
2937            ];
2938            prepare_and_initiate_pool(
2939                BOB,
2940                STAKING,
2941                &entries,
2942                INDEX_OPTIMIZED_STAKING,
2943                POOL_MANAGED_STAKING,
2944                COMMISSION_LOW,
2945            )
2946            .unwrap();
2947            Pallet::place_commit(
2948                &ALICE,
2949                &STAKING,
2950                &INDEX_OPTIMIZED_STAKING,
2951                STANDARD_COMMIT,
2952                &Directive::new(Precision::BestEffort, Fortitude::Force),
2953            )
2954            .unwrap();
2955            assert_eq!(
2956                Pallet::determine_digest(&POOL_MANAGED_STAKING, &STAKING),
2957                Ok(DigestVariant::Pool(POOL_MANAGED_STAKING))
2958            );
2959        })
2960    }
2961
2962    #[test]
2963    fn determine_digest_err_digest_not_found_to_determine() {
2964        commit_test_ext().execute_with(|| {
2965            set_default_user_balance_and_standard_hold(ALICE).unwrap();
2966            Pallet::place_commit(
2967                &ALICE,
2968                &GOVERNANCE,
2969                &PROPOSAL_TREASURY_SPEND,
2970                STANDARD_COMMIT,
2971                &Directive::new(Precision::Exact, Fortitude::Force),
2972            )
2973            .unwrap();
2974            assert_err!(
2975                Pallet::determine_digest(&PROPOSAL_RUNTIME_UPGRADE, &GOVERNANCE),
2976                Error::DigestNotFoundToDetermine
2977            );
2978        })
2979    }
2980
2981    // -------------------------------------------------------------------------------
2982    // ````````````````````````````````` COMMITMENT ``````````````````````````````````
2983    // -------------------------------------------------------------------------------
2984
2985    #[test]
2986    fn place_commmit_success_for_digest() {
2987        commit_test_ext().execute_with(|| {
2988            set_default_user_balance_and_standard_hold(ALICE).unwrap();
2989            assert_err!(
2990                Pallet::commit_exists(&ALICE, &GOVERNANCE),
2991                Error::CommitNotFound
2992            );
2993            assert_err!(
2994                Pallet::digest_exists(&GOVERNANCE, &PROPOSAL_TREASURY_SPEND),
2995                Error::DigestNotFound
2996            );
2997            assert_ok!(Pallet::place_commit(
2998                &ALICE,
2999                &GOVERNANCE,
3000                &PROPOSAL_TREASURY_SPEND,
3001                STANDARD_COMMIT,
3002                &Directive::new(Precision::Exact, Fortitude::Force)
3003            ));
3004            // Commit and digest enquirey
3005            assert_ok!(Pallet::commit_exists(&ALICE, &GOVERNANCE));
3006            assert_ok!(Pallet::digest_exists(&GOVERNANCE, &PROPOSAL_TREASURY_SPEND));
3007            // Balance and freeze enquirey
3008            let balace_after = AssetOf::balance(&ALICE);
3009            let hold_balance_after = AssetOf::balance_on_hold(&PREPARE_FOR_COMMIT, &ALICE);
3010            let expected_balance_after = INITIAL_BALANCE;
3011            let expected_hold_balance_after = 250;
3012            assert_eq!(expected_balance_after, balace_after);
3013            assert_eq!(expected_hold_balance_after, hold_balance_after);
3014            assert_eq!(
3015                AssetOf::balance_frozen(&GOVERNANCE, &ALICE),
3016                STANDARD_COMMIT
3017            );
3018            // Digest info enquiry
3019            let digest_info = DigestMap::get((GOVERNANCE, PROPOSAL_TREASURY_SPEND)).unwrap();
3020            let digests = digest_info.reveal();
3021            let digest_of = digests.get(0).unwrap();
3022            let effective =
3023                balance_total(digest_of, &Default::default(), &PROPOSAL_TREASURY_SPEND).unwrap();
3024            assert_eq!(effective, 250);
3025            // Commit info enquiry
3026            let commit_info = CommitMap::get((ALICE, GOVERNANCE)).unwrap();
3027            assert_eq!(commit_info.digest(), PROPOSAL_TREASURY_SPEND);
3028            let commits = commit_info.commits();
3029            let commit = commits.get(0).unwrap();
3030            let principal = receipt_active_value(
3031                digest_of,
3032                &Default::default(),
3033                &PROPOSAL_TREASURY_SPEND,
3034                commit,
3035            )
3036            .unwrap();
3037            assert_eq!(principal, STANDARD_COMMIT);
3038            // Total value enquiry
3039            let reason_value = ReasonValue::get(GOVERNANCE).unwrap();
3040            assert_eq!(reason_value, 250);
3041        })
3042    }
3043
3044    #[test]
3045    fn place_commit_success_for_index() {
3046        commit_test_ext().execute_with(|| {
3047            set_default_user_balance_and_standard_hold(ALICE).unwrap();
3048            set_default_user_balance_and_standard_hold(BOB).unwrap();
3049            set_default_user_balance_and_standard_hold(CHARLIE).unwrap();
3050
3051            Pallet::place_commit(
3052                &ALICE,
3053                &STAKING,
3054                &VALIDATOR_ALPHA,
3055                STANDARD_COMMIT,
3056                &Directive::new(Precision::Exact, Fortitude::Force),
3057            )
3058            .unwrap();
3059            Pallet::place_commit(
3060                &BOB,
3061                &STAKING,
3062                &VALIDATOR_BETA,
3063                STANDARD_COMMIT,
3064                &Directive::new(Precision::Exact, Fortitude::Force),
3065            )
3066            .unwrap();
3067            prepare_and_initiate_index(
3068                MIKE,
3069                STAKING,
3070                &[
3071                    (VALIDATOR_ALPHA, SHARE_MAJOR),
3072                    (VALIDATOR_BETA, SHARE_DOMINANT),
3073                ],
3074                INDEX_OPTIMIZED_STAKING,
3075            )
3076            .unwrap();
3077            assert_ok!(Pallet::index_exists(&STAKING, &INDEX_OPTIMIZED_STAKING));
3078            // Before placing a commit to the index
3079            let index_info = Pallet::get_index(&STAKING, &INDEX_OPTIMIZED_STAKING).unwrap();
3080            assert_eq!(index_info.capital(), 100);
3081            assert_eq!(index_info.principal(), ZERO_VALUE);
3082            let actual_entries_value =
3083                Pallet::get_entries_value(&STAKING, &INDEX_OPTIMIZED_STAKING).unwrap();
3084            let expected_entries_value = vec![(VALIDATOR_ALPHA, 0), (VALIDATOR_BETA, 0)];
3085            assert_eq!(actual_entries_value, expected_entries_value);
3086
3087            let reason_value = ReasonValue::get(STAKING).unwrap();
3088            assert_eq!(reason_value, 500);
3089
3090            // Place commit to an index
3091            assert_ok!(Pallet::place_commit(
3092                &CHARLIE,
3093                &STAKING,
3094                &INDEX_OPTIMIZED_STAKING,
3095                STANDARD_COMMIT,
3096                &Directive::new(Precision::Exact, Fortitude::Force)
3097            ));
3098            // After placing a commit to the index
3099            let index_value = Pallet::get_index_value(&STAKING, &INDEX_OPTIMIZED_STAKING).unwrap();
3100            assert_eq!(index_value, 250);
3101            let actual_entries_value =
3102                Pallet::get_entries_value(&STAKING, &INDEX_OPTIMIZED_STAKING).unwrap();
3103            let expected_entries_value = vec![(VALIDATOR_ALPHA, 100), (VALIDATOR_BETA, 150)];
3104            assert_eq!(actual_entries_value, expected_entries_value);
3105            // Entry info
3106            let entry_info_alpha =
3107                EntryMap::get((STAKING, INDEX_OPTIMIZED_STAKING, VALIDATOR_ALPHA, CHARLIE))
3108                    .unwrap();
3109            let alpha_commits = entry_info_alpha.commits();
3110            let alpha_derived_bal = alpha_commits.get(0).unwrap();
3111            assert_eq!(receipt_deposit_value(alpha_derived_bal).unwrap(), 100);
3112            let entry_info_beta =
3113                EntryMap::get((STAKING, INDEX_OPTIMIZED_STAKING, VALIDATOR_BETA, CHARLIE)).unwrap();
3114            let beta_commits = entry_info_beta.commits();
3115            let alpha_derived_bal = beta_commits.get(0).unwrap();
3116            assert_eq!(receipt_deposit_value(alpha_derived_bal).unwrap(), 150);
3117            // Total reason value
3118            let reason_value = ReasonValue::get(STAKING).unwrap();
3119            assert_eq!(reason_value, 750);
3120            // Balance and freeze enquirey
3121            let balace_after = AssetOf::balance(&CHARLIE);
3122            let hold_balance_after = AssetOf::balance_on_hold(&PREPARE_FOR_COMMIT, &CHARLIE);
3123            let expected_balance_after = INITIAL_BALANCE;
3124            let expected_hold_balance_after = 250;
3125            assert_eq!(expected_balance_after, balace_after);
3126            assert_eq!(expected_hold_balance_after, hold_balance_after);
3127            assert_eq!(AssetOf::balance_frozen(&STAKING, &CHARLIE), STANDARD_COMMIT);
3128        })
3129    }
3130
3131    #[test]
3132    fn place_commit_success_for_pool() {
3133        commit_test_ext().execute_with(|| {
3134            set_default_user_balance_and_standard_hold(ALICE).unwrap();
3135            set_default_user_balance_and_standard_hold(BOB).unwrap();
3136            set_default_user_balance_and_standard_hold(CHARLIE).unwrap();
3137            Pallet::place_commit(
3138                &ALICE,
3139                &STAKING,
3140                &VALIDATOR_ALPHA,
3141                STANDARD_COMMIT,
3142                &Directive::new(Precision::Exact, Fortitude::Force),
3143            )
3144            .unwrap();
3145            Pallet::place_commit(
3146                &BOB,
3147                &STAKING,
3148                &VALIDATOR_BETA,
3149                STANDARD_COMMIT,
3150                &Directive::new(Precision::Exact, Fortitude::Force),
3151            )
3152            .unwrap();
3153            let entries = vec![
3154                (VALIDATOR_ALPHA, SHARE_MAJOR),
3155                (VALIDATOR_BETA, SHARE_DOMINANT),
3156            ];
3157            prepare_and_initiate_pool(
3158                MIKE,
3159                STAKING,
3160                &entries,
3161                INDEX_OPTIMIZED_STAKING,
3162                POOL_MANAGED_STAKING,
3163                COMMISSION_LOW,
3164            )
3165            .unwrap();
3166            // Before placing commit to pool
3167            assert_eq!(
3168                Pallet::get_manager(&STAKING, &POOL_MANAGED_STAKING),
3169                Ok(MIKE)
3170            );
3171            let pool_info = PoolMap::get((STAKING, POOL_MANAGED_STAKING)).unwrap();
3172            let pool_balance_of = pool_info.balance();
3173            assert_eq!(
3174                balance_total(&pool_balance_of, &Default::default(), &POOL_MANAGED_STAKING)
3175                    .unwrap(),
3176                0
3177            );
3178            assert!(
3179                has_deposits(&pool_balance_of, &Default::default(), &POOL_MANAGED_STAKING).is_err()
3180            );
3181            let pool_capital = pool_info.capital();
3182            assert_eq!(pool_capital, 100);
3183            let actual_slots_value =
3184                Pallet::get_slots_value(&STAKING, &POOL_MANAGED_STAKING).unwrap();
3185            let expected_slots_value = vec![(VALIDATOR_ALPHA, 0), (VALIDATOR_BETA, 0)];
3186            assert_eq!(actual_slots_value, expected_slots_value);
3187            let reason_value = ReasonValue::get(STAKING).unwrap();
3188            assert_eq!(reason_value, 500);
3189            // Placing commit to pool
3190            assert_ok!(Pallet::place_commit(
3191                &CHARLIE,
3192                &STAKING,
3193                &POOL_MANAGED_STAKING,
3194                STANDARD_COMMIT,
3195                &Directive::new(Precision::Exact, Fortitude::Force)
3196            ));
3197            // After placing commit to pool
3198            let pool_info = PoolMap::get((STAKING, POOL_MANAGED_STAKING)).unwrap();
3199            let pool_balance_of = pool_info.balance();
3200            assert_eq!(
3201                balance_total(&pool_balance_of, &Default::default(), &POOL_MANAGED_STAKING)
3202                    .unwrap(),
3203                250
3204            );
3205            assert!(
3206                has_deposits(&pool_balance_of, &Default::default(), &POOL_MANAGED_STAKING).is_ok()
3207            );
3208            let actual_slots_value =
3209                Pallet::get_slots_value(&STAKING, &POOL_MANAGED_STAKING).unwrap();
3210            let expected_slots_value = vec![(VALIDATOR_ALPHA, 100), (VALIDATOR_BETA, 150)];
3211            assert_eq!(actual_slots_value, expected_slots_value);
3212
3213            let reason_value = ReasonValue::get(STAKING).unwrap();
3214            assert_eq!(reason_value, 750);
3215            // Balance and freeze enquirey
3216            let balace_after = AssetOf::balance(&CHARLIE);
3217            let hold_balance_after = AssetOf::balance_on_hold(&PREPARE_FOR_COMMIT, &CHARLIE);
3218            let expected_balance_after = INITIAL_BALANCE;
3219            let expected_hold_balance_after = 250;
3220            assert_eq!(expected_balance_after, balace_after);
3221            assert_eq!(expected_hold_balance_after, hold_balance_after);
3222            assert_eq!(AssetOf::balance_frozen(&STAKING, &CHARLIE), STANDARD_COMMIT);
3223        })
3224    }
3225
3226    #[test]
3227    fn place_commit_marker_error_for_value_zero() {
3228        commit_test_ext().execute_with(|| {
3229            set_default_user_balance_and_standard_hold(ALICE).unwrap();
3230            assert_err!(
3231                Pallet::place_commit(
3232                    &ALICE,
3233                    &STAKING,
3234                    &VALIDATOR_ALPHA,
3235                    ZERO_VALUE,
3236                    &Directive::new(Precision::BestEffort, Fortitude::Force)
3237                ),
3238                Error::MarkerCommitNotAllowed
3239            );
3240            // Commit and digest enquirey
3241            assert_err!(
3242                Pallet::commit_exists(&ALICE, &STAKING),
3243                Error::CommitNotFound
3244            );
3245            assert_err!(
3246                Pallet::digest_exists(&STAKING, &VALIDATOR_ALPHA),
3247                Error::DigestNotFound
3248            );
3249        })
3250    }
3251
3252    #[test]
3253    fn place_commit_err_commit_already_exists_for_reason() {
3254        commit_test_ext().execute_with(|| {
3255            let commit_info = CommitInfo::new(
3256                CONTRACT_FREELANCE,
3257                CommitInstance::default(),
3258                Default::default(),
3259            )
3260            .unwrap();
3261            CommitMap::insert((&ALICE, &ESCROW), commit_info);
3262            assert_err!(
3263                Pallet::place_commit(
3264                    &ALICE,
3265                    &ESCROW,
3266                    &CONTRACT_SUPPLY_CHAIN,
3267                    STANDARD_COMMIT,
3268                    &Directive::new(Precision::BestEffort, Fortitude::Polite)
3269                ),
3270                Error::CommitAlreadyExists
3271            );
3272        })
3273    }
3274
3275    #[test]
3276    fn place_commit_err_insufficient_funds() {
3277        commit_test_ext().execute_with(|| {
3278            set_default_user_balance_and_standard_hold(ALICE).unwrap();
3279            assert_eq!(AssetOf::total_balance(&ALICE), 1500);
3280            let insufficient_commit = 1600;
3281            assert_err!(
3282                Pallet::place_commit(
3283                    &ALICE,
3284                    &ESCROW,
3285                    &CONTRACT_SUPPLY_CHAIN,
3286                    insufficient_commit,
3287                    &Directive::new(Precision::Exact, Fortitude::Force)
3288                ),
3289                Error::InsufficientFunds
3290            );
3291        })
3292    }
3293
3294    #[test]
3295    fn raise_commit_success_for_direct() {
3296        commit_test_ext().execute_with(|| {
3297            System::set_block_number(10);
3298            set_default_user_balance_and_standard_hold(ALICE).unwrap();
3299            Pallet::place_commit(
3300                &ALICE,
3301                &STAKING,
3302                &VALIDATOR_ALPHA,
3303                STANDARD_COMMIT,
3304                &Directive::new(Precision::Exact, Fortitude::Force),
3305            )
3306            .unwrap();
3307            let commit_value_before = Pallet::get_commit_value(&ALICE, &STAKING).unwrap();
3308            assert_eq!(commit_value_before, STANDARD_COMMIT);
3309            assert_eq!(AssetOf::balance_frozen(&STAKING, &ALICE), STANDARD_COMMIT);
3310            let reason_value = Pallet::get_total_value(&STAKING);
3311            assert_eq!(reason_value, 250);
3312            assert_ok!(Pallet::raise_commit(
3313                &ALICE,
3314                &STAKING,
3315                SMALL_COMMIT,
3316                &Directive::new(Precision::Exact, Fortitude::Force)
3317            ));
3318            let commit_value_after = Pallet::get_commit_value(&ALICE, &STAKING).unwrap();
3319            assert_eq!(commit_value_after, 350);
3320            assert_eq!(AssetOf::balance_frozen(&STAKING, &ALICE), 350);
3321            let reason_value = Pallet::get_total_value(&STAKING);
3322            assert_eq!(reason_value, 350);
3323            // new commit instance added for raise commit
3324            let commit_info = CommitMap::get((ALICE, STAKING)).unwrap();
3325            let commits = commit_info.commits();
3326            let commit = commits.get(1).unwrap();
3327            assert_eq!(receipt_deposit_value(commit).unwrap(), SMALL_COMMIT);
3328
3329            #[cfg(not(feature = "dev"))]
3330            System::assert_last_event(Event::CommitRaised {
3331                 who: ALICE, 
3332                 reason: STAKING, 
3333                 digest: VALIDATOR_ALPHA, 
3334                 value: SMALL_COMMIT
3335                }
3336                .into()
3337            );
3338
3339            #[cfg(feature = "dev")]
3340            System::assert_last_event(Event::CommitRaised {
3341                 who: ALICE, 
3342                 reason: STAKING, 
3343                 model: DigestVariant::Direct(VALIDATOR_ALPHA), 
3344                 value: SMALL_COMMIT
3345                }
3346                .into()
3347            );            
3348        })
3349    }
3350
3351    #[test]
3352    fn raise_commit_success_for_index() {
3353        commit_test_ext().execute_with(|| {
3354            System::set_block_number(10);
3355            set_default_user_balance_and_standard_hold(ALICE).unwrap();
3356            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
3357            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
3358            prepare_and_initiate_index(
3359                MIKE,
3360                STAKING,
3361                &[
3362                    (VALIDATOR_ALPHA, SHARE_EQUAL),
3363                    (VALIDATOR_BETA, SHARE_EQUAL),
3364                ],
3365                INDEX_BALANCED_STAKING,
3366            )
3367            .unwrap();
3368
3369            Pallet::place_commit(
3370                &ALICE,
3371                &STAKING,
3372                &INDEX_BALANCED_STAKING,
3373                STANDARD_COMMIT,
3374                &Directive::new(Precision::Exact, Fortitude::Force),
3375            )
3376            .unwrap();
3377            // Before raising the index commit value
3378            let index_info = Pallet::get_index(&STAKING, &INDEX_BALANCED_STAKING).unwrap();
3379            assert_eq!(index_info.capital(), 200);
3380            assert_eq!(index_info.principal(), 250);
3381            let actual_entries_value =
3382                Pallet::get_entries_value(&STAKING, &INDEX_BALANCED_STAKING).unwrap();
3383            let expected_entries_value = vec![(VALIDATOR_ALPHA, 125), (VALIDATOR_BETA, 125)];
3384            assert_eq!(actual_entries_value, expected_entries_value);
3385            let reason_value = ReasonValue::get(STAKING).unwrap();
3386            assert_eq!(reason_value, 250);
3387            // alice balance inspect
3388            assert_eq!(AssetOf::balance(&ALICE), INITIAL_BALANCE);
3389            assert_eq!(AssetOf::balance_on_hold(&PREPARE_FOR_COMMIT, &ALICE), 250);
3390            // Raise commit value
3391            assert_ok!(Pallet::raise_commit(
3392                &ALICE,
3393                &STAKING,
3394                SMALL_COMMIT,
3395                &Directive::new(Precision::Exact, Fortitude::Force)
3396            ));
3397            // After placing a raise commit to the index
3398            let index_value = Pallet::get_index_value(&STAKING, &INDEX_BALANCED_STAKING).unwrap();
3399            assert_eq!(index_value, 350);
3400            let actual_entries_value =
3401                Pallet::get_entries_value(&STAKING, &INDEX_BALANCED_STAKING).unwrap();
3402            let expected_entries_value = vec![(VALIDATOR_ALPHA, 175), (VALIDATOR_BETA, 175)];
3403            assert_eq!(actual_entries_value, expected_entries_value);
3404
3405            let reason_value = ReasonValue::get(STAKING).unwrap();
3406            assert_eq!(reason_value, 350);
3407            // New commit instances added for raise commit
3408            // Commit info check
3409            let commit_info = CommitMap::get((ALICE, STAKING)).unwrap();
3410            let commits = commit_info.commits();
3411            // The raise commit instance should be at index 1, but a placeholder only for index commits
3412            // EntryMap only holds all individual entries commits, so querying value via this
3413            // returns error due to default receipt - i.e., invalid
3414            let raise_commit = commits.get(1).unwrap();
3415            assert!(receipt_deposit_value(raise_commit).is_err(),);
3416
3417            // Entry info check
3418            // For VALIDATOR_ALPHA entry
3419            let alpha_entry_info =
3420                EntryMap::get((STAKING, INDEX_BALANCED_STAKING, VALIDATOR_ALPHA, ALICE)).unwrap();
3421            let alpha_commits = alpha_entry_info.commits();
3422            let commit = alpha_commits.get(1).unwrap();
3423            assert_eq!(receipt_deposit_value(commit).unwrap(), 50);
3424            // For VALIDATOR_BETA entry
3425            let beta_entry_info =
3426                EntryMap::get((STAKING, INDEX_BALANCED_STAKING, VALIDATOR_BETA, ALICE)).unwrap();
3427            let beta_commits = beta_entry_info.commits();
3428            let commit = beta_commits.get(1).unwrap();
3429            assert_eq!(receipt_deposit_value(commit).unwrap(), 50);
3430            // Balance and freeze enquirey
3431            let actual_balance = AssetOf::balance(&ALICE);
3432            let actual_hold_balance = AssetOf::balance_on_hold(&PREPARE_FOR_COMMIT, &ALICE);
3433            let expected_balance = INITIAL_BALANCE;
3434            let expected_hold_balance = 150;
3435            assert_eq!(actual_balance, expected_balance);
3436            assert_eq!(actual_hold_balance, expected_hold_balance);
3437            assert_eq!(AssetOf::balance_frozen(&STAKING, &ALICE), 350);
3438
3439            #[cfg(not(feature = "dev"))]
3440            System::assert_last_event(Event::CommitRaised {
3441                 who: ALICE, 
3442                 reason: STAKING, 
3443                 digest: INDEX_BALANCED_STAKING, 
3444                 value: SMALL_COMMIT
3445                }
3446                .into()
3447            );
3448
3449            #[cfg(feature = "dev")]
3450            System::assert_last_event(Event::CommitRaised {
3451                 who: ALICE, 
3452                 reason: STAKING, 
3453                 model: DigestVariant::Index(INDEX_BALANCED_STAKING), 
3454                 value: SMALL_COMMIT
3455                }
3456                .into()
3457            );  
3458        })
3459    }
3460
3461    #[test]
3462    fn raise_commit_success_for_pool() {
3463        commit_test_ext().execute_with(|| {
3464            System::set_block_number(10);
3465            set_default_user_balance_and_standard_hold(ALICE).unwrap();
3466            set_default_user_balance_and_standard_hold(BOB).unwrap();
3467            set_default_user_balance_and_standard_hold(CHARLIE).unwrap();
3468            Pallet::place_commit(
3469                &BOB,
3470                &STAKING,
3471                &VALIDATOR_ALPHA,
3472                STANDARD_COMMIT,
3473                &Directive::new(Precision::Exact, Fortitude::Force),
3474            )
3475            .unwrap();
3476            Pallet::place_commit(
3477                &CHARLIE,
3478                &STAKING,
3479                &VALIDATOR_BETA,
3480                STANDARD_COMMIT,
3481                &Directive::new(Precision::Exact, Fortitude::Force),
3482            )
3483            .unwrap();
3484            let entries = vec![
3485                (VALIDATOR_ALPHA, SHARE_MAJOR),
3486                (VALIDATOR_BETA, SHARE_DOMINANT),
3487            ];
3488            prepare_and_initiate_pool(
3489                MIKE,
3490                STAKING,
3491                &entries,
3492                INDEX_OPTIMIZED_STAKING,
3493                POOL_MANAGED_STAKING,
3494                COMMISSION_ZERO,
3495            )
3496            .unwrap();
3497
3498            Pallet::place_commit(
3499                &ALICE,
3500                &STAKING,
3501                &POOL_MANAGED_STAKING,
3502                STANDARD_COMMIT,
3503                &Directive::new(Precision::Exact, Fortitude::Force),
3504            )
3505            .unwrap();
3506            // Before raising the pool commit value
3507            let pool_info = PoolMap::get((STAKING, POOL_MANAGED_STAKING)).unwrap();
3508            let pool_balance_of = pool_info.balance();
3509            assert_ok!(has_deposits(
3510                &pool_balance_of,
3511                &Default::default(),
3512                &POOL_MANAGED_STAKING
3513            ));
3514            assert_eq!(
3515                balance_total(&pool_balance_of, &Default::default(), &POOL_MANAGED_STAKING)
3516                    .unwrap(),
3517                250
3518            );
3519            let pool_capital = pool_info.capital();
3520            assert_eq!(pool_capital, 100);
3521            let actual_slots_value =
3522                Pallet::get_slots_value(&STAKING, &POOL_MANAGED_STAKING).unwrap();
3523            let expected_slots_value = vec![(VALIDATOR_ALPHA, 100), (VALIDATOR_BETA, 150)];
3524            assert_eq!(actual_slots_value, expected_slots_value);
3525            let reason_value = ReasonValue::get(STAKING).unwrap();
3526            assert_eq!(reason_value, 750);
3527            // Raise commit value
3528            assert_ok!(Pallet::raise_commit(
3529                &ALICE,
3530                &STAKING,
3531                SMALL_COMMIT,
3532                &Directive::new(Precision::Exact, Fortitude::Force)
3533            ));
3534            // After raising the pool commit value
3535            let pool_info = PoolMap::get((STAKING, POOL_MANAGED_STAKING)).unwrap();
3536            let pool_balance_of = pool_info.balance();
3537            assert_ok!(has_deposits(
3538                &pool_balance_of,
3539                &Default::default(),
3540                &POOL_MANAGED_STAKING
3541            ));
3542            assert_eq!(
3543                balance_total(&pool_balance_of, &Default::default(), &POOL_MANAGED_STAKING)
3544                    .unwrap(),
3545                350
3546            );
3547            let actual_slots_value =
3548                Pallet::get_slots_value(&STAKING, &POOL_MANAGED_STAKING).unwrap();
3549            let expected_slots_value = vec![(VALIDATOR_ALPHA, 140), (VALIDATOR_BETA, 210)];
3550            assert_eq!(actual_slots_value, expected_slots_value);
3551            let reason_value = ReasonValue::get(STAKING).unwrap();
3552            assert_eq!(reason_value, 850);
3553            // New commit instances added for raise commit
3554            // Commit info check
3555            let commit_info = CommitMap::get((ALICE, STAKING)).unwrap();
3556            let commits = commit_info.commits();
3557            // The initial commit instance should be at index 0
3558            let initial_commit = commits.get(0).unwrap();
3559            assert_eq!(
3560                receipt_deposit_value(initial_commit).unwrap(),
3561                STANDARD_COMMIT
3562            );
3563            // The raise commit instance should be at index 1
3564            let raise_commit = commits.get(1).unwrap();
3565            assert_eq!(receipt_deposit_value(raise_commit).unwrap(), SMALL_COMMIT);
3566
3567            #[cfg(not(feature = "dev"))]
3568            System::assert_last_event(Event::CommitRaised {
3569                 who: ALICE, 
3570                 reason: STAKING, 
3571                 digest: POOL_MANAGED_STAKING, 
3572                 value: SMALL_COMMIT
3573                }
3574                .into()
3575            );
3576
3577            #[cfg(feature = "dev")]
3578            System::assert_last_event(Event::CommitRaised {
3579                 who: ALICE, 
3580                 reason: STAKING, 
3581                 model: DigestVariant::Pool(POOL_MANAGED_STAKING), 
3582                 value: SMALL_COMMIT
3583                }
3584                .into()
3585            );  
3586        })
3587    }
3588
3589    #[test]
3590    fn raise_commit_err_commit_not_found() {
3591        commit_test_ext().execute_with(|| {
3592            set_default_user_balance_and_standard_hold(ALICE).unwrap();
3593            Pallet::place_commit(
3594                &ALICE,
3595                &STAKING,
3596                &VALIDATOR_ALPHA,
3597                STANDARD_COMMIT,
3598                &Directive::new(Precision::Exact, Fortitude::Force),
3599            )
3600            .unwrap();
3601            assert_err!(
3602                Pallet::raise_commit(
3603                    &ALICE,
3604                    &ESCROW,
3605                    SMALL_COMMIT,
3606                    &Directive::new(Precision::Exact, Fortitude::Force)
3607                ),
3608                Error::CommitNotFound
3609            );
3610        });
3611    }
3612
3613    #[test]
3614    fn raise_commit_err_insifficient_funds() {
3615        commit_test_ext().execute_with(|| {
3616            set_default_user_balance_and_standard_hold(ALICE).unwrap();
3617            Pallet::place_commit(
3618                &ALICE,
3619                &STAKING,
3620                &VALIDATOR_ALPHA,
3621                LARGE_COMMIT,
3622                &Directive::new(Precision::Exact, Fortitude::Force),
3623            )
3624            .unwrap();
3625            let insufficient_commit = SMALL_COMMIT;
3626            assert_err!(
3627                Pallet::raise_commit(
3628                    &ALICE,
3629                    &STAKING,
3630                    insufficient_commit,
3631                    &Directive::new(Precision::Exact, Fortitude::Polite)
3632                ),
3633                Error::InsufficientFunds
3634            );
3635        })
3636    }
3637
3638    #[test]
3639    fn raise_commit_err_marker_commit_not_allowed() {
3640        commit_test_ext().execute_with(|| {
3641            set_default_user_balance_and_standard_hold(ALICE).unwrap();
3642            Pallet::place_commit(
3643                &ALICE,
3644                &STAKING,
3645                &VALIDATOR_ALPHA,
3646                STANDARD_COMMIT,
3647                &Directive::new(Precision::Exact, Fortitude::Force),
3648            )
3649            .unwrap();
3650            let invalid_commit_val = ZERO_VALUE;
3651            assert_err!(
3652                Pallet::raise_commit(
3653                    &ALICE,
3654                    &STAKING,
3655                    invalid_commit_val,
3656                    &Directive::new(Precision::Exact, Fortitude::Polite)
3657                ),
3658                Error::MarkerCommitNotAllowed
3659            );
3660        })
3661    }
3662
3663    #[test]
3664    fn resolve_commit_success_for_direct() {
3665        commit_test_ext().execute_with(|| {
3666            System::set_block_number(10);
3667            set_default_user_balance_and_standard_hold(ALICE).unwrap();
3668            Pallet::place_commit(
3669                &ALICE,
3670                &GOVERNANCE,
3671                &PROPOSAL_RUNTIME_UPGRADE,
3672                STANDARD_COMMIT,
3673                &Directive::new(Precision::Exact, Fortitude::Force),
3674            )
3675            .unwrap();
3676            // Before resolving the commit
3677            assert_eq!(
3678                AssetOf::balance_frozen(&GOVERNANCE, &ALICE),
3679                STANDARD_COMMIT
3680            );
3681            assert_eq!(AssetOf::balance(&ALICE), INITIAL_BALANCE);
3682            let commit_value = Pallet::get_commit_value(&ALICE, &GOVERNANCE).unwrap();
3683            assert_eq!(commit_value, STANDARD_COMMIT);
3684
3685            let reason_value = Pallet::get_total_value(&GOVERNANCE);
3686            assert_eq!(reason_value, 250);
3687            // Resolve commit
3688            assert_ok!(Pallet::resolve_commit(&ALICE, &GOVERNANCE));
3689            // After resolving the commit
3690            assert_eq!(AssetOf::balance(&ALICE), 1250);
3691            assert_eq!(AssetOf::balance_frozen(&GOVERNANCE, &ALICE), ZERO_VALUE);
3692            assert_err!(
3693                Pallet::commit_exists(&ALICE, &GOVERNANCE),
3694                Error::CommitNotFound
3695            );
3696            let digets_value =
3697                Pallet::get_digest_value(&GOVERNANCE, &PROPOSAL_RUNTIME_UPGRADE).unwrap();
3698            assert_eq!(digets_value, ZERO_VALUE);
3699
3700            let reason_value = Pallet::get_total_value(&GOVERNANCE);
3701            assert_eq!(reason_value, ZERO_VALUE);
3702
3703            #[cfg(not(feature = "dev"))]
3704            System::assert_last_event(Event::CommitResolved { 
3705                who: ALICE, 
3706                reason: GOVERNANCE, 
3707                digest: PROPOSAL_RUNTIME_UPGRADE, 
3708                value: 250
3709                }
3710                .into()
3711            );
3712
3713            #[cfg(feature = "dev")]
3714            System::assert_last_event(Event::CommitResolved { 
3715                who: ALICE, 
3716                reason: GOVERNANCE, 
3717                model: DigestVariant::Direct(PROPOSAL_RUNTIME_UPGRADE), 
3718                value: 250
3719                }
3720                .into()
3721            );
3722        })
3723    }
3724
3725    #[test]
3726    fn resolve_commit_for_direct_success_with_reward() {
3727        commit_test_ext().execute_with(|| {
3728            set_default_user_balance_and_standard_hold(ALICE).unwrap();
3729            Pallet::place_commit(
3730                &ALICE,
3731                &STAKING,
3732                &VALIDATOR_ALPHA,
3733                STANDARD_COMMIT,
3734                &Directive::new(Precision::Exact, Fortitude::Force),
3735            )
3736            .unwrap();
3737            assert_eq!(AssetOf::balance_frozen(&STAKING, &ALICE), STANDARD_COMMIT);
3738            assert_eq!(
3739                Pallet::get_commit_value(&ALICE, &STAKING),
3740                Ok(STANDARD_COMMIT)
3741            );
3742            assert_eq!(
3743                Pallet::get_digest_value(&STAKING, &VALIDATOR_ALPHA,),
3744                Ok(STANDARD_COMMIT)
3745            );
3746            // Reward manupulation
3747            let new_reward_value = STANDARD_COMMIT + STANDARD_REWARD; // 250 + 50 -> 300
3748            Pallet::set_digest_value(
3749                &STAKING,
3750                &VALIDATOR_ALPHA,
3751                new_reward_value,
3752                &Default::default(),
3753            )
3754            .unwrap();
3755            assert_eq!(
3756                Pallet::get_digest_value(&STAKING, &VALIDATOR_ALPHA,),
3757                Ok(new_reward_value)
3758            );
3759            assert_eq!(Pallet::get_commit_value(&ALICE, &STAKING), Ok(300));
3760
3761            // Resolving the commit after reward accumulation
3762            assert_ok!(Pallet::resolve_commit(&ALICE, &STAKING));
3763
3764            assert_eq!(AssetOf::balance_frozen(&STAKING, &ALICE), ZERO_VALUE);
3765            assert_eq!(AssetOf::balance(&ALICE), 1300); // existing balance + deposit + reward = 1000 + 250 + 50 = 1300
3766            assert_err!(
3767                Pallet::commit_exists(&ALICE, &STAKING),
3768                Error::CommitNotFound
3769            );
3770            assert_eq!(Pallet::get_digest_value(&STAKING, &VALIDATOR_ALPHA), Ok(0));
3771        });
3772    }
3773
3774    #[test]
3775    fn resolve_commit_withdraw_direct_with_penalty_success() {
3776        commit_test_ext().execute_with(|| {
3777            set_default_user_balance_and_standard_hold(ALICE).unwrap();
3778            Pallet::place_commit(
3779                &ALICE,
3780                &STAKING,
3781                &VALIDATOR_ALPHA,
3782                STANDARD_COMMIT,
3783                &Directive::new(Precision::Exact, Fortitude::Force),
3784            )
3785            .unwrap();
3786
3787            assert_eq!(AssetOf::balance_frozen(&STAKING, &ALICE), STANDARD_COMMIT);
3788            assert_eq!(
3789                Pallet::get_commit_value(&ALICE, &STAKING),
3790                Ok(STANDARD_COMMIT)
3791            );
3792            let digest_value = Pallet::get_digest_value(&STAKING, &VALIDATOR_ALPHA).unwrap();
3793            // Penalty manupulation
3794            let new_penalty_value = digest_value - STANDARD_PENALTY; // 250 - 100 -> 150;
3795            Pallet::set_digest_value(
3796                &STAKING,
3797                &VALIDATOR_ALPHA,
3798                new_penalty_value,
3799                &Default::default(),
3800            )
3801            .unwrap();
3802            assert_eq!(Pallet::get_commit_value(&ALICE, &STAKING), Ok(150));
3803            // Resolving the commit after penalty
3804            assert_ok!(Pallet::resolve_commit(&ALICE, &STAKING));
3805
3806            assert_eq!(AssetOf::balance_frozen(&STAKING, &ALICE), ZERO_VALUE);
3807            assert_eq!(AssetOf::balance(&ALICE), 1150); // existing balance + deposit - penalty = 1000 + 250 - 100 = 1150
3808            assert_err!(
3809                Pallet::commit_exists(&ALICE, &STAKING),
3810                Error::CommitNotFound
3811            );
3812            assert_eq!(
3813                Pallet::get_digest_value(&STAKING, &VALIDATOR_ALPHA),
3814                Ok(ZERO_VALUE)
3815            );
3816        });
3817    }
3818
3819    #[test]
3820    fn resolve_direct_commit_with_penalty_and_reward_by_entry_time() {
3821        commit_test_ext().execute_with(|| {
3822            set_user_balance_and_hold(ALICE, 10000, 5000).unwrap();
3823            set_user_balance_and_hold(BOB, 10000, 5000).unwrap();
3824            set_user_balance_and_hold(CHARLIE, 10000, 5000).unwrap();
3825
3826            Pallet::place_commit(
3827                &ALICE,
3828                &STAKING,
3829                &VALIDATOR_ALPHA,
3830                1000,
3831                &Directive::new(Precision::Exact, Fortitude::Polite),
3832            )
3833            .unwrap();
3834
3835            Pallet::place_commit(
3836                &CHARLIE,
3837                &STAKING,
3838                &VALIDATOR_ALPHA,
3839                1000,
3840                &Directive::new(Precision::Exact, Fortitude::Polite),
3841            )
3842            .unwrap();
3843
3844            // Penalty manupulation
3845            Pallet::set_digest_value(&STAKING, &VALIDATOR_ALPHA, 900, &Default::default()).unwrap();
3846
3847            Pallet::place_commit(
3848                &BOB,
3849                &STAKING,
3850                &VALIDATOR_ALPHA,
3851                400,
3852                &Directive::new(Precision::Exact, Fortitude::Polite),
3853            )
3854            .unwrap();
3855
3856            // Reward manupulation
3857            Pallet::set_digest_value(&STAKING, &VALIDATOR_ALPHA, 1500, &Default::default())
3858                .unwrap();
3859
3860            // Both, alice and charlie absorbed the penalty and reward shock according to their weight
3861            let alice_resolved = Pallet::resolve_commit(&ALICE, &STAKING).unwrap();
3862            assert_eq!(alice_resolved, 519);
3863
3864            let charlie_resolved = Pallet::resolve_commit(&CHARLIE, &STAKING).unwrap();
3865            assert_eq!(charlie_resolved, 519);
3866
3867            // Since, bob placed a commit after the penalty he dosen't
3868            // absorbed the penalty shock but absorbs reward shock.
3869            let bob_resolved = Pallet::resolve_commit(&BOB, &STAKING).unwrap();
3870            assert_eq!(bob_resolved, 462);
3871        });
3872    }
3873
3874    #[test]
3875    fn resolve_commit_success_for_index() {
3876        commit_test_ext().execute_with(|| {
3877            System::set_block_number(10);
3878            set_default_user_balance_and_standard_hold(ALICE).unwrap();
3879            set_default_user_balance_and_standard_hold(CHARLIE).unwrap();
3880            set_default_user_balance_and_standard_hold(BOB).unwrap();
3881            Pallet::place_commit(
3882                &CHARLIE,
3883                &STAKING,
3884                &VALIDATOR_ALPHA,
3885                STANDARD_COMMIT,
3886                &Directive::new(Precision::Exact, Fortitude::Force),
3887            )
3888            .unwrap();
3889            Pallet::place_commit(
3890                &BOB,
3891                &STAKING,
3892                &VALIDATOR_BETA,
3893                STANDARD_COMMIT,
3894                &Directive::new(Precision::Exact, Fortitude::Force),
3895            )
3896            .unwrap();
3897            prepare_and_initiate_index(
3898                MIKE,
3899                STAKING,
3900                &[
3901                    (VALIDATOR_ALPHA, SHARE_MAJOR),
3902                    (VALIDATOR_BETA, SHARE_DOMINANT),
3903                ],
3904                INDEX_OPTIMIZED_STAKING,
3905            )
3906            .unwrap();
3907            Pallet::place_commit(
3908                &ALICE,
3909                &STAKING,
3910                &INDEX_OPTIMIZED_STAKING,
3911                LARGE_COMMIT,
3912                &Directive::new(Precision::Exact, Fortitude::Force),
3913            )
3914            .unwrap();
3915            // Alice balance state before resolving
3916            assert_eq!(AssetOf::balance(&ALICE), INITIAL_BALANCE);
3917            assert_eq!(AssetOf::balance_frozen(&STAKING, &ALICE), 500);
3918            assert_eq!(Pallet::get_commit_value(&ALICE, &STAKING), Ok(500));
3919            let actual_entries_value =
3920                Pallet::get_entries_value(&STAKING, &INDEX_OPTIMIZED_STAKING).unwrap();
3921            let expected_entries_value = vec![(VALIDATOR_ALPHA, 200), (VALIDATOR_BETA, 300)];
3922            assert_eq!(actual_entries_value, expected_entries_value);
3923
3924            assert_ok!(Pallet::resolve_commit(&ALICE, &STAKING));
3925            // alice's balance state after resolving
3926            assert_eq!(AssetOf::balance_frozen(&STAKING, &ALICE), ZERO_VALUE);
3927            assert_eq!(AssetOf::balance(&ALICE), 1500); // existing balance + resolved balance -> 1000 + 500 = 1500
3928            assert_err!(
3929                Pallet::commit_exists(&ALICE, &STAKING),
3930                Error::CommitNotFound
3931            );
3932            assert_eq!(
3933                Pallet::get_index_value(&STAKING, &INDEX_OPTIMIZED_STAKING),
3934                Ok(ZERO_VALUE)
3935            );
3936            let actual_entries_value =
3937                Pallet::get_entries_value(&STAKING, &INDEX_OPTIMIZED_STAKING).unwrap();
3938            let expected_entries_value =
3939                vec![(VALIDATOR_ALPHA, ZERO_VALUE), (VALIDATOR_BETA, ZERO_VALUE)];
3940            assert_eq!(actual_entries_value, expected_entries_value);
3941
3942            #[cfg(not(feature = "dev"))]
3943            System::assert_last_event(Event::CommitResolved { 
3944                who: ALICE, 
3945                reason: STAKING, 
3946                digest: INDEX_OPTIMIZED_STAKING, 
3947                value: 500
3948                }
3949                .into()
3950            );
3951
3952            #[cfg(feature = "dev")]
3953            System::assert_last_event(Event::CommitResolved { 
3954                who: ALICE, 
3955                reason: STAKING, 
3956                model: DigestVariant::Index(INDEX_OPTIMIZED_STAKING), 
3957                value: 500
3958                }
3959                .into()
3960            );
3961        })
3962    }
3963
3964    #[test]
3965    fn resolve_commit_index_with_rewards() {
3966        commit_test_ext().execute_with(|| {
3967            set_default_user_balance_and_standard_hold(ALICE).unwrap();
3968            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
3969            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
3970            prepare_and_initiate_index(
3971                ALICE,
3972                STAKING,
3973                &[
3974                    (VALIDATOR_ALPHA, SHARE_MAJOR),
3975                    (VALIDATOR_BETA, SHARE_DOMINANT),
3976                ],
3977                INDEX_OPTIMIZED_STAKING,
3978            )
3979            .unwrap();
3980            Pallet::place_commit(
3981                &ALICE,
3982                &STAKING,
3983                &INDEX_OPTIMIZED_STAKING,
3984                350,
3985                &Directive::new(Precision::BestEffort, Fortitude::Polite),
3986            )
3987            .unwrap();
3988            assert_eq!(AssetOf::balance_on_hold(&PREPARE_FOR_COMMIT, &ALICE), 150);
3989            assert_eq!(AssetOf::balance_frozen(&STAKING, &ALICE), 350);
3990            assert_eq!(Pallet::get_commit_value(&ALICE, &STAKING), Ok(350));
3991            // index balance
3992            assert_eq!(
3993                Pallet::get_index_value(&STAKING, &INDEX_OPTIMIZED_STAKING),
3994                Ok(350)
3995            );
3996            let actual_entries_value =
3997                Pallet::get_entries_value(&STAKING, &INDEX_OPTIMIZED_STAKING).unwrap();
3998            let expected_entries_value = vec![(VALIDATOR_ALPHA, 140), (VALIDATOR_BETA, 210)];
3999            assert_eq!(actual_entries_value, expected_entries_value);
4000            // Stimulating reward senario
4001            // Apply rewards: Alpha 14 -> 18 (+4), Beta 21 -> 27 (+6)
4002            let new_alpha_reward_value = 180;
4003            Pallet::set_digest_value(
4004                &STAKING,
4005                &VALIDATOR_ALPHA,
4006                new_alpha_reward_value,
4007                &Default::default(),
4008            )
4009            .unwrap();
4010            let new_beta_reward_value = 270;
4011            Pallet::set_digest_value(
4012                &STAKING,
4013                &VALIDATOR_BETA,
4014                new_beta_reward_value,
4015                &Default::default(),
4016            )
4017            .unwrap();
4018            // Expected index value: 180 + 270 = 450
4019            assert_eq!(
4020                Pallet::get_index_value(&STAKING, &INDEX_OPTIMIZED_STAKING),
4021                Ok(450)
4022            );
4023
4024            let actual_entries_value =
4025                Pallet::get_entries_value(&STAKING, &INDEX_OPTIMIZED_STAKING).unwrap();
4026            let expected_entries_value = vec![(VALIDATOR_ALPHA, 180), (VALIDATOR_BETA, 270)];
4027            assert_eq!(actual_entries_value, expected_entries_value);
4028            // Alice resolves commitment and gets 450 tokens
4029            assert_ok!(Pallet::resolve_commit(&ALICE, &STAKING));
4030            // Final balance: 1000 (existing) + 450 (resolved with precision loss) = 1450
4031            assert_eq!(AssetOf::balance_frozen(&STAKING, &ALICE), 0);
4032            assert_eq!(AssetOf::balance(&ALICE), 1450);
4033            assert_err!(
4034                Pallet::commit_exists(&ALICE, &STAKING),
4035                Error::CommitNotFound
4036            );
4037            assert_eq!(Pallet::get_digest_value(&STAKING, &VALIDATOR_ALPHA), Ok(0));
4038            assert_eq!(Pallet::get_digest_value(&STAKING, &VALIDATOR_BETA), Ok(0));
4039        });
4040    }
4041
4042    #[test]
4043    fn resolve_commit_withdraw_index_with_reward_success() {
4044        commit_test_ext().execute_with(|| {
4045            set_default_user_balance_and_standard_hold(ALICE).unwrap();
4046            set_default_user_balance_and_standard_hold(CHARLIE).unwrap();
4047            set_default_user_balance_and_standard_hold(BOB).unwrap();
4048            Pallet::place_commit(
4049                &CHARLIE,
4050                &STAKING,
4051                &VALIDATOR_ALPHA,
4052                STANDARD_COMMIT,
4053                &Directive::new(Precision::Exact, Fortitude::Force),
4054            )
4055            .unwrap();
4056            Pallet::place_commit(
4057                &BOB,
4058                &STAKING,
4059                &VALIDATOR_BETA,
4060                STANDARD_COMMIT,
4061                &Directive::new(Precision::Exact, Fortitude::Force),
4062            )
4063            .unwrap();
4064            prepare_and_initiate_index(
4065                MIKE,
4066                STAKING,
4067                &[
4068                    (VALIDATOR_ALPHA, SHARE_MAJOR),
4069                    (VALIDATOR_BETA, SHARE_DOMINANT),
4070                ],
4071                INDEX_OPTIMIZED_STAKING,
4072            )
4073            .unwrap();
4074            Pallet::place_commit(
4075                &ALICE,
4076                &STAKING,
4077                &INDEX_OPTIMIZED_STAKING,
4078                LARGE_COMMIT,
4079                &Directive::new(Precision::BestEffort, Fortitude::Polite),
4080            )
4081            .unwrap();
4082            // Alice balance state before resolvig
4083            assert_eq!(
4084                AssetOf::balance_on_hold(&PREPARE_FOR_COMMIT, &ALICE),
4085                ZERO_VALUE
4086            );
4087            assert_eq!(AssetOf::balance_frozen(&STAKING, &ALICE), LARGE_COMMIT);
4088            assert_eq!(Pallet::get_commit_value(&ALICE, &STAKING), Ok(LARGE_COMMIT));
4089            assert_eq!(
4090                Pallet::get_index_value(&STAKING, &INDEX_OPTIMIZED_STAKING),
4091                Ok(500)
4092            );
4093            let actual_entries_value =
4094                Pallet::get_entries_value(&STAKING, &INDEX_OPTIMIZED_STAKING).unwrap();
4095            let expected_entries_value = vec![(VALIDATOR_ALPHA, 200), (VALIDATOR_BETA, 300)];
4096            assert_eq!(actual_entries_value, expected_entries_value);
4097            // Stimulating reward senario
4098            let alpha_digest_value = Pallet::get_digest_value(&STAKING, &VALIDATOR_ALPHA).unwrap();
4099            assert_eq!(alpha_digest_value, 450);
4100            let new_alpha_reward_value = alpha_digest_value + STANDARD_REWARD;
4101            Pallet::set_digest_value(
4102                &STAKING,
4103                &VALIDATOR_ALPHA,
4104                new_alpha_reward_value,
4105                &Default::default(),
4106            )
4107            .unwrap();
4108            let beta_digest_value = Pallet::get_digest_value(&STAKING, &VALIDATOR_BETA).unwrap();
4109            assert_eq!(beta_digest_value, 550);
4110            let new_beta_reward_value = beta_digest_value + STANDARD_REWARD;
4111            Pallet::set_digest_value(
4112                &STAKING,
4113                &VALIDATOR_BETA,
4114                new_beta_reward_value,
4115                &Default::default(),
4116            )
4117            .unwrap();
4118            // Index value after reward application
4119            // Note: There might be slight precision loss due to fixed-point bias arithmetic
4120            // Alice's portion should reflect proportional rewards
4121            // VALIDATOR_ALPHA bias: 500/450 ~= 1.111, VALIDATOR_BETA bias: 600/550 ~= 1.091
4122            // Alice's new value: (200 × 1.111) + (300 × 1.091) ~= 222 + 327 = 549
4123            assert_eq!(
4124                Pallet::get_index_value(&STAKING, &INDEX_OPTIMIZED_STAKING),
4125                Ok(549)
4126            );
4127            let actual_entries_value =
4128                Pallet::get_entries_value(&STAKING, &INDEX_OPTIMIZED_STAKING).unwrap();
4129            let expected_entries_value = vec![(VALIDATOR_ALPHA, 222), (VALIDATOR_BETA, 327)];
4130            assert_eq!(actual_entries_value, expected_entries_value);
4131            // Alice resolves commitment
4132            assert_ok!(Pallet::resolve_commit(&ALICE, &STAKING));
4133            assert_eq!(AssetOf::balance_frozen(&STAKING, &ALICE), ZERO_VALUE);
4134            // Final balance = 1000 (existing) + 549 (resolved with precision loss) = 1549
4135            assert_eq!(AssetOf::balance(&ALICE), 1549);
4136            assert_err!(
4137                Pallet::commit_exists(&ALICE, &STAKING),
4138                Error::CommitNotFound
4139            );
4140            // Remaining digest balances after alice withdrawal
4141            assert_eq!(
4142                Pallet::get_digest_value(&STAKING, &VALIDATOR_ALPHA),
4143                Ok(278)
4144            );
4145            assert_eq!(Pallet::get_digest_value(&STAKING, &VALIDATOR_BETA), Ok(273));
4146        });
4147    }
4148
4149    #[test]
4150    fn resolve_commit_withdraw_index_with_penalty_success() {
4151        commit_test_ext().execute_with(|| {
4152            set_default_user_balance_and_standard_hold(ALICE).unwrap();
4153            set_default_user_balance_and_standard_hold(CHARLIE).unwrap();
4154            set_default_user_balance_and_standard_hold(BOB).unwrap();
4155            Pallet::place_commit(
4156                &CHARLIE,
4157                &STAKING,
4158                &VALIDATOR_ALPHA,
4159                STANDARD_COMMIT,
4160                &Directive::new(Precision::Exact, Fortitude::Force),
4161            )
4162            .unwrap();
4163            Pallet::place_commit(
4164                &BOB,
4165                &STAKING,
4166                &VALIDATOR_BETA,
4167                STANDARD_COMMIT,
4168                &Directive::new(Precision::Exact, Fortitude::Force),
4169            )
4170            .unwrap();
4171            prepare_and_initiate_index(
4172                MIKE,
4173                STAKING,
4174                &[
4175                    (VALIDATOR_ALPHA, SHARE_MAJOR),
4176                    (VALIDATOR_BETA, SHARE_DOMINANT),
4177                ],
4178                INDEX_OPTIMIZED_STAKING,
4179            )
4180            .unwrap();
4181            Pallet::place_commit(
4182                &ALICE,
4183                &STAKING,
4184                &INDEX_OPTIMIZED_STAKING,
4185                LARGE_COMMIT,
4186                &Directive::new(Precision::Exact, Fortitude::Force),
4187            )
4188            .unwrap();
4189            assert_eq!(AssetOf::balance_frozen(&STAKING, &ALICE), LARGE_COMMIT);
4190            assert_eq!(Pallet::get_commit_value(&ALICE, &STAKING), Ok(LARGE_COMMIT));
4191            assert_eq!(
4192                Pallet::get_index_value(&STAKING, &INDEX_OPTIMIZED_STAKING),
4193                Ok(500)
4194            );
4195            let actual_entries_value =
4196                Pallet::get_entries_value(&STAKING, &INDEX_OPTIMIZED_STAKING).unwrap();
4197            let expected_entries_value = vec![(VALIDATOR_ALPHA, 200), (VALIDATOR_BETA, 300)];
4198            assert_eq!(actual_entries_value, expected_entries_value);
4199            // Stimulating penalty senario
4200            let alpha_digest_value = Pallet::get_digest_value(&STAKING, &VALIDATOR_ALPHA).unwrap();
4201            assert_eq!(alpha_digest_value, 450);
4202            let new_alpha_penalty_value = alpha_digest_value - STANDARD_PENALTY;
4203            Pallet::set_digest_value(
4204                &STAKING,
4205                &VALIDATOR_ALPHA,
4206                new_alpha_penalty_value,
4207                &Default::default(),
4208            )
4209            .unwrap();
4210            let beta_digest_value = Pallet::get_digest_value(&STAKING, &VALIDATOR_BETA).unwrap();
4211            assert_eq!(beta_digest_value, 550);
4212            let new_beta_penalty_value = beta_digest_value - STANDARD_PENALTY;
4213            Pallet::set_digest_value(
4214                &STAKING,
4215                &VALIDATOR_BETA,
4216                new_beta_penalty_value,
4217                &Default::default(),
4218            )
4219            .unwrap();
4220            // Index value after penalty application
4221            // Note: There might be slight precision loss due to fixed-point bias arithmetic
4222            // Alice's portion should reflect proportional penalty suffer
4223            assert_eq!(
4224                Pallet::get_index_value(&STAKING, &INDEX_OPTIMIZED_STAKING),
4225                Ok(400)
4226            );
4227            let actual_entries_value =
4228                Pallet::get_entries_value(&STAKING, &INDEX_OPTIMIZED_STAKING).unwrap();
4229            let expected_entries_value = vec![(VALIDATOR_ALPHA, 155), (VALIDATOR_BETA, 245)];
4230            assert_eq!(actual_entries_value, expected_entries_value);
4231            // Alice resolves commitment
4232            assert_ok!(Pallet::resolve_commit(&ALICE, &STAKING));
4233            // Final balance = 1000 (existing) + 400 (resolved) = 1400
4234            assert_eq!(AssetOf::balance(&ALICE), 1400);
4235            assert_eq!(AssetOf::balance_frozen(&STAKING, &ALICE), ZERO_VALUE);
4236            assert_err!(
4237                Pallet::commit_exists(&ALICE, &STAKING),
4238                Error::CommitNotFound
4239            );
4240            // Remaining digest balances after alice withdrawal
4241            assert_eq!(
4242                Pallet::get_digest_value(&STAKING, &VALIDATOR_ALPHA),
4243                Ok(195)
4244            );
4245            assert_eq!(Pallet::get_digest_value(&STAKING, &VALIDATOR_BETA), Ok(205));
4246        });
4247    }
4248
4249    #[test]
4250    fn resolve_index_commit_with_penalty_and_reward_distribution_by_entry_time() {
4251        commit_test_ext().execute_with(|| {
4252            set_default_user_balance_and_standard_hold(ALICE).unwrap();
4253            set_default_user_balance_and_standard_hold(CHARLIE).unwrap();
4254            set_default_user_balance_and_standard_hold(BOB).unwrap();
4255            set_default_user_balance_and_standard_hold(ALAN).unwrap();
4256            set_default_user_balance_and_standard_hold(NIX).unwrap();
4257            set_default_user_balance_and_standard_hold(MIKE).unwrap();
4258            Pallet::place_commit(
4259                &CHARLIE,
4260                &STAKING,
4261                &VALIDATOR_ALPHA,
4262                STANDARD_COMMIT,
4263                &Directive::new(Precision::Exact, Fortitude::Force),
4264            )
4265            .unwrap();
4266            Pallet::place_commit(
4267                &BOB,
4268                &STAKING,
4269                &VALIDATOR_BETA,
4270                STANDARD_COMMIT,
4271                &Directive::new(Precision::Exact, Fortitude::Force),
4272            )
4273            .unwrap();
4274            prepare_and_initiate_index(
4275                MIKE,
4276                STAKING,
4277                &[
4278                    (VALIDATOR_ALPHA, SHARE_MAJOR),
4279                    (VALIDATOR_BETA, SHARE_DOMINANT),
4280                ],
4281                INDEX_OPTIMIZED_STAKING,
4282            )
4283            .unwrap();
4284
4285            Pallet::place_commit(
4286                &ALAN,
4287                &STAKING,
4288                &INDEX_OPTIMIZED_STAKING,
4289                LARGE_COMMIT,
4290                &Directive::new(Precision::Exact, Fortitude::Force),
4291            )
4292            .unwrap();
4293
4294            Pallet::place_commit(
4295                &MIKE,
4296                &STAKING,
4297                &INDEX_OPTIMIZED_STAKING,
4298                STANDARD_COMMIT,
4299                &Directive::new(Precision::Exact, Fortitude::Force),
4300            )
4301            .unwrap();
4302
4303            let alan_entries_value =
4304                Pallet::get_entries_value_for(&ALAN, &STAKING, &INDEX_OPTIMIZED_STAKING).unwrap();
4305            let mike_entries_value =
4306                Pallet::get_entries_value_for(&MIKE, &STAKING, &INDEX_OPTIMIZED_STAKING).unwrap();
4307            let expected_alan_entries_value = vec![(VALIDATOR_ALPHA, 200), (VALIDATOR_BETA, 300)];
4308            let expected_mike_entries_value = vec![(VALIDATOR_ALPHA, 100), (VALIDATOR_BETA, 150)];
4309            assert_eq!(expected_alan_entries_value, alan_entries_value);
4310            assert_eq!(expected_mike_entries_value, mike_entries_value);
4311
4312            // Penalty manupulation
4313            Pallet::set_digest_value(&STAKING, &VALIDATOR_ALPHA, 500, &Default::default()).unwrap();
4314
4315            let alan_entries_value =
4316                Pallet::get_entries_value_for(&ALAN, &STAKING, &INDEX_OPTIMIZED_STAKING).unwrap();
4317            let mike_entries_value =
4318                Pallet::get_entries_value_for(&MIKE, &STAKING, &INDEX_OPTIMIZED_STAKING).unwrap();
4319            let expected_alan_entries_value = vec![(VALIDATOR_ALPHA, 181), (VALIDATOR_BETA, 300)];
4320            let expected_mike_entries_value = vec![(VALIDATOR_ALPHA, 90), (VALIDATOR_BETA, 150)];
4321            assert_eq!(expected_alan_entries_value, alan_entries_value);
4322            assert_eq!(expected_mike_entries_value, mike_entries_value);
4323
4324            let charlie_digest_value = Pallet::get_commit_value(&CHARLIE, &STAKING).unwrap();
4325            let expected_charlie_digest_value = 227;
4326            assert_eq!(expected_charlie_digest_value, charlie_digest_value);
4327
4328            Pallet::place_commit(
4329                &NIX,
4330                &STAKING,
4331                &INDEX_OPTIMIZED_STAKING,
4332                STANDARD_COMMIT,
4333                &Directive::new(Precision::Exact, Fortitude::Force),
4334            )
4335            .unwrap();
4336
4337            let nix_entries_value =
4338                Pallet::get_entries_value_for(&NIX, &STAKING, &INDEX_OPTIMIZED_STAKING).unwrap();
4339            let expected_nix_entries_value = vec![(VALIDATOR_ALPHA, 99), (VALIDATOR_BETA, 150)];
4340            assert_eq!(expected_nix_entries_value, nix_entries_value);
4341
4342            // Reward manupulation
4343            Pallet::set_digest_value(&STAKING, &VALIDATOR_ALPHA, 1000, &Default::default())
4344                .unwrap();
4345
4346            let alan_entries_value =
4347                Pallet::get_entries_value_for(&ALAN, &STAKING, &INDEX_OPTIMIZED_STAKING).unwrap();
4348            let mike_entries_value =
4349                Pallet::get_entries_value_for(&MIKE, &STAKING, &INDEX_OPTIMIZED_STAKING).unwrap();
4350            let nix_entries_value =
4351                Pallet::get_entries_value_for(&NIX, &STAKING, &INDEX_OPTIMIZED_STAKING).unwrap();
4352            let expected_alan_entries_value = vec![(VALIDATOR_ALPHA, 303), (VALIDATOR_BETA, 300)];
4353            let expected_mike_entries_value = vec![(VALIDATOR_ALPHA, 151), (VALIDATOR_BETA, 150)];
4354            let expected_nix_entries_value = vec![(VALIDATOR_ALPHA, 166), (VALIDATOR_BETA, 150)];
4355            assert_eq!(expected_alan_entries_value, alan_entries_value);
4356            assert_eq!(expected_mike_entries_value, mike_entries_value);
4357            assert_eq!(expected_nix_entries_value, nix_entries_value);
4358
4359            let charlie_digest_value = Pallet::get_commit_value(&CHARLIE, &STAKING).unwrap();
4360            let expected_charlie_digest_value = 378;
4361            assert_eq!(expected_charlie_digest_value, charlie_digest_value);
4362
4363            // Both, mike and alan absorbed the penalty and reward shock according to their weight
4364            let mike_resolved = Pallet::resolve_commit(&MIKE, &STAKING).unwrap();
4365            assert_eq!(mike_resolved, 301);
4366
4367            let alan_resolved = Pallet::resolve_commit(&ALAN, &STAKING).unwrap();
4368            assert_eq!(alan_resolved, 603);
4369
4370            // Since, nix placed a commit after the penalty he dosen't
4371            // absorbed the penalty shock but absorbs reward shock.
4372            let nix_resolved = Pallet::resolve_commit(&NIX, &STAKING).unwrap();
4373            assert_eq!(nix_resolved, 316);
4374
4375            // Charlie, as the last withdrawer, receives the remaining dust from rounding.
4376            let charlie_resolved = Pallet::resolve_commit(&CHARLIE, &STAKING).unwrap();
4377            assert_eq!(charlie_resolved, 380);
4378        })
4379    }
4380
4381    #[test]
4382    fn resolve_commit_success_for_pool() {
4383        commit_test_ext().execute_with(|| {
4384            System::set_block_number(10);
4385            set_default_user_balance_and_standard_hold(ALICE).unwrap();
4386            set_default_user_balance_and_standard_hold(BOB).unwrap();
4387            set_default_user_balance_and_standard_hold(CHARLIE).unwrap();
4388            set_default_user_balance_and_standard_hold(MIKE).unwrap();
4389
4390            Pallet::place_commit(
4391                &BOB,
4392                &STAKING,
4393                &VALIDATOR_ALPHA,
4394                STANDARD_COMMIT,
4395                &Directive::new(Precision::Exact, Fortitude::Force),
4396            )
4397            .unwrap();
4398            Pallet::place_commit(
4399                &CHARLIE,
4400                &STAKING,
4401                &VALIDATOR_BETA,
4402                STANDARD_COMMIT,
4403                &Directive::new(Precision::Exact, Fortitude::Force),
4404            )
4405            .unwrap();
4406            let entries = vec![
4407                (VALIDATOR_ALPHA, SHARE_MAJOR),
4408                (VALIDATOR_BETA, SHARE_DOMINANT),
4409            ];
4410            prepare_and_initiate_pool(
4411                MIKE,
4412                STAKING,
4413                &entries,
4414                INDEX_OPTIMIZED_STAKING,
4415                POOL_MANAGED_STAKING,
4416                COMMISSION_STANDARD,
4417            )
4418            .unwrap();
4419
4420            Pallet::place_commit(
4421                &ALICE,
4422                &STAKING,
4423                &POOL_MANAGED_STAKING,
4424                STANDARD_COMMIT,
4425                &Directive::new(Precision::Exact, Fortitude::Force),
4426            )
4427            .unwrap();
4428            // Before resolving the commit
4429            let pool_info = PoolMap::get((STAKING, POOL_MANAGED_STAKING)).unwrap();
4430            let pool_balance_of = pool_info.balance();
4431            assert_ok!(has_deposits(
4432                &pool_balance_of,
4433                &Default::default(),
4434                &POOL_MANAGED_STAKING
4435            ));
4436            assert_eq!(
4437                balance_total(&pool_balance_of, &Default::default(), &POOL_MANAGED_STAKING)
4438                    .unwrap(),
4439                STANDARD_COMMIT
4440            );
4441            let pool_capital = pool_info.capital();
4442            assert_eq!(pool_capital, 100);
4443            let actual_slots_value =
4444                Pallet::get_slots_value(&STAKING, &POOL_MANAGED_STAKING).unwrap();
4445            let expected_slots_value = vec![(VALIDATOR_ALPHA, 100), (VALIDATOR_BETA, 150)];
4446            assert_eq!(actual_slots_value, expected_slots_value);
4447
4448            let reason_value = ReasonValue::get(STAKING).unwrap();
4449            assert_eq!(reason_value, 750);
4450            // resolve the commit
4451            assert_ok!(Pallet::resolve_commit(&ALICE, &STAKING));
4452            // After resolving the commit
4453            let pool_info = PoolMap::get((STAKING, POOL_MANAGED_STAKING)).unwrap();
4454            let pool_balance_of = pool_info.balance();
4455            assert!(
4456                has_deposits(&pool_balance_of, &Default::default(), &POOL_MANAGED_STAKING).is_err()
4457            );
4458            assert_eq!(
4459                balance_total(&pool_balance_of, &Default::default(), &POOL_MANAGED_STAKING)
4460                    .unwrap(),
4461                ZERO_VALUE,
4462            );
4463            let pool_capital = pool_info.capital();
4464            assert_eq!(pool_capital, 100);
4465            let actual_slots_value =
4466                Pallet::get_slots_value(&STAKING, &POOL_MANAGED_STAKING).unwrap();
4467            let expected_slots_value =
4468                vec![(VALIDATOR_ALPHA, ZERO_VALUE), (VALIDATOR_BETA, ZERO_VALUE)];
4469            assert_eq!(actual_slots_value, expected_slots_value);
4470            let reason_value = ReasonValue::get(STAKING).unwrap();
4471            assert_eq!(reason_value, 500);
4472            // Alice Balance check
4473            // Final balance = 1000(initial) + 250(commit) - 10%(commission) -> 1225
4474            let expected_balance =
4475                INITIAL_BALANCE + STANDARD_COMMIT - (COMMISSION_STANDARD * STANDARD_COMMIT);
4476            dbg!(expected_balance);
4477            let actual_balance = AssetOf::balance(&ALICE);
4478            assert_eq!(actual_balance, expected_balance);
4479            assert_eq!(AssetOf::balance_frozen(&STAKING, &ALICE), ZERO_VALUE);
4480            // Mike Balnce check
4481            // Final balance = 1000(initial) + 10%(commission) -> 1025
4482            let expected_balance = INITIAL_BALANCE + (COMMISSION_STANDARD * STANDARD_COMMIT);
4483            let actual_balance = AssetOf::balance(&MIKE);
4484            assert_eq!(actual_balance, expected_balance);
4485
4486            #[cfg(not(feature = "dev"))]
4487            System::assert_last_event(Event::CommitResolved { 
4488                who: ALICE, 
4489                reason: STAKING, 
4490                digest: POOL_MANAGED_STAKING, 
4491                value: 225
4492                }
4493                .into()
4494            );
4495
4496            #[cfg(feature = "dev")]
4497            System::assert_last_event(Event::CommitResolved { 
4498                who: ALICE, 
4499                reason: STAKING, 
4500                model: DigestVariant::Pool(POOL_MANAGED_STAKING), 
4501                value: 225
4502                }
4503                .into()
4504            );
4505        })
4506    }
4507
4508    #[test]
4509    fn resolve_commit_withdraw_pool_with_reward_success() {
4510        commit_test_ext().execute_with(|| {
4511            set_default_user_balance_and_standard_hold(ALICE).unwrap();
4512            set_default_user_balance_and_standard_hold(BOB).unwrap();
4513            set_default_user_balance_and_standard_hold(CHARLIE).unwrap();
4514            set_default_user_balance_and_standard_hold(ALAN).unwrap();
4515            set_default_user_balance_and_standard_hold(MIKE).unwrap();
4516
4517            Pallet::place_commit(
4518                &ALICE,
4519                &STAKING,
4520                &VALIDATOR_ALPHA,
4521                STANDARD_COMMIT,
4522                &Directive::new(Precision::Exact, Fortitude::Force),
4523            )
4524            .unwrap();
4525            Pallet::place_commit(
4526                &BOB,
4527                &STAKING,
4528                &VALIDATOR_BETA,
4529                STANDARD_COMMIT,
4530                &Directive::new(Precision::Exact, Fortitude::Force),
4531            )
4532            .unwrap();
4533            let entries = vec![
4534                (VALIDATOR_ALPHA, SHARE_MAJOR),
4535                (VALIDATOR_BETA, SHARE_DOMINANT),
4536            ];
4537            prepare_and_initiate_pool(
4538                MIKE,
4539                STAKING,
4540                &entries,
4541                INDEX_OPTIMIZED_STAKING,
4542                POOL_MANAGED_STAKING,
4543                COMMISSION_STANDARD,
4544            )
4545            .unwrap();
4546            Pallet::place_commit(
4547                &CHARLIE,
4548                &STAKING,
4549                &POOL_MANAGED_STAKING,
4550                LARGE_COMMIT,
4551                &Directive::new(Precision::Exact, Fortitude::Force),
4552            )
4553            .unwrap();
4554            // Before resolving the commit
4555            let actual_pool_balance =
4556                Pallet::get_pool_value(&STAKING, &POOL_MANAGED_STAKING).unwrap();
4557            assert_eq!(actual_pool_balance, LARGE_COMMIT);
4558            let actual_slots_value =
4559                Pallet::get_slots_value(&STAKING, &POOL_MANAGED_STAKING).unwrap();
4560            let expected_slots_value = vec![(VALIDATOR_ALPHA, 200), (VALIDATOR_BETA, 300)];
4561            assert_eq!(actual_slots_value, expected_slots_value);
4562
4563            let reason_value = ReasonValue::get(STAKING).unwrap();
4564            assert_eq!(reason_value, 1000);
4565            // Simulating reward senario
4566            let alpha_digest_value = Pallet::get_digest_value(&STAKING, &VALIDATOR_ALPHA).unwrap();
4567            assert_eq!(alpha_digest_value, 450);
4568            let new_alpha_reward_value = alpha_digest_value + STANDARD_REWARD; //450 + 50(reward) -> 500
4569            Pallet::set_digest_value(
4570                &STAKING,
4571                &VALIDATOR_ALPHA,
4572                new_alpha_reward_value,
4573                &Default::default(),
4574            )
4575            .unwrap();
4576            let beta_digest_value = Pallet::get_digest_value(&STAKING, &VALIDATOR_BETA).unwrap();
4577            assert_eq!(beta_digest_value, 550);
4578            let new_beta_reward_value = beta_digest_value + STANDARD_REWARD; //550 + 50(reward) -> 600
4579            Pallet::set_digest_value(
4580                &STAKING,
4581                &VALIDATOR_BETA,
4582                new_beta_reward_value,
4583                &Default::default(),
4584            )
4585            .unwrap();
4586            // Underlying digests value after reward senario
4587            let alpha_digest_value = Pallet::get_digest_value(&STAKING, &VALIDATOR_ALPHA).unwrap();
4588            assert_eq!(alpha_digest_value, 500);
4589            let beta_digest_value = Pallet::get_digest_value(&STAKING, &VALIDATOR_BETA).unwrap();
4590            assert_eq!(beta_digest_value, 600);
4591            // Pool value updated
4592            let pool_value = Pallet::get_pool_value(&STAKING, &POOL_MANAGED_STAKING).unwrap();
4593            // precision loss catered
4594            assert_eq!(pool_value, 549);
4595            let actual_slots_value =
4596                Pallet::get_slots_value(&STAKING, &POOL_MANAGED_STAKING).unwrap();
4597            let expected_slots_value = vec![(VALIDATOR_ALPHA, 222), (VALIDATOR_BETA, 327)];
4598            assert_eq!(actual_slots_value, expected_slots_value);
4599
4600            // resolve the commit
4601            assert_ok!(Pallet::resolve_commit(&CHARLIE, &STAKING));
4602
4603            // After resolving the commit
4604            let pool_info = PoolMap::get((STAKING, POOL_MANAGED_STAKING)).unwrap();
4605            let pool_balance_of = pool_info.balance();
4606            assert!(
4607                has_deposits(&pool_balance_of, &Default::default(), &POOL_MANAGED_STAKING).is_err()
4608            );
4609            assert_eq!(
4610                balance_total(&pool_balance_of, &Default::default(), &POOL_MANAGED_STAKING)
4611                    .unwrap(),
4612                0,
4613            );
4614
4615            let actual_slots_value =
4616                Pallet::get_slots_value(&STAKING, &POOL_MANAGED_STAKING).unwrap();
4617            let expected_slots_value = vec![(VALIDATOR_ALPHA, 0), (VALIDATOR_BETA, 0)];
4618            assert_eq!(actual_slots_value, expected_slots_value);
4619
4620            // Underlying digests value after resolving pool commit
4621            let alpha_digest_value = Pallet::get_digest_value(&STAKING, &VALIDATOR_ALPHA).unwrap();
4622            assert_eq!(alpha_digest_value, 278);
4623            let beta_digest_value = Pallet::get_digest_value(&STAKING, &VALIDATOR_BETA).unwrap();
4624            assert_eq!(beta_digest_value, 273);
4625
4626            let reason_value = ReasonValue::get(STAKING).unwrap();
4627            assert_eq!(reason_value, 551);
4628
4629            // Charlies balance check
4630            let actual_balance = AssetOf::balance(&CHARLIE);
4631            let expected_balance = INITIAL_BALANCE + 495; // initial_balance + resolved_blance with commission deduction
4632
4633            assert_eq!(actual_balance, expected_balance);
4634            let actual_freeze_balance = AssetOf::balance_frozen(&STAKING, &CHARLIE);
4635            assert_eq!(actual_freeze_balance, ZERO_VALUE);
4636
4637            // Mike balance check for commison settlement
4638            let actual_balance = AssetOf::balance(&MIKE);
4639            let expected_balance = INITIAL_BALANCE + 54; // initial_balance + commission for the resolved commit
4640            assert_eq!(actual_balance, expected_balance);
4641        });
4642    }
4643
4644    #[test]
4645    fn resolve_commit_withdraw_pool_with_penalty_success() {
4646        commit_test_ext().execute_with(|| {
4647            set_default_user_balance_and_standard_hold(ALICE).unwrap();
4648            set_default_user_balance_and_standard_hold(BOB).unwrap();
4649            set_default_user_balance_and_standard_hold(CHARLIE).unwrap();
4650            set_default_user_balance_and_standard_hold(ALAN).unwrap();
4651            set_default_user_balance_and_standard_hold(MIKE).unwrap();
4652
4653            Pallet::place_commit(
4654                &ALICE,
4655                &STAKING,
4656                &VALIDATOR_ALPHA,
4657                STANDARD_COMMIT,
4658                &Directive::new(Precision::Exact, Fortitude::Force),
4659            )
4660            .unwrap();
4661            Pallet::place_commit(
4662                &BOB,
4663                &STAKING,
4664                &VALIDATOR_BETA,
4665                STANDARD_COMMIT,
4666                &Directive::new(Precision::Exact, Fortitude::Force),
4667            )
4668            .unwrap();
4669            let entries = vec![
4670                (VALIDATOR_ALPHA, SHARE_MAJOR),
4671                (VALIDATOR_BETA, SHARE_DOMINANT),
4672            ];
4673            prepare_and_initiate_pool(
4674                MIKE,
4675                STAKING,
4676                &entries,
4677                INDEX_OPTIMIZED_STAKING,
4678                POOL_MANAGED_STAKING,
4679                COMMISSION_STANDARD,
4680            )
4681            .unwrap();
4682            Pallet::place_commit(
4683                &CHARLIE,
4684                &STAKING,
4685                &POOL_MANAGED_STAKING,
4686                LARGE_COMMIT,
4687                &Directive::new(Precision::Exact, Fortitude::Force),
4688            )
4689            .unwrap();
4690            // Before resolving the commit
4691            let actual_pool_balance =
4692                Pallet::get_pool_value(&STAKING, &POOL_MANAGED_STAKING).unwrap();
4693            assert_eq!(actual_pool_balance, LARGE_COMMIT);
4694            let actual_slots_value =
4695                Pallet::get_slots_value(&STAKING, &POOL_MANAGED_STAKING).unwrap();
4696            let expected_slots_value = vec![(VALIDATOR_ALPHA, 200), (VALIDATOR_BETA, 300)];
4697            assert_eq!(actual_slots_value, expected_slots_value);
4698
4699            let reason_value = ReasonValue::get(STAKING).unwrap();
4700            assert_eq!(reason_value, 1000);
4701            // Simulating penalty senario
4702            let alpha_digest_value = Pallet::get_digest_value(&STAKING, &VALIDATOR_ALPHA).unwrap();
4703            assert_eq!(alpha_digest_value, 450);
4704            let new_alpha_reward_value = alpha_digest_value - STANDARD_PENALTY; //450 - 100(penalty) -> 350
4705            Pallet::set_digest_value(
4706                &STAKING,
4707                &VALIDATOR_ALPHA,
4708                new_alpha_reward_value,
4709                &Default::default(),
4710            )
4711            .unwrap();
4712            let beta_digest_value = Pallet::get_digest_value(&STAKING, &VALIDATOR_BETA).unwrap();
4713            assert_eq!(beta_digest_value, 550);
4714            let new_beta_reward_value = beta_digest_value - SMALL_PENALTY; //550 - 10(reward) -> 540
4715            Pallet::set_digest_value(
4716                &STAKING,
4717                &VALIDATOR_BETA,
4718                new_beta_reward_value,
4719                &Default::default(),
4720            )
4721            .unwrap();
4722            // Underlying digests value after penalty senario
4723            let alpha_digest_value = Pallet::get_digest_value(&STAKING, &VALIDATOR_ALPHA).unwrap();
4724            assert_eq!(alpha_digest_value, 350);
4725            let beta_digest_value = Pallet::get_digest_value(&STAKING, &VALIDATOR_BETA).unwrap();
4726            assert_eq!(beta_digest_value, 540);
4727            // Pool value updated
4728            let pool_value = Pallet::get_pool_value(&STAKING, &POOL_MANAGED_STAKING).unwrap();
4729            assert_eq!(pool_value, 449);
4730            let actual_slots_value =
4731                Pallet::get_slots_value(&STAKING, &POOL_MANAGED_STAKING).unwrap();
4732            let expected_slots_value = vec![(VALIDATOR_ALPHA, 155), (VALIDATOR_BETA, 294)];
4733            assert_eq!(actual_slots_value, expected_slots_value);
4734
4735            // resolve the commit
4736            assert_ok!(Pallet::resolve_commit(&CHARLIE, &STAKING));
4737
4738            // After resolving the commit
4739            let pool_info = PoolMap::get((STAKING, POOL_MANAGED_STAKING)).unwrap();
4740            let pool_balance_of = pool_info.balance();
4741            assert!(
4742                has_deposits(&pool_balance_of, &Default::default(), &POOL_MANAGED_STAKING).is_err()
4743            );
4744            assert_eq!(
4745                balance_total(&pool_balance_of, &Default::default(), &POOL_MANAGED_STAKING)
4746                    .unwrap(),
4747                0,
4748            );
4749
4750            let actual_slots_value =
4751                Pallet::get_slots_value(&STAKING, &POOL_MANAGED_STAKING).unwrap();
4752            let expected_slots_value = vec![(VALIDATOR_ALPHA, 0), (VALIDATOR_BETA, 0)];
4753            assert_eq!(actual_slots_value, expected_slots_value);
4754
4755            // Underlying digests value after resolving pool commit
4756            let alpha_digest_value = Pallet::get_digest_value(&STAKING, &VALIDATOR_ALPHA).unwrap();
4757            assert_eq!(alpha_digest_value, 195);
4758            let beta_digest_value = Pallet::get_digest_value(&STAKING, &VALIDATOR_BETA).unwrap();
4759            assert_eq!(beta_digest_value, 246);
4760
4761            let reason_value = ReasonValue::get(STAKING).unwrap();
4762            assert_eq!(reason_value, 441);
4763
4764            // Charlies balance check
4765            let actual_balance = AssetOf::balance(&CHARLIE);
4766            let expected_balance = INITIAL_BALANCE + 405; // initial_balance + resolved_blance with commission deduction
4767
4768            assert_eq!(actual_balance, expected_balance);
4769            let actual_freeze_balance = AssetOf::balance_frozen(&STAKING, &CHARLIE);
4770            assert_eq!(actual_freeze_balance, ZERO_VALUE);
4771
4772            // Mike balance check for commison settlement
4773            let actual_balance = AssetOf::balance(&MIKE);
4774            let expected_balance = INITIAL_BALANCE + 44; // initial_balance + commission for the resolved commit
4775            assert_eq!(actual_balance, expected_balance);
4776        });
4777    }
4778
4779    #[test]
4780    fn resolve_pool_commit_with_penalty_and_reward_distribution_by_entry_time() {
4781        commit_test_ext().execute_with(|| {
4782            set_default_user_balance_and_standard_hold(ALICE).unwrap();
4783            set_default_user_balance_and_standard_hold(CHARLIE).unwrap();
4784            set_default_user_balance_and_standard_hold(BOB).unwrap();
4785            set_default_user_balance_and_standard_hold(ALAN).unwrap();
4786            set_default_user_balance_and_standard_hold(NIX).unwrap();
4787            set_default_user_balance_and_standard_hold(MIKE).unwrap();
4788            set_default_user_balance_and_standard_hold(DAVE).unwrap();
4789
4790            Pallet::place_commit(
4791                &CHARLIE,
4792                &STAKING,
4793                &VALIDATOR_ALPHA,
4794                STANDARD_COMMIT,
4795                &Directive::new(Precision::Exact, Fortitude::Force),
4796            )
4797            .unwrap();
4798            Pallet::place_commit(
4799                &BOB,
4800                &STAKING,
4801                &VALIDATOR_BETA,
4802                STANDARD_COMMIT,
4803                &Directive::new(Precision::Exact, Fortitude::Force),
4804            )
4805            .unwrap();
4806
4807            prepare_and_initiate_pool(
4808                DAVE,
4809                STAKING,
4810                &[
4811                    (VALIDATOR_ALPHA, SHARE_MAJOR),
4812                    (VALIDATOR_BETA, SHARE_DOMINANT),
4813                ],
4814                INDEX_OPTIMIZED_STAKING,
4815                POOL_MANAGED_STAKING,
4816                COMMISSION_STANDARD,
4817            )
4818            .unwrap();
4819
4820            Pallet::place_commit(
4821                &ALAN,
4822                &STAKING,
4823                &POOL_MANAGED_STAKING,
4824                LARGE_COMMIT,
4825                &Directive::new(Precision::Exact, Fortitude::Force),
4826            )
4827            .unwrap();
4828
4829            Pallet::place_commit(
4830                &MIKE,
4831                &STAKING,
4832                &POOL_MANAGED_STAKING,
4833                STANDARD_COMMIT,
4834                &Directive::new(Precision::Exact, Fortitude::Force),
4835            )
4836            .unwrap();
4837
4838            let pool_value = Pallet::get_pool_value(&STAKING, &POOL_MANAGED_STAKING).unwrap();
4839            assert_eq!(pool_value, 750);
4840            let alan_slots_value =
4841                Pallet::get_slots_value_for(&ALAN, &STAKING, &POOL_MANAGED_STAKING).unwrap();
4842            let mike_slots_value =
4843                Pallet::get_slots_value_for(&MIKE, &STAKING, &POOL_MANAGED_STAKING).unwrap();
4844            let expected_alan_slots_value = vec![(VALIDATOR_ALPHA, 200), (VALIDATOR_BETA, 300)];
4845            let expected_mike_slots_value = vec![(VALIDATOR_ALPHA, 100), (VALIDATOR_BETA, 150)];
4846            assert_eq!(expected_alan_slots_value, alan_slots_value);
4847            assert_eq!(expected_mike_slots_value, mike_slots_value);
4848
4849            // Penalty manupulation
4850            Pallet::set_digest_value(&STAKING, &VALIDATOR_ALPHA, 500, &Default::default()).unwrap();
4851
4852            let pool_value = Pallet::get_pool_value(&STAKING, &POOL_MANAGED_STAKING).unwrap();
4853            assert_eq!(pool_value, 722);
4854
4855            let alan_slots_value =
4856                Pallet::get_slots_value_for(&ALAN, &STAKING, &POOL_MANAGED_STAKING).unwrap();
4857            let mike_slots_value =
4858                Pallet::get_slots_value_for(&MIKE, &STAKING, &POOL_MANAGED_STAKING).unwrap();
4859            let expected_alan_slots_value = vec![(VALIDATOR_ALPHA, 192), (VALIDATOR_BETA, 288)];
4860            let expected_mike_slots_value = vec![(VALIDATOR_ALPHA, 96), (VALIDATOR_BETA, 144)];
4861            assert_eq!(expected_alan_slots_value, alan_slots_value);
4862            assert_eq!(expected_mike_slots_value, mike_slots_value);
4863
4864            let charlie_digest_value = Pallet::get_commit_value(&CHARLIE, &STAKING).unwrap();
4865            let expected_charlie_digest_value = 227;
4866            assert_eq!(expected_charlie_digest_value, charlie_digest_value);
4867
4868            Pallet::place_commit(
4869                &NIX,
4870                &STAKING,
4871                &POOL_MANAGED_STAKING,
4872                STANDARD_COMMIT,
4873                &Directive::new(Precision::Exact, Fortitude::Force),
4874            )
4875            .unwrap();
4876
4877            let nix_slots_value =
4878                Pallet::get_slots_value_for(&NIX, &STAKING, &POOL_MANAGED_STAKING).unwrap();
4879            let expected_nix_slots_value = vec![(VALIDATOR_ALPHA, 99), (VALIDATOR_BETA, 148)];
4880            assert_eq!(expected_nix_slots_value, nix_slots_value);
4881
4882            // Reward manupulation
4883            Pallet::set_digest_value(&STAKING, &VALIDATOR_ALPHA, 1000, &Default::default())
4884                .unwrap();
4885
4886            let pool_value = Pallet::get_pool_value(&STAKING, &POOL_MANAGED_STAKING).unwrap();
4887            assert_eq!(pool_value, 1213);
4888            let alan_slots_value =
4889                Pallet::get_slots_value_for(&ALAN, &STAKING, &POOL_MANAGED_STAKING).unwrap();
4890            let mike_slots_value =
4891                Pallet::get_slots_value_for(&MIKE, &STAKING, &POOL_MANAGED_STAKING).unwrap();
4892            let nix_slots_value =
4893                Pallet::get_slots_value_for(&NIX, &STAKING, &POOL_MANAGED_STAKING).unwrap();
4894            let expected_alan_slots_value = vec![(VALIDATOR_ALPHA, 240), (VALIDATOR_BETA, 360)];
4895            let expected_mike_slots_value = vec![(VALIDATOR_ALPHA, 120), (VALIDATOR_BETA, 180)];
4896            let expected_nix_slots_value = vec![(VALIDATOR_ALPHA, 124), (VALIDATOR_BETA, 186)];
4897            assert_eq!(expected_alan_slots_value, alan_slots_value);
4898            assert_eq!(expected_mike_slots_value, mike_slots_value);
4899            assert_eq!(expected_nix_slots_value, nix_slots_value);
4900
4901            let charlie_digest_value = Pallet::get_commit_value(&CHARLIE, &STAKING).unwrap();
4902            let expected_charlie_digest_value = 369;
4903            assert_eq!(expected_charlie_digest_value, charlie_digest_value);
4904
4905            // Both, mike and alan absorbed the penalty and reward shock according to their weight
4906            let mike_resolved = Pallet::resolve_commit(&MIKE, &STAKING).unwrap();
4907            assert_eq!(mike_resolved, 270);
4908
4909            let alan_resolved = Pallet::resolve_commit(&ALAN, &STAKING).unwrap();
4910            assert_eq!(alan_resolved, 540);
4911
4912            // Since, nix placed a commit after the penalty he dosen't
4913            // absorbed the penalty shock but absorbs reward shock.
4914            let nix_resolved = Pallet::resolve_commit(&NIX, &STAKING).unwrap();
4915            assert_eq!(nix_resolved, 278);
4916
4917            // Charlie, as the last withdrawer, receives the remaining dust from rounding.
4918            let charlie_resolved = Pallet::resolve_commit(&CHARLIE, &STAKING).unwrap();
4919            assert_eq!(charlie_resolved, 374);
4920
4921            // dev gets his commission from all above resolutions
4922            let dev_balance = AssetOf::balance(&DAVE);
4923            assert_eq!(dev_balance, 1122); // 1000 -> 1122 (commission)
4924        })
4925    }
4926
4927    #[test]
4928    fn resolve_commit_withdraw_pool_with_hundred_percent_commisison() {
4929        commit_test_ext().execute_with(|| {
4930            set_default_user_balance_and_standard_hold(ALICE).unwrap();
4931            set_default_user_balance_and_standard_hold(BOB).unwrap();
4932            set_default_user_balance_and_standard_hold(CHARLIE).unwrap();
4933            set_default_user_balance_and_standard_hold(ALAN).unwrap();
4934            set_default_user_balance_and_standard_hold(MIKE).unwrap();
4935
4936            Pallet::place_commit(
4937                &ALICE,
4938                &STAKING,
4939                &VALIDATOR_ALPHA,
4940                STANDARD_COMMIT,
4941                &Directive::new(Precision::Exact, Fortitude::Force),
4942            )
4943            .unwrap();
4944            Pallet::place_commit(
4945                &BOB,
4946                &STAKING,
4947                &VALIDATOR_BETA,
4948                STANDARD_COMMIT,
4949                &Directive::new(Precision::Exact, Fortitude::Force),
4950            )
4951            .unwrap();
4952            let entries = vec![
4953                (VALIDATOR_ALPHA, SHARE_MAJOR),
4954                (VALIDATOR_BETA, SHARE_DOMINANT),
4955            ];
4956            prepare_and_initiate_pool(
4957                MIKE,
4958                STAKING,
4959                &entries,
4960                INDEX_OPTIMIZED_STAKING,
4961                POOL_MANAGED_STAKING,
4962                COMMISSION_MAX,
4963            )
4964            .unwrap();
4965            Pallet::place_commit(
4966                &CHARLIE,
4967                &STAKING,
4968                &POOL_MANAGED_STAKING,
4969                LARGE_COMMIT,
4970                &Directive::new(Precision::Exact, Fortitude::Force),
4971            )
4972            .unwrap();
4973            // Before resolving the commit
4974            let actual_pool_balance =
4975                Pallet::get_pool_value(&STAKING, &POOL_MANAGED_STAKING).unwrap();
4976            assert_eq!(actual_pool_balance, LARGE_COMMIT);
4977            let actual_slots_value =
4978                Pallet::get_slots_value(&STAKING, &POOL_MANAGED_STAKING).unwrap();
4979            let expected_slots_value = vec![(VALIDATOR_ALPHA, 200), (VALIDATOR_BETA, 300)];
4980            assert_eq!(actual_slots_value, expected_slots_value);
4981
4982            let reason_value = ReasonValue::get(STAKING).unwrap();
4983            assert_eq!(reason_value, 1000);
4984
4985            // resolve the commit
4986            assert_ok!(Pallet::resolve_commit(&CHARLIE, &STAKING));
4987
4988            // After resolving the commit
4989            let pool_info = PoolMap::get((STAKING, POOL_MANAGED_STAKING)).unwrap();
4990            let pool_balance_of = pool_info.balance();
4991            assert!(
4992                has_deposits(&pool_balance_of, &Default::default(), &POOL_MANAGED_STAKING).is_err()
4993            );
4994            assert_eq!(
4995                balance_total(&pool_balance_of, &Default::default(), &POOL_MANAGED_STAKING)
4996                    .unwrap(),
4997                0,
4998            );
4999
5000            let actual_slots_value =
5001                Pallet::get_slots_value(&STAKING, &POOL_MANAGED_STAKING).unwrap();
5002            let expected_slots_value = vec![(VALIDATOR_ALPHA, 0), (VALIDATOR_BETA, 0)];
5003            assert_eq!(actual_slots_value, expected_slots_value);
5004
5005            // Underlying digests value after resolving pool commit
5006            let alpha_digest_value = Pallet::get_digest_value(&STAKING, &VALIDATOR_ALPHA).unwrap();
5007            assert_eq!(alpha_digest_value, 250);
5008            let beta_digest_value = Pallet::get_digest_value(&STAKING, &VALIDATOR_BETA).unwrap();
5009            assert_eq!(beta_digest_value, 250);
5010
5011            let reason_value = ReasonValue::get(STAKING).unwrap();
5012            assert_eq!(reason_value, 500);
5013
5014            // Charlies balance check (resolving balance = 0, since the commisison is 100% of the total resolved balance)
5015            let actual_balance = AssetOf::balance(&CHARLIE);
5016            let expected_balance = INITIAL_BALANCE + 0; // initial_balance + resolved_blance with commission deduction
5017
5018            assert_eq!(actual_balance, expected_balance);
5019            let actual_freeze_balance = AssetOf::balance_frozen(&STAKING, &CHARLIE);
5020            assert_eq!(actual_freeze_balance, ZERO_VALUE);
5021
5022            // Mike balance check for commison settlement (100% commission)
5023            let actual_balance = AssetOf::balance(&MIKE);
5024            let expected_balance = INITIAL_BALANCE + 500; // initial_balance + commission for the resolved commit
5025            assert_eq!(actual_balance, expected_balance);
5026        });
5027    }
5028
5029    #[test]
5030    fn resolve_commit_err_commit_not_found() {
5031        commit_test_ext().execute_with(|| {
5032            set_default_user_balance_and_standard_hold(ALICE).unwrap();
5033            Pallet::place_commit(
5034                &ALICE,
5035                &GOVERNANCE,
5036                &PROPOSAL_RUNTIME_UPGRADE,
5037                STANDARD_COMMIT,
5038                &Directive::new(Precision::BestEffort, Fortitude::Polite),
5039            )
5040            .unwrap();
5041            assert_err!(
5042                Pallet::resolve_commit(&ALICE, &ESCROW,),
5043                Error::CommitNotFound
5044            );
5045        })
5046    }
5047
5048    #[test]
5049    fn gen_digest_success() {
5050        commit_test_ext().execute_with(|| {
5051            set_default_user_balance_and_standard_hold(ALICE).unwrap();
5052            set_default_user_balance_and_standard_hold(BOB).unwrap();
5053            let gen_digest_1 = Pallet::gen_digest(&ALICE);
5054            assert!(gen_digest_1.is_ok());
5055            let gen_digest_2 = Pallet::gen_digest(&ALICE);
5056            assert!(gen_digest_2.is_ok());
5057            assert_eq!(gen_digest_1, gen_digest_2); // deterministic key generation
5058
5059            // manual mutation of account nonce
5060            Account::mutate(&ALICE, |info| {
5061                info.nonce = 2;
5062            });
5063            let gen_digest_3 = Pallet::gen_digest(&ALICE);
5064            assert!(gen_digest_3.is_ok());
5065            assert_ne!(gen_digest_2, gen_digest_3); // unique key generation accross different nonce
5066
5067            // manual mutation of account nonce
5068            Account::mutate(&ALICE, |info| {
5069                info.nonce = 4;
5070            });
5071            let gen_digest_4 = Pallet::gen_digest(&ALICE);
5072            assert!(gen_digest_4.is_ok());
5073            assert_ne!(gen_digest_3, gen_digest_4); // unique key generation accross same source with different nonce
5074            let gen_digest_5 = Pallet::gen_digest(&BOB);
5075            assert!(gen_digest_5.is_ok());
5076            assert_ne!(gen_digest_5, gen_digest_4); // unique key generation accross different source
5077            let gen_digest_6 = Pallet::gen_digest(&CHARLIE);
5078            assert!(gen_digest_6.is_ok());
5079            assert_ne!(gen_digest_5, gen_digest_6); // unique key generation accross different source with same nonce
5080        })
5081    }
5082
5083    #[test]
5084    fn commit_exists_ok() {
5085        commit_test_ext().execute_with(|| {
5086            set_default_user_balance_and_standard_hold(ALICE).unwrap();
5087            Pallet::place_commit(
5088                &ALICE,
5089                &STAKING,
5090                &VALIDATOR_ALPHA,
5091                STANDARD_COMMIT,
5092                &Directive::new(Precision::Exact, Fortitude::Force),
5093            )
5094            .unwrap();
5095            assert_ok!(Pallet::commit_exists(&ALICE, &STAKING));
5096        })
5097    }
5098
5099    #[test]
5100    fn commit_exists_err_commit_not_found() {
5101        commit_test_ext().execute_with(|| {
5102            set_default_user_balance_and_standard_hold(ALICE).unwrap();
5103            Pallet::place_commit(
5104                &ALICE,
5105                &STAKING,
5106                &VALIDATOR_ALPHA,
5107                STANDARD_COMMIT,
5108                &Directive::new(Precision::BestEffort, Fortitude::Force),
5109            )
5110            .unwrap();
5111            assert_err!(
5112                Pallet::commit_exists(&ALICE, &GOVERNANCE),
5113                Error::CommitNotFound
5114            );
5115        })
5116    }
5117
5118    #[test]
5119    fn digest_exists_ok() {
5120        commit_test_ext().execute_with(|| {
5121            initiate_digest_with_default_balance(GOVERNANCE, PROPOSAL_RUNTIME_UPGRADE).unwrap();
5122            assert_ok!(Pallet::digest_exists(
5123                &GOVERNANCE,
5124                &PROPOSAL_RUNTIME_UPGRADE,
5125            ));
5126        });
5127    }
5128
5129    #[test]
5130    fn digest_exists_err_digest_not_found() {
5131        commit_test_ext().execute_with(|| {
5132            initiate_digest_with_default_balance(GOVERNANCE, PROPOSAL_RUNTIME_UPGRADE).unwrap();
5133            assert_err!(
5134                Pallet::digest_exists(&GOVERNANCE, &PROPOSAL_TREASURY_SPEND),
5135                Error::DigestNotFound
5136            );
5137        })
5138    }
5139
5140    #[test]
5141    fn get_commit_digest_success() {
5142        commit_test_ext().execute_with(|| {
5143            set_default_user_balance_and_standard_hold(ALICE).unwrap();
5144            Pallet::place_commit(
5145                &ALICE,
5146                &ESCROW,
5147                &CONTRACT_FREELANCE,
5148                STANDARD_COMMIT,
5149                &Directive::new(Precision::Exact, Fortitude::Force),
5150            )
5151            .unwrap();
5152            let commit_digest = Pallet::get_commit_digest(&ALICE, &ESCROW).unwrap();
5153            assert_eq!(commit_digest, CONTRACT_FREELANCE);
5154        })
5155    }
5156
5157    #[test]
5158    fn get_commit_digest_err_commit_not_found() {
5159        commit_test_ext().execute_with(|| {
5160            set_default_user_balance_and_standard_hold(ALICE).unwrap();
5161            Pallet::place_commit(
5162                &ALICE,
5163                &ESCROW,
5164                &CONTRACT_FREELANCE,
5165                STANDARD_COMMIT,
5166                &Directive::new(Precision::Exact, Fortitude::Force),
5167            )
5168            .unwrap();
5169            assert_err!(
5170                Pallet::get_commit_digest(&ALICE, &STAKING),
5171                Error::CommitNotFound
5172            );
5173        })
5174    }
5175
5176    #[test]
5177    fn get_total_value_works() {
5178        commit_test_ext().execute_with(|| {
5179            set_default_user_balance_and_standard_hold(ALICE).unwrap();
5180            set_default_user_balance_and_standard_hold(BOB).unwrap();
5181            set_default_user_balance_and_standard_hold(CHARLIE).unwrap();
5182
5183            Pallet::place_commit(
5184                &ALICE,
5185                &STAKING,
5186                &VALIDATOR_ALPHA,
5187                STANDARD_COMMIT,
5188                &Directive::new(Precision::Exact, Fortitude::Force),
5189            )
5190            .unwrap();
5191            let total_value = Pallet::get_total_value(&STAKING);
5192            assert_eq!(total_value, 250);
5193            Pallet::place_commit(
5194                &BOB,
5195                &STAKING,
5196                &VALIDATOR_BETA,
5197                LARGE_COMMIT,
5198                &Directive::new(Precision::Exact, Fortitude::Force),
5199            )
5200            .unwrap();
5201            // total commited value for staking reason
5202            let total_value = Pallet::get_total_value(&STAKING);
5203            assert_eq!(total_value, 750); // 250 + 500 -> 750
5204
5205            // total commited value for bet reason
5206            let total_value = Pallet::get_total_value(&ESCROW);
5207            assert_eq!(total_value, 0); // no commits for escrow reason yet
5208            Pallet::place_commit(
5209                &BOB,
5210                &ESCROW,
5211                &CONTRACT_FREELANCE,
5212                LARGE_COMMIT,
5213                &Directive::new(Precision::Exact, Fortitude::Force),
5214            )
5215            .unwrap();
5216            // total commited value for escrow reason
5217            let total_value = Pallet::get_total_value(&ESCROW);
5218            assert_eq!(total_value, 500);
5219        })
5220    }
5221
5222    #[test]
5223    fn get_commit_value_for_direct_success() {
5224        commit_test_ext().execute_with(|| {
5225            set_default_user_balance_and_standard_hold(ALICE).unwrap();
5226            Pallet::place_commit(
5227                &ALICE,
5228                &STAKING,
5229                &VALIDATOR_ALPHA,
5230                STANDARD_COMMIT,
5231                &Directive::new(Precision::Exact, Fortitude::Force),
5232            )
5233            .unwrap();
5234            let commit_value = Pallet::get_commit_value(&ALICE, &STAKING).unwrap();
5235            assert_eq!(commit_value, STANDARD_COMMIT);
5236        })
5237    }
5238
5239    #[test]
5240    fn get_commit_value_for_index_success() {
5241        commit_test_ext().execute_with(|| {
5242            set_default_user_balance_and_standard_hold(ALICE).unwrap();
5243            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
5244            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
5245            prepare_and_initiate_index(
5246                MIKE,
5247                STAKING,
5248                &[
5249                    (VALIDATOR_ALPHA, SHARE_EQUAL),
5250                    (VALIDATOR_BETA, SHARE_EQUAL),
5251                ],
5252                INDEX_BALANCED_STAKING,
5253            )
5254            .unwrap();
5255            assert_ok!(Pallet::index_exists(&STAKING, &INDEX_BALANCED_STAKING));
5256            // Place commit to an index
5257            Pallet::place_commit(
5258                &ALICE,
5259                &STAKING,
5260                &INDEX_BALANCED_STAKING,
5261                STANDARD_COMMIT,
5262                &Directive::new(Precision::Exact, Fortitude::Force),
5263            )
5264            .unwrap();
5265            let actual_commit_value = Pallet::get_commit_value(&ALICE, &STAKING).unwrap();
5266            assert_eq!(actual_commit_value, STANDARD_COMMIT);
5267        })
5268    }
5269
5270    #[test]
5271    fn get_commit_value_for_pool_success() {
5272        commit_test_ext().execute_with(|| {
5273            set_default_user_balance_and_standard_hold(BOB).unwrap();
5274            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
5275            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
5276            let entries = vec![
5277                (VALIDATOR_ALPHA, SHARE_EQUAL),
5278                (VALIDATOR_BETA, SHARE_EQUAL),
5279            ];
5280            prepare_and_initiate_pool(
5281                MIKE,
5282                STAKING,
5283                &entries,
5284                INDEX_BALANCED_STAKING,
5285                POOL_MANAGED_STAKING,
5286                COMMISSION_LOW,
5287            )
5288            .unwrap();
5289            Pallet::place_commit(
5290                &BOB,
5291                &STAKING,
5292                &POOL_MANAGED_STAKING,
5293                LARGE_COMMIT,
5294                &Directive::new(Precision::Exact, Fortitude::Force),
5295            )
5296            .unwrap();
5297            let actual_commit_value = Pallet::get_commit_value(&BOB, &STAKING).unwrap();
5298            assert_eq!(actual_commit_value, LARGE_COMMIT);
5299        })
5300    }
5301
5302    #[test]
5303    fn get_digets_value_success() {
5304        commit_test_ext().execute_with(|| {
5305            set_default_user_balance_and_standard_hold(ALICE).unwrap();
5306            set_default_user_balance_and_standard_hold(BOB).unwrap();
5307
5308            Pallet::place_commit(
5309                &ALICE,
5310                &ESCROW,
5311                &CONTRACT_FREELANCE,
5312                STANDARD_COMMIT,
5313                &Directive::new(Precision::Exact, Fortitude::Force),
5314            )
5315            .unwrap();
5316            let digets_value = Pallet::get_digest_value(&ESCROW, &CONTRACT_FREELANCE).unwrap();
5317            assert_eq!(digets_value, STANDARD_COMMIT);
5318            Pallet::place_commit(
5319                &BOB,
5320                &ESCROW,
5321                &CONTRACT_FREELANCE,
5322                SMALL_COMMIT,
5323                &Directive::new(Precision::Exact, Fortitude::Force),
5324            )
5325            .unwrap();
5326            let digets_value = Pallet::get_digest_value(&ESCROW, &CONTRACT_FREELANCE).unwrap();
5327            assert_eq!(digets_value, 350); // 250 (alice's commit) + 100 (bob's commit) -> 350
5328        })
5329    }
5330
5331    #[test]
5332    fn set_digest_value_mint_ok() {
5333        commit_test_ext().execute_with(|| {
5334            set_default_user_balance_and_standard_hold(ALICE).unwrap();
5335            Pallet::place_commit(
5336                &ALICE,
5337                &ESCROW,
5338                &CONTRACT_FREELANCE,
5339                SMALL_COMMIT,
5340                &Directive::new(Precision::Exact, Fortitude::Polite),
5341            )
5342            .unwrap();
5343            // before set_digest_value
5344            assert_eq!(
5345                Pallet::get_digest_value(&ESCROW, &CONTRACT_FREELANCE),
5346                Ok(SMALL_COMMIT)
5347            );
5348            let asset_to_issue = AssetToIssue::get();
5349            assert_eq!(asset_to_issue, ZERO_VALUE);
5350            let reason_value = Pallet::get_total_value(&ESCROW);
5351            assert_eq!(reason_value, 100);
5352            // setting a new digest value > current digest value
5353            assert_ok!(Pallet::set_digest_value(
5354                &ESCROW,
5355                &CONTRACT_FREELANCE,
5356                STANDARD_COMMIT,
5357                &Default::default(),
5358            ));
5359            // after set_digest_value (minting senario)
5360            let asset_to_issue = AssetToIssue::get();
5361            assert_eq!(asset_to_issue, 150);
5362            let reason_value = Pallet::get_total_value(&ESCROW);
5363            assert_eq!(reason_value, 250);
5364            assert_eq!(
5365                Pallet::get_digest_value(&ESCROW, &CONTRACT_FREELANCE),
5366                Ok(250)
5367            );
5368        })
5369    }
5370
5371    #[test]
5372    fn set_digest_value_equal_ok() {
5373        commit_test_ext().execute_with(|| {
5374            set_default_user_balance_and_standard_hold(ALICE).unwrap();
5375            Pallet::place_commit(
5376                &ALICE,
5377                &ESCROW,
5378                &CONTRACT_FREELANCE,
5379                STANDARD_COMMIT,
5380                &Directive::new(Precision::BestEffort, Fortitude::Polite),
5381            )
5382            .unwrap();
5383            // before set_digest_value
5384            assert_eq!(
5385                Pallet::get_digest_value(&ESCROW, &CONTRACT_FREELANCE),
5386                Ok(STANDARD_COMMIT)
5387            );
5388            let asset_to_reap = AssetToReap::get();
5389            assert_eq!(asset_to_reap, ZERO_VALUE);
5390            let reason_value = Pallet::get_total_value(&ESCROW);
5391            assert_eq!(reason_value, 250);
5392            // setting a new digest value == current digest value
5393            assert_ok!(Pallet::set_digest_value(
5394                &ESCROW,
5395                &CONTRACT_FREELANCE,
5396                STANDARD_COMMIT,
5397                &Default::default(),
5398            ));
5399            // after set_digest_value (no changes)
5400            let asset_to_reap = AssetToReap::get();
5401            assert_eq!(asset_to_reap, ZERO_VALUE);
5402            let reason_value = Pallet::get_total_value(&ESCROW);
5403            assert_eq!(reason_value, 250);
5404            assert_eq!(
5405                Pallet::get_digest_value(&ESCROW, &CONTRACT_FREELANCE),
5406                Ok(STANDARD_COMMIT)
5407            );
5408        })
5409    }
5410
5411    #[test]
5412    fn set_digest_value_reap_ok() {
5413        commit_test_ext().execute_with(|| {
5414            set_default_user_balance_and_standard_hold(ALICE).unwrap();
5415            Pallet::place_commit(
5416                &ALICE,
5417                &ESCROW,
5418                &CONTRACT_FREELANCE,
5419                STANDARD_COMMIT,
5420                &Directive::new(Precision::Exact, Fortitude::Force),
5421            )
5422            .unwrap();
5423            // before set_digest_value
5424            assert_eq!(
5425                Pallet::get_digest_value(&ESCROW, &CONTRACT_FREELANCE),
5426                Ok(STANDARD_COMMIT)
5427            );
5428            let asset_to_reap = AssetToReap::get();
5429            assert_eq!(asset_to_reap, ZERO_VALUE);
5430            let reason_value = Pallet::get_total_value(&ESCROW);
5431            assert_eq!(reason_value, 250);
5432            // setting a new digest value < current digest value
5433            assert_ok!(Pallet::set_digest_value(
5434                &ESCROW,
5435                &CONTRACT_FREELANCE,
5436                SMALL_COMMIT,
5437                &Default::default(),
5438            ));
5439            // after set_digest_value (reaping senario)
5440            let asset_to_reap = AssetToReap::get();
5441            assert_eq!(asset_to_reap, 150);
5442            let reason_value = Pallet::get_total_value(&ESCROW);
5443            assert_eq!(reason_value, 100);
5444            assert_eq!(
5445                Pallet::get_digest_value(&ESCROW, &CONTRACT_FREELANCE),
5446                Ok(100)
5447            );
5448        })
5449    }
5450
5451    #[test]
5452    fn on_commit_place_event_emmission_success() {
5453        commit_test_ext().execute_with(|| {
5454            set_default_user_balance_and_standard_hold(ALICE).unwrap();
5455            System::set_block_number(BLOCK_EARLY);
5456            Pallet::place_commit(
5457                &ALICE,
5458                &GOVERNANCE,
5459                &PROPOSAL_RUNTIME_UPGRADE,
5460                STANDARD_COMMIT,
5461                &Directive::new(Precision::Exact, Fortitude::Force),
5462            )
5463            .unwrap();
5464            // event emmitted
5465            System::assert_last_event(
5466                Event::CommitPlaced {
5467                    who: ALICE,
5468                    reason: GOVERNANCE,
5469                    #[cfg(feature = "dev")]
5470                    model: DigestVariant::Direct(PROPOSAL_RUNTIME_UPGRADE),
5471                    #[cfg(not(feature = "dev"))]
5472                    digest: PROPOSAL_RUNTIME_UPGRADE,
5473                    value: STANDARD_COMMIT,
5474                    variant: Position::default(),
5475                }
5476                .into(),
5477            );
5478        })
5479    }
5480
5481    #[test]
5482    fn on_commit_raise_event_emmission_success() {
5483        commit_test_ext().execute_with(|| {
5484            set_default_user_balance_and_standard_hold(ALICE).unwrap();
5485            System::set_block_number(2);
5486            Pallet::place_commit(
5487                &ALICE,
5488                &STAKING,
5489                &VALIDATOR_ALPHA,
5490                STANDARD_COMMIT,
5491                &Directive::new(Precision::Exact, Fortitude::Force),
5492            )
5493            .unwrap();
5494            Pallet::raise_commit(
5495                &ALICE,
5496                &STAKING,
5497                SMALL_COMMIT,
5498                &Directive::new(Precision::Exact, Fortitude::Force),
5499            )
5500            .unwrap();
5501            System::assert_last_event(
5502                Event::CommitRaised {
5503                    who: ALICE,
5504                    reason: STAKING,
5505                    #[cfg(feature = "dev")]
5506                    model: DigestVariant::Direct(VALIDATOR_ALPHA),
5507                    #[cfg(not(feature = "dev"))]
5508                    digest: VALIDATOR_ALPHA,
5509                    value: SMALL_COMMIT,
5510                }
5511                .into(),
5512            );
5513        })
5514    }
5515
5516    #[test]
5517    fn on_commit_resolve_event_emmission_success() {
5518        commit_test_ext().execute_with(|| {
5519            set_default_user_balance_and_standard_hold(ALICE).unwrap();
5520            System::set_block_number(2);
5521            Pallet::place_commit(
5522                &ALICE,
5523                &STAKING,
5524                &VALIDATOR_ALPHA,
5525                STANDARD_COMMIT,
5526                &Directive::new(Precision::Exact, Fortitude::Force),
5527            )
5528            .unwrap();
5529            System::set_block_number(3);
5530            Pallet::resolve_commit(&ALICE, &STAKING).unwrap();
5531            // event emmitted
5532            System::assert_last_event(
5533                Event::CommitResolved {
5534                    who: ALICE,
5535                    reason: STAKING,
5536                    #[cfg(feature = "dev")]
5537                    model: DigestVariant::Direct(VALIDATOR_ALPHA),
5538                    #[cfg(not(feature = "dev"))]
5539                    digest: VALIDATOR_ALPHA,
5540                    value: STANDARD_COMMIT,
5541                }
5542                .into(),
5543            );
5544        })
5545    }
5546
5547    #[test]
5548    fn on_digest_update_event_emmission_success() {
5549        commit_test_ext().execute_with(|| {
5550            set_default_user_balance_and_standard_hold(ALICE).unwrap();
5551            System::set_block_number(2);
5552            Pallet::place_commit(
5553                &ALICE,
5554                &STAKING,
5555                &VALIDATOR_ALPHA,
5556                STANDARD_COMMIT,
5557                &Directive::new(Precision::Exact, Fortitude::Force),
5558            )
5559            .unwrap();
5560            System::set_block_number(3);
5561            Pallet::set_digest_value(
5562                &STAKING,
5563                &VALIDATOR_ALPHA,
5564                LARGE_COMMIT,
5565                &Directive::new(Precision::Exact, Fortitude::Force),
5566            )
5567            .unwrap();
5568            // event emmitted
5569            System::assert_last_event(
5570                Event::DigestInfo {
5571                    digest: VALIDATOR_ALPHA,
5572                    reason: STAKING,
5573                    value: LARGE_COMMIT,
5574                    variant: Position::default(),
5575                }
5576                .into(),
5577            );
5578        })
5579    }
5580
5581    #[test]
5582    fn reap_digest_ok() {
5583        commit_test_ext().execute_with(|| {
5584            set_default_user_balance_and_standard_hold(ALICE).unwrap();
5585            System::set_block_number(2);
5586            Pallet::place_commit(
5587                &ALICE,
5588                &ESCROW,
5589                &CONTRACT_FREELANCE,
5590                STANDARD_COMMIT,
5591                &Directive::new(Precision::Exact, Fortitude::Force),
5592            )
5593            .unwrap();
5594            Pallet::resolve_commit(&ALICE, &ESCROW).unwrap();
5595            // before reaping
5596            assert_ok!(Pallet::digest_exists(&ESCROW, &CONTRACT_FREELANCE));
5597            // reaping the digest which has no funds
5598            assert_ok!(Pallet::reap_digest(&CONTRACT_FREELANCE, &ESCROW));
5599            // after reaping
5600            assert_err!(
5601                Pallet::digest_exists(&ESCROW, &CONTRACT_FREELANCE),
5602                Error::DigestNotFound
5603            )
5604        })
5605    }
5606
5607    #[test]
5608    fn reap_digest_err_digest_has_funds() {
5609        commit_test_ext().execute_with(|| {
5610            set_default_user_balance_and_standard_hold(ALICE).unwrap();
5611            System::set_block_number(2);
5612            Pallet::place_commit(
5613                &ALICE,
5614                &ESCROW,
5615                &CONTRACT_FREELANCE,
5616                STANDARD_COMMIT,
5617                &Directive::new(Precision::BestEffort, Fortitude::Polite),
5618            )
5619            .unwrap();
5620            assert_ok!(Pallet::digest_exists(&ESCROW, &CONTRACT_FREELANCE));
5621            // since, digest has a commited funds, it cannot be reaped
5622            assert_err!(
5623                Pallet::reap_digest(&CONTRACT_FREELANCE, &ESCROW),
5624                Error::DigestHasFunds
5625            );
5626        })
5627    }
5628
5629    #[test]
5630    fn on_reap_digest_event_emmission_success() {
5631        commit_test_ext().execute_with(|| {
5632            set_default_user_balance_and_standard_hold(ALICE).unwrap();
5633            System::set_block_number(2);
5634            Pallet::place_commit(
5635                &ALICE,
5636                &ESCROW,
5637                &CONTRACT_FREELANCE,
5638                STANDARD_COMMIT,
5639                &Directive::new(Precision::Exact, Fortitude::Force),
5640            )
5641            .unwrap();
5642            System::set_block_number(3);
5643            Pallet::resolve_commit(&ALICE, &ESCROW).unwrap();
5644            System::set_block_number(4);
5645            Pallet::reap_digest(&CONTRACT_FREELANCE, &ESCROW).unwrap();
5646            System::assert_last_event(
5647                Event::DigestReaped {
5648                    digest: CONTRACT_FREELANCE,
5649                    reason: ESCROW,
5650                    dust: Zero::zero(),
5651                }
5652                .into(),
5653            );
5654        })
5655    }
5656
5657    #[test]
5658    fn can_place_commit_ok() {
5659        commit_test_ext().execute_with(|| {
5660            set_default_user_balance_and_standard_hold(ALICE).unwrap();
5661            assert_ok!(Pallet::can_place_commit(
5662                &ALICE,
5663                &ESCROW,
5664                &ALPHA_DIGEST,
5665                STANDARD_COMMIT,
5666                &Default::default()
5667            ));
5668        })
5669    }
5670
5671    #[test]
5672    fn can_place_commit_err_commit_already_exists_for_reason() {
5673        commit_test_ext().execute_with(|| {
5674            set_default_user_balance_and_standard_hold(ALICE).unwrap();
5675            Pallet::place_commit(
5676                &ALICE,
5677                &STAKING,
5678                &VALIDATOR_ALPHA,
5679                STANDARD_COMMIT,
5680                &Directive::new(Precision::Exact, Fortitude::Force),
5681            )
5682            .unwrap();
5683            assert_err!(
5684                Pallet::can_place_commit(
5685                    &ALICE,
5686                    &STAKING,
5687                    &VALIDATOR_ALPHA,
5688                    SMALL_COMMIT,
5689                    &Directive::new(Precision::Exact, Fortitude::Force)
5690                ),
5691                Error::CommitAlreadyExists
5692            );
5693        })
5694    }
5695
5696    #[test]
5697    fn can_place_commit_err_insufficient_funds() {
5698        commit_test_ext().execute_with(|| {
5699            set_default_user_balance_and_standard_hold(ALICE).unwrap();
5700            assert_err!(
5701                Pallet::can_place_commit(
5702                    &ALICE,
5703                    &GOVERNANCE,
5704                    &ALPHA_DIGEST,
5705                    1600,
5706                    &Default::default()
5707                ),
5708                Error::InsufficientFunds
5709            );
5710        })
5711    }
5712
5713    #[test]
5714    fn can_raise_commit_ok() {
5715        commit_test_ext().execute_with(|| {
5716            set_default_user_balance_and_standard_hold(ALICE).unwrap();
5717            Pallet::place_commit(
5718                &ALICE,
5719                &STAKING,
5720                &VALIDATOR_ALPHA,
5721                STANDARD_COMMIT,
5722                &Directive::new(Precision::Exact, Fortitude::Force),
5723            )
5724            .unwrap();
5725            assert_ok!(Pallet::can_raise_commit(
5726                &ALICE,
5727                &STAKING,
5728                SMALL_COMMIT,
5729                &Default::default()
5730            ));
5731        })
5732    }
5733
5734    #[test]
5735    fn can_raise_commit_err_insufficient_funds() {
5736        commit_test_ext().execute_with(|| {
5737            set_default_user_balance_and_standard_hold(ALICE).unwrap();
5738            Pallet::place_commit(
5739                &ALICE,
5740                &ESCROW,
5741                &CONTRACT_SUPPLY_CHAIN,
5742                LARGE_COMMIT,
5743                &Directive::new(Precision::Exact, Fortitude::Force),
5744            )
5745            .unwrap();
5746            assert_err!(
5747                Pallet::can_raise_commit(&ALICE, &ESCROW, 1200, &Default::default()),
5748                Error::InsufficientFunds
5749            );
5750        })
5751    }
5752
5753    #[test]
5754    fn can_resolve_commit_ok() {
5755        commit_test_ext().execute_with(|| {
5756            set_default_user_balance_and_standard_hold(ALICE).unwrap();
5757            Pallet::place_commit(
5758                &ALICE,
5759                &GOVERNANCE,
5760                &PROPOSAL_TREASURY_SPEND,
5761                STANDARD_COMMIT,
5762                &Directive::new(Precision::Exact, Fortitude::Force),
5763            )
5764            .unwrap();
5765            assert_ok!(Pallet::can_resolve_commit(&ALICE, &GOVERNANCE));
5766        })
5767    }
5768
5769    #[test]
5770    fn can_resolve_commit_index_ok() {
5771        commit_test_ext().execute_with(|| {
5772            set_default_user_balance_and_standard_hold(ALICE).unwrap();
5773            set_default_user_balance_and_standard_hold(BOB).unwrap();
5774            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
5775            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
5776            prepare_and_initiate_index(
5777                BOB,
5778                STAKING,
5779                &[
5780                    (VALIDATOR_ALPHA, SHARE_EQUAL),
5781                    (VALIDATOR_BETA, SHARE_EQUAL),
5782                ],
5783                INDEX_BALANCED_STAKING,
5784            )
5785            .unwrap();
5786            Pallet::place_commit(
5787                &ALICE,
5788                &STAKING,
5789                &INDEX_BALANCED_STAKING,
5790                STANDARD_COMMIT,
5791                &Directive::new(Precision::BestEffort, Fortitude::Polite),
5792            )
5793            .unwrap();
5794            assert_ok!(Pallet::can_resolve_commit(&ALICE, &STAKING));
5795        })
5796    }
5797
5798    #[test]
5799    fn can_resolve_commit_pool_ok() {
5800        commit_test_ext().execute_with(|| {
5801            set_default_user_balance_and_standard_hold(ALICE).unwrap();
5802            set_default_user_balance_and_standard_hold(BOB).unwrap();
5803            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
5804            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
5805            let entries = vec![
5806                (VALIDATOR_ALPHA, SHARE_DOMINANT),
5807                (VALIDATOR_BETA, SHARE_MAJOR),
5808            ];
5809            prepare_and_initiate_pool(
5810                BOB,
5811                STAKING,
5812                &entries,
5813                INDEX_OPTIMIZED_STAKING,
5814                POOL_MANAGED_STAKING,
5815                COMMISSION_LOW,
5816            )
5817            .unwrap();
5818            Pallet::place_commit(
5819                &ALICE,
5820                &STAKING,
5821                &POOL_MANAGED_STAKING,
5822                STANDARD_COMMIT,
5823                &Directive::new(Precision::BestEffort, Fortitude::Polite),
5824            )
5825            .unwrap();
5826            assert_ok!(Pallet::can_resolve_commit(&ALICE, &STAKING));
5827        })
5828    }
5829
5830    // -------------------------------------------------------------------------------
5831    // ```````````````````````````````` COMMIT VARIANT `````````````````````````````````
5832    // -------------------------------------------------------------------------------
5833
5834    #[test]
5835    fn get_commit_variant_success() {
5836        commit_test_ext().execute_with(|| {
5837            set_default_user_balance_and_standard_hold(ALICE).unwrap();
5838            Pallet::place_commit_of_variant(
5839                &ALICE,
5840                &GOVERNANCE,
5841                &PROPOSAL_TREASURY_SPEND,
5842                STANDARD_COMMIT,
5843                &Position::position_of(2).unwrap(),
5844                &Directive::new(Precision::Exact, Fortitude::Force),
5845            )
5846            .unwrap();
5847            let actual_commit_variant = Pallet::get_commit_variant(&ALICE, &GOVERNANCE).unwrap();
5848            let expected_commit_variant = Position::position_of(2).unwrap();
5849            assert_eq!(actual_commit_variant, expected_commit_variant);
5850        })
5851    }
5852
5853    #[test]
5854    fn get_commit_variant_fail_commit_not_found() {
5855        commit_test_ext().execute_with(|| {
5856            set_default_user_balance_and_standard_hold(ALICE).unwrap();
5857            Pallet::place_commit(
5858                &ALICE,
5859                &GOVERNANCE,
5860                &PROPOSAL_TREASURY_SPEND,
5861                STANDARD_COMMIT,
5862                &Directive::new(Precision::Exact, Fortitude::Force),
5863            )
5864            .unwrap();
5865            assert_err!(
5866                Pallet::get_commit_variant(&ALICE, &ESCROW),
5867                Error::CommitNotFound
5868            );
5869        })
5870    }
5871
5872    #[test]
5873    fn get_digest_variant_value_success() {
5874        commit_test_ext().execute_with(|| {
5875            set_default_user_balance_and_standard_hold(ALICE).unwrap();
5876            set_default_user_balance_and_standard_hold(BOB).unwrap();
5877            Pallet::place_commit(
5878                &ALICE,
5879                &GOVERNANCE,
5880                &PROPOSAL_RUNTIME_UPGRADE,
5881                STANDARD_COMMIT,
5882                &Directive::new(Precision::Exact, Fortitude::Force),
5883            )
5884            .unwrap();
5885            let digest_variant_value = Pallet::get_digest_variant_value(
5886                &GOVERNANCE,
5887                &PROPOSAL_RUNTIME_UPGRADE,
5888                &Position::default(),
5889            )
5890            .unwrap();
5891            assert_eq!(digest_variant_value, STANDARD_COMMIT);
5892            // zero returned when variant does not exists
5893            let digest_variant_value = Pallet::get_digest_variant_value(
5894                &GOVERNANCE,
5895                &PROPOSAL_RUNTIME_UPGRADE,
5896                &Position::position_of(1).unwrap(),
5897            )
5898            .unwrap();
5899            assert_eq!(digest_variant_value, ZERO_VALUE);
5900            Pallet::place_commit_of_variant(
5901                &BOB,
5902                &GOVERNANCE,
5903                &PROPOSAL_TREASURY_SPEND,
5904                LARGE_COMMIT,
5905                &Position::position_of(2).unwrap(),
5906                &Directive::new(Precision::BestEffort, Fortitude::Polite),
5907            )
5908            .unwrap();
5909            let digest_variant_value = Pallet::get_digest_variant_value(
5910                &GOVERNANCE,
5911                &PROPOSAL_TREASURY_SPEND,
5912                &Position::position_of(2).unwrap(),
5913            )
5914            .unwrap();
5915            assert_eq!(digest_variant_value, LARGE_COMMIT);
5916            // raising the commit value of commit with variant Affirmative
5917            Pallet::raise_commit(
5918                &ALICE,
5919                &GOVERNANCE,
5920                SMALL_COMMIT,
5921                &Directive::new(Precision::BestEffort, Fortitude::Force),
5922            )
5923            .unwrap();
5924            // raised value refected in the digets variant
5925            let digest_variant_value = Pallet::get_digest_variant_value(
5926                &GOVERNANCE,
5927                &PROPOSAL_RUNTIME_UPGRADE,
5928                &Position::default(),
5929            )
5930            .unwrap();
5931            let raised_value = STANDARD_COMMIT + SMALL_COMMIT;
5932            assert_eq!(digest_variant_value, raised_value);
5933        })
5934    }
5935
5936    #[test]
5937    fn get_digest_variant_value_fail_digest_not_found() {
5938        commit_test_ext().execute_with(|| {
5939            set_default_user_balance_and_standard_hold(ALICE).unwrap();
5940            Pallet::place_commit(
5941                &ALICE,
5942                &GOVERNANCE,
5943                &PROPOSAL_RUNTIME_UPGRADE,
5944                STANDARD_COMMIT,
5945                &Directive::new(Precision::Exact, Fortitude::Force),
5946            )
5947            .unwrap();
5948            assert_err!(
5949                Pallet::get_digest_variant_value(
5950                    &GOVERNANCE,
5951                    &PROPOSAL_TREASURY_SPEND,
5952                    &Position::default(),
5953                ),
5954                Error::DigestNotFound
5955            );
5956        })
5957    }
5958
5959    #[test]
5960    fn set_digest_variant_value_mint_ok() {
5961        commit_test_ext().execute_with(|| {
5962            System::set_block_number(10);
5963            set_default_user_balance_and_standard_hold(ALICE).unwrap();
5964            Pallet::place_commit(
5965                &ALICE,
5966                &GOVERNANCE,
5967                &PROPOSAL_TREASURY_SPEND,
5968                STANDARD_COMMIT,
5969                &Directive::new(Precision::Exact, Fortitude::Force),
5970            )
5971            .unwrap();
5972            // before set_digest_variant_value
5973            assert_eq!(
5974                Pallet::get_digest_value(&GOVERNANCE, &PROPOSAL_TREASURY_SPEND),
5975                Ok(STANDARD_COMMIT)
5976            );
5977            let asset_to_issue = AssetToIssue::get();
5978            assert_eq!(asset_to_issue, ZERO_VALUE);
5979            let total_value = Pallet::get_total_value(&GOVERNANCE);
5980            assert_eq!(total_value, 250);
5981            // setting a new digest value with specific variant > current digest value
5982            assert_ok!(Pallet::set_digest_variant_value(
5983                &GOVERNANCE,
5984                &PROPOSAL_TREASURY_SPEND,
5985                LARGE_COMMIT,
5986                &Position::default(),
5987                &Default::default(),
5988            ));
5989            // after set_digest_variant_value (minting senario)
5990            let asset_to_issue = AssetToIssue::get();
5991            assert_eq!(asset_to_issue, 250);
5992            let total_value = Pallet::get_total_value(&GOVERNANCE);
5993            assert_eq!(total_value, 500);
5994            assert_eq!(
5995                Pallet::get_digest_value(&GOVERNANCE, &PROPOSAL_TREASURY_SPEND),
5996                Ok(LARGE_COMMIT)
5997            );
5998
5999            System::assert_last_event(Event::DigestInfo { 
6000                    digest: PROPOSAL_TREASURY_SPEND, 
6001                    reason: GOVERNANCE, 
6002                    value: total_value, 
6003                    variant: Disposition::default() 
6004                }
6005                .into()
6006            );
6007        })
6008    }
6009
6010    #[test]
6011    fn set_digest_variant_value_equal_ok() {
6012        commit_test_ext().execute_with(|| {
6013            set_default_user_balance_and_standard_hold(ALICE).unwrap();
6014            Pallet::place_commit(
6015                &ALICE,
6016                &GOVERNANCE,
6017                &PROPOSAL_TREASURY_SPEND,
6018                STANDARD_COMMIT,
6019                &Directive::new(Precision::Exact, Fortitude::Force),
6020            )
6021            .unwrap();
6022            // before set_digest_variant_value
6023            assert_eq!(
6024                Pallet::get_digest_value(&GOVERNANCE, &PROPOSAL_TREASURY_SPEND),
6025                Ok(STANDARD_COMMIT)
6026            );
6027            let asset_to_reap = AssetToReap::get();
6028            assert_eq!(asset_to_reap, ZERO_VALUE);
6029            let total_value = Pallet::get_total_value(&GOVERNANCE);
6030            assert_eq!(total_value, 250);
6031            // setting a new digest value with specific variant == current digest value
6032            assert_ok!(Pallet::set_digest_variant_value(
6033                &GOVERNANCE,
6034                &PROPOSAL_TREASURY_SPEND,
6035                STANDARD_COMMIT,
6036                &Position::default(),
6037                &Default::default(),
6038            ));
6039            // after set_digest_variant_value (no changes)
6040            let asset_to_reap = AssetToReap::get();
6041            assert_eq!(asset_to_reap, ZERO_VALUE);
6042            let total_value = Pallet::get_total_value(&GOVERNANCE);
6043            assert_eq!(total_value, 250);
6044            assert_eq!(
6045                Pallet::get_digest_value(&GOVERNANCE, &PROPOSAL_TREASURY_SPEND),
6046                Ok(STANDARD_COMMIT)
6047            );
6048        })
6049    }
6050
6051    #[test]
6052    fn set_digest_variant_value_reap_ok() {
6053        commit_test_ext().execute_with(|| {
6054            set_default_user_balance_and_standard_hold(ALICE).unwrap();
6055            Pallet::place_commit(
6056                &ALICE,
6057                &GOVERNANCE,
6058                &PROPOSAL_TREASURY_SPEND,
6059                STANDARD_COMMIT,
6060                &Directive::new(Precision::Exact, Fortitude::Force),
6061            )
6062            .unwrap();
6063            // before set_digest_variant_value
6064            assert_eq!(
6065                Pallet::get_digest_value(&GOVERNANCE, &PROPOSAL_TREASURY_SPEND),
6066                Ok(STANDARD_COMMIT)
6067            );
6068            let asset_to_reap = AssetToReap::get();
6069            assert_eq!(asset_to_reap, ZERO_VALUE);
6070            let total_value = Pallet::get_total_value(&GOVERNANCE);
6071            assert_eq!(total_value, 250);
6072            // setting a new digest value with specific variant < current digest value
6073            assert_ok!(Pallet::set_digest_variant_value(
6074                &GOVERNANCE,
6075                &PROPOSAL_TREASURY_SPEND,
6076                SMALL_COMMIT,
6077                &Position::default(),
6078                &Default::default(),
6079            ));
6080            // after set_digest_variant_value (reaping senario)
6081            let asset_to_reap = AssetToReap::get();
6082            assert_eq!(asset_to_reap, 150);
6083            let total_value = Pallet::get_total_value(&GOVERNANCE);
6084            assert_eq!(total_value, 100);
6085            assert_eq!(
6086                Pallet::get_digest_value(&GOVERNANCE, &PROPOSAL_TREASURY_SPEND),
6087                Ok(SMALL_COMMIT)
6088            );
6089        })
6090    }
6091
6092    #[test]
6093    fn set_digest_variant_value_err_cannot_mint_asset() {
6094        commit_test_ext().execute_with(|| {
6095            set_default_user_balance_and_standard_hold(ALICE).unwrap();
6096            Pallet::place_commit(
6097                &ALICE,
6098                &GOVERNANCE,
6099                &PROPOSAL_TREASURY_SPEND,
6100                STANDARD_COMMIT,
6101                &Directive::new(Precision::Exact, Fortitude::Force),
6102            )
6103            .unwrap();
6104            AssetToIssue::put(MAX_VALUE);
6105            assert_err!(
6106                Pallet::set_digest_variant_value(
6107                    &GOVERNANCE,
6108                    &PROPOSAL_TREASURY_SPEND,
6109                    LARGE_COMMIT,
6110                    &Position::default(),
6111                    &Default::default(),
6112                ),
6113                Error::MaxAssetIssued
6114            );
6115        });
6116    }
6117
6118    #[test]
6119    fn set_digest_variant_value_err_cannot_reap_asset() {
6120        commit_test_ext().execute_with(|| {
6121            set_default_user_balance_and_standard_hold(ALICE).unwrap();
6122            Pallet::place_commit(
6123                &ALICE,
6124                &GOVERNANCE,
6125                &PROPOSAL_TREASURY_SPEND,
6126                STANDARD_COMMIT,
6127                &Directive::new(Precision::Exact, Fortitude::Force),
6128            )
6129            .unwrap();
6130            AssetToReap::put(MAX_VALUE);
6131            assert_err!(
6132                Pallet::set_digest_variant_value(
6133                    &GOVERNANCE,
6134                    &PROPOSAL_TREASURY_SPEND,
6135                    SMALL_COMMIT,
6136                    &Position::default(),
6137                    &Default::default(),
6138                ),
6139                Error::MaxAssetReaped
6140            );
6141        });
6142    }
6143
6144    #[test]
6145    fn set_digest_variant_value_err_digest_not_found() {
6146        commit_test_ext().execute_with(|| {
6147            set_default_user_balance_and_standard_hold(ALICE).unwrap();
6148            Pallet::place_commit(
6149                &ALICE,
6150                &GOVERNANCE,
6151                &PROPOSAL_TREASURY_SPEND,
6152                STANDARD_COMMIT,
6153                &Directive::new(Precision::Exact, Fortitude::Force),
6154            )
6155            .unwrap();
6156            assert_err!(
6157                Pallet::set_digest_variant_value(
6158                    &GOVERNANCE,
6159                    &PROPOSAL_RUNTIME_UPGRADE,
6160                    SMALL_COMMIT,
6161                    &Position::default(),
6162                    &Default::default(),
6163                ),
6164                Error::DigestNotFound
6165            );
6166        });
6167    }
6168
6169    #[test]
6170    fn place_commit_of_variant_success_for_digest() {
6171        commit_test_ext().execute_with(|| {
6172            System::set_block_number(10);
6173            set_default_user_balance_and_standard_hold(ALICE).unwrap();
6174            assert_err!(
6175                Pallet::commit_exists(&ALICE, &GOVERNANCE),
6176                Error::CommitNotFound
6177            );
6178            assert_err!(
6179                Pallet::digest_exists(&GOVERNANCE, &PROPOSAL_TREASURY_SPEND),
6180                Error::DigestNotFound
6181            );
6182            assert_ok!(Pallet::place_commit_of_variant(
6183                &ALICE,
6184                &GOVERNANCE,
6185                &PROPOSAL_TREASURY_SPEND,
6186                STANDARD_COMMIT,
6187                &Position::position_of(1).unwrap(),
6188                &Directive::new(Precision::Exact, Fortitude::Force)
6189            ));
6190            // Variant enquiry
6191            let actual_variant = Pallet::get_commit_variant(&ALICE, &GOVERNANCE).unwrap();
6192            let expected_variant = Position::position_of(1).unwrap();
6193            assert_eq!(expected_variant, actual_variant);
6194            let actual_varinat_value = Pallet::get_digest_variant_value(
6195                &GOVERNANCE,
6196                &PROPOSAL_TREASURY_SPEND,
6197                &Position::position_of(1).unwrap(),
6198            )
6199            .unwrap();
6200            assert_eq!(actual_varinat_value, STANDARD_COMMIT);
6201            // Commit and digest enquirey
6202            assert_ok!(Pallet::commit_exists(&ALICE, &GOVERNANCE));
6203            assert_ok!(Pallet::digest_exists(&GOVERNANCE, &PROPOSAL_TREASURY_SPEND));
6204            // Balance and freeze enquirey
6205            let balace_after = AssetOf::balance(&ALICE);
6206            let hold_balance_after = AssetOf::balance_on_hold(&PREPARE_FOR_COMMIT, &ALICE);
6207            let expected_balance_after = INITIAL_BALANCE;
6208            let expected_hold_balance_after = 250;
6209            assert_eq!(expected_balance_after, balace_after);
6210            assert_eq!(expected_hold_balance_after, hold_balance_after);
6211            assert_eq!(
6212                AssetOf::balance_frozen(&GOVERNANCE, &ALICE),
6213                STANDARD_COMMIT
6214            );
6215            // Commit info enquiry
6216            let commit_info = CommitMap::get((ALICE, GOVERNANCE)).unwrap();
6217            assert_eq!(commit_info.digest(), PROPOSAL_TREASURY_SPEND);
6218            let variant = commit_info.variant();
6219            let index = variant.index();
6220            let commits = commit_info.commits();
6221            let commit = commits.get(0).unwrap();
6222            assert_eq!(receipt_deposit_value(commit).unwrap(), STANDARD_COMMIT);
6223            // Digest info enquiry
6224            let digest_info = DigestMap::get((GOVERNANCE, PROPOSAL_TREASURY_SPEND)).unwrap();
6225            let digests = digest_info.reveal();
6226            let digest_of = digests.get(index).unwrap();
6227            assert_ok!(has_deposits(digest_of, &variant, &PROPOSAL_TREASURY_SPEND));
6228            assert_eq!(
6229                balance_total(digest_of, &variant, &PROPOSAL_TREASURY_SPEND).unwrap(),
6230                STANDARD_COMMIT,
6231            );
6232            // Total value enquiry
6233            let reason_value = ReasonValue::get(GOVERNANCE).unwrap();
6234            assert_eq!(reason_value, 250);
6235
6236            #[cfg(not(feature = "dev"))]
6237            System::assert_last_event(Event::CommitPlaced { 
6238                    who: ALICE, 
6239                    reason: GOVERNANCE, 
6240                    digest: PROPOSAL_TREASURY_SPEND, 
6241                    value: STANDARD_COMMIT, 
6242                    variant: Position::position_of(1).unwrap() 
6243                }
6244                .into()
6245            );
6246
6247            #[cfg(feature = "dev")]
6248            System::assert_last_event(Event::CommitPlaced { 
6249                    who: ALICE, 
6250                    reason: GOVERNANCE, 
6251                    model: DigestVariant::Direct(PROPOSAL_TREASURY_SPEND), 
6252                    value: STANDARD_COMMIT, 
6253                    variant: Position::position_of(1).unwrap() 
6254                }
6255                .into()
6256            );
6257        })
6258    }
6259
6260    #[test]
6261    fn place_commit_of_variant_marker_error_for_value_zero() {
6262        commit_test_ext().execute_with(|| {
6263            set_default_user_balance_and_standard_hold(ALICE).unwrap();
6264            assert_err!(
6265                Pallet::place_commit_of_variant(
6266                    &ALICE,
6267                    &GOVERNANCE,
6268                    &PROPOSAL_RUNTIME_UPGRADE,
6269                    ZERO_VALUE,
6270                    &Position::default(),
6271                    &Directive::new(Precision::Exact, Fortitude::Force)
6272                ),
6273                Error::MarkerCommitNotAllowed
6274            );
6275            // Commit and digest enquirey
6276            assert_err!(
6277                Pallet::commit_exists(&ALICE, &GOVERNANCE),
6278                Error::CommitNotFound
6279            );
6280            assert_err!(
6281                Pallet::digest_exists(&GOVERNANCE, &PROPOSAL_RUNTIME_UPGRADE),
6282                Error::DigestNotFound
6283            );
6284        })
6285    }
6286
6287    #[test]
6288    fn place_commit_of_variant_success_for_index() {
6289        commit_test_ext().execute_with(|| {
6290            System::set_block_number(10);
6291            set_default_user_balance_and_standard_hold(ALICE).unwrap();
6292            set_default_user_balance_and_standard_hold(BOB).unwrap();
6293            set_default_user_balance_and_standard_hold(CHARLIE).unwrap();
6294
6295            Pallet::place_commit(
6296                &ALICE,
6297                &STAKING,
6298                &VALIDATOR_ALPHA,
6299                STANDARD_COMMIT,
6300                &Directive::new(Precision::Exact, Fortitude::Force),
6301            )
6302            .unwrap();
6303            Pallet::place_commit(
6304                &BOB,
6305                &STAKING,
6306                &VALIDATOR_BETA,
6307                STANDARD_COMMIT,
6308                &Directive::new(Precision::Exact, Fortitude::Force),
6309            )
6310            .unwrap();
6311            prepare_and_initiate_index(
6312                MIKE,
6313                STAKING,
6314                &[
6315                    (VALIDATOR_ALPHA, SHARE_MAJOR),
6316                    (VALIDATOR_BETA, SHARE_DOMINANT),
6317                ],
6318                INDEX_OPTIMIZED_STAKING,
6319            )
6320            .unwrap();
6321            assert_ok!(Pallet::index_exists(&STAKING, &INDEX_OPTIMIZED_STAKING));
6322            // Before placing a commit to the index
6323            let index_info = Pallet::get_index(&STAKING, &INDEX_OPTIMIZED_STAKING).unwrap();
6324            assert_eq!(index_info.capital(), 100);
6325            assert_eq!(index_info.principal(), ZERO_VALUE);
6326            let actual_entries_value =
6327                Pallet::get_entries_value(&STAKING, &INDEX_OPTIMIZED_STAKING).unwrap();
6328            let expected_entries_value = vec![(VALIDATOR_ALPHA, 0), (VALIDATOR_BETA, 0)];
6329            assert_eq!(actual_entries_value, expected_entries_value);
6330
6331            let reason_value = ReasonValue::get(STAKING).unwrap();
6332            assert_eq!(reason_value, 500);
6333            // Place commit to an index with variant
6334            assert_ok!(Pallet::place_commit_of_variant(
6335                &CHARLIE,
6336                &STAKING,
6337                &INDEX_OPTIMIZED_STAKING,
6338                STANDARD_COMMIT,
6339                &Position::position_of(2).unwrap(),
6340                &Directive::new(Precision::Exact, Fortitude::Force)
6341            ));
6342            // After placing a commit to the index with a variant
6343            let index_value = Pallet::get_index_value(&STAKING, &INDEX_OPTIMIZED_STAKING).unwrap();
6344            assert_eq!(index_value, 250);
6345            let actual_entries_value =
6346                Pallet::get_entries_value(&STAKING, &INDEX_OPTIMIZED_STAKING).unwrap();
6347            let expected_entries_value = vec![(VALIDATOR_ALPHA, 100), (VALIDATOR_BETA, 150)];
6348            assert_eq!(actual_entries_value, expected_entries_value);
6349
6350            let reason_value = ReasonValue::get(STAKING).unwrap();
6351            assert_eq!(reason_value, 750);
6352            // Variant check
6353            assert_eq!(
6354                Pallet::get_commit_variant(&CHARLIE, &STAKING),
6355                Ok(Position::position_of(2).unwrap())
6356            );
6357
6358            #[cfg(not(feature = "dev"))]
6359            System::assert_last_event(Event::CommitPlaced { 
6360                    who: CHARLIE, 
6361                    reason: STAKING, 
6362                    digest: INDEX_OPTIMIZED_STAKING, 
6363                    value: STANDARD_COMMIT, 
6364                    variant: Position::position_of(2).unwrap() 
6365                }
6366                .into()
6367            );
6368
6369            #[cfg(feature = "dev")]
6370            System::assert_last_event(Event::CommitPlaced { 
6371                    who: CHARLIE, 
6372                    reason: STAKING, 
6373                    model: DigestVariant::Index(INDEX_OPTIMIZED_STAKING), 
6374                    value: STANDARD_COMMIT, 
6375                    variant: Position::position_of(2).unwrap() 
6376                }
6377                .into()
6378            );
6379        })
6380    }
6381
6382    #[test]
6383    fn place_commit_of_variant_success_for_pool() {
6384        commit_test_ext().execute_with(|| {
6385            System::set_block_number(10);
6386            set_default_user_balance_and_standard_hold(ALICE).unwrap();
6387            set_default_user_balance_and_standard_hold(BOB).unwrap();
6388            set_default_user_balance_and_standard_hold(CHARLIE).unwrap();
6389            Pallet::place_commit(
6390                &ALICE,
6391                &STAKING,
6392                &VALIDATOR_ALPHA,
6393                STANDARD_COMMIT,
6394                &Directive::new(Precision::Exact, Fortitude::Force),
6395            )
6396            .unwrap();
6397            Pallet::place_commit(
6398                &BOB,
6399                &STAKING,
6400                &VALIDATOR_BETA,
6401                STANDARD_COMMIT,
6402                &Directive::new(Precision::Exact, Fortitude::Force),
6403            )
6404            .unwrap();
6405            let entries = vec![
6406                (VALIDATOR_ALPHA, SHARE_MAJOR),
6407                (VALIDATOR_BETA, SHARE_DOMINANT),
6408            ];
6409            prepare_and_initiate_pool(
6410                MIKE,
6411                STAKING,
6412                &entries,
6413                INDEX_OPTIMIZED_STAKING,
6414                POOL_MANAGED_STAKING,
6415                COMMISSION_LOW,
6416            )
6417            .unwrap();
6418            // Before placing commit to pool
6419            assert_eq!(
6420                Pallet::get_manager(&STAKING, &POOL_MANAGED_STAKING),
6421                Ok(MIKE)
6422            );
6423            let pool_info = PoolMap::get((STAKING, POOL_MANAGED_STAKING)).unwrap();
6424            let pool_balance_of = pool_info.balance();
6425            assert!(
6426                has_deposits(&pool_balance_of, &Default::default(), &POOL_MANAGED_STAKING).is_err()
6427            );
6428            assert_eq!(
6429                balance_total(&pool_balance_of, &Default::default(), &POOL_MANAGED_STAKING)
6430                    .unwrap(),
6431                0,
6432            );
6433            let pool_capital = pool_info.capital();
6434            assert_eq!(pool_capital, 100);
6435            let actual_slots_value =
6436                Pallet::get_slots_value(&STAKING, &POOL_MANAGED_STAKING).unwrap();
6437            let expected_slots_value = vec![(VALIDATOR_ALPHA, 0), (VALIDATOR_BETA, 0)];
6438            assert_eq!(actual_slots_value, expected_slots_value);
6439            let reason_value = ReasonValue::get(STAKING).unwrap();
6440            assert_eq!(reason_value, 500);
6441            // Placing commit to pool with variant
6442            assert_ok!(Pallet::place_commit_of_variant(
6443                &CHARLIE,
6444                &STAKING,
6445                &POOL_MANAGED_STAKING,
6446                STANDARD_COMMIT,
6447                &Position::position_of(1).unwrap(),
6448                &Directive::new(Precision::Exact, Fortitude::Force)
6449            ));
6450            // After placing commit to pool
6451            let pool_info = PoolMap::get((STAKING, POOL_MANAGED_STAKING)).unwrap();
6452            let pool_balance_of = pool_info.balance();
6453            assert_ok!(has_deposits(
6454                &pool_balance_of,
6455                &Default::default(),
6456                &POOL_MANAGED_STAKING
6457            ));
6458            assert_eq!(
6459                balance_total(&pool_balance_of, &Default::default(), &POOL_MANAGED_STAKING)
6460                    .unwrap(),
6461                250,
6462            );
6463            let actual_slots_value =
6464                Pallet::get_slots_value(&STAKING, &POOL_MANAGED_STAKING).unwrap();
6465            let expected_slots_value = vec![(VALIDATOR_ALPHA, 100), (VALIDATOR_BETA, 150)];
6466            assert_eq!(actual_slots_value, expected_slots_value);
6467
6468            let reason_value = ReasonValue::get(STAKING).unwrap();
6469            assert_eq!(reason_value, 750);
6470            // Balance and freeze enquirey
6471            let balace_after = AssetOf::balance(&CHARLIE);
6472            let hold_balance_after = AssetOf::balance_on_hold(&PREPARE_FOR_COMMIT, &CHARLIE);
6473            let expected_balance_after = INITIAL_BALANCE;
6474            let expected_hold_balance_after = 250;
6475            assert_eq!(expected_balance_after, balace_after);
6476            assert_eq!(expected_hold_balance_after, hold_balance_after);
6477            assert_eq!(AssetOf::balance_frozen(&STAKING, &CHARLIE), STANDARD_COMMIT);
6478            // Variant check
6479            assert_eq!(
6480                Pallet::get_commit_variant(&CHARLIE, &STAKING),
6481                Ok(Position::position_of(1).unwrap())
6482            );
6483
6484            #[cfg(not(feature = "dev"))]
6485            System::assert_last_event(Event::CommitPlaced { 
6486                    who: CHARLIE, 
6487                    reason: STAKING, 
6488                    digest: POOL_MANAGED_STAKING, 
6489                    value: STANDARD_COMMIT, 
6490                    variant: Position::position_of(1).unwrap() 
6491                }
6492                .into()
6493            );
6494
6495            #[cfg(feature = "dev")]
6496            System::assert_last_event(Event::CommitPlaced { 
6497                    who: CHARLIE, 
6498                    reason: STAKING, 
6499                    model: DigestVariant::Pool(POOL_MANAGED_STAKING), 
6500                    value: STANDARD_COMMIT, 
6501                    variant: Position::position_of(1).unwrap() 
6502                }
6503                .into()
6504            );
6505        })
6506    }
6507
6508    #[test]
6509    fn on_place_commit_on_variant_event_emmission_success() {
6510        commit_test_ext().execute_with(|| {
6511            set_default_user_balance_and_standard_hold(ALICE).unwrap();
6512            System::set_block_number(2);
6513            Pallet::place_commit(
6514                &ALICE,
6515                &GOVERNANCE,
6516                &PROPOSAL_RUNTIME_UPGRADE,
6517                STANDARD_COMMIT,
6518                &Directive::new(Precision::Exact, Fortitude::Force),
6519            )
6520            .unwrap();
6521            System::assert_last_event(
6522                Event::CommitPlaced {
6523                    who: ALICE,
6524                    reason: GOVERNANCE,
6525                    #[cfg(feature = "dev")]
6526                    model: DigestVariant::Direct(PROPOSAL_RUNTIME_UPGRADE),
6527                    #[cfg(not(feature = "dev"))]
6528                    digest: PROPOSAL_RUNTIME_UPGRADE,
6529                    value: STANDARD_COMMIT,
6530                    variant: Position::default(),
6531                }
6532                .into(),
6533            );
6534        })
6535    }
6536
6537    #[test]
6538    fn on_set_commit_variant_event_emmission_success() {
6539        commit_test_ext().execute_with(|| {
6540            set_default_user_balance_and_standard_hold(ALICE).unwrap();
6541            System::set_block_number(2);
6542            Pallet::place_commit(
6543                &ALICE,
6544                &GOVERNANCE,
6545                &PROPOSAL_RUNTIME_UPGRADE,
6546                STANDARD_COMMIT,
6547                &Directive::new(Precision::Exact, Fortitude::Force),
6548            )
6549            .unwrap();
6550            System::assert_last_event(
6551                Event::CommitPlaced {
6552                    who: ALICE,
6553                    reason: GOVERNANCE,
6554                    #[cfg(feature = "dev")]
6555                    model: DigestVariant::Direct(PROPOSAL_RUNTIME_UPGRADE),
6556                    #[cfg(not(feature = "dev"))]
6557                    digest: PROPOSAL_RUNTIME_UPGRADE,
6558                    value: STANDARD_COMMIT,
6559                    variant: Position::position_of(0).unwrap(),
6560                }
6561                .into(),
6562            );
6563        })
6564    }
6565
6566    #[test]
6567    fn on_set_digest_variant_event_emmission_success() {
6568        commit_test_ext().execute_with(|| {
6569            set_default_user_balance_and_standard_hold(ALICE).unwrap();
6570            System::set_block_number(2);
6571            Pallet::place_commit_of_variant(
6572                &ALICE,
6573                &STAKING,
6574                &PROPOSAL_RUNTIME_UPGRADE,
6575                STANDARD_COMMIT,
6576                &Position::position_of(1).unwrap(),
6577                &Directive::new(Precision::BestEffort, Fortitude::Force),
6578            )
6579            .unwrap();
6580            System::set_block_number(3);
6581            Pallet::set_digest_variant_value(
6582                &STAKING,
6583                &PROPOSAL_RUNTIME_UPGRADE,
6584                SMALL_COMMIT,
6585                &Position::position_of(1).unwrap(),
6586                &Directive::new(Precision::BestEffort, Fortitude::Force),
6587            )
6588            .unwrap();
6589            System::assert_last_event(
6590                Event::DigestInfo {
6591                    digest: PROPOSAL_RUNTIME_UPGRADE,
6592                    reason: STAKING,
6593                    value: SMALL_COMMIT,
6594                    variant: Position::position_of(1).unwrap(),
6595                }
6596                .into(),
6597            );
6598        })
6599    }
6600
6601    #[test]
6602    fn set_commit_variant_success() {
6603        commit_test_ext().execute_with(|| {
6604            set_default_user_balance_and_standard_hold(ALICE).unwrap();
6605            Pallet::place_commit(
6606                &ALICE,
6607                &GOVERNANCE,
6608                &PROPOSAL_TREASURY_SPEND,
6609                STANDARD_COMMIT,
6610                &Directive::new(Precision::Exact, Fortitude::Force),
6611            )
6612            .unwrap();
6613            let current_commit_variant = Pallet::get_commit_variant(&ALICE, &GOVERNANCE).unwrap();
6614            assert_eq!(current_commit_variant, Position::default());
6615            // setting a new variant
6616            let new_commit_variant = Position::position_of(1).unwrap();
6617            assert_ok!(Pallet::set_commit_variant(
6618                &ALICE,
6619                &GOVERNANCE,
6620                &new_commit_variant,
6621                &Default::default(),
6622            ));
6623            // new variant updated
6624            let current_commit_variant = Pallet::get_commit_variant(&ALICE, &GOVERNANCE).unwrap();
6625            assert_eq!(current_commit_variant, Position::position_of(1).unwrap());
6626        })
6627    }
6628
6629    #[test]
6630    fn set_commit_variant_same_variant_safe_return() {
6631        commit_test_ext().execute_with(|| {
6632            set_default_user_balance_and_standard_hold(ALICE).unwrap();
6633            Pallet::place_commit(
6634                &ALICE,
6635                &GOVERNANCE,
6636                &PROPOSAL_TREASURY_SPEND,
6637                STANDARD_COMMIT,
6638                &Directive::new(Precision::Exact, Fortitude::Force),
6639            )
6640            .unwrap();
6641            let current_commit_variant = Pallet::get_commit_variant(&ALICE, &GOVERNANCE).unwrap();
6642            assert_eq!(current_commit_variant, Position::default());
6643            // setting the same variant
6644            assert_ok!(Pallet::set_commit_variant(
6645                &ALICE,
6646                &GOVERNANCE,
6647                &current_commit_variant,
6648                &Default::default(),
6649            ));
6650        })
6651    }
6652
6653    // -------------------------------------------------------------------------------
6654    // ```````````````````````````````` COMMIT INDEX `````````````````````````````````
6655    // -------------------------------------------------------------------------------
6656
6657    #[test]
6658    fn index_exists_ok() {
6659        commit_test_ext().execute_with(|| {
6660            prepare_and_initiate_index(
6661                ALICE,
6662                STAKING,
6663                &[
6664                    (VALIDATOR_ALPHA, SHARE_EQUAL),
6665                    (VALIDATOR_BETA, SHARE_EQUAL),
6666                ],
6667                INDEX_BALANCED_STAKING,
6668            )
6669            .unwrap();
6670            assert_ok!(Pallet::index_exists(&STAKING, &INDEX_BALANCED_STAKING));
6671        })
6672    }
6673
6674    #[test]
6675    fn index_exists_err_index_not_found() {
6676        commit_test_ext().execute_with(|| {
6677            prepare_and_initiate_index(
6678                ALICE,
6679                STAKING,
6680                &[
6681                    (VALIDATOR_ALPHA, SHARE_EQUAL),
6682                    (VALIDATOR_BETA, SHARE_EQUAL),
6683                ],
6684                INDEX_BALANCED_STAKING,
6685            )
6686            .unwrap();
6687            assert_err!(
6688                Pallet::index_exists(&STAKING, &INDEX_OPTIMIZED_STAKING),
6689                Error::IndexNotFound
6690            );
6691        })
6692    }
6693
6694    #[test]
6695    fn entry_exists_ok() {
6696        commit_test_ext().execute_with(|| {
6697            prepare_and_initiate_index(
6698                ALICE,
6699                STAKING,
6700                &[
6701                    (VALIDATOR_ALPHA, SHARE_EQUAL),
6702                    (VALIDATOR_BETA, SHARE_EQUAL),
6703                ],
6704                INDEX_BALANCED_STAKING,
6705            )
6706            .unwrap();
6707            assert_ok!(Pallet::entry_exists(
6708                &STAKING,
6709                &INDEX_BALANCED_STAKING,
6710                &VALIDATOR_ALPHA
6711            ));
6712            assert_ok!(Pallet::entry_exists(
6713                &STAKING,
6714                &INDEX_BALANCED_STAKING,
6715                &VALIDATOR_BETA
6716            ));
6717        })
6718    }
6719
6720    #[test]
6721    fn entry_exists_err_entry_not_found() {
6722        commit_test_ext().execute_with(|| {
6723            prepare_and_initiate_index(
6724                ALICE,
6725                STAKING,
6726                &[
6727                    (VALIDATOR_ALPHA, SHARE_EQUAL),
6728                    (VALIDATOR_BETA, SHARE_EQUAL),
6729                ],
6730                INDEX_BALANCED_STAKING,
6731            )
6732            .unwrap();
6733            assert_err!(
6734                Pallet::entry_exists(&STAKING, &INDEX_BALANCED_STAKING, &VALIDATOR_GAMMA),
6735                Error::EntryOfIndexNotFound
6736            );
6737        })
6738    }
6739
6740    #[test]
6741    fn has_index_ok() {
6742        commit_test_ext().execute_with(|| {
6743            prepare_and_initiate_index(
6744                ALICE,
6745                STAKING,
6746                &[
6747                    (VALIDATOR_ALPHA, SHARE_EQUAL),
6748                    (VALIDATOR_BETA, SHARE_EQUAL),
6749                ],
6750                INDEX_BALANCED_STAKING,
6751            )
6752            .unwrap();
6753            assert_ok!(Pallet::has_index(&STAKING));
6754        })
6755    }
6756
6757    #[test]
6758    fn has_index_err_index_not_found() {
6759        commit_test_ext().execute_with(|| {
6760            prepare_and_initiate_index(
6761                ALICE,
6762                STAKING,
6763                &[
6764                    (VALIDATOR_ALPHA, SHARE_EQUAL),
6765                    (VALIDATOR_BETA, SHARE_EQUAL),
6766                ],
6767                INDEX_BALANCED_STAKING,
6768            )
6769            .unwrap();
6770            assert_err!(Pallet::has_index(&GOVERNANCE), Error::IndexNotFound);
6771        })
6772    }
6773
6774    #[test]
6775    fn get_index_sucess() {
6776        commit_test_ext().execute_with(|| {
6777            prepare_and_initiate_index(
6778                ALICE,
6779                STAKING,
6780                &[
6781                    (VALIDATOR_ALPHA, SHARE_EQUAL),
6782                    (VALIDATOR_BETA, SHARE_EQUAL),
6783                ],
6784                INDEX_BALANCED_STAKING,
6785            )
6786            .unwrap();
6787            let expected_index = IndexMap::get((STAKING, INDEX_BALANCED_STAKING)).unwrap();
6788            let actual_index = Pallet::get_index(&STAKING, &INDEX_BALANCED_STAKING).unwrap();
6789            assert_eq!(expected_index.principal(), actual_index.principal());
6790            assert_eq!(expected_index.capital(), actual_index.capital());
6791            assert_eq!(
6792                expected_index.entries().get(0),
6793                actual_index.entries().get(0)
6794            );
6795            assert_eq!(
6796                expected_index.entries().get(1),
6797                actual_index.entries().get(1)
6798            );
6799        })
6800    }
6801
6802    #[test]
6803    fn get_entries_shares_success() {
6804        commit_test_ext().execute_with(|| {
6805            prepare_and_initiate_index(
6806                ALICE,
6807                STAKING,
6808                &[
6809                    (VALIDATOR_ALPHA, SHARE_MAJOR),
6810                    (VALIDATOR_BETA, SHARE_DOMINANT),
6811                ],
6812                INDEX_OPTIMIZED_STAKING,
6813            )
6814            .unwrap();
6815            let expected_entries_shares = vec![
6816                (VALIDATOR_ALPHA, SHARE_MAJOR),
6817                (VALIDATOR_BETA, SHARE_DOMINANT),
6818            ];
6819            let actual_entries_shares =
6820                Pallet::get_entries_shares(&STAKING, &INDEX_OPTIMIZED_STAKING).unwrap();
6821            assert_eq!(actual_entries_shares, expected_entries_shares);
6822        })
6823    }
6824
6825    #[test]
6826    fn get_entry_value_success() {
6827        commit_test_ext().execute_with(|| {
6828            set_default_user_balance_and_standard_hold(ALICE).unwrap();
6829            set_default_user_balance_and_standard_hold(BOB).unwrap();
6830            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
6831            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
6832            prepare_and_initiate_index(
6833                ALICE,
6834                STAKING,
6835                &[
6836                    (VALIDATOR_ALPHA, SHARE_MAJOR),
6837                    (VALIDATOR_BETA, SHARE_DOMINANT),
6838                ],
6839                INDEX_OPTIMIZED_STAKING,
6840            )
6841            .unwrap();
6842            Pallet::place_commit(
6843                &ALICE,
6844                &STAKING,
6845                &INDEX_OPTIMIZED_STAKING,
6846                STANDARD_COMMIT,
6847                &Directive::new(Precision::Exact, Fortitude::Force),
6848            )
6849            .unwrap();
6850            let entry_value_alpha =
6851                Pallet::get_entry_value(&STAKING, &INDEX_OPTIMIZED_STAKING, &VALIDATOR_ALPHA)
6852                    .unwrap();
6853            let entry_value_beta =
6854                Pallet::get_entry_value(&STAKING, &INDEX_OPTIMIZED_STAKING, &VALIDATOR_BETA)
6855                    .unwrap();
6856            assert_eq!(entry_value_alpha, 100);
6857            assert_eq!(entry_value_beta, 150);
6858            // placing another commit to the same index
6859            Pallet::place_commit(
6860                &BOB,
6861                &STAKING,
6862                &INDEX_OPTIMIZED_STAKING,
6863                STANDARD_COMMIT,
6864                &Directive::new(Precision::Exact, Fortitude::Force),
6865            )
6866            .unwrap();
6867            // aggregated value of specific entries accross different proprietors
6868            let entry_value_alpha =
6869                Pallet::get_entry_value(&STAKING, &INDEX_OPTIMIZED_STAKING, &VALIDATOR_ALPHA)
6870                    .unwrap();
6871            let entry_value_beta =
6872                Pallet::get_entry_value(&STAKING, &INDEX_OPTIMIZED_STAKING, &VALIDATOR_BETA)
6873                    .unwrap();
6874            assert_eq!(entry_value_alpha, 200);
6875            assert_eq!(entry_value_beta, 300);
6876        })
6877    }
6878
6879    #[test]
6880    fn get_entry_value_for_success() {
6881        commit_test_ext().execute_with(|| {
6882            set_default_user_balance_and_standard_hold(ALICE).unwrap();
6883            set_default_user_balance_and_standard_hold(BOB).unwrap();
6884            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
6885            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
6886            prepare_and_initiate_index(
6887                ALICE,
6888                STAKING,
6889                &[
6890                    (VALIDATOR_ALPHA, SHARE_MAJOR),
6891                    (VALIDATOR_BETA, SHARE_DOMINANT),
6892                ],
6893                INDEX_OPTIMIZED_STAKING,
6894            )
6895            .unwrap();
6896            Pallet::place_commit(
6897                &ALICE,
6898                &STAKING,
6899                &INDEX_OPTIMIZED_STAKING,
6900                STANDARD_COMMIT,
6901                &Directive::new(Precision::Exact, Fortitude::Force),
6902            )
6903            .unwrap();
6904            Pallet::place_commit(
6905                &BOB,
6906                &STAKING,
6907                &INDEX_OPTIMIZED_STAKING,
6908                SMALL_COMMIT,
6909                &Directive::new(Precision::Exact, Fortitude::Force),
6910            )
6911            .unwrap();
6912            // Index entry balance of alice
6913            let alpha_entry_value = Pallet::get_entry_value_for(
6914                &ALICE,
6915                &STAKING,
6916                &INDEX_OPTIMIZED_STAKING,
6917                &VALIDATOR_ALPHA,
6918            )
6919            .unwrap();
6920            let beta_entry_value = Pallet::get_entry_value_for(
6921                &ALICE,
6922                &STAKING,
6923                &INDEX_OPTIMIZED_STAKING,
6924                &VALIDATOR_BETA,
6925            )
6926            .unwrap();
6927            assert_eq!(alpha_entry_value, 100);
6928            assert_eq!(beta_entry_value, 150);
6929            // Index entry balance of bob
6930            let alpha_entry_value = Pallet::get_entry_value_for(
6931                &BOB,
6932                &STAKING,
6933                &INDEX_OPTIMIZED_STAKING,
6934                &VALIDATOR_ALPHA,
6935            )
6936            .unwrap();
6937            let beta_entry_value = Pallet::get_entry_value_for(
6938                &BOB,
6939                &STAKING,
6940                &INDEX_OPTIMIZED_STAKING,
6941                &VALIDATOR_BETA,
6942            )
6943            .unwrap();
6944            assert_eq!(alpha_entry_value, 40);
6945            assert_eq!(beta_entry_value, 60);
6946        })
6947    }
6948
6949    #[test]
6950    fn prepare_index_success() {
6951        commit_test_ext().execute_with(|| {
6952            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
6953            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
6954            let index = Pallet::prepare_index(
6955                &ALICE,
6956                &STAKING,
6957                &[
6958                    (VALIDATOR_ALPHA, SHARE_EQUAL),
6959                    (VALIDATOR_BETA, SHARE_EQUAL),
6960                ],
6961            )
6962            .unwrap();
6963            assert_eq!(index.principal(), ZERO_VALUE);
6964            assert_eq!(index.capital(), 200);
6965            let expected_alpha_entry_info =
6966                EntryInfo::new(VALIDATOR_ALPHA, SHARE_EQUAL, Default::default()).unwrap();
6967            let expected_beta_entry_info =
6968                EntryInfo::new(VALIDATOR_BETA, SHARE_EQUAL, Default::default()).unwrap();
6969            let entries = index.entries();
6970            let actual_alice_entry_info = entries.get(0).unwrap();
6971            let actual_beta_entry_info = entries.get(1).unwrap();
6972            assert_eq!(expected_alpha_entry_info, *actual_alice_entry_info);
6973            assert_eq!(expected_beta_entry_info, *actual_beta_entry_info);
6974        })
6975    }
6976
6977    #[test]
6978    fn prepare_index_err_duplicate_entry() {
6979        commit_test_ext().execute_with(|| {
6980            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
6981            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
6982            let actual_err = Pallet::prepare_index(
6983                &ALICE,
6984                &STAKING,
6985                &[
6986                    (VALIDATOR_ALPHA, SHARE_EQUAL),
6987                    (VALIDATOR_BETA, SHARE_EQUAL),
6988                    (VALIDATOR_ALPHA, SHARE_EQUAL),
6989                ],
6990            )
6991            .unwrap_err();
6992            let expected_err = Error::DuplicateEntry.into();
6993            assert_eq!(actual_err, expected_err);
6994        })
6995    }
6996
6997    #[test]
6998    fn prepare_index_err_max_index_reached() {
6999        commit_test_ext().execute_with(|| {
7000            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
7001            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
7002            initiate_digest_with_default_balance(STAKING, VALIDATOR_GAMMA).unwrap();
7003            initiate_digest_with_default_balance(STAKING, VALIDATOR_DELTA).unwrap();
7004            let actual_err = Pallet::prepare_index(
7005                &ALICE,
7006                &STAKING,
7007                &[
7008                    (VALIDATOR_ALPHA, SHARE_EQUAL),
7009                    (VALIDATOR_BETA, SHARE_EQUAL),
7010                    (VALIDATOR_GAMMA, SHARE_EQUAL),
7011                    (VALIDATOR_DELTA, SHARE_EQUAL),
7012                ],
7013            )
7014            .unwrap_err();
7015            // since MaxEntries is set to 3, adding a fourth entry results in err
7016            assert_eq!(actual_err, Error::MaxEntriesReached.into());
7017        })
7018    }
7019
7020    #[test]
7021    fn set_index_ok() {
7022        commit_test_ext().execute_with(|| {
7023            System::set_block_number(10);
7024            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
7025            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
7026            let index = Pallet::prepare_index(
7027                &ALICE,
7028                &STAKING,
7029                &[
7030                    (VALIDATOR_ALPHA, SHARE_EQUAL),
7031                    (VALIDATOR_BETA, SHARE_EQUAL),
7032                ],
7033            )
7034            .unwrap();
7035            assert_err!(
7036                Pallet::index_exists(&STAKING, &INDEX_BALANCED_STAKING),
7037                Error::IndexNotFound
7038            );
7039            assert_ok!(Pallet::set_index(
7040                &ALICE,
7041                &STAKING,
7042                &index,
7043                &INDEX_BALANCED_STAKING
7044            ));
7045            assert_ok!(Pallet::index_exists(&STAKING, &INDEX_BALANCED_STAKING));
7046
7047            #[cfg(not(feature = "dev"))]
7048            System::assert_last_event(Event::IndexInitialized {
7049                    index_of: INDEX_BALANCED_STAKING, 
7050                    reason: STAKING,
7051                }
7052                .into()
7053            );
7054
7055            #[cfg(feature = "dev")]
7056            {
7057                let entries = vec![(VALIDATOR_ALPHA, SHARE_EQUAL, Disposition::default()), (VALIDATOR_BETA, SHARE_EQUAL, Disposition::default())];
7058                System::assert_last_event(Event::IndexInitialized { 
7059                        index_of: INDEX_BALANCED_STAKING, 
7060                        reason: STAKING,
7061                        entries: entries
7062                    }
7063                    .into()
7064                );  
7065            }
7066        })
7067    }
7068
7069    #[test]
7070    fn set_index_err_index_already_exists() {
7071        commit_test_ext().execute_with(|| {
7072            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
7073            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
7074            let index = Pallet::prepare_index(
7075                &ALICE,
7076                &STAKING,
7077                &[
7078                    (VALIDATOR_ALPHA, SHARE_EQUAL),
7079                    (VALIDATOR_BETA, SHARE_EQUAL),
7080                ],
7081            )
7082            .unwrap();
7083            assert_ok!(Pallet::set_index(
7084                &ALICE,
7085                &STAKING,
7086                &index,
7087                &INDEX_BALANCED_STAKING
7088            ));
7089            assert_ok!(Pallet::index_exists(&STAKING, &INDEX_BALANCED_STAKING));
7090            // Error while creating an existing index
7091            assert_err!(
7092                Pallet::set_index(&ALICE, &STAKING, &index, &INDEX_BALANCED_STAKING),
7093                Error::IndexDigestTaken
7094            );
7095        })
7096    }
7097
7098    #[test]
7099    fn set_entry_shares_success() {
7100        commit_test_ext().execute_with(|| {
7101            set_default_user_balance_and_standard_hold(ALICE).unwrap();
7102            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
7103            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
7104            prepare_and_initiate_index(
7105                ALICE,
7106                STAKING,
7107                &[
7108                    (VALIDATOR_ALPHA, SHARE_DOMINANT),
7109                    (VALIDATOR_BETA, SHARE_MAJOR),
7110                ],
7111                INDEX_OPTIMIZED_STAKING,
7112            )
7113            .unwrap();
7114            Pallet::place_commit(
7115                &ALICE,
7116                &STAKING,
7117                &INDEX_OPTIMIZED_STAKING,
7118                STANDARD_COMMIT,
7119                &Directive::new(Precision::Exact, Fortitude::Force),
7120            )
7121            .unwrap();
7122            assert_ok!(Pallet::index_exists(&STAKING, &INDEX_OPTIMIZED_STAKING));
7123            let new_shares = SHARE_MAJOR;
7124            let new_index_digest = Pallet::set_entry_shares(
7125                &ALICE,
7126                &STAKING,
7127                &INDEX_OPTIMIZED_STAKING,
7128                &VALIDATOR_ALPHA,
7129                new_shares,
7130            )
7131            .unwrap();
7132            // new index created with updated shares
7133            assert_ok!(Pallet::index_exists(&STAKING, &new_index_digest));
7134            let actual_index_entries =
7135                Pallet::get_entries_shares(&STAKING, &new_index_digest).unwrap();
7136            let expected_index_entries = vec![
7137                (VALIDATOR_BETA, SHARE_MAJOR),
7138                (VALIDATOR_ALPHA, SHARE_MAJOR),
7139            ];
7140            assert_eq!(actual_index_entries, expected_index_entries);
7141            // old index exists and unchanged
7142            assert_ok!(Pallet::index_exists(&STAKING, &INDEX_OPTIMIZED_STAKING));
7143            let actual_index_entries =
7144                Pallet::get_entries_shares(&STAKING, &INDEX_OPTIMIZED_STAKING).unwrap();
7145            let expected_index_entries = vec![
7146                (VALIDATOR_ALPHA, SHARE_DOMINANT),
7147                (VALIDATOR_BETA, SHARE_MAJOR),
7148            ];
7149            assert_eq!(actual_index_entries, expected_index_entries);
7150        })
7151    }
7152
7153    #[test]
7154    fn set_entry_shares_success_removing_entry_when_shares_set_to_zero() {
7155        commit_test_ext().execute_with(|| {
7156            set_default_user_balance_and_standard_hold(ALICE).unwrap();
7157            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
7158            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
7159            prepare_and_initiate_index(
7160                ALICE,
7161                STAKING,
7162                &[
7163                    (VALIDATOR_ALPHA, SHARE_DOMINANT),
7164                    (VALIDATOR_BETA, SHARE_MAJOR),
7165                ],
7166                INDEX_OPTIMIZED_STAKING,
7167            )
7168            .unwrap();
7169            Pallet::place_commit(
7170                &ALICE,
7171                &STAKING,
7172                &INDEX_OPTIMIZED_STAKING,
7173                STANDARD_COMMIT,
7174                &Directive::new(Precision::Exact, Fortitude::Force),
7175            )
7176            .unwrap();
7177            assert_ok!(Pallet::index_exists(&STAKING, &INDEX_OPTIMIZED_STAKING));
7178            let new_shares = ZERO_SHARE;
7179            let new_index_digest = Pallet::set_entry_shares(
7180                &ALICE,
7181                &STAKING,
7182                &INDEX_OPTIMIZED_STAKING,
7183                &VALIDATOR_ALPHA,
7184                new_shares,
7185            )
7186            .unwrap();
7187            // new index created without the entry of which shares is set to 0
7188            assert_ok!(Pallet::index_exists(&STAKING, &new_index_digest));
7189            // Entry removed
7190            assert_err!(
7191                Pallet::entry_exists(&STAKING, &new_index_digest, &VALIDATOR_ALPHA),
7192                Error::EntryOfIndexNotFound
7193            );
7194            let actual_index_entries =
7195                Pallet::get_entries_shares(&STAKING, &new_index_digest).unwrap();
7196            let expected_index_entries = vec![(VALIDATOR_BETA, SHARE_MAJOR)];
7197            assert_eq!(actual_index_entries, expected_index_entries);
7198            // old index exists and unchanged
7199            assert_ok!(Pallet::index_exists(&STAKING, &INDEX_OPTIMIZED_STAKING));
7200            let actual_index_entries =
7201                Pallet::get_entries_shares(&STAKING, &INDEX_OPTIMIZED_STAKING).unwrap();
7202            let expected_index_entries = vec![
7203                (VALIDATOR_ALPHA, SHARE_DOMINANT),
7204                (VALIDATOR_BETA, SHARE_MAJOR),
7205            ];
7206            assert_eq!(actual_index_entries, expected_index_entries);
7207        })
7208    }
7209
7210    #[test]
7211    fn set_entry_shares_success_adding_new_entry() {
7212        commit_test_ext().execute_with(|| {
7213            set_default_user_balance_and_standard_hold(ALICE).unwrap();
7214            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
7215            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
7216            initiate_digest_with_default_balance(STAKING, VALIDATOR_GAMMA).unwrap();
7217            prepare_and_initiate_index(
7218                ALICE,
7219                STAKING,
7220                &[
7221                    (VALIDATOR_ALPHA, SHARE_EQUAL),
7222                    (VALIDATOR_BETA, SHARE_EQUAL),
7223                ],
7224                INDEX_BALANCED_STAKING,
7225            )
7226            .unwrap();
7227            Pallet::place_commit(
7228                &ALICE,
7229                &STAKING,
7230                &INDEX_BALANCED_STAKING,
7231                STANDARD_COMMIT,
7232                &Directive::new(Precision::BestEffort, Fortitude::Polite),
7233            )
7234            .unwrap();
7235            assert_ok!(Pallet::index_exists(&STAKING, &INDEX_BALANCED_STAKING));
7236            let new_shares = SHARE_EQUAL;
7237            let new_index_digest = Pallet::set_entry_shares(
7238                &ALICE,
7239                &STAKING,
7240                &INDEX_BALANCED_STAKING,
7241                &VALIDATOR_GAMMA,
7242                new_shares,
7243            )
7244            .unwrap();
7245            // new index created with an addition of new entry
7246            assert_ok!(Pallet::index_exists(&STAKING, &new_index_digest));
7247            let actual_entries = Pallet::get_entries_shares(&STAKING, &new_index_digest).unwrap();
7248            let expected_entries = vec![
7249                (VALIDATOR_ALPHA, SHARE_EQUAL),
7250                (VALIDATOR_BETA, SHARE_EQUAL),
7251                (VALIDATOR_GAMMA, SHARE_EQUAL),
7252            ];
7253            assert_eq!(expected_entries, actual_entries);
7254            // old index exists and unchanged
7255            assert_ok!(Pallet::index_exists(&STAKING, &INDEX_BALANCED_STAKING));
7256            let actual_entries =
7257                Pallet::get_entries_shares(&STAKING, &INDEX_BALANCED_STAKING).unwrap();
7258            let expected_entries = vec![
7259                (VALIDATOR_ALPHA, SHARE_EQUAL),
7260                (VALIDATOR_BETA, SHARE_EQUAL),
7261            ];
7262            assert_eq!(expected_entries, actual_entries);
7263        })
7264    }
7265
7266    #[test]
7267    fn set_entry_shares_when_share_zero_for_entry() {
7268        commit_test_ext().execute_with(|| {
7269            set_default_user_balance_and_standard_hold(ALICE).unwrap();
7270            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
7271            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
7272            initiate_digest_with_default_balance(STAKING, VALIDATOR_GAMMA).unwrap();
7273            prepare_and_initiate_index(
7274                ALICE,
7275                STAKING,
7276                &[
7277                    (VALIDATOR_ALPHA, SHARE_EQUAL),
7278                    (VALIDATOR_BETA, SHARE_EQUAL),
7279                ],
7280                INDEX_BALANCED_STAKING,
7281            )
7282            .unwrap();
7283            Pallet::place_commit(
7284                &ALICE,
7285                &STAKING,
7286                &INDEX_BALANCED_STAKING,
7287                STANDARD_COMMIT,
7288                &Directive::new(Precision::BestEffort, Fortitude::Polite),
7289            )
7290            .unwrap();
7291            assert_ok!(Pallet::index_exists(&STAKING, &INDEX_BALANCED_STAKING));
7292            // Expected to pass when tried to remove a non-entry digest
7293            // by giving shares zero
7294            assert_ok!(Pallet::set_entry_shares(
7295                &ALICE,
7296                &STAKING,
7297                &INDEX_BALANCED_STAKING,
7298                &VALIDATOR_GAMMA,
7299                ZERO_SHARE,
7300            ));
7301
7302            // While success to remove a existing entry by setting share to zero
7303            Pallet::set_entry_shares(
7304                &ALICE,
7305                &STAKING,
7306                &INDEX_BALANCED_STAKING,
7307                &VALIDATOR_BETA,
7308                ZERO_SHARE,
7309            )
7310            .unwrap();
7311        })
7312    }
7313
7314    #[test]
7315    fn set_entry_shares_err_max_entries_reached() {
7316        commit_test_ext().execute_with(|| {
7317            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
7318            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
7319            initiate_digest_with_default_balance(STAKING, VALIDATOR_GAMMA).unwrap();
7320            initiate_digest_with_default_balance(STAKING, VALIDATOR_DELTA).unwrap();
7321            prepare_and_initiate_index(
7322                ALICE,
7323                STAKING,
7324                &[
7325                    (VALIDATOR_ALPHA, SHARE_EQUAL),
7326                    (VALIDATOR_BETA, SHARE_EQUAL),
7327                    (VALIDATOR_GAMMA, SHARE_EQUAL),
7328                ],
7329                INDEX_BALANCED_STAKING,
7330            )
7331            .unwrap();
7332            assert_err!(
7333                Pallet::set_entry_shares(
7334                    &ALICE,
7335                    &STAKING,
7336                    &INDEX_BALANCED_STAKING,
7337                    &VALIDATOR_DELTA,
7338                    SHARE_EQUAL
7339                ),
7340                Error::MaxEntriesReached
7341            );
7342        })
7343    }
7344
7345    #[test]
7346    fn reap_index_ok() {
7347        commit_test_ext().execute_with(|| {
7348            System::set_block_number(10);
7349            prepare_and_initiate_index(
7350                ALICE,
7351                STAKING,
7352                &[
7353                    (VALIDATOR_ALPHA, SHARE_EQUAL),
7354                    (VALIDATOR_BETA, SHARE_EQUAL),
7355                ],
7356                INDEX_BALANCED_STAKING,
7357            )
7358            .unwrap();
7359            assert_ok!(Pallet::index_exists(&STAKING, &INDEX_BALANCED_STAKING));
7360            assert_ok!(Pallet::reap_index(&STAKING, &INDEX_BALANCED_STAKING));
7361            assert_err!(
7362                Pallet::index_exists(&STAKING, &INDEX_BALANCED_STAKING),
7363                Error::IndexNotFound
7364            );
7365
7366            System::assert_last_event(Event::IndexReaped { 
7367                    index_of: INDEX_BALANCED_STAKING, 
7368                    reason: STAKING
7369                }
7370                .into()
7371            );
7372        })
7373    }
7374
7375    #[test]
7376    fn reap_index_err_index_has_funds() {
7377        commit_test_ext().execute_with(|| {
7378            set_default_user_balance_and_standard_hold(ALICE).unwrap();
7379            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
7380            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
7381            prepare_and_initiate_index(
7382                ALICE,
7383                STAKING,
7384                &[
7385                    (VALIDATOR_ALPHA, SHARE_EQUAL),
7386                    (VALIDATOR_BETA, SHARE_EQUAL),
7387                ],
7388                INDEX_BALANCED_STAKING,
7389            )
7390            .unwrap();
7391            Pallet::place_commit(
7392                &ALICE,
7393                &STAKING,
7394                &INDEX_BALANCED_STAKING,
7395                STANDARD_COMMIT,
7396                &Directive::new(Precision::Exact, Fortitude::Force),
7397            )
7398            .unwrap();
7399            assert_ok!(Pallet::index_exists(&STAKING, &INDEX_BALANCED_STAKING));
7400            assert_err!(
7401                Pallet::reap_index(&STAKING, &INDEX_BALANCED_STAKING),
7402                Error::IndexHasFunds
7403            );
7404        })
7405    }
7406
7407    #[test]
7408    fn gen_index_digest_success() {
7409        commit_test_ext().execute_with(|| {
7410            set_default_user_balance_and_standard_hold(ALICE).unwrap();
7411            let index_a = Pallet::prepare_index(
7412                &ALICE,
7413                &STAKING,
7414                &[
7415                    (VALIDATOR_ALPHA, SHARE_EQUAL),
7416                    (VALIDATOR_BETA, SHARE_EQUAL),
7417                ],
7418            )
7419            .unwrap();
7420            let gen_index_digest_1 = Pallet::gen_index_digest(&ALICE, &STAKING, &index_a);
7421            assert!(gen_index_digest_1.is_ok());
7422            let gen_index_digest_2 = Pallet::gen_index_digest(&ALICE, &STAKING, &index_a);
7423            assert!(gen_index_digest_2.is_ok());
7424            assert_eq!(gen_index_digest_1, gen_index_digest_2); // deterministic key generation for same input
7425
7426            // new index with small change in shares
7427            let index_b = Pallet::prepare_index(
7428                &ALICE,
7429                &STAKING,
7430                &[
7431                    (VALIDATOR_ALPHA, SHARE_EQUAL),
7432                    (VALIDATOR_BETA, SHARE_DOMINANT),
7433                ],
7434            )
7435            .unwrap();
7436            let gen_index_digest_3 = Pallet::gen_index_digest(&ALICE, &STAKING, &index_b);
7437            assert!(gen_index_digest_3.is_ok());
7438            assert_ne!(gen_index_digest_2, gen_index_digest_3); // Unique key generation with even a small change in the input
7439
7440            // same index and propritor with different reason
7441            let gen_index_digest_4 = Pallet::gen_index_digest(&ALICE, &ESCROW, &index_b);
7442            assert!(gen_index_digest_4.is_ok());
7443            assert_ne!(gen_index_digest_2, gen_index_digest_4);
7444            assert_ne!(gen_index_digest_3, gen_index_digest_4);
7445        })
7446    }
7447
7448    #[test]
7449    fn on_create_index_event_emmision_success() {
7450        commit_test_ext().execute_with(|| {
7451            System::set_block_number(2);
7452            let entries = vec![
7453                (VALIDATOR_ALPHA, SHARE_MAJOR),
7454                (VALIDATOR_BETA, SHARE_DOMINANT),
7455            ];
7456            let index = Pallet::prepare_index(&ALICE, &STAKING, &entries).unwrap();
7457            Pallet::set_index(&ALICE, &STAKING, &index, &INDEX_OPTIMIZED_STAKING).unwrap();
7458            #[cfg(feature = "dev")]
7459            let entries_defaults = vec![
7460                (VALIDATOR_ALPHA, SHARE_MAJOR, Position::default()),
7461                (VALIDATOR_BETA, SHARE_DOMINANT, Position::default()),
7462            ];
7463            System::assert_last_event(
7464                Event::IndexInitialized {
7465                    index_of: INDEX_OPTIMIZED_STAKING,
7466                    reason: STAKING,
7467                    #[cfg(feature = "dev")]
7468                    entries: entries_defaults,
7469                }
7470                .into(),
7471            );
7472        })
7473    }
7474
7475    #[test]
7476    fn on_reap_index_event_emmision_success() {
7477        commit_test_ext().execute_with(|| {
7478            System::set_block_number(2);
7479            let entries = vec![
7480                (VALIDATOR_ALPHA, SHARE_MAJOR),
7481                (VALIDATOR_BETA, SHARE_DOMINANT),
7482            ];
7483            let index = Pallet::prepare_index(&ALICE, &STAKING, &entries).unwrap();
7484            Pallet::set_index(&ALICE, &STAKING, &index, &INDEX_OPTIMIZED_STAKING).unwrap();
7485            System::set_block_number(3);
7486            Pallet::reap_index(&STAKING, &INDEX_OPTIMIZED_STAKING).unwrap();
7487            System::assert_last_event(
7488                Event::IndexReaped {
7489                    index_of: INDEX_OPTIMIZED_STAKING,
7490                    reason: STAKING,
7491                }
7492                .into(),
7493            );
7494        })
7495    }
7496
7497    #[test]
7498    fn get_index_value_success() {
7499        commit_test_ext().execute_with(|| {
7500            set_default_user_balance_and_standard_hold(ALICE).unwrap();
7501            set_default_user_balance_and_standard_hold(BOB).unwrap();
7502            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
7503            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
7504            prepare_and_initiate_index(
7505                ALICE,
7506                STAKING,
7507                &[
7508                    (VALIDATOR_ALPHA, SHARE_MAJOR),
7509                    (VALIDATOR_BETA, SHARE_DOMINANT),
7510                ],
7511                INDEX_OPTIMIZED_STAKING,
7512            )
7513            .unwrap();
7514            Pallet::place_commit(
7515                &ALICE,
7516                &STAKING,
7517                &INDEX_OPTIMIZED_STAKING,
7518                STANDARD_COMMIT,
7519                &Directive::new(Precision::Exact, Fortitude::Force),
7520            )
7521            .unwrap();
7522            // index value after alice commited
7523            let expected_index_value = STANDARD_COMMIT;
7524            let actual_index_value =
7525                Pallet::get_index_value(&STAKING, &INDEX_OPTIMIZED_STAKING).unwrap();
7526            assert_eq!(expected_index_value, actual_index_value);
7527            Pallet::place_commit(
7528                &BOB,
7529                &STAKING,
7530                &INDEX_OPTIMIZED_STAKING,
7531                LARGE_COMMIT,
7532                &Directive::new(Precision::Exact, Fortitude::Force),
7533            )
7534            .unwrap();
7535            // index value after alice and bob commited
7536            let expected_index_value = STANDARD_COMMIT + LARGE_COMMIT;
7537            let actual_index_value =
7538                Pallet::get_index_value(&STAKING, &INDEX_OPTIMIZED_STAKING).unwrap();
7539            assert_eq!(expected_index_value, actual_index_value);
7540        })
7541    }
7542
7543    #[test]
7544    fn get_entries_value_success() {
7545        commit_test_ext().execute_with(|| {
7546            set_default_user_balance_and_standard_hold(ALICE).unwrap();
7547            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
7548            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
7549            prepare_and_initiate_index(
7550                ALICE,
7551                STAKING,
7552                &[
7553                    (VALIDATOR_ALPHA, SHARE_MAJOR),
7554                    (VALIDATOR_BETA, SHARE_DOMINANT),
7555                ],
7556                INDEX_OPTIMIZED_STAKING,
7557            )
7558            .unwrap();
7559            Pallet::place_commit(
7560                &ALICE,
7561                &STAKING,
7562                &INDEX_OPTIMIZED_STAKING,
7563                STANDARD_COMMIT,
7564                &Directive::new(Precision::Exact, Fortitude::Force),
7565            )
7566            .unwrap();
7567            let actual_entries_value =
7568                Pallet::get_entries_value(&STAKING, &INDEX_OPTIMIZED_STAKING).unwrap();
7569            let expected_entries_value = vec![(VALIDATOR_ALPHA, 100), (VALIDATOR_BETA, 150)];
7570            assert_eq!(expected_entries_value, actual_entries_value);
7571        })
7572    }
7573
7574    #[test]
7575    fn get_entries_value_for_success() {
7576        commit_test_ext().execute_with(|| {
7577            set_default_user_balance_and_standard_hold(ALICE).unwrap();
7578            set_default_user_balance_and_standard_hold(BOB).unwrap();
7579            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
7580            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
7581            prepare_and_initiate_index(
7582                ALICE,
7583                STAKING,
7584                &[
7585                    (VALIDATOR_ALPHA, SHARE_MAJOR),
7586                    (VALIDATOR_BETA, SHARE_DOMINANT),
7587                ],
7588                INDEX_OPTIMIZED_STAKING,
7589            )
7590            .unwrap();
7591            Pallet::place_commit(
7592                &ALICE,
7593                &STAKING,
7594                &INDEX_OPTIMIZED_STAKING,
7595                STANDARD_COMMIT,
7596                &Directive::new(Precision::Exact, Fortitude::Force),
7597            )
7598            .unwrap();
7599            Pallet::place_commit(
7600                &BOB,
7601                &STAKING,
7602                &INDEX_OPTIMIZED_STAKING,
7603                SMALL_COMMIT,
7604                &Directive::new(Precision::Exact, Fortitude::Force),
7605            )
7606            .unwrap();
7607            // Index entries balance of alice
7608            let actual_alice_entries_value =
7609                Pallet::get_entries_value_for(&ALICE, &STAKING, &INDEX_OPTIMIZED_STAKING).unwrap();
7610            let expected_alice_entries_value = vec![(VALIDATOR_ALPHA, 100), (VALIDATOR_BETA, 150)];
7611            assert_eq!(actual_alice_entries_value, expected_alice_entries_value);
7612            // Index entries balance of bob
7613            let actual_bob_entries_value =
7614                Pallet::get_entries_value_for(&BOB, &STAKING, &INDEX_OPTIMIZED_STAKING).unwrap();
7615            let expected_bob_entries_value = vec![(VALIDATOR_ALPHA, 40), (VALIDATOR_BETA, 60)];
7616            assert_eq!(actual_bob_entries_value, expected_bob_entries_value);
7617        })
7618    }
7619
7620    #[test]
7621    fn get_index_value_for_success() {
7622        commit_test_ext().execute_with(|| {
7623            set_default_user_balance_and_standard_hold(ALICE).unwrap();
7624            set_default_user_balance_and_standard_hold(BOB).unwrap();
7625            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
7626            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
7627            prepare_and_initiate_index(
7628                ALICE,
7629                STAKING,
7630                &[
7631                    (VALIDATOR_ALPHA, SHARE_MAJOR),
7632                    (VALIDATOR_BETA, SHARE_DOMINANT),
7633                ],
7634                INDEX_OPTIMIZED_STAKING,
7635            )
7636            .unwrap();
7637            Pallet::place_commit(
7638                &ALICE,
7639                &STAKING,
7640                &INDEX_OPTIMIZED_STAKING,
7641                STANDARD_COMMIT,
7642                &Directive::new(Precision::Exact, Fortitude::Force),
7643            )
7644            .unwrap();
7645            Pallet::place_commit(
7646                &BOB,
7647                &STAKING,
7648                &INDEX_OPTIMIZED_STAKING,
7649                SMALL_COMMIT,
7650                &Directive::new(Precision::Exact, Fortitude::Force),
7651            )
7652            .unwrap();
7653            // Index value of alice
7654            let alice_index_value =
7655                Pallet::get_index_value_for(&ALICE, &STAKING, &INDEX_OPTIMIZED_STAKING).unwrap();
7656            assert_eq!(alice_index_value, STANDARD_COMMIT);
7657            // Index value of bob
7658            let bob_index_value =
7659                Pallet::get_index_value_for(&BOB, &STAKING, &INDEX_OPTIMIZED_STAKING).unwrap();
7660            assert_eq!(bob_index_value, SMALL_COMMIT);
7661        })
7662    }
7663
7664    // -------------------------------------------------------------------------------
7665    // ```````````````````````````````` INDEX VARIANT ````````````````````````````````
7666    // -------------------------------------------------------------------------------
7667
7668    #[test]
7669    fn get_entry_variant_success() {
7670        commit_test_ext().execute_with(|| {
7671            set_default_user_balance_and_standard_hold(ALICE).unwrap();
7672            set_default_user_balance_and_standard_hold(BOB).unwrap();
7673            set_default_user_balance_and_standard_hold(CHARLIE).unwrap();
7674            let bob_commit_variant = Position::position_of(1).unwrap();
7675            Pallet::place_commit_of_variant(
7676                &BOB,
7677                &STAKING,
7678                &VALIDATOR_ALPHA,
7679                STANDARD_COMMIT,
7680                &bob_commit_variant,
7681                &Directive::new(Precision::Exact, Fortitude::Force),
7682            )
7683            .unwrap();
7684            let charlie_commit_varinat = Position::position_of(2).unwrap();
7685            Pallet::place_commit_of_variant(
7686                &CHARLIE,
7687                &STAKING,
7688                &VALIDATOR_BETA,
7689                STANDARD_COMMIT,
7690                &charlie_commit_varinat,
7691                &Directive::new(Precision::Exact, Fortitude::Force),
7692            )
7693            .unwrap();
7694            prepare_and_initiate_index(
7695                MIKE,
7696                STAKING,
7697                &[
7698                    (VALIDATOR_ALPHA, SHARE_MAJOR),
7699                    (VALIDATOR_BETA, SHARE_DOMINANT),
7700                ],
7701                INDEX_OPTIMIZED_STAKING,
7702            )
7703            .unwrap();
7704            Pallet::place_commit(
7705                &ALICE,
7706                &STAKING,
7707                &INDEX_OPTIMIZED_STAKING,
7708                STANDARD_COMMIT,
7709                &Directive::new(Precision::Exact, Fortitude::Force),
7710            )
7711            .unwrap();
7712            // Since all entries are initialized with the default variant (regardless of the actual commit variant),
7713            // below entries should have the default variant at this point.
7714            let default_varinat = Position::default();
7715            let alpha_entry_variant =
7716                Pallet::get_entry_variant(&STAKING, &INDEX_OPTIMIZED_STAKING, &VALIDATOR_ALPHA)
7717                    .unwrap();
7718            assert_ne!(alpha_entry_variant, bob_commit_variant);
7719            assert_eq!(alpha_entry_variant, default_varinat);
7720            let beta_entry_variant =
7721                Pallet::get_entry_variant(&STAKING, &INDEX_OPTIMIZED_STAKING, &VALIDATOR_BETA)
7722                    .unwrap();
7723            assert_ne!(beta_entry_variant, charlie_commit_varinat);
7724            assert_eq!(beta_entry_variant, default_varinat);
7725        })
7726    }
7727
7728    #[test]
7729    fn get_entry_variant_err_entry_not_found() {
7730        commit_test_ext().execute_with(|| {
7731            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
7732            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
7733            prepare_and_initiate_index(
7734                ALICE,
7735                STAKING,
7736                &[
7737                    (VALIDATOR_ALPHA, SHARE_EQUAL),
7738                    (VALIDATOR_BETA, SHARE_EQUAL),
7739                ],
7740                INDEX_BALANCED_STAKING,
7741            )
7742            .unwrap();
7743            assert_err!(
7744                Pallet::get_entry_variant(&STAKING, &INDEX_BALANCED_STAKING, &VALIDATOR_GAMMA,),
7745                Error::EntryOfIndexNotFound
7746            );
7747        })
7748    }
7749
7750    #[test]
7751    fn set_entry_of_variant_success_only_variant() {
7752        commit_test_ext().execute_with(|| {
7753            set_default_user_balance_and_standard_hold(ALICE).unwrap();
7754            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
7755            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
7756            prepare_and_initiate_index(
7757                ALICE,
7758                STAKING,
7759                &[
7760                    (VALIDATOR_ALPHA, SHARE_MAJOR),
7761                    (VALIDATOR_BETA, SHARE_DOMINANT),
7762                ],
7763                INDEX_OPTIMIZED_STAKING,
7764            )
7765            .unwrap();
7766            Pallet::place_commit(
7767                &ALICE,
7768                &STAKING,
7769                &INDEX_OPTIMIZED_STAKING,
7770                STANDARD_COMMIT,
7771                &Directive::new(Precision::Exact, Fortitude::Force),
7772            )
7773            .unwrap();
7774            let current_variant =
7775                Pallet::get_entry_variant(&STAKING, &INDEX_OPTIMIZED_STAKING, &VALIDATOR_ALPHA)
7776                    .unwrap();
7777            assert_eq!(current_variant, Position::default());
7778            // updating the variant of an existing entry
7779            let new_variant = Position::position_of(2).unwrap();
7780            let new_index_digest = Pallet::set_entry_of_variant(
7781                &ALICE,
7782                &STAKING,
7783                &INDEX_OPTIMIZED_STAKING,
7784                &VALIDATOR_ALPHA,
7785                new_variant,
7786                None,
7787            )
7788            .unwrap();
7789            assert_ne!(INDEX_OPTIMIZED_STAKING, new_index_digest);
7790            // variant updated in new index
7791            let actual_variant =
7792                Pallet::get_entry_variant(&STAKING, &new_index_digest, &VALIDATOR_ALPHA).unwrap();
7793            assert_eq!(actual_variant, new_variant);
7794            // variant unaffected in old index
7795            let actual_variant =
7796                Pallet::get_entry_variant(&STAKING, &INDEX_OPTIMIZED_STAKING, &VALIDATOR_ALPHA)
7797                    .unwrap();
7798            assert_eq!(actual_variant, current_variant);
7799        })
7800    }
7801
7802    #[test]
7803    fn set_entry_of_variant_success_variant_with_shares() {
7804        commit_test_ext().execute_with(|| {
7805            set_default_user_balance_and_standard_hold(ALICE).unwrap();
7806            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
7807            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
7808            prepare_and_initiate_index(
7809                ALICE,
7810                STAKING,
7811                &[
7812                    (VALIDATOR_ALPHA, SHARE_MAJOR),
7813                    (VALIDATOR_BETA, SHARE_DOMINANT),
7814                ],
7815                INDEX_OPTIMIZED_STAKING,
7816            )
7817            .unwrap();
7818            Pallet::place_commit(
7819                &ALICE,
7820                &STAKING,
7821                &INDEX_OPTIMIZED_STAKING,
7822                STANDARD_COMMIT,
7823                &Directive::new(Precision::Exact, Fortitude::Force),
7824            )
7825            .unwrap();
7826            let new_shares = SHARE_DOMINANT;
7827            let new_variant = Position::position_of(2).unwrap();
7828            let new_index_digest = Pallet::set_entry_of_variant(
7829                &ALICE,
7830                &STAKING,
7831                &INDEX_OPTIMIZED_STAKING,
7832                &VALIDATOR_BETA,
7833                new_variant,
7834                Some(new_shares),
7835            )
7836            .unwrap();
7837            assert_ne!(INDEX_OPTIMIZED_STAKING, new_index_digest);
7838            // variant and shares updated in new index
7839            let actual_variant =
7840                Pallet::get_entry_variant(&STAKING, &new_index_digest, &VALIDATOR_BETA).unwrap();
7841            assert_eq!(actual_variant, new_variant);
7842            let actual_shares = Pallet::get_entries_shares(&STAKING, &new_index_digest).unwrap();
7843            let expected_shares =
7844                vec![(VALIDATOR_ALPHA, SHARE_MAJOR), (VALIDATOR_BETA, new_shares)];
7845            assert_eq!(actual_shares, expected_shares);
7846            // variant and shares unaffected in old index
7847            let default_variant = Position::default();
7848            let actual_variant =
7849                Pallet::get_entry_variant(&STAKING, &INDEX_OPTIMIZED_STAKING, &VALIDATOR_BETA)
7850                    .unwrap();
7851            assert_eq!(actual_variant, default_variant);
7852            let actual_shares =
7853                Pallet::get_entries_shares(&STAKING, &INDEX_OPTIMIZED_STAKING).unwrap();
7854            let expected_shares = vec![
7855                (VALIDATOR_ALPHA, SHARE_MAJOR),
7856                (VALIDATOR_BETA, SHARE_DOMINANT),
7857            ];
7858            assert_eq!(actual_shares, expected_shares);
7859        })
7860    }
7861
7862    #[test]
7863    fn set_entry_of_variant_new_entry_shares_cannot_be_none() {
7864        commit_test_ext().execute_with(|| {
7865            set_default_user_balance_and_standard_hold(ALICE).unwrap();
7866            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
7867            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
7868            prepare_and_initiate_index(
7869                ALICE,
7870                STAKING,
7871                &[
7872                    (VALIDATOR_ALPHA, SHARE_MAJOR),
7873                    (VALIDATOR_BETA, SHARE_DOMINANT),
7874                ],
7875                INDEX_OPTIMIZED_STAKING,
7876            )
7877            .unwrap();
7878            Pallet::place_commit(
7879                &ALICE,
7880                &STAKING,
7881                &INDEX_OPTIMIZED_STAKING,
7882                STANDARD_COMMIT,
7883                &Directive::new(Precision::Exact, Fortitude::Force),
7884            )
7885            .unwrap();
7886            let new_variant = Position::position_of(2).unwrap();
7887            let new_index_digest = Pallet::set_entry_of_variant(
7888                &ALICE,
7889                &STAKING,
7890                &INDEX_OPTIMIZED_STAKING,
7891                &VALIDATOR_GAMMA,
7892                new_variant,
7893                None,
7894            )
7895            .unwrap();
7896            // Since, the new entry share is set to `None`, same index digest is returned.
7897            assert_eq!(new_index_digest, INDEX_OPTIMIZED_STAKING);
7898        })
7899    }
7900
7901    #[test]
7902    fn prepare_index_of_variants_success() {
7903        commit_test_ext().execute_with(|| {
7904            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
7905            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
7906            let entries = vec![
7907                (
7908                    VALIDATOR_ALPHA,
7909                    SHARE_DOMINANT,
7910                    Position::position_of(2).unwrap(),
7911                ),
7912                (
7913                    VALIDATOR_BETA,
7914                    SHARE_MAJOR,
7915                    Position::position_of(1).unwrap(),
7916                ),
7917            ];
7918            let index = Pallet::prepare_index_of_variants(&ALICE, &STAKING, entries).unwrap();
7919            assert_eq!(index.principal(), ZERO_VALUE);
7920            assert_eq!(index.capital(), 100);
7921            let expected_entry_alpha = EntryInfo::new(
7922                VALIDATOR_ALPHA,
7923                SHARE_DOMINANT,
7924                Position::position_of(2).unwrap(),
7925            )
7926            .unwrap();
7927            let expected_entry_beta = EntryInfo::new(
7928                VALIDATOR_BETA,
7929                SHARE_MAJOR,
7930                Position::position_of(1).unwrap(),
7931            )
7932            .unwrap();
7933            assert_eq!(index.entries().get(0).unwrap(), &expected_entry_alpha);
7934            assert_eq!(index.entries().get(1).unwrap(), &expected_entry_beta);
7935        })
7936    }
7937
7938    #[test]
7939    fn prepare_index_of_variants_err_duplicate_entry() {
7940        commit_test_ext().execute_with(|| {
7941            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
7942            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
7943            let actual_err = Pallet::prepare_index_of_variants(
7944                &ALICE,
7945                &STAKING,
7946                vec![
7947                    (VALIDATOR_ALPHA, SHARE_EQUAL, Position::default()),
7948                    (VALIDATOR_BETA, SHARE_EQUAL, Position::default()),
7949                    (VALIDATOR_ALPHA, SHARE_EQUAL, Position::default()),
7950                ],
7951            )
7952            .unwrap_err();
7953            let expected_err = Error::DuplicateEntry.into();
7954            assert_eq!(actual_err, expected_err);
7955        })
7956    }
7957
7958    #[test]
7959    fn prepare_index_of_variants_err_max_index_reached() {
7960        commit_test_ext().execute_with(|| {
7961            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
7962            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
7963            initiate_digest_with_default_balance(STAKING, VALIDATOR_GAMMA).unwrap();
7964            initiate_digest_with_default_balance(STAKING, VALIDATOR_DELTA).unwrap();
7965            let actual_err = Pallet::prepare_index_of_variants(
7966                &ALICE,
7967                &STAKING,
7968                vec![
7969                    (VALIDATOR_ALPHA, SHARE_EQUAL, Position::default()),
7970                    (
7971                        VALIDATOR_BETA,
7972                        SHARE_EQUAL,
7973                        Position::position_of(2).unwrap(),
7974                    ),
7975                    (VALIDATOR_GAMMA, SHARE_EQUAL, Position::default()),
7976                    (VALIDATOR_DELTA, SHARE_EQUAL, Position::default()),
7977                ],
7978            )
7979            .unwrap_err();
7980            // since MaxEntries is set to 3, adding a fourth entry results in err
7981            assert_eq!(actual_err, Error::MaxEntriesReached.into());
7982        })
7983    }
7984
7985    // -------------------------------------------------------------------------------
7986    // ````````````````````````````````` COMMIT POOL `````````````````````````````````
7987    // -------------------------------------------------------------------------------
7988
7989    #[test]
7990    fn pool_exists_ok() {
7991        commit_test_ext().execute_with(|| {
7992            let entries = vec![
7993                (VALIDATOR_ALPHA, SHARE_EQUAL),
7994                (VALIDATOR_BETA, SHARE_EQUAL),
7995            ];
7996            prepare_and_initiate_pool(
7997                ALICE,
7998                STAKING,
7999                &entries,
8000                INDEX_BALANCED_STAKING,
8001                POOL_MANAGED_STAKING,
8002                COMMISSION_LOW,
8003            )
8004            .unwrap();
8005            assert_ok!(Pallet::pool_exists(&STAKING, &POOL_MANAGED_STAKING));
8006        })
8007    }
8008
8009    #[test]
8010    fn pool_exists_err_pool_not_found() {
8011        commit_test_ext().execute_with(|| {
8012            let entries = vec![
8013                (VALIDATOR_ALPHA, SHARE_EQUAL),
8014                (VALIDATOR_BETA, SHARE_EQUAL),
8015            ];
8016            prepare_and_initiate_pool(
8017                ALICE,
8018                STAKING,
8019                &entries,
8020                INDEX_BALANCED_STAKING,
8021                POOL_MANAGED_STAKING,
8022                COMMISSION_LOW,
8023            )
8024            .unwrap();
8025            assert_err!(
8026                Pallet::pool_exists(&ESCROW, &POOL_PROFESSIONAL_ESCROW),
8027                Error::PoolNotFound
8028            );
8029        })
8030    }
8031
8032    #[test]
8033    fn slot_exists_ok() {
8034        commit_test_ext().execute_with(|| {
8035            let entries = vec![
8036                (VALIDATOR_ALPHA, SHARE_EQUAL),
8037                (VALIDATOR_BETA, SHARE_EQUAL),
8038            ];
8039            prepare_and_initiate_pool(
8040                ALICE,
8041                STAKING,
8042                &entries,
8043                INDEX_BALANCED_STAKING,
8044                POOL_MANAGED_STAKING,
8045                COMMISSION_LOW,
8046            )
8047            .unwrap();
8048            assert_ok!(Pallet::slot_exists(
8049                &STAKING,
8050                &POOL_MANAGED_STAKING,
8051                &VALIDATOR_ALPHA
8052            ));
8053        })
8054    }
8055
8056    #[test]
8057    fn slot_exists_err_slot_not_found() {
8058        commit_test_ext().execute_with(|| {
8059            let entries = vec![
8060                (VALIDATOR_ALPHA, SHARE_EQUAL),
8061                (VALIDATOR_BETA, SHARE_EQUAL),
8062            ];
8063            prepare_and_initiate_pool(
8064                ALICE,
8065                STAKING,
8066                &entries,
8067                INDEX_BALANCED_STAKING,
8068                POOL_MANAGED_STAKING,
8069                COMMISSION_LOW,
8070            )
8071            .unwrap();
8072            assert_err!(
8073                Pallet::slot_exists(&STAKING, &POOL_MANAGED_STAKING, &VALIDATOR_GAMMA),
8074                Error::SlotOfPoolNotFound
8075            );
8076        })
8077    }
8078
8079    #[test]
8080    fn has_pool_ok() {
8081        commit_test_ext().execute_with(|| {
8082            let entries = vec![
8083                (VALIDATOR_ALPHA, SHARE_EQUAL),
8084                (VALIDATOR_BETA, SHARE_EQUAL),
8085            ];
8086            prepare_and_initiate_pool(
8087                ALICE,
8088                STAKING,
8089                &entries,
8090                INDEX_BALANCED_STAKING,
8091                POOL_MANAGED_STAKING,
8092                COMMISSION_LOW,
8093            )
8094            .unwrap();
8095            assert_ok!(Pallet::has_pool(&STAKING));
8096        })
8097    }
8098
8099    #[test]
8100    fn has_pool_err_pool_not_found() {
8101        commit_test_ext().execute_with(|| {
8102            let entries = vec![
8103                (VALIDATOR_ALPHA, SHARE_EQUAL),
8104                (VALIDATOR_BETA, SHARE_EQUAL),
8105            ];
8106            prepare_and_initiate_pool(
8107                ALICE,
8108                STAKING,
8109                &entries,
8110                INDEX_BALANCED_STAKING,
8111                POOL_MANAGED_STAKING,
8112                COMMISSION_LOW,
8113            )
8114            .unwrap();
8115            assert_err!(Pallet::has_pool(&GOVERNANCE), Error::PoolNotFound);
8116        })
8117    }
8118
8119    #[test]
8120    fn get_manager_success() {
8121        commit_test_ext().execute_with(|| {
8122            let entries = vec![
8123                (VALIDATOR_ALPHA, SHARE_EQUAL),
8124                (VALIDATOR_BETA, SHARE_EQUAL),
8125            ];
8126            prepare_and_initiate_pool(
8127                ALICE,
8128                STAKING,
8129                &entries,
8130                INDEX_BALANCED_STAKING,
8131                POOL_MANAGED_STAKING,
8132                COMMISSION_LOW,
8133            )
8134            .unwrap();
8135            assert_eq!(
8136                Pallet::get_manager(&STAKING, &POOL_MANAGED_STAKING),
8137                Ok(ALICE)
8138            );
8139        })
8140    }
8141
8142    #[test]
8143    fn get_manager_err_pool_not_found() {
8144        commit_test_ext().execute_with(|| {
8145            let entries = vec![
8146                (VALIDATOR_ALPHA, SHARE_EQUAL),
8147                (VALIDATOR_BETA, SHARE_EQUAL),
8148            ];
8149            prepare_and_initiate_pool(
8150                ALICE,
8151                STAKING,
8152                &entries,
8153                INDEX_BALANCED_STAKING,
8154                POOL_MANAGED_STAKING,
8155                COMMISSION_LOW,
8156            )
8157            .unwrap();
8158            // checks pool first
8159            assert_err!(
8160                Pallet::get_manager(&ESCROW, &POOL_PROFESSIONAL_ESCROW),
8161                Error::PoolNotFound
8162            );
8163        })
8164    }
8165
8166    #[test]
8167    fn get_pool_success() {
8168        commit_test_ext().execute_with(|| {
8169            let entries = vec![
8170                (VALIDATOR_ALPHA, SHARE_EQUAL),
8171                (VALIDATOR_BETA, SHARE_EQUAL),
8172            ];
8173            prepare_and_initiate_pool(
8174                ALICE,
8175                STAKING,
8176                &entries,
8177                INDEX_BALANCED_STAKING,
8178                POOL_MANAGED_STAKING,
8179                COMMISSION_LOW,
8180            )
8181            .unwrap();
8182            let expected_pool = PoolMap::get((STAKING, POOL_MANAGED_STAKING)).unwrap();
8183            let actual_pool = Pallet::get_pool(&STAKING, &POOL_MANAGED_STAKING).unwrap();
8184            assert_eq!(expected_pool.balance(), actual_pool.balance());
8185            assert_eq!(expected_pool.capital(), actual_pool.capital());
8186            assert_eq!(expected_pool.commission(), actual_pool.commission());
8187            assert_eq!(
8188                expected_pool.slots().get(0).unwrap(),
8189                actual_pool.slots().get(0).unwrap()
8190            );
8191            assert_eq!(
8192                expected_pool.slots().get(1).unwrap(),
8193                actual_pool.slots().get(1).unwrap()
8194            );
8195        })
8196    }
8197
8198    #[test]
8199    fn get_pool_err_pool_not_found() {
8200        commit_test_ext().execute_with(|| {
8201            let entries = vec![
8202                (VALIDATOR_ALPHA, SHARE_EQUAL),
8203                (VALIDATOR_BETA, SHARE_EQUAL),
8204            ];
8205            prepare_and_initiate_pool(
8206                ALICE,
8207                STAKING,
8208                &entries,
8209                INDEX_BALANCED_STAKING,
8210                POOL_MANAGED_STAKING,
8211                COMMISSION_LOW,
8212            )
8213            .unwrap();
8214            let err = Pallet::get_pool(&ESCROW, &POOL_PROFESSIONAL_ESCROW).unwrap_err();
8215            assert_eq!(err, Error::PoolNotFound.into());
8216        })
8217    }
8218
8219    #[test]
8220    fn get_commission_success() {
8221        commit_test_ext().execute_with(|| {
8222            let entries = vec![
8223                (VALIDATOR_ALPHA, SHARE_EQUAL),
8224                (VALIDATOR_BETA, SHARE_EQUAL),
8225            ];
8226            prepare_and_initiate_pool(
8227                ALICE,
8228                STAKING,
8229                &entries,
8230                INDEX_BALANCED_STAKING,
8231                POOL_MANAGED_STAKING,
8232                COMMISSION_STANDARD,
8233            )
8234            .unwrap();
8235            let actual_commission =
8236                Pallet::get_commission(&STAKING, &POOL_MANAGED_STAKING).unwrap();
8237            assert_eq!(actual_commission, COMMISSION_STANDARD);
8238        })
8239    }
8240
8241    #[test]
8242    fn get_commission_err_pool_not_found() {
8243        commit_test_ext().execute_with(|| {
8244            let entries = vec![
8245                (VALIDATOR_ALPHA, SHARE_EQUAL),
8246                (VALIDATOR_BETA, SHARE_EQUAL),
8247            ];
8248            prepare_and_initiate_pool(
8249                ALICE,
8250                STAKING,
8251                &entries,
8252                INDEX_BALANCED_STAKING,
8253                POOL_MANAGED_STAKING,
8254                COMMISSION_STANDARD,
8255            )
8256            .unwrap();
8257            let err = Pallet::get_commission(&GOVERNANCE, &POOL_EXPERT_GOVERNANCE).unwrap_err();
8258            assert_eq!(err, Error::PoolNotFound.into());
8259        })
8260    }
8261
8262    #[test]
8263    fn set_pool_success() {
8264        commit_test_ext().execute_with(|| {
8265            System::set_block_number(10);
8266            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
8267            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
8268            prepare_and_initiate_index(
8269                ALICE,
8270                STAKING,
8271                &[
8272                    (VALIDATOR_ALPHA, SHARE_MAJOR),
8273                    (VALIDATOR_BETA, SHARE_DOMINANT),
8274                ],
8275                INDEX_OPTIMIZED_STAKING,
8276            )
8277            .unwrap();
8278            assert_ok!(Pallet::index_exists(&STAKING, &INDEX_OPTIMIZED_STAKING));
8279            assert_err!(
8280                Pallet::pool_exists(&STAKING, &POOL_MANAGED_STAKING),
8281                Error::PoolNotFound
8282            );
8283            let commission = COMMISSION_STANDARD;
8284            Pallet::set_pool(
8285                &ALICE,
8286                &STAKING,
8287                &POOL_MANAGED_STAKING,
8288                &INDEX_OPTIMIZED_STAKING,
8289                commission,
8290            )
8291            .unwrap();
8292            assert_ok!(Pallet::pool_exists(&STAKING, &POOL_MANAGED_STAKING));
8293            let pool_info = PoolMap::get((STAKING, POOL_MANAGED_STAKING)).unwrap();
8294            assert_eq!(pool_info.capital(), 100);
8295            assert_eq!(pool_info.commission(), commission);
8296            assert_eq!(
8297                Pallet::get_manager(&STAKING, &POOL_MANAGED_STAKING),
8298                Ok(ALICE)
8299            );
8300
8301            #[cfg(not(feature = "dev"))]
8302            System::assert_last_event(Event::PoolInitialized {
8303                    pool_of: POOL_MANAGED_STAKING,
8304                    reason: STAKING,
8305                    commission: commission
8306                }
8307                .into()
8308            );
8309
8310            #[cfg(feature = "dev")]
8311            {            
8312                let slots = vec![(VALIDATOR_ALPHA, SHARE_MAJOR, Disposition::default()), (VALIDATOR_BETA, SHARE_DOMINANT, Disposition::default())];
8313                System::assert_last_event(Event::PoolInitialized {
8314                        pool_of: POOL_MANAGED_STAKING,
8315                        reason: STAKING,
8316                        commission: commission,
8317                        slots
8318                    }
8319                    .into()
8320                );
8321            }
8322        })
8323    }
8324
8325    #[test]
8326    fn set_pool_err_pool_already_exists() {
8327        commit_test_ext().execute_with(|| {
8328            let entries = vec![
8329                (VALIDATOR_ALPHA, SHARE_EQUAL),
8330                (VALIDATOR_BETA, SHARE_EQUAL),
8331            ];
8332            prepare_and_initiate_pool(
8333                ALICE,
8334                STAKING,
8335                &entries,
8336                INDEX_BALANCED_STAKING,
8337                POOL_MANAGED_STAKING,
8338                COMMISSION_LOW,
8339            )
8340            .unwrap();
8341            assert_err!(
8342                Pallet::set_pool(
8343                    &ALICE,
8344                    &STAKING,
8345                    &POOL_MANAGED_STAKING,
8346                    &INDEX_BALANCED_STAKING,
8347                    COMMISSION_STANDARD,
8348                ),
8349                Error::PoolDigestTaken
8350            );
8351        })
8352    }
8353
8354    #[test]
8355    fn set_pool_manager_ok() {
8356        commit_test_ext().execute_with(|| {
8357            let entries = vec![
8358                (VALIDATOR_ALPHA, SHARE_EQUAL),
8359                (VALIDATOR_BETA, SHARE_EQUAL),
8360            ];
8361            prepare_and_initiate_pool(
8362                ALICE,
8363                STAKING,
8364                &entries,
8365                INDEX_BALANCED_STAKING,
8366                POOL_MANAGED_STAKING,
8367                COMMISSION_LOW,
8368            )
8369            .unwrap();
8370            assert_eq!(
8371                Pallet::get_manager(&STAKING, &POOL_MANAGED_STAKING),
8372                Ok(ALICE)
8373            );
8374            // change manager from alice -> charlie
8375            assert_ok!(Pallet::set_pool_manager(
8376                &STAKING,
8377                &POOL_MANAGED_STAKING,
8378                &CHARLIE
8379            ));
8380            // manager changed
8381            assert_eq!(
8382                Pallet::get_manager(&STAKING, &POOL_MANAGED_STAKING),
8383                Ok(CHARLIE)
8384            );
8385        })
8386    }
8387
8388    #[test]
8389    fn set_slot_shares_success_updating_existing_slot_shares() {
8390        commit_test_ext().execute_with(|| {
8391            set_default_user_balance_and_standard_hold(ALICE).unwrap();
8392            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
8393            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
8394            let entries = vec![
8395                (VALIDATOR_ALPHA, SHARE_DOMINANT),
8396                (VALIDATOR_BETA, SHARE_MAJOR),
8397            ];
8398            prepare_and_initiate_pool(
8399                ALICE,
8400                STAKING,
8401                &entries,
8402                INDEX_OPTIMIZED_STAKING,
8403                POOL_MANAGED_STAKING,
8404                COMMISSION_LOW,
8405            )
8406            .unwrap();
8407            Pallet::place_commit(
8408                &ALICE,
8409                &STAKING,
8410                &INDEX_OPTIMIZED_STAKING,
8411                STANDARD_COMMIT,
8412                &Directive::new(Precision::Exact, Fortitude::Force),
8413            )
8414            .unwrap();
8415
8416            assert_ok!(Pallet::set_slot_shares(
8417                &ALICE,
8418                &STAKING,
8419                &POOL_MANAGED_STAKING,
8420                &VALIDATOR_ALPHA,
8421                SHARE_EQUAL
8422            ));
8423            assert_ok!(Pallet::set_slot_shares(
8424                &ALICE,
8425                &STAKING,
8426                &POOL_MANAGED_STAKING,
8427                &VALIDATOR_BETA,
8428                SHARE_EQUAL
8429            ));
8430            let expected_slots_shares = vec![
8431                (VALIDATOR_ALPHA, SHARE_EQUAL),
8432                (VALIDATOR_BETA, SHARE_EQUAL),
8433            ];
8434            let actual_slots_shares =
8435                Pallet::get_slots_shares(&STAKING, &POOL_MANAGED_STAKING).unwrap();
8436            assert_eq!(actual_slots_shares, expected_slots_shares)
8437        })
8438    }
8439
8440    #[test]
8441    fn set_slot_shares_success_removing_slot_with_zero_share() {
8442        commit_test_ext().execute_with(|| {
8443            set_default_user_balance_and_standard_hold(ALICE).unwrap();
8444            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
8445            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
8446            let entries = vec![
8447                (VALIDATOR_ALPHA, SHARE_DOMINANT),
8448                (VALIDATOR_BETA, SHARE_MAJOR),
8449            ];
8450            prepare_and_initiate_pool(
8451                MIKE,
8452                STAKING,
8453                &entries,
8454                INDEX_OPTIMIZED_STAKING,
8455                POOL_MANAGED_STAKING,
8456                COMMISSION_LOW,
8457            )
8458            .unwrap();
8459            Pallet::place_commit(
8460                &ALICE,
8461                &STAKING,
8462                &INDEX_OPTIMIZED_STAKING,
8463                STANDARD_COMMIT,
8464                &Directive::new(Precision::Exact, Fortitude::Force),
8465            )
8466            .unwrap();
8467
8468            assert_ok!(Pallet::set_slot_shares(
8469                &MIKE,
8470                &STAKING,
8471                &POOL_MANAGED_STAKING,
8472                &VALIDATOR_ALPHA,
8473                SHARE_EQUAL
8474            ));
8475            assert_ok!(Pallet::set_slot_shares(
8476                &MIKE,
8477                &STAKING,
8478                &POOL_MANAGED_STAKING,
8479                &VALIDATOR_BETA,
8480                ZERO_SHARE,
8481            ));
8482            // VALIDATOR_BETA is removed
8483            let expected_slots_shares = vec![(VALIDATOR_ALPHA, SHARE_EQUAL)];
8484            let actual_slots_shares =
8485                Pallet::get_slots_shares(&STAKING, &POOL_MANAGED_STAKING).unwrap();
8486            assert_eq!(actual_slots_shares, expected_slots_shares)
8487        })
8488    }
8489
8490    #[test]
8491    fn set_slot_shares_success_creating_new_slot_with_non_zero_shares() {
8492        commit_test_ext().execute_with(|| {
8493            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
8494            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
8495            initiate_digest_with_default_balance(STAKING, VALIDATOR_GAMMA).unwrap();
8496
8497            let entries = vec![
8498                (VALIDATOR_ALPHA, SHARE_EQUAL),
8499                (VALIDATOR_BETA, SHARE_EQUAL),
8500            ];
8501            prepare_and_initiate_pool(
8502                MIKE,
8503                STAKING,
8504                &entries,
8505                INDEX_BALANCED_STAKING,
8506                POOL_MANAGED_STAKING,
8507                COMMISSION_LOW,
8508            )
8509            .unwrap();
8510
8511            assert_ok!(Pallet::set_slot_shares(
8512                &MIKE,
8513                &STAKING,
8514                &POOL_MANAGED_STAKING,
8515                &VALIDATOR_GAMMA,
8516                SHARE_EQUAL
8517            ));
8518            // VALIDATOR_GAMMA is added to the existing slots
8519            let expected_slots_shares = vec![
8520                (VALIDATOR_ALPHA, SHARE_EQUAL),
8521                (VALIDATOR_BETA, SHARE_EQUAL),
8522                (VALIDATOR_GAMMA, SHARE_EQUAL),
8523            ];
8524            let actual_slots_shares =
8525                Pallet::get_slots_shares(&STAKING, &POOL_MANAGED_STAKING).unwrap();
8526            assert_eq!(actual_slots_shares, expected_slots_shares)
8527        })
8528    }
8529
8530    #[test]
8531    fn gen_pool_digest_success() {
8532        commit_test_ext().execute_with(|| {
8533            set_default_user_balance_and_standard_hold(ALICE).unwrap();
8534            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
8535            initiate_digest_with_default_balance(ESCROW, CONTRACT_FREELANCE).unwrap();
8536            prepare_and_initiate_index(
8537                ALICE,
8538                STAKING,
8539                &[(VALIDATOR_ALPHA, SHARE_EQUAL)],
8540                INDEX_BALANCED_STAKING,
8541            )
8542            .unwrap();
8543            prepare_and_initiate_index(
8544                CHARLIE,
8545                ESCROW,
8546                &[(CONTRACT_FREELANCE, SHARE_EQUAL)],
8547                INDEX_ESCROW_DISTRIBUTION,
8548            )
8549            .unwrap();
8550            let gen_pool_diget_1 =
8551                Pallet::gen_pool_digest(&ALICE, &STAKING, &INDEX_BALANCED_STAKING, COMMISSION_LOW);
8552            assert!(gen_pool_diget_1.is_ok());
8553            let gen_pool_diget_2 =
8554                Pallet::gen_pool_digest(&ALICE, &STAKING, &INDEX_BALANCED_STAKING, COMMISSION_LOW);
8555            assert!(gen_pool_diget_2.is_ok());
8556            assert_eq!(gen_pool_diget_1, gen_pool_diget_2); // deterministic key generation for same inputs
8557
8558            // Different index digest, manager and reason
8559            let gen_pool_diget_3 = Pallet::gen_pool_digest(
8560                &CHARLIE,
8561                &ESCROW,
8562                &INDEX_ESCROW_DISTRIBUTION,
8563                COMMISSION_LOW,
8564            );
8565            assert!(gen_pool_diget_3.is_ok());
8566            assert_ne!(gen_pool_diget_2, gen_pool_diget_3);
8567            // Same proprietor, reason, index_digest with different commission
8568            let gen_pool_diget_4 = Pallet::gen_pool_digest(
8569                &CHARLIE,
8570                &ESCROW,
8571                &INDEX_ESCROW_DISTRIBUTION,
8572                COMMISSION_STANDARD,
8573            );
8574            assert!(gen_pool_diget_4.is_ok());
8575            assert_ne!(gen_pool_diget_3, gen_pool_diget_4);
8576            assert_ne!(gen_pool_diget_2, gen_pool_diget_4);
8577        })
8578    }
8579
8580    #[test]
8581    fn get_slot_shares_success() {
8582        commit_test_ext().execute_with(|| {
8583            let entries = vec![
8584                (VALIDATOR_ALPHA, SHARE_DOMINANT),
8585                (VALIDATOR_BETA, SHARE_MAJOR),
8586            ];
8587            prepare_and_initiate_pool(
8588                MIKE,
8589                STAKING,
8590                &entries,
8591                INDEX_OPTIMIZED_STAKING,
8592                POOL_MANAGED_STAKING,
8593                COMMISSION_LOW,
8594            )
8595            .unwrap();
8596            let expected_slots_shares = vec![
8597                (VALIDATOR_ALPHA, SHARE_DOMINANT),
8598                (VALIDATOR_BETA, SHARE_MAJOR),
8599            ];
8600            let actual_slots_shares =
8601                Pallet::get_slots_shares(&STAKING, &POOL_MANAGED_STAKING).unwrap();
8602            assert_eq!(actual_slots_shares, expected_slots_shares)
8603        })
8604    }
8605
8606    #[test]
8607    fn get_slot_value_success() {
8608        commit_test_ext().execute_with(|| {
8609            set_default_user_balance_and_standard_hold(ALICE).unwrap();
8610            set_default_user_balance_and_standard_hold(BOB).unwrap();
8611            set_default_user_balance_and_standard_hold(CHARLIE).unwrap();
8612            set_default_user_balance_and_standard_hold(ALAN).unwrap();
8613            Pallet::place_commit(
8614                &BOB,
8615                &STAKING,
8616                &VALIDATOR_ALPHA,
8617                STANDARD_COMMIT,
8618                &Directive::new(Precision::Exact, Fortitude::Force),
8619            )
8620            .unwrap();
8621            Pallet::place_commit(
8622                &ALAN,
8623                &STAKING,
8624                &VALIDATOR_BETA,
8625                STANDARD_COMMIT,
8626                &Directive::new(Precision::Exact, Fortitude::Force),
8627            )
8628            .unwrap();
8629
8630            let entries = vec![
8631                (VALIDATOR_ALPHA, SHARE_DOMINANT),
8632                (VALIDATOR_BETA, SHARE_MAJOR),
8633            ];
8634            prepare_and_initiate_pool(
8635                MIKE,
8636                STAKING,
8637                &entries,
8638                INDEX_OPTIMIZED_STAKING,
8639                POOL_MANAGED_STAKING,
8640                COMMISSION_LOW,
8641            )
8642            .unwrap();
8643            Pallet::place_commit(
8644                &ALICE,
8645                &STAKING,
8646                &POOL_MANAGED_STAKING,
8647                STANDARD_COMMIT,
8648                &Directive::new(Precision::Exact, Fortitude::Force),
8649            )
8650            .unwrap();
8651
8652            let expected_alpha_slot_value = 150;
8653            let actual_alpha_slot_value =
8654                Pallet::get_slot_value(&STAKING, &POOL_MANAGED_STAKING, &VALIDATOR_ALPHA).unwrap();
8655            assert_eq!(expected_alpha_slot_value, actual_alpha_slot_value);
8656
8657            let expected_beta_slot_value = 100;
8658            let actual_beta_slot_value =
8659                Pallet::get_slot_value(&STAKING, &POOL_MANAGED_STAKING, &VALIDATOR_BETA).unwrap();
8660            assert_eq!(expected_beta_slot_value, actual_beta_slot_value);
8661
8662            Pallet::place_commit(
8663                &CHARLIE,
8664                &STAKING,
8665                &POOL_MANAGED_STAKING,
8666                LARGE_COMMIT,
8667                &Directive::new(Precision::Exact, Fortitude::Force),
8668            )
8669            .unwrap();
8670
8671            let expected_alpha_slot_value = 450;
8672            let actual_alpha_slot_value =
8673                Pallet::get_slot_value(&STAKING, &POOL_MANAGED_STAKING, &VALIDATOR_ALPHA).unwrap();
8674            assert_eq!(expected_alpha_slot_value, actual_alpha_slot_value);
8675
8676            let expected_beta_slot_value = 300;
8677            let actual_beta_slot_value =
8678                Pallet::get_slot_value(&STAKING, &POOL_MANAGED_STAKING, &VALIDATOR_BETA).unwrap();
8679            assert_eq!(expected_beta_slot_value, actual_beta_slot_value);
8680        })
8681    }
8682
8683    #[test]
8684    fn get_slot_value_for_success() {
8685        commit_test_ext().execute_with(|| {
8686            set_default_user_balance_and_standard_hold(ALICE).unwrap();
8687            set_default_user_balance_and_standard_hold(BOB).unwrap();
8688            set_default_user_balance_and_standard_hold(CHARLIE).unwrap();
8689            set_default_user_balance_and_standard_hold(ALAN).unwrap();
8690            Pallet::place_commit(
8691                &BOB,
8692                &STAKING,
8693                &VALIDATOR_ALPHA,
8694                STANDARD_COMMIT,
8695                &Directive::new(Precision::Exact, Fortitude::Force),
8696            )
8697            .unwrap();
8698            Pallet::place_commit(
8699                &ALAN,
8700                &STAKING,
8701                &VALIDATOR_BETA,
8702                STANDARD_COMMIT,
8703                &Directive::new(Precision::Exact, Fortitude::Force),
8704            )
8705            .unwrap();
8706
8707            let entries = vec![
8708                (VALIDATOR_ALPHA, SHARE_DOMINANT),
8709                (VALIDATOR_BETA, SHARE_MAJOR),
8710            ];
8711            prepare_and_initiate_pool(
8712                MIKE,
8713                STAKING,
8714                &entries,
8715                INDEX_OPTIMIZED_STAKING,
8716                POOL_MANAGED_STAKING,
8717                COMMISSION_LOW,
8718            )
8719            .unwrap();
8720            Pallet::place_commit(
8721                &ALICE,
8722                &STAKING,
8723                &POOL_MANAGED_STAKING,
8724                STANDARD_COMMIT,
8725                &Directive::new(Precision::Exact, Fortitude::Force),
8726            )
8727            .unwrap();
8728
8729            Pallet::place_commit(
8730                &CHARLIE,
8731                &STAKING,
8732                &POOL_MANAGED_STAKING,
8733                SMALL_COMMIT,
8734                &Directive::new(Precision::Exact, Fortitude::Force),
8735            )
8736            .unwrap();
8737            // pool slot value of alice
8738            let expected_alpha_slot_value = 150;
8739            let actual_alpha_slot_value = Pallet::get_slot_value_for(
8740                &ALICE,
8741                &STAKING,
8742                &POOL_MANAGED_STAKING,
8743                &VALIDATOR_ALPHA,
8744            )
8745            .unwrap();
8746            assert_eq!(expected_alpha_slot_value, actual_alpha_slot_value);
8747
8748            let expected_beta_slot_value = 100;
8749            let actual_beta_slot_value = Pallet::get_slot_value_for(
8750                &ALICE,
8751                &STAKING,
8752                &POOL_MANAGED_STAKING,
8753                &VALIDATOR_BETA,
8754            )
8755            .unwrap();
8756            assert_eq!(expected_beta_slot_value, actual_beta_slot_value);
8757            // pool slot value of charlie
8758            let expected_alpha_slot_value = 60;
8759            let actual_alpha_slot_value = Pallet::get_slot_value_for(
8760                &CHARLIE,
8761                &STAKING,
8762                &POOL_MANAGED_STAKING,
8763                &VALIDATOR_ALPHA,
8764            )
8765            .unwrap();
8766            assert_eq!(expected_alpha_slot_value, actual_alpha_slot_value);
8767
8768            let expected_beta_slot_value = 40;
8769            let actual_beta_slot_value = Pallet::get_slot_value_for(
8770                &CHARLIE,
8771                &STAKING,
8772                &POOL_MANAGED_STAKING,
8773                &VALIDATOR_BETA,
8774            )
8775            .unwrap();
8776            assert_eq!(expected_beta_slot_value, actual_beta_slot_value);
8777        })
8778    }
8779
8780    #[test]
8781    fn reap_pool_success() {
8782        commit_test_ext().execute_with(|| {
8783            let entries = vec![
8784                (VALIDATOR_ALPHA, SHARE_MAJOR),
8785                (VALIDATOR_BETA, SHARE_DOMINANT),
8786            ];
8787            prepare_and_initiate_pool(
8788                ALICE,
8789                STAKING,
8790                &entries,
8791                INDEX_OPTIMIZED_STAKING,
8792                POOL_MANAGED_STAKING,
8793                COMMISSION_LOW,
8794            )
8795            .unwrap();
8796            assert_ok!(Pallet::pool_exists(&STAKING, &POOL_MANAGED_STAKING));
8797            assert_ok!(Pallet::reap_pool(&STAKING, &POOL_MANAGED_STAKING));
8798            assert_err!(
8799                Pallet::pool_exists(&STAKING, &POOL_MANAGED_STAKING),
8800                Error::PoolNotFound
8801            );
8802        })
8803    }
8804
8805    #[test]
8806    fn reap_pool_err_pool_has_funds() {
8807        commit_test_ext().execute_with(|| {
8808            set_default_user_balance_and_standard_hold(ALICE).unwrap();
8809            set_default_user_balance_and_standard_hold(BOB).unwrap();
8810            set_default_user_balance_and_standard_hold(CHARLIE).unwrap();
8811            Pallet::place_commit(
8812                &BOB,
8813                &STAKING,
8814                &VALIDATOR_ALPHA,
8815                STANDARD_COMMIT,
8816                &Directive::new(Precision::Exact, Fortitude::Force),
8817            )
8818            .unwrap();
8819            Pallet::place_commit(
8820                &CHARLIE,
8821                &STAKING,
8822                &VALIDATOR_BETA,
8823                STANDARD_COMMIT,
8824                &Directive::new(Precision::Exact, Fortitude::Force),
8825            )
8826            .unwrap();
8827            let entries = vec![
8828                (VALIDATOR_ALPHA, SHARE_EQUAL),
8829                (VALIDATOR_BETA, SHARE_EQUAL),
8830            ];
8831            prepare_and_initiate_pool(
8832                ALICE,
8833                STAKING,
8834                &entries,
8835                INDEX_BALANCED_STAKING,
8836                POOL_MANAGED_STAKING,
8837                COMMISSION_LOW,
8838            )
8839            .unwrap();
8840            Pallet::place_commit(
8841                &ALICE,
8842                &STAKING,
8843                &POOL_MANAGED_STAKING,
8844                STANDARD_COMMIT,
8845                &Directive::new(Precision::Exact, Fortitude::Force),
8846            )
8847            .unwrap();
8848            assert_err!(
8849                Pallet::reap_pool(&STAKING, &POOL_MANAGED_STAKING),
8850                Error::PoolHasFunds
8851            );
8852        })
8853    }
8854
8855    #[test]
8856    fn on_set_pool_event_emmission_success() {
8857        commit_test_ext().execute_with(|| {
8858            let entries = vec![
8859                (VALIDATOR_ALPHA, SHARE_EQUAL),
8860                (VALIDATOR_BETA, SHARE_EQUAL),
8861            ];
8862            System::set_block_number(BLOCK_EARLY);
8863            prepare_and_initiate_pool(
8864                ALICE,
8865                STAKING,
8866                &entries,
8867                INDEX_BALANCED_STAKING,
8868                POOL_MANAGED_STAKING,
8869                COMMISSION_LOW,
8870            )
8871            .unwrap();
8872            #[cfg(feature = "dev")]
8873            let entries_defaults = vec![
8874                (VALIDATOR_ALPHA, SHARE_EQUAL, Position::default()),
8875                (VALIDATOR_BETA, SHARE_EQUAL, Position::default()),
8876            ];
8877            System::assert_last_event(
8878                Event::PoolInitialized {
8879                    pool_of: POOL_MANAGED_STAKING,
8880                    reason: STAKING,
8881                    commission: COMMISSION_LOW,
8882                    #[cfg(feature = "dev")]
8883                    slots: entries_defaults,
8884                }
8885                .into(),
8886            );
8887        })
8888    }
8889
8890    #[test]
8891    fn on_set_manager_event_emmission_success() {
8892        commit_test_ext().execute_with(|| {
8893            let entries = vec![
8894                (VALIDATOR_ALPHA, SHARE_EQUAL),
8895                (VALIDATOR_BETA, SHARE_EQUAL),
8896            ];
8897            System::set_block_number(BLOCK_EARLY);
8898            prepare_and_initiate_pool(
8899                ALICE,
8900                STAKING,
8901                &entries,
8902                INDEX_BALANCED_STAKING,
8903                POOL_MANAGED_STAKING,
8904                COMMISSION_LOW,
8905            )
8906            .unwrap();
8907            Pallet::set_pool_manager(&STAKING, &POOL_MANAGED_STAKING, &BOB).unwrap();
8908            System::assert_last_event(
8909                Event::PoolManager {
8910                    pool_of: POOL_MANAGED_STAKING,
8911                    reason: STAKING,
8912                    manager: BOB,
8913                }
8914                .into(),
8915            );
8916        })
8917    }
8918
8919    #[test]
8920    fn on_set_slot_shares_event_emmission_success() {
8921        commit_test_ext().execute_with(|| {
8922            let entries = vec![
8923                (VALIDATOR_ALPHA, SHARE_EQUAL),
8924                (VALIDATOR_BETA, SHARE_EQUAL),
8925            ];
8926            System::set_block_number(BLOCK_EARLY);
8927            prepare_and_initiate_pool(
8928                ALICE,
8929                STAKING,
8930                &entries,
8931                INDEX_BALANCED_STAKING,
8932                POOL_MANAGED_STAKING,
8933                COMMISSION_LOW,
8934            )
8935            .unwrap();
8936            Pallet::set_slot_shares(
8937                &ALICE,
8938                &STAKING,
8939                &POOL_MANAGED_STAKING,
8940                &VALIDATOR_ALPHA,
8941                SHARE_DOMINANT,
8942            )
8943            .unwrap();
8944            System::assert_last_event(
8945                Event::PoolSlot {
8946                    pool_of: POOL_MANAGED_STAKING,
8947                    reason: STAKING,
8948                    slot_of: VALIDATOR_ALPHA,
8949                    variant: Position::default(),
8950                    shares: SHARE_DOMINANT,
8951                }
8952                .into(),
8953            );
8954        })
8955    }
8956
8957    #[test]
8958    fn on_reap_pool_event_emmission_success() {
8959        commit_test_ext().execute_with(|| {
8960            let entries = vec![
8961                (VALIDATOR_ALPHA, SHARE_EQUAL),
8962                (VALIDATOR_BETA, SHARE_EQUAL),
8963            ];
8964            System::set_block_number(2);
8965            prepare_and_initiate_pool(
8966                ALICE,
8967                STAKING,
8968                &entries,
8969                INDEX_BALANCED_STAKING,
8970                POOL_MANAGED_STAKING,
8971                COMMISSION_LOW,
8972            )
8973            .unwrap();
8974            Pallet::reap_pool(&STAKING, &POOL_MANAGED_STAKING).unwrap();
8975            System::assert_last_event(
8976                Event::PoolReaped {
8977                    pool_of: POOL_MANAGED_STAKING,
8978                    reason: STAKING,
8979                }
8980                .into(),
8981            );
8982        })
8983    }
8984
8985    #[test]
8986    fn get_pool_value_success() {
8987        commit_test_ext().execute_with(|| {
8988            set_default_user_balance_and_standard_hold(ALICE).unwrap();
8989            set_default_user_balance_and_standard_hold(BOB).unwrap();
8990            set_default_user_balance_and_standard_hold(CHARLIE).unwrap();
8991            set_default_user_balance_and_standard_hold(ALAN).unwrap();
8992            Pallet::place_commit(
8993                &BOB,
8994                &STAKING,
8995                &VALIDATOR_ALPHA,
8996                STANDARD_COMMIT,
8997                &Directive::new(Precision::Exact, Fortitude::Force),
8998            )
8999            .unwrap();
9000            Pallet::place_commit(
9001                &ALAN,
9002                &STAKING,
9003                &VALIDATOR_BETA,
9004                STANDARD_COMMIT,
9005                &Directive::new(Precision::Exact, Fortitude::Force),
9006            )
9007            .unwrap();
9008            let entries = vec![
9009                (VALIDATOR_ALPHA, SHARE_DOMINANT),
9010                (VALIDATOR_BETA, SHARE_MAJOR),
9011            ];
9012            prepare_and_initiate_pool(
9013                MIKE,
9014                STAKING,
9015                &entries,
9016                INDEX_OPTIMIZED_STAKING,
9017                POOL_MANAGED_STAKING,
9018                COMMISSION_LOW,
9019            )
9020            .unwrap();
9021            Pallet::place_commit(
9022                &ALICE,
9023                &STAKING,
9024                &POOL_MANAGED_STAKING,
9025                LARGE_COMMIT,
9026                &Directive::new(Precision::BestEffort, Fortitude::Polite),
9027            )
9028            .unwrap();
9029            let actual_pool_value =
9030                Pallet::get_pool_value(&STAKING, &POOL_MANAGED_STAKING).unwrap();
9031            assert_eq!(actual_pool_value, LARGE_COMMIT);
9032        })
9033    }
9034
9035    #[test]
9036    fn get_pool_value_for_success() {
9037        commit_test_ext().execute_with(|| {
9038            set_default_user_balance_and_standard_hold(ALICE).unwrap();
9039            set_default_user_balance_and_standard_hold(BOB).unwrap();
9040            set_default_user_balance_and_standard_hold(CHARLIE).unwrap();
9041            set_default_user_balance_and_standard_hold(ALAN).unwrap();
9042            Pallet::place_commit(
9043                &BOB,
9044                &STAKING,
9045                &VALIDATOR_ALPHA,
9046                STANDARD_COMMIT,
9047                &Directive::new(Precision::Exact, Fortitude::Force),
9048            )
9049            .unwrap();
9050            Pallet::place_commit(
9051                &ALAN,
9052                &STAKING,
9053                &VALIDATOR_BETA,
9054                STANDARD_COMMIT,
9055                &Directive::new(Precision::Exact, Fortitude::Force),
9056            )
9057            .unwrap();
9058            let entries = vec![
9059                (VALIDATOR_ALPHA, SHARE_DOMINANT),
9060                (VALIDATOR_BETA, SHARE_MAJOR),
9061            ];
9062            prepare_and_initiate_pool(
9063                MIKE,
9064                STAKING,
9065                &entries,
9066                INDEX_OPTIMIZED_STAKING,
9067                POOL_MANAGED_STAKING,
9068                COMMISSION_LOW,
9069            )
9070            .unwrap();
9071            Pallet::place_commit(
9072                &ALICE,
9073                &STAKING,
9074                &POOL_MANAGED_STAKING,
9075                LARGE_COMMIT,
9076                &Directive::new(Precision::Exact, Fortitude::Force),
9077            )
9078            .unwrap();
9079            Pallet::place_commit(
9080                &CHARLIE,
9081                &STAKING,
9082                &POOL_MANAGED_STAKING,
9083                STANDARD_COMMIT,
9084                &Directive::new(Precision::Exact, Fortitude::Force),
9085            )
9086            .unwrap();
9087            // Alice pool value
9088            let alice_pool_value =
9089                Pallet::get_pool_value_for(&ALICE, &STAKING, &POOL_MANAGED_STAKING).unwrap();
9090            assert_eq!(alice_pool_value, LARGE_COMMIT);
9091
9092            // Charlie pool value
9093            let mike_pool_value =
9094                Pallet::get_pool_value_for(&CHARLIE, &STAKING, &POOL_MANAGED_STAKING).unwrap();
9095            assert_eq!(mike_pool_value, STANDARD_COMMIT);
9096        })
9097    }
9098
9099    #[test]
9100    fn get_slots_value_success() {
9101        commit_test_ext().execute_with(|| {
9102            set_default_user_balance_and_standard_hold(ALICE).unwrap();
9103            set_default_user_balance_and_standard_hold(BOB).unwrap();
9104            set_default_user_balance_and_standard_hold(CHARLIE).unwrap();
9105            set_default_user_balance_and_standard_hold(ALAN).unwrap();
9106            Pallet::place_commit(
9107                &BOB,
9108                &STAKING,
9109                &VALIDATOR_ALPHA,
9110                STANDARD_COMMIT,
9111                &Directive::new(Precision::Exact, Fortitude::Force),
9112            )
9113            .unwrap();
9114            Pallet::place_commit(
9115                &ALAN,
9116                &STAKING,
9117                &VALIDATOR_BETA,
9118                STANDARD_COMMIT,
9119                &Directive::new(Precision::Exact, Fortitude::Force),
9120            )
9121            .unwrap();
9122            let entries = vec![
9123                (VALIDATOR_ALPHA, SHARE_DOMINANT),
9124                (VALIDATOR_BETA, SHARE_MAJOR),
9125            ];
9126            prepare_and_initiate_pool(
9127                MIKE,
9128                STAKING,
9129                &entries,
9130                INDEX_OPTIMIZED_STAKING,
9131                POOL_MANAGED_STAKING,
9132                COMMISSION_LOW,
9133            )
9134            .unwrap();
9135            Pallet::place_commit(
9136                &ALICE,
9137                &STAKING,
9138                &POOL_MANAGED_STAKING,
9139                LARGE_COMMIT,
9140                &Directive::new(Precision::Exact, Fortitude::Force),
9141            )
9142            .unwrap();
9143            let actual_pool_slots_value =
9144                Pallet::get_slots_value(&STAKING, &POOL_MANAGED_STAKING).unwrap();
9145            let expected_pool_slots_value = vec![(VALIDATOR_ALPHA, 300), (VALIDATOR_BETA, 200)];
9146            assert_eq!(actual_pool_slots_value, expected_pool_slots_value);
9147        })
9148    }
9149
9150    #[test]
9151    fn get_slots_value_for_success() {
9152        commit_test_ext().execute_with(|| {
9153            set_default_user_balance_and_standard_hold(ALICE).unwrap();
9154            set_default_user_balance_and_standard_hold(BOB).unwrap();
9155            set_default_user_balance_and_standard_hold(CHARLIE).unwrap();
9156            set_default_user_balance_and_standard_hold(ALAN).unwrap();
9157            Pallet::place_commit(
9158                &BOB,
9159                &STAKING,
9160                &VALIDATOR_ALPHA,
9161                STANDARD_COMMIT,
9162                &Directive::new(Precision::Exact, Fortitude::Force),
9163            )
9164            .unwrap();
9165            Pallet::place_commit(
9166                &ALAN,
9167                &STAKING,
9168                &VALIDATOR_BETA,
9169                STANDARD_COMMIT,
9170                &Directive::new(Precision::Exact, Fortitude::Force),
9171            )
9172            .unwrap();
9173            let entries = vec![
9174                (VALIDATOR_ALPHA, SHARE_DOMINANT),
9175                (VALIDATOR_BETA, SHARE_MAJOR),
9176            ];
9177            prepare_and_initiate_pool(
9178                MIKE,
9179                STAKING,
9180                &entries,
9181                INDEX_OPTIMIZED_STAKING,
9182                POOL_MANAGED_STAKING,
9183                COMMISSION_LOW,
9184            )
9185            .unwrap();
9186            Pallet::place_commit(
9187                &ALICE,
9188                &STAKING,
9189                &POOL_MANAGED_STAKING,
9190                LARGE_COMMIT,
9191                &Directive::new(Precision::Exact, Fortitude::Force),
9192            )
9193            .unwrap();
9194            Pallet::place_commit(
9195                &CHARLIE,
9196                &STAKING,
9197                &POOL_MANAGED_STAKING,
9198                SMALL_COMMIT,
9199                &Directive::new(Precision::Exact, Fortitude::Force),
9200            )
9201            .unwrap();
9202            // Pool slots value for alice
9203            let alice_pool_slots_value =
9204                Pallet::get_slots_value_for(&ALICE, &STAKING, &POOL_MANAGED_STAKING).unwrap();
9205            let expected_alice_pool_slots_value =
9206                vec![(VALIDATOR_ALPHA, 300), (VALIDATOR_BETA, 200)];
9207            assert_eq!(expected_alice_pool_slots_value, alice_pool_slots_value);
9208            // Pool slots value for charlie
9209            let alan_pool_slots_value =
9210                Pallet::get_slots_value_for(&CHARLIE, &STAKING, &POOL_MANAGED_STAKING).unwrap();
9211            let expected_alan_pool_slots_value = vec![(VALIDATOR_ALPHA, 60), (VALIDATOR_BETA, 40)];
9212            assert_eq!(expected_alan_pool_slots_value, alan_pool_slots_value);
9213        })
9214    }
9215
9216    #[test]
9217    fn set_commission_success() {
9218        commit_test_ext().execute_with(|| {
9219            set_default_user_balance_and_standard_hold(ALICE).unwrap();
9220            set_default_user_balance_and_standard_hold(BOB).unwrap();
9221            set_default_user_balance_and_standard_hold(CHARLIE).unwrap();
9222            set_default_user_balance_and_standard_hold(ALAN).unwrap();
9223            Pallet::place_commit(
9224                &BOB,
9225                &STAKING,
9226                &VALIDATOR_ALPHA,
9227                STANDARD_COMMIT,
9228                &Directive::new(Precision::Exact, Fortitude::Force),
9229            )
9230            .unwrap();
9231            Pallet::place_commit(
9232                &ALAN,
9233                &STAKING,
9234                &VALIDATOR_BETA,
9235                STANDARD_COMMIT,
9236                &Directive::new(Precision::Exact, Fortitude::Force),
9237            )
9238            .unwrap();
9239
9240            let commission = COMMISSION_LOW;
9241            let entries = vec![
9242                (VALIDATOR_ALPHA, SHARE_DOMINANT),
9243                (VALIDATOR_BETA, SHARE_EQUAL),
9244            ];
9245            prepare_and_initiate_pool(
9246                MIKE,
9247                STAKING,
9248                &entries,
9249                INDEX_OPTIMIZED_STAKING,
9250                POOL_MANAGED_STAKING,
9251                commission,
9252            )
9253            .unwrap();
9254
9255            // Settign a new commission which generated a new pool digest
9256            let new_commission = COMMISSION_STANDARD;
9257            let new_pool_digest =
9258                Pallet::set_commission(&ALICE, &STAKING, &INDEX_OPTIMIZED_STAKING, new_commission)
9259                    .unwrap();
9260            // new pool created with updated commission
9261            assert_ok!(Pallet::pool_exists(&STAKING, &new_pool_digest));
9262            let actual_commission = Pallet::get_commission(&STAKING, &new_pool_digest).unwrap();
9263            assert_eq!(actual_commission, new_commission);
9264            // old pool commission left unaffected
9265            let old_pool_commission =
9266                Pallet::get_commission(&STAKING, &POOL_MANAGED_STAKING).unwrap();
9267            assert_eq!(old_pool_commission, commission);
9268        })
9269    }
9270
9271    // -------------------------------------------------------------------------------
9272    // ````````````````````````````````` POOL VARIANT ````````````````````````````````
9273    // -------------------------------------------------------------------------------
9274
9275    #[test]
9276    fn get_slot_variant_success() {
9277        commit_test_ext().execute_with(|| {
9278            set_default_user_balance_and_standard_hold(ALICE).unwrap();
9279            set_default_user_balance_and_standard_hold(BOB).unwrap();
9280            set_default_user_balance_and_standard_hold(CHARLIE).unwrap();
9281            set_default_user_balance_and_standard_hold(ALAN).unwrap();
9282            Pallet::place_commit(
9283                &BOB,
9284                &STAKING,
9285                &VALIDATOR_ALPHA,
9286                STANDARD_COMMIT,
9287                &Directive::new(Precision::Exact, Fortitude::Force),
9288            )
9289            .unwrap();
9290            Pallet::place_commit(
9291                &ALAN,
9292                &STAKING,
9293                &VALIDATOR_BETA,
9294                STANDARD_COMMIT,
9295                &Directive::new(Precision::Exact, Fortitude::Force),
9296            )
9297            .unwrap();
9298
9299            let commission = COMMISSION_LOW;
9300            let entries = vec![
9301                (VALIDATOR_ALPHA, SHARE_DOMINANT),
9302                (VALIDATOR_BETA, SHARE_EQUAL),
9303            ];
9304            prepare_and_initiate_pool(
9305                ALICE,
9306                STAKING,
9307                &entries,
9308                INDEX_OPTIMIZED_STAKING,
9309                POOL_MANAGED_STAKING,
9310                commission,
9311            )
9312            .unwrap();
9313
9314            let expected_slot_variant = Position::default();
9315            let actual_slot_variant =
9316                Pallet::get_slot_variant(&STAKING, &POOL_MANAGED_STAKING, &VALIDATOR_ALPHA)
9317                    .unwrap();
9318            assert_eq!(expected_slot_variant, actual_slot_variant);
9319            // Affirmative -> Contrary
9320            Pallet::set_slot_of_variant(
9321                &ALICE,
9322                &STAKING,
9323                &POOL_MANAGED_STAKING,
9324                &VALIDATOR_ALPHA,
9325                Position::position_of(1).unwrap(),
9326                None,
9327            )
9328            .unwrap();
9329            let expected_slot_variant = Position::position_of(1).unwrap();
9330            let actual_slot_variant =
9331                Pallet::get_slot_variant(&STAKING, &POOL_MANAGED_STAKING, &VALIDATOR_ALPHA)
9332                    .unwrap();
9333            assert_eq!(expected_slot_variant, actual_slot_variant);
9334        })
9335    }
9336
9337    #[test]
9338    fn get_slot_variant_err_slot_not_found() {
9339        commit_test_ext().execute_with(|| {
9340            initiate_digest_with_default_balance(STAKING, VALIDATOR_ALPHA).unwrap();
9341            initiate_digest_with_default_balance(STAKING, VALIDATOR_BETA).unwrap();
9342            let entries = vec![
9343                (VALIDATOR_ALPHA, SHARE_EQUAL),
9344                (VALIDATOR_BETA, SHARE_EQUAL),
9345            ];
9346            prepare_and_initiate_pool(
9347                MIKE,
9348                STAKING,
9349                &entries,
9350                INDEX_BALANCED_STAKING,
9351                POOL_MANAGED_STAKING,
9352                COMMISSION_LOW,
9353            )
9354            .unwrap();
9355            assert_err!(
9356                Pallet::get_slot_variant(&STAKING, &POOL_MANAGED_STAKING, &VALIDATOR_GAMMA,),
9357                Error::SlotOfPoolNotFound
9358            );
9359        })
9360    }
9361
9362    #[test]
9363    fn set_slot_of_variant_success_variant_update() {
9364        commit_test_ext().execute_with(|| {
9365            commit_test_ext().execute_with(|| {
9366                set_default_user_balance_and_standard_hold(ALICE).unwrap();
9367                set_default_user_balance_and_standard_hold(BOB).unwrap();
9368                Pallet::place_commit(
9369                    &ALICE,
9370                    &STAKING,
9371                    &VALIDATOR_ALPHA,
9372                    STANDARD_COMMIT,
9373                    &Directive::new(Precision::Exact, Fortitude::Force),
9374                )
9375                .unwrap();
9376                Pallet::place_commit(
9377                    &BOB,
9378                    &STAKING,
9379                    &VALIDATOR_BETA,
9380                    STANDARD_COMMIT,
9381                    &Directive::new(Precision::Exact, Fortitude::Force),
9382                )
9383                .unwrap();
9384
9385                let entries = vec![
9386                    (VALIDATOR_ALPHA, SHARE_EQUAL),
9387                    (VALIDATOR_BETA, SHARE_EQUAL),
9388                ];
9389                prepare_and_initiate_pool(
9390                    MIKE,
9391                    STAKING,
9392                    &entries,
9393                    INDEX_BALANCED_STAKING,
9394                    POOL_MANAGED_STAKING,
9395                    COMMISSION_LOW,
9396                )
9397                .unwrap();
9398                let expected_slot_variant = Position::default();
9399                let actual_slot_variant =
9400                    Pallet::get_slot_variant(&STAKING, &POOL_MANAGED_STAKING, &VALIDATOR_ALPHA)
9401                        .unwrap();
9402                assert_eq!(expected_slot_variant, actual_slot_variant);
9403                let new_slot_variant = Position::position_of(1).unwrap();
9404                Pallet::set_slot_of_variant(
9405                    &MIKE,
9406                    &STAKING,
9407                    &POOL_MANAGED_STAKING,
9408                    &VALIDATOR_ALPHA,
9409                    new_slot_variant,
9410                    None,
9411                )
9412                .unwrap();
9413
9414                let actual_slot_variant =
9415                    Pallet::get_slot_variant(&STAKING, &POOL_MANAGED_STAKING, &VALIDATOR_ALPHA)
9416                        .unwrap();
9417                assert_eq!(actual_slot_variant, new_slot_variant);
9418            })
9419        })
9420    }
9421
9422    #[test]
9423    fn set_slot_of_variant_success_variant_and_shares_update() {
9424        commit_test_ext().execute_with(|| {
9425            commit_test_ext().execute_with(|| {
9426                set_default_user_balance_and_standard_hold(ALICE).unwrap();
9427                set_default_user_balance_and_standard_hold(BOB).unwrap();
9428                Pallet::place_commit(
9429                    &ALICE,
9430                    &STAKING,
9431                    &VALIDATOR_ALPHA,
9432                    STANDARD_COMMIT,
9433                    &Directive::new(Precision::Exact, Fortitude::Force),
9434                )
9435                .unwrap();
9436                Pallet::place_commit(
9437                    &BOB,
9438                    &STAKING,
9439                    &VALIDATOR_BETA,
9440                    STANDARD_COMMIT,
9441                    &Directive::new(Precision::Exact, Fortitude::Force),
9442                )
9443                .unwrap();
9444
9445                let entries = vec![
9446                    (VALIDATOR_ALPHA, SHARE_EQUAL),
9447                    (VALIDATOR_BETA, SHARE_EQUAL),
9448                ];
9449                prepare_and_initiate_pool(
9450                    MIKE,
9451                    STAKING,
9452                    &entries,
9453                    INDEX_BALANCED_STAKING,
9454                    POOL_MANAGED_STAKING,
9455                    COMMISSION_LOW,
9456                )
9457                .unwrap();
9458                let expected_slot_variant = Position::default();
9459                let actual_slot_variant =
9460                    Pallet::get_slot_variant(&STAKING, &POOL_MANAGED_STAKING, &VALIDATOR_ALPHA)
9461                        .unwrap();
9462                let actual_slot_shares =
9463                    Pallet::get_slots_shares(&STAKING, &POOL_MANAGED_STAKING).unwrap();
9464                let expected_slot_shares = entries;
9465                assert_eq!(expected_slot_variant, actual_slot_variant);
9466                assert_eq!(expected_slot_shares, actual_slot_shares);
9467                let new_slot_variant = Position::position_of(2).unwrap();
9468                Pallet::set_slot_of_variant(
9469                    &ALICE,
9470                    &STAKING,
9471                    &POOL_MANAGED_STAKING,
9472                    &VALIDATOR_ALPHA,
9473                    new_slot_variant,
9474                    Some(SHARE_DOMINANT),
9475                )
9476                .unwrap();
9477                let actual_slot_variant =
9478                    Pallet::get_slot_variant(&STAKING, &POOL_MANAGED_STAKING, &VALIDATOR_ALPHA)
9479                        .unwrap();
9480                let actual_slot_shares =
9481                    Pallet::get_slots_shares(&STAKING, &POOL_MANAGED_STAKING).unwrap();
9482                let expected_slot_shares = vec![
9483                    (VALIDATOR_BETA, SHARE_EQUAL),
9484                    (VALIDATOR_ALPHA, SHARE_DOMINANT),
9485                ];
9486                assert_eq!(actual_slot_variant, new_slot_variant);
9487                assert_eq!(actual_slot_shares, expected_slot_shares);
9488            })
9489        })
9490    }
9491
9492    #[test]
9493    fn set_slot_of_variant_new_slot_shares_cannot_be_none() {
9494        commit_test_ext().execute_with(|| {
9495            commit_test_ext().execute_with(|| {
9496                set_default_user_balance_and_standard_hold(ALICE).unwrap();
9497                set_default_user_balance_and_standard_hold(BOB).unwrap();
9498                set_default_user_balance_and_standard_hold(CHARLIE).unwrap();
9499                Pallet::place_commit(
9500                    &ALICE,
9501                    &STAKING,
9502                    &VALIDATOR_ALPHA,
9503                    STANDARD_COMMIT,
9504                    &Directive::new(Precision::Exact, Fortitude::Force),
9505                )
9506                .unwrap();
9507                Pallet::place_commit(
9508                    &BOB,
9509                    &STAKING,
9510                    &VALIDATOR_BETA,
9511                    STANDARD_COMMIT,
9512                    &Directive::new(Precision::Exact, Fortitude::Force),
9513                )
9514                .unwrap();
9515                let entries = vec![
9516                    (VALIDATOR_ALPHA, SHARE_EQUAL),
9517                    (VALIDATOR_BETA, SHARE_EQUAL),
9518                ];
9519                prepare_and_initiate_pool(
9520                    MIKE,
9521                    STAKING,
9522                    &entries,
9523                    INDEX_BALANCED_STAKING,
9524                    POOL_MANAGED_STAKING,
9525                    COMMISSION_LOW,
9526                )
9527                .unwrap();
9528                let expected_slot_variant = Position::default();
9529                let actual_slot_variant =
9530                    Pallet::get_slot_variant(&STAKING, &POOL_MANAGED_STAKING, &VALIDATOR_ALPHA)
9531                        .unwrap();
9532                let actual_slot_shares =
9533                    Pallet::get_slots_shares(&STAKING, &POOL_MANAGED_STAKING).unwrap();
9534                let expected_slot_shares = vec![
9535                    (VALIDATOR_ALPHA, SHARE_EQUAL),
9536                    (VALIDATOR_BETA, SHARE_EQUAL),
9537                ];
9538                assert_eq!(expected_slot_variant, actual_slot_variant);
9539                assert_eq!(expected_slot_shares, actual_slot_shares);
9540                Pallet::place_commit(
9541                    &CHARLIE,
9542                    &STAKING,
9543                    &VALIDATOR_GAMMA,
9544                    STANDARD_COMMIT,
9545                    &Directive::new(Precision::Exact, Fortitude::Force),
9546                )
9547                .unwrap();
9548                // Adding a new slot digest while shares is set to None returns Error
9549                assert_err!(
9550                    Pallet::set_slot_of_variant(
9551                        &ALICE,
9552                        &STAKING,
9553                        &POOL_MANAGED_STAKING,
9554                        &VALIDATOR_GAMMA,
9555                        Position::position_of(1).unwrap(),
9556                        None,
9557                    ),
9558                    Error::SlotOfPoolNotFound
9559                );
9560                // Nothing changes due to invalid shares
9561                let actual_slot_variant =
9562                    Pallet::get_slot_variant(&STAKING, &POOL_MANAGED_STAKING, &VALIDATOR_ALPHA)
9563                        .unwrap();
9564                let actual_slot_shares =
9565                    Pallet::get_slots_shares(&STAKING, &POOL_MANAGED_STAKING).unwrap();
9566                assert_eq!(actual_slot_variant, expected_slot_variant);
9567                assert_eq!(actual_slot_shares, expected_slot_shares);
9568            })
9569        })
9570    }
9571
9572    #[test]
9573    fn set_slot_of_variant_new_slot_digest() {
9574        commit_test_ext().execute_with(|| {
9575            commit_test_ext().execute_with(|| {
9576                set_default_user_balance_and_standard_hold(ALICE).unwrap();
9577                set_default_user_balance_and_standard_hold(BOB).unwrap();
9578                Pallet::place_commit(
9579                    &ALICE,
9580                    &STAKING,
9581                    &VALIDATOR_ALPHA,
9582                    STANDARD_COMMIT,
9583                    &Directive::new(Precision::Exact, Fortitude::Force),
9584                )
9585                .unwrap();
9586                Pallet::place_commit(
9587                    &BOB,
9588                    &STAKING,
9589                    &VALIDATOR_BETA,
9590                    STANDARD_COMMIT,
9591                    &Directive::new(Precision::Exact, Fortitude::Force),
9592                )
9593                .unwrap();
9594                let entries = vec![
9595                    (VALIDATOR_ALPHA, SHARE_EQUAL),
9596                    (VALIDATOR_BETA, SHARE_EQUAL),
9597                ];
9598                prepare_and_initiate_pool(
9599                    MIKE,
9600                    STAKING,
9601                    &entries,
9602                    INDEX_BALANCED_STAKING,
9603                    POOL_MANAGED_STAKING,
9604                    COMMISSION_LOW,
9605                )
9606                .unwrap();
9607                Pallet::set_slot_of_variant(
9608                    &ALICE,
9609                    &STAKING,
9610                    &POOL_MANAGED_STAKING,
9611                    &VALIDATOR_GAMMA,
9612                    Position::position_of(1).unwrap(),
9613                    Some(SHARE_EQUAL),
9614                )
9615                .unwrap();
9616
9617                assert_eq!(
9618                    Pallet::get_slot_variant(&STAKING, &POOL_MANAGED_STAKING, &VALIDATOR_GAMMA,)
9619                        .unwrap(),
9620                    Position::position_of(1).unwrap(),
9621                );
9622                // Without shares returns error to set a new-variant for a new-digest
9623                assert_err!(
9624                    Pallet::set_slot_of_variant(
9625                        &ALICE,
9626                        &STAKING,
9627                        &POOL_MANAGED_STAKING,
9628                        &VALIDATOR_DELTA,
9629                        Position::position_of(1).unwrap(),
9630                        None,
9631                    ),
9632                    Error::SlotOfPoolNotFound
9633                );
9634                assert_err!(
9635                    Pallet::get_slot_variant(&STAKING, &POOL_MANAGED_STAKING, &VALIDATOR_DELTA,),
9636                    Error::SlotOfPoolNotFound
9637                );
9638                // Since its semantically no-op if its zero shares hence its silently passes
9639                assert_ok!(Pallet::set_slot_of_variant(
9640                    &ALICE,
9641                    &STAKING,
9642                    &POOL_MANAGED_STAKING,
9643                    &VALIDATOR_DELTA,
9644                    Position::position_of(1).unwrap(),
9645                    Some(0),
9646                ));
9647            })
9648        })
9649    }
9650
9651    #[test]
9652    fn on_set_slot_of_variant() {
9653        commit_test_ext().execute_with(|| {
9654            set_default_user_balance_and_standard_hold(ALICE).unwrap();
9655            Pallet::place_commit(
9656                &ALICE,
9657                &STAKING,
9658                &VALIDATOR_ALPHA,
9659                STANDARD_COMMIT,
9660                &Directive::new(Precision::Exact, Fortitude::Force),
9661            )
9662            .unwrap();
9663            let entries = vec![(VALIDATOR_ALPHA, SHARE_EQUAL)];
9664            prepare_and_initiate_pool(
9665                ALICE,
9666                STAKING,
9667                &entries,
9668                INDEX_BALANCED_STAKING,
9669                POOL_MANAGED_STAKING,
9670                COMMISSION_LOW,
9671            )
9672            .unwrap();
9673            System::set_block_number(2);
9674            let new_shares = SHARE_DOMINANT;
9675            let new_variant = Position::position_of(1).unwrap();
9676            Pallet::on_set_slot_of_variant(
9677                &POOL_MANAGED_STAKING,
9678                &STAKING,
9679                &VALIDATOR_ALPHA,
9680                Some(new_shares),
9681                &new_variant,
9682            );
9683            System::assert_last_event(
9684                Event::PoolSlot {
9685                    pool_of: POOL_MANAGED_STAKING,
9686                    reason: STAKING,
9687                    slot_of: VALIDATOR_ALPHA,
9688                    variant: new_variant,
9689                    shares: new_shares,
9690                }
9691                .into(),
9692            );
9693        })
9694    }
9695
9696    #[test]
9697    fn on_set_slot_of_variant_shares_is_zero() {
9698        commit_test_ext().execute_with(|| {
9699            set_default_user_balance_and_standard_hold(ALICE).unwrap();
9700            Pallet::place_commit(
9701                &ALICE,
9702                &STAKING,
9703                &VALIDATOR_ALPHA,
9704                STANDARD_COMMIT,
9705                &Directive::new(Precision::Exact, Fortitude::Force),
9706            )
9707            .unwrap();
9708            let entries = vec![(VALIDATOR_ALPHA, SHARE_EQUAL)];
9709            prepare_and_initiate_pool(
9710                ALICE,
9711                STAKING,
9712                &entries,
9713                INDEX_BALANCED_STAKING,
9714                POOL_MANAGED_STAKING,
9715                COMMISSION_LOW,
9716            )
9717            .unwrap();
9718            System::set_block_number(2);
9719            let new_shares = 0;
9720            let new_variant = Position::position_of(1).unwrap();
9721            Pallet::on_set_slot_of_variant(
9722                &POOL_MANAGED_STAKING,
9723                &STAKING,
9724                &VALIDATOR_ALPHA,
9725                Some(new_shares),
9726                &new_variant,
9727            );
9728            System::assert_last_event(
9729                Event::PoolSlotRemoved {
9730                    pool_of: POOL_MANAGED_STAKING,
9731                    reason: STAKING,
9732                    slot_of: VALIDATOR_ALPHA,
9733                    variant: new_variant,
9734                }
9735                .into(),
9736            );
9737        })
9738    }
9739}