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