frame_suite/
assets.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// ````````````````````````````````` ASSETS SUITE ````````````````````````````````
14// ===============================================================================
15
16//! A collection of composable, type-driven balance models built on
17//! [`virtual`](crate::virtuals) abstractions and
18//! [`plugin`](crate::plugins)-based execution.
19//!
20//! ## Motivation
21//!
22//! Traditional balance systems tightly couple:
23//! - storage layout
24//! - accounting logic
25//! - and update propagation
26//!
27//! This leads to:
28//! - costly updates across all dependent states
29//! - rigid data structures that are hard to evolve
30//! - difficulty composing new behaviors or extending models
31//!
32//! This module addresses these limitations by:
33//!
34//! - **decoupling structure, storage, and behavior**
35//! - **deferring computation until it is actually needed**
36//! - **allowing logic to be injected via plugins**
37//!
38//! ## Lazy Balance Model
39//!
40//! The primary model is [`LazyBalance`], a **receipt-based,
41//! lazily evaluated accounting system**.
42//!
43//! ```text
44//! deposit -> issue receipt
45//! mutate balance -> affect global state only
46//! withdraw -> resolve receipt value lazily
47//! ```
48//!
49//! In this model:
50//!
51//! - deposits create **receipts (claims)**
52//! - balance mutations affect **global state only**
53//! - receipt value is computed **at withdrawal time**
54//!
55//! This avoids eagerly updating all receipts while preserving correctness.
56//!
57//! ## Design
58//!
59//! ### Virtual Architecture
60//!
61//! All balance models are built on shared virtual primitives:
62//!
63//! - [`VirtualDynField`] -> defines structured components (asset, rational, time)
64//! - [`VirtualDynExtension`] -> enables extensibility via addons
65//! - [`VirtualDynBound`] -> provides external constraints (e.g. capacity)
66//! - [`VirtualNMap`] -> externalizes heavy or dynamic storage
67//!
68//! These abstractions allow:
69//!
70//! - structure to remain **representation-agnostic**
71//! - storage to be **externalized or optimized independently**
72//! - components to be **composed across contexts**
73//!
74//! ### Plugin-Driven Execution
75//!
76//! Behavior is defined through plugin families via:
77//!
78//! - [`declare_family`]
79//! - [`plugin_types`]
80//! - [`plugin_output`]
81//!
82//! This enables:
83//!
84//! - **compile-time dispatch of operations**
85//! - **modular and replaceable logic**
86//! - **context-driven customization**
87//!
88//! Each operation (deposit, withdraw, etc.) is selected via a discriminant
89//! and resolved through the plugin family.
90//!
91//! ## Summary
92//!
93//! This module provides a foundation for building diverse balance systems where:
94//!
95//! - accounting is **lazy and efficient**
96//! - structure is **flexible and composable**
97//! - behavior is **modular and replaceable**
98//!
99//! enabling multiple accounting strategies to coexist under a unified,
100//! type-driven architecture.
101
102// ===============================================================================
103// ``````````````````````````````````` IMPORTS ```````````````````````````````````
104// ===============================================================================
105
106// --- Local crate imports ---
107use crate::{
108    base::{Asset, Delimited, Fractional, RuntimeEnum, Time},
109    declare_family, discriminants, impl_discriminants,
110    misc::{Directive, Extent},
111    mutation::MutHandle,
112    plugin_output, plugin_types,
113    plugins::ModelContext,
114    virtuals::*,
115};
116
117// --- Substrate primitives ---
118use sp_runtime::Cow;
119
120// ===============================================================================
121// ``````````````````````````````````` ALIASES ```````````````````````````````````
122// ===============================================================================
123
124/// Resolved context type for a given [`LazyBalance`] implementation.
125///
126/// Extracts the concrete context provided by `BalanceContext`.
127pub type Context<T> = <<T as LazyBalance>::BalanceContext as ModelContext>::Context;
128
129/// Resolved error type for a given [`LazyBalance`] implementation.
130///
131/// Provided by the context via [`VirtualError`].
132pub type Error<T> = <Context<T> as VirtualError<LazyBalanceError>>::Error;
133
134// ===============================================================================
135// ````````````````````````````````` LAZY BALANCE ````````````````````````````````
136// ===============================================================================
137
138/// A composable, plugin-driven balance system with **lazy
139/// receipt-based accounting**.
140///
141/// `LazyBalance` defines a generic balance abstraction where value is managed
142/// through deposits, receipts, and deferred redemption semantics.
143///
144/// The system integrates with the plugin framework to express all behavior
145/// (validation, mutation, and queries) as pluggable operations resolved at
146/// compile time.
147///
148/// ## Core Semantics
149///
150/// The defining property of `LazyBalance` is its *lazy propagation model*:
151///
152/// - Balance updates (e.g. `mint`, `reap`) are applied **immediately** to the
153///   underlying balance state.
154/// - Issued receipts are **not eagerly updated** when such changes occur.
155/// - Instead, the effect of these updates is **deferred and applied at the time
156///   of receipt redemption (`withdraw`)**.
157///
158/// In other words:
159///
160/// ```text
161/// Deposits -> Issue Receipts
162/// Balance Mutations -> Affect Global State Only
163/// Withdrawals -> Resolve Final Value Lazily
164/// ```
165///
166/// This allows the system to:
167/// - avoid costly updates across all outstanding receipts
168/// - maintain correctness through deferred evaluation
169/// - support dynamic balance adjustments without recomputation overhead
170///
171/// ## Conceptual Model
172///
173/// The system is composed of three primary components:
174///
175/// - **Balance**
176///   - Canonical mutable state
177///   - Receives all direct updates (`deposit`, `mint`, `reap`, `drain`)
178///
179/// - **Receipt**
180///   - Represents a claim issued at deposit time
181///   - Encodes entitlement, not fixed value
182///   - Final value is determined lazily at withdrawal
183///
184/// - **SnapShot**
185///   - Time-indexed projection of balance state
186///   - Enables historical queries and deferred computations
187///
188/// This forms a *claim-based accounting model*:
189/// - deposits create claims
190/// - withdrawals resolve claims
191/// - intermediate balance changes influence claim outcomes
192///
193/// ## Storage Model
194///
195/// Snapshot data is externalized via [`VirtualNMap`] using
196/// `SnapShotStorage`, allowing:
197///
198/// - efficient storage of time-indexed data
199/// - separation of heavy state from encoded balance representation
200/// - iteration and prefix queries over historical state
201///
202/// ## Plugin-Driven Behavior
203///
204/// All operations are expressed through a plugin family [`declare_family`]:
205///
206/// Each operation:
207/// - is type-driven via [`Self::Input`] and [`Self::Output`]
208/// - is resolved through the [`Self::BalanceFamily`] plugin
209/// - may be customized by runtime configuration
210///
211/// ## Limits
212///
213/// Operations may expose dynamic limits (e.g. minimum, maximum, optimal)
214/// via dedicated limit queries. These limits are derived from current
215/// balance state and provide bounded inputs for safe execution.
216///
217/// ## Execution DispatchPolicy
218///
219/// Operations may be parameterized by a [`Self::Subject`], which encodes
220/// qualitative behavior such as precision and fortitude,
221/// influencing how operations are evaluated and executed.
222///
223/// ## Design Properties
224///
225/// - **Lazy evaluation**: receipt value is computed at redemption time
226/// - **Efficient**: avoids eager updates across all outstanding claims
227/// - **Composable**: behavior is modular via plugin families
228/// - **Decoupled**: storage, logic, and types evolve independently
229/// - **Type-safe**: all invariants enforced at compile time
230///
231pub trait LazyBalance:
232    VirtualNMap<
233    Self::Balance,
234    SnapShotStorage,
235    Key = (Self::Id, Self::Variant, Self::Time),
236    Value = Self::SnapShot,
237    Query = Option<Self::SnapShot>,
238>
239where
240    Self: Sized,
241{
242    /// The underlying unit of value tracked by the system.
243    ///
244    /// Represents tokens, points, or any fungible asset.
245    type Asset: Asset;
246
247    /// The numeric representation used for value calculations.
248    ///
249    /// Typically a fixed-point or fractional type ensuring precise arithmetic.
250    type Rational: Fractional;
251
252    /// The temporal index of the system.
253    ///
254    /// A monotonically increasing counter (e.g. block number or timestamp)
255    /// used for snapshotting and time-based computations.
256    type Time: Time;
257
258    /// Logical partition of a balance.
259    ///
260    /// Allows multiple independent balance states per `Id`
261    /// (e.g. free, locked, reserved).
262    type Variant: Delimited + RuntimeEnum + Default;
263
264    /// Identifier of a balance owner or entity.
265    ///
266    /// Combined with `Variant` and `Time` to uniquely address balance state.
267    type Id: Delimited;
268
269    /// Represents the directive / intent of an operation.
270    ///
271    /// Encodes behavioral characteristics such as precision and fortitude,
272    /// influencing how operations are evaluated and executed.
273    ///
274    /// Passed alongside operation inputs to allow context-aware behavior
275    /// (e.g. exact vs approximate, polite vs forceful execution).
276    type Subject: Delimited + Directive + Default;
277
278    /// Primary mutable balance state.
279    ///
280    /// Represents the canonical stored value and is modified by all
281    /// state-transition operations.
282    type Balance: LazyBalanceComponent<
283        Self,
284        BalanceAsset,
285        BalanceRational,
286        BalanceTime,
287        Context<Self>,
288        BalanceAddon,
289    >;
290
291    /// Time-indexed projection of balance state.
292    ///
293    /// Used for historical queries and deferred computations.
294    type SnapShot: LazyBalanceComponent<
295        Self,
296        SnapShotAsset,
297        SnapShotRational,
298        SnapShotTime,
299        Context<Self>,
300        SnapShotAddon,
301    >;
302
303    /// Claim over deposited value.
304    ///
305    /// Issued on deposit and consumed on withdrawal.
306    ///
307    /// A receipt represents the right to redeem value from the balance.
308    /// Its redeemable value may differ from its original deposit value
309    /// due to balance adjustments (e.g. minting or reaping).
310    type Receipt: LazyBalanceComponent<
311        Self,
312        ReceiptAsset,
313        ReceiptRational,
314        ReceiptTime,
315        Context<Self>,
316        ReceiptAddon,
317    >;
318
319    /// Abstract representation of operation limits.
320    ///
321    /// Encapsulates derived values across different extents
322    /// (e.g. minimum, maximum, optimal) for balance operations.
323    ///
324    /// Used by limit query operations to provide safe bounded values
325    /// under current conditions.
326    type Limits: LazyBalanceLimits<Self>;
327
328    /// Input carrier for all operations.
329    ///
330    /// Encodes operation-specific arguments while supporting both
331    /// mutable and immutable access patterns.
332    type Input<'a>: LazyBalanceInput<
333        'a,
334        Self::Balance,
335        Self::Variant,
336        Self::Id,
337        Self::Asset,
338        Self::Receipt,
339        Self,
340    >;
341
342    /// Output carrier for all operations.
343    ///
344    /// Encodes operation-specific results and error handling.
345    type Output<'a>: LazyBalanceOutput<
346        'a,
347        Self::Asset,
348        Self::Receipt,
349        Self::SnapShot,
350        Self::Time,
351        Self::Limits,
352        Self,
353    >;
354
355    // Plugin Family defining all balance behavior.
356    //
357    // The family groups all operations (validation, mutation, queries)
358    // under a single type-level interface.
359    //
360    // Resolution flow:
361    // - `root` defines the set of operation discriminants
362    // - `family` binds those discriminants to concrete implementations
363    // - `context` provides environment, constraints, and dependencies
364    //
365    // Together, they form a compile-time dispatch system where
366    // behavior is selected by discriminant and resolved through context.
367    plugin_types! {
368        input: Self::Input<'a>,
369        output: Self::Output<'a>,
370        borrow: ['a],
371        root: LazyBalanceRoot,
372        /// [`Plugin Family`](crate::plugins) implementing all [`LazyBalance`] operations.
373        ///
374        /// Binds each operation discriminant (defined in [`LazyBalanceRoot`])
375        /// to a concrete plugin model, determining how validation, mutation,
376        /// and query logic is executed.
377        ///
378        /// Combined with the resolved context, this enables compile-time
379        /// dispatch of behavior while keeping logic modular and replaceable.
380        family: BalanceFamily,
381
382        /// Context provider for all [`LazyBalance`] operations via [`Self::BalanceFamily`].
383        ///
384        /// Implements [`ModelContext`], producing a concrete execution
385        /// context whose associated `Context` type must satisfy
386        /// [`LazyBalanceContext`].
387        ///
388        /// Since [`LazyBalance`] components (e.g. Balance, SnapShot, Receipt)
389        /// are modeled as *[`virtual`](crate::virtuals) structures*
390        /// (see [`VirtualDynField`]) and support extensibility
391        /// (see [`VirtualDynExtension`]), they do not
392        /// define their bounds, schemas, or error types internally.
393        ///
394        /// Instead, these are supplied by the resolved context, which is
395        /// required (via [`LazyBalanceContext`]) to provide:
396        ///
397        /// - dynamic bounds for all core dimensions through [`VirtualDynBound`]
398        ///   (asset, rational, and time for balance, snapshot, and receipt)
399        /// - extension schemas via [`VirtualDynExtensionSchema`] for addon
400        ///   composition across components
401        /// - a unified error type via [`VirtualError`] for all operations
402        ///
403        /// This enforces that all [`plugins`](crate::plugins) in this
404        /// family execute within a fully specified environment where structure,
405        /// constraints, and extensibility are externally defined yet statically guaranteed.
406        context: BalanceContext,
407        provides: [LazyBalanceContext],
408    }
409
410    // ---------- Capability checks ------------
411
412    plugin_output! {
413        /// Returns whether a deposit is permitted.
414        fn can_deposit,
415        input: Self::Input<'a>,
416        output: Self::Output<'a>,
417        borrow: ['a],
418        root: LazyBalanceRoot,
419        family: Self::BalanceFamily<'a>,
420        child: CanDeposit,
421        context: Self::BalanceContext
422    }
423
424    plugin_output! {
425        /// Returns whether a withdrawal is permitted.
426        fn can_withdraw,
427        input: Self::Input<'a>,
428        output: Self::Output<'a>,
429        borrow: ['a],
430        root: LazyBalanceRoot,
431        family: Self::BalanceFamily<'a>,
432        child: CanWithdraw,
433        context: Self::BalanceContext
434    }
435
436    plugin_output! {
437        /// Returns whether minting is permitted.
438        fn can_mint,
439        input: Self::Input<'a>,
440        output: Self::Output<'a>,
441        borrow: ['a],
442        root: LazyBalanceRoot,
443        family: Self::BalanceFamily<'a>,
444        child: CanMint,
445        context: Self::BalanceContext
446    }
447
448    plugin_output! {
449        /// Returns whether reaping is permitted.
450        fn can_reap,
451        input: Self::Input<'a>,
452        output: Self::Output<'a>,
453        borrow: ['a],
454        root: LazyBalanceRoot,
455        family: Self::BalanceFamily<'a>,
456        child: CanReap,
457        context: Self::BalanceContext
458    }
459
460    // ---------- State transitions -------------
461
462    plugin_output! {
463        /// Deposits value into the balance and issues a receipt
464        /// along with depositted amount.
465        fn deposit,
466        input: Self::Input<'a>,
467        output: Self::Output<'a>,
468        borrow: ['a],
469        root: LazyBalanceRoot,
470        family: Self::BalanceFamily<'a>,
471        child: Deposit,
472        context: Self::BalanceContext
473    }
474
475    plugin_output! {
476        /// Withdraws value by consuming a receipt, returning withdrawed amount.
477        fn withdraw,
478        input: Self::Input<'a>,
479        output: Self::Output<'a>,
480        borrow: ['a],
481        root: LazyBalanceRoot,
482        family: Self::BalanceFamily<'a>,
483        child: Withdraw,
484        context: Self::BalanceContext
485    }
486
487    plugin_output! {
488        /// Introduces new value into the balance, returning minted amount.
489        fn mint,
490        input: Self::Input<'a>,
491        output: Self::Output<'a>,
492        borrow: ['a],
493        root: LazyBalanceRoot,
494        family: Self::BalanceFamily<'a>,
495        child: Mint,
496        context: Self::BalanceContext
497    }
498
499    plugin_output! {
500        /// Removes or adjusts value from the balance, returning reaped amount.
501        fn reap,
502        input: Self::Input<'a>,
503        output: Self::Output<'a>,
504        borrow: ['a],
505        root: LazyBalanceRoot,
506        family: Self::BalanceFamily<'a>,
507        child: Reap,
508        context: Self::BalanceContext
509    }
510
511    plugin_output! {
512        /// Clears the balance state, returning drained amount.
513        fn drain,
514        input: Self::Input<'a>,
515        output: Self::Output<'a>,
516        borrow: ['a],
517        root: LazyBalanceRoot,
518        family: Self::BalanceFamily<'a>,
519        child: Drain,
520        context: Self::BalanceContext
521    }
522
523    // ---------- Queries -------------
524
525    plugin_output! {
526        /// Returns the total value of the balance.
527        fn total_value,
528        input: Self::Input<'a>,
529        output: Self::Output<'a>,
530        borrow: ['a],
531        root: LazyBalanceRoot,
532        family: Self::BalanceFamily<'a>,
533        child: TotalValue,
534        context: Self::BalanceContext
535    }
536
537    plugin_output! {
538        /// Returns the current redeemable value of a receipt.
539        fn receipt_active_value,
540        input: Self::Input<'a>,
541        output: Self::Output<'a>,
542        borrow: ['a],
543        root: LazyBalanceRoot,
544        family: Self::BalanceFamily<'a>,
545        child: ReceiptActiveValue,
546        context: Self::BalanceContext
547    }
548
549    plugin_output! {
550        /// Returns whether the balance has any deposits.
551        fn has_deposits,
552        input: Self::Input<'a>,
553        output: Self::Output<'a>,
554        borrow: ['a],
555        root: LazyBalanceRoot,
556        family: Self::BalanceFamily<'a>,
557        child: HasDeposits,
558        context: Self::BalanceContext
559    }
560
561    plugin_output! {
562        /// Returns the original deposited value represented by a receipt.
563        fn receipt_deposit_value,
564        input: Self::Input<'a>,
565        output: Self::Output<'a>,
566        borrow: ['a],
567        root: LazyBalanceRoot,
568        family: Self::BalanceFamily<'a>,
569        child: ReceiptDepositValue,
570        context: Self::BalanceContext
571    }
572
573    plugin_output! {
574        /// Returns limits for deposit operations if any, else permissive.
575        fn deposit_limits,
576        input: Self::Input<'a>,
577        output: Self::Output<'a>,
578        borrow: ['a],
579        root: LazyBalanceRoot,
580        family: Self::BalanceFamily<'a>,
581        child: DepositLimits,
582        context: Self::BalanceContext
583    }
584
585    plugin_output! {
586        /// Returns limits for mint operations if any, else permissive.
587        fn mint_limits,
588        input: Self::Input<'a>,
589        output: Self::Output<'a>,
590        borrow: ['a],
591        root: LazyBalanceRoot,
592        family: Self::BalanceFamily<'a>,
593        child: MintLimits,
594        context: Self::BalanceContext
595    }
596
597    plugin_output! {
598        /// Returns limits for reap operations if any, else permissive.
599        fn reap_limits,
600        input: Self::Input<'a>,
601        output: Self::Output<'a>,
602        borrow: ['a],
603        root: LazyBalanceRoot,
604        family: Self::BalanceFamily<'a>,
605        child: ReapLimits,
606        context: Self::BalanceContext
607    }
608}
609
610// ===============================================================================
611// ```````````````````````````````` PLUGIN FAMILY ````````````````````````````````
612// ===============================================================================
613
614declare_family!(
615    /// [`Plugin Family`](crate::plugins) Root trait for [`LazyBalance`]
616    ///
617    /// Each child (associated-type) acts as a **discriminant** selecting
618    /// a specific operation within the lazy balance model.
619    root: pub LazyBalanceRoot,
620    child: [
621        // ----- Capability checks -----
622
623        /// Discriminant for deposit validation.
624        ///
625        /// Determines whether a deposit operation is permitted.
626        CanDeposit,
627
628        /// Discriminant for withdrawal validation.
629        ///
630        /// Determines whether a receipt can be redeemed.
631        CanWithdraw,
632
633        /// Discriminant for reap validation.
634        ///
635        /// Determines whether value can be removed or adjusted.
636        CanReap,
637
638        /// Discriminant for mint validation.
639        ///
640        /// Determines whether new value can be introduced.
641        CanMint,
642
643
644        // ----- Mutations -----
645
646        /// Discriminant for deposit execution.
647        ///
648        /// Deposits value and issues a receipt.
649        Deposit,
650
651        /// Discriminant for withdrawal execution.
652        ///
653        /// Redeems a receipt, resolving value lazily at withdrawal time.
654        Withdraw,
655
656        /// Discriminant for reap execution.
657        ///
658        /// Removes or adjusts balance value without mutating existing receipts.
659        Reap,
660
661        /// Discriminant for drain execution.
662        ///
663        /// Clears or resets the entire balance state.
664        Drain,
665
666        /// Discriminant for mint execution.
667        ///
668        /// Introduces new value affecting future receipt redemption.
669        Mint,
670
671
672        // ----- Queries -----
673
674        /// Discriminant for total balance query.
675        ///
676        /// Returns the total value held in the balance.
677        TotalValue,
678
679        /// Discriminant for active receipt value query.
680        ///
681        /// Returns the current redeemable value, reflecting lazy adjustments.
682        ReceiptActiveValue,
683
684        /// Discriminant for receipt deposit value query.
685        ///
686        /// Returns the original deposited value, ignoring later adjustments.
687        ReceiptDepositValue,
688
689        /// Discriminant for deposit presence query.
690        ///
691        /// Indicates whether any deposits exist in the balance.
692        HasDeposits,
693
694        // ----- Limits -----
695
696        /// Discriminant for deposit limits query.
697        ///
698        /// Provides limits such as the minimum and maximum deposit
699        /// allowed under given conditions.
700        DepositLimits,
701
702        /// Discriminant for mint limits query.
703        ///
704        /// Provides limits such as the minimum and maximum mint
705        /// allowed under given conditions.
706        MintLimits,
707
708        /// Discriminant for reap limits query.
709        ///
710        /// Provides limits such as the minimum and maximum reap
711        /// allowed under given conditions.
712        ReapLimits,
713    ]
714);
715
716// ===============================================================================
717// ````````````````````` PLUGIN FAMILY's CHILD DISCRIMINANTS `````````````````````
718// ===============================================================================
719
720impl_discriminants! {
721    CanDeposit,
722    CanWithdraw,
723    CanReap,
724    CanMint,
725    Deposit,
726    Withdraw,
727    Reap,
728    Drain,
729    Mint,
730    TotalValue,
731    ReceiptActiveValue,
732    ReceiptDepositValue,
733    HasDeposits,
734    DepositLimits,
735    ReapLimits,
736    MintLimits,
737}
738
739// ===============================================================================
740// ```````````````````````````````` TRAIT ALIASES ````````````````````````````````
741// ===============================================================================
742
743/// Execution context for the [`LazyBalance`] system.
744///
745/// `LazyBalanceContext` aggregates all external dependencies required to
746/// materialize a concrete lazy balance model. It serves as the **type-level
747/// environment** that binds:
748///
749/// - **Bounds** for all core components:
750///   - `Balance` (asset, rational, time)
751///   - `SnapShot` (asset, rational, time)
752///   - `Receipt` (asset, rational, time)
753///
754/// - **Error type** via [`VirtualError`], defining the unified failure mode
755///   for all balance operations.
756///
757/// - **Extension schemas** for:
758///   - [`BalanceAddon`]
759///   - [`SnapShotAddon`]
760///   - [`ReceiptAddon`]
761///
762/// ## Role in the System
763///
764/// This trait does not define behavior directly. Instead, it provides the
765/// **configuration layer** required by:
766///
767/// - [`VirtualDynField`] -> for allocation and capacity constraints
768/// - [`VirtualDynExtension`] -> for external extensibility
769/// - [`Plugin`](crate::plugins) execution -> for resolving context-dependent logic
770///
771/// In essence, it acts as a **dependency injection boundary** at the type level,
772/// allowing the same [`LazyBalance`] implementation to operate under different:
773///
774/// - capacity limits
775/// - extension schemas
776/// - error definitions
777///
778/// without changing its core logic.
779pub trait LazyBalanceContext:
780    VirtualDynBound<BalanceAsset>
781    + VirtualDynBound<BalanceRational>
782    + VirtualDynBound<BalanceTime>
783    + VirtualDynBound<SnapShotAsset>
784    + VirtualDynBound<SnapShotRational>
785    + VirtualDynBound<SnapShotTime>
786    + VirtualDynBound<ReceiptAsset>
787    + VirtualDynBound<ReceiptRational>
788    + VirtualDynBound<ReceiptTime>
789    + VirtualError<LazyBalanceError>
790    + VirtualDynExtensionSchema<BalanceAddon>
791    + VirtualDynExtensionSchema<SnapShotAddon>
792    + VirtualDynExtensionSchema<ReceiptAddon>
793{
794}
795
796impl<T> LazyBalanceContext for T where
797    T: VirtualDynBound<BalanceAsset>
798        + VirtualDynBound<BalanceRational>
799        + VirtualDynBound<BalanceTime>
800        + VirtualDynBound<SnapShotAsset>
801        + VirtualDynBound<SnapShotRational>
802        + VirtualDynBound<SnapShotTime>
803        + VirtualDynBound<ReceiptAsset>
804        + VirtualDynBound<ReceiptRational>
805        + VirtualDynBound<ReceiptTime>
806        + VirtualError<LazyBalanceError>
807        + VirtualDynExtensionSchema<BalanceAddon>
808        + VirtualDynExtensionSchema<SnapShotAddon>
809        + VirtualDynExtensionSchema<ReceiptAddon>
810{
811}
812
813/// Input contract for all [`LazyBalance`] operations.
814///
815/// This trait defines how operation-specific arguments are supplied
816/// through a unified input type.
817///
818/// Each operation (e.g. `Deposit`, `Withdraw`) declares the exact
819/// shape of input it requires via [`VirtualCollector`].
820///
821/// Key semantics:
822/// - Mutating operations receive `MutHandle<Balance>`
823/// - Read-only operations receive `Cow<Balance>`
824/// - Additional parameters (e.g. `Asset`, `Receipt`) are selectively required
825/// - Operations may include a `Subject` to influence execution behavior
826///   (e.g. precision and fortitude)
827///
828/// This enables a single input type to serve all operations while
829/// preserving strict typing per operation.
830pub trait LazyBalanceInput<'a, Balance, Variant, Id, Asset, Receipt, T: LazyBalance>:
831    VirtualCollector<
832        (
833            MutHandle<'a, Balance>,
834            Cow<'a, Variant>,
835            Cow<'a, Id>,
836            Cow<'a, Asset>,
837            Cow<'a, T::Subject>,
838        ),
839        Deposit,
840    > + VirtualCollector<
841        (
842            MutHandle<'a, Balance>,
843            Cow<'a, Variant>,
844            Cow<'a, Id>,
845            Cow<'a, Asset>,
846            Cow<'a, T::Subject>,
847        ),
848        Mint,
849    > + VirtualCollector<
850        (
851            MutHandle<'a, Balance>,
852            Cow<'a, Variant>,
853            Cow<'a, Id>,
854            Cow<'a, Asset>,
855            Cow<'a, T::Subject>,
856        ),
857        Reap,
858    > + VirtualCollector<
859        (
860            MutHandle<'a, Balance>,
861            Cow<'a, Variant>,
862            Cow<'a, Id>,
863            Cow<'a, Receipt>,
864        ),
865        Withdraw,
866    > + VirtualCollector<(MutHandle<'a, Balance>, Cow<'a, Variant>, Cow<'a, Id>), Drain>
867    + VirtualCollector<
868        (
869            Cow<'a, Balance>,
870            Cow<'a, Variant>,
871            Cow<'a, Id>,
872            Cow<'a, Asset>,
873            Cow<'a, T::Subject>,
874        ),
875        CanDeposit,
876    > + VirtualCollector<
877        (
878            Cow<'a, Balance>,
879            Cow<'a, Variant>,
880            Cow<'a, Id>,
881            Cow<'a, Asset>,
882            Cow<'a, T::Subject>,
883        ),
884        CanMint,
885    > + VirtualCollector<
886        (
887            Cow<'a, Balance>,
888            Cow<'a, Variant>,
889            Cow<'a, Id>,
890            Cow<'a, Asset>,
891            Cow<'a, T::Subject>,
892        ),
893        CanReap,
894    > + VirtualCollector<
895        (
896            Cow<'a, Balance>,
897            Cow<'a, Variant>,
898            Cow<'a, Id>,
899            Cow<'a, Receipt>,
900        ),
901        CanWithdraw,
902    > + VirtualCollector<(Cow<'a, Balance>, Cow<'a, Variant>, Cow<'a, Id>), TotalValue>
903    + VirtualCollector<
904        (
905            Cow<'a, Balance>,
906            Cow<'a, Variant>,
907            Cow<'a, Id>,
908            Cow<'a, Receipt>,
909        ),
910        ReceiptActiveValue,
911    > + VirtualCollector<(Cow<'a, Balance>, Cow<'a, Variant>, Cow<'a, Id>), HasDeposits>
912    + VirtualCollector<Cow<'a, Receipt>, ReceiptDepositValue>
913    + VirtualCollector<
914        (
915            Cow<'a, Balance>,
916            Cow<'a, Variant>,
917            Cow<'a, Id>,
918            Cow<'a, T::Subject>,
919        ),
920        DepositLimits,
921    > + VirtualCollector<
922        (
923            Cow<'a, Balance>,
924            Cow<'a, Variant>,
925            Cow<'a, Id>,
926            Cow<'a, T::Subject>,
927        ),
928        MintLimits,
929    > + VirtualCollector<
930        (
931            Cow<'a, Balance>,
932            Cow<'a, Variant>,
933            Cow<'a, Id>,
934            Cow<'a, T::Subject>,
935        ),
936        ReapLimits,
937    >
938where
939    Balance: 'a + Clone,
940    Variant: 'a + Clone,
941    Id: 'a + Clone,
942    Asset: 'a + Clone,
943    Receipt: 'a + Clone,
944{
945}
946
947impl<'a, T, Balance, Variant, Id, Asset, Receipt, B>
948    LazyBalanceInput<'a, Balance, Variant, Id, Asset, Receipt, B> for T
949where
950    Balance: 'a + Clone,
951    Variant: 'a + Clone,
952    Id: 'a + Clone,
953    Asset: 'a + Clone,
954    Receipt: 'a + Clone,
955    B: LazyBalance,
956    T: VirtualCollector<
957            (
958                MutHandle<'a, Balance>,
959                Cow<'a, Variant>,
960                Cow<'a, Id>,
961                Cow<'a, Asset>,
962                Cow<'a, B::Subject>,
963            ),
964            Deposit,
965        > + VirtualCollector<
966            (
967                MutHandle<'a, Balance>,
968                Cow<'a, Variant>,
969                Cow<'a, Id>,
970                Cow<'a, Asset>,
971                Cow<'a, B::Subject>,
972            ),
973            Mint,
974        > + VirtualCollector<
975            (
976                MutHandle<'a, Balance>,
977                Cow<'a, Variant>,
978                Cow<'a, Id>,
979                Cow<'a, Asset>,
980                Cow<'a, B::Subject>,
981            ),
982            Reap,
983        > + VirtualCollector<
984            (
985                MutHandle<'a, Balance>,
986                Cow<'a, Variant>,
987                Cow<'a, Id>,
988                Cow<'a, Receipt>,
989            ),
990            Withdraw,
991        > + VirtualCollector<(MutHandle<'a, Balance>, Cow<'a, Variant>, Cow<'a, Id>), Drain>
992        + VirtualCollector<
993            (
994                Cow<'a, Balance>,
995                Cow<'a, Variant>,
996                Cow<'a, Id>,
997                Cow<'a, Asset>,
998                Cow<'a, B::Subject>,
999            ),
1000            CanDeposit,
1001        > + VirtualCollector<
1002            (
1003                Cow<'a, Balance>,
1004                Cow<'a, Variant>,
1005                Cow<'a, Id>,
1006                Cow<'a, Asset>,
1007                Cow<'a, B::Subject>,
1008            ),
1009            CanMint,
1010        > + VirtualCollector<
1011            (
1012                Cow<'a, Balance>,
1013                Cow<'a, Variant>,
1014                Cow<'a, Id>,
1015                Cow<'a, Asset>,
1016                Cow<'a, B::Subject>,
1017            ),
1018            CanReap,
1019        > + VirtualCollector<
1020            (
1021                Cow<'a, Balance>,
1022                Cow<'a, Variant>,
1023                Cow<'a, Id>,
1024                Cow<'a, Receipt>,
1025            ),
1026            CanWithdraw,
1027        > + VirtualCollector<(Cow<'a, Balance>, Cow<'a, Variant>, Cow<'a, Id>), TotalValue>
1028        + VirtualCollector<
1029            (
1030                Cow<'a, Balance>,
1031                Cow<'a, Variant>,
1032                Cow<'a, Id>,
1033                Cow<'a, Receipt>,
1034            ),
1035            ReceiptActiveValue,
1036        > + VirtualCollector<(Cow<'a, Balance>, Cow<'a, Variant>, Cow<'a, Id>), HasDeposits>
1037        + VirtualCollector<Cow<'a, Receipt>, ReceiptDepositValue>
1038        + VirtualCollector<
1039            (
1040                Cow<'a, Balance>,
1041                Cow<'a, Variant>,
1042                Cow<'a, Id>,
1043                Cow<'a, B::Subject>,
1044            ),
1045            DepositLimits,
1046        > + VirtualCollector<
1047            (
1048                Cow<'a, Balance>,
1049                Cow<'a, Variant>,
1050                Cow<'a, Id>,
1051                Cow<'a, B::Subject>,
1052            ),
1053            MintLimits,
1054        > + VirtualCollector<
1055            (
1056                Cow<'a, Balance>,
1057                Cow<'a, Variant>,
1058                Cow<'a, Id>,
1059                Cow<'a, B::Subject>,
1060            ),
1061            ReapLimits,
1062        >,
1063{
1064}
1065
1066/// Output contract for all [`LazyBalance`] operations.
1067///
1068/// This trait defines how results are returned for each operation.
1069///
1070/// Each operation maps to a distinct result type via [`VirtualCollector`],
1071/// typically wrapped in `Result<_, Error<T>>`.
1072///
1073/// Key semantics:
1074/// - State-changing operations return success or error
1075/// - Value-producing operations return assets or receipts
1076/// - Validation operations return `Result<(), Error<T>>`
1077///
1078/// This forms a unified, type-indexed result space across all operations.
1079pub trait LazyBalanceOutput<'a, Asset, Receipt, SnapShot, Time, Limits, T>:
1080    VirtualCollector<Result<(Cow<'a, Asset>, Cow<'a, Receipt>), Error<T>>, Deposit>
1081    + VirtualCollector<Result<Cow<'a, Asset>, Error<T>>, Mint>
1082    + VirtualCollector<Result<Cow<'a, Asset>, Error<T>>, Reap>
1083    + VirtualCollector<Result<Cow<'a, Asset>, Error<T>>, Drain>
1084    + VirtualCollector<Result<Cow<'a, Asset>, Error<T>>, Withdraw>
1085    + VirtualCollector<Result<(), Error<T>>, CanDeposit>
1086    + VirtualCollector<Result<(), Error<T>>, CanMint>
1087    + VirtualCollector<Result<(), Error<T>>, CanReap>
1088    + VirtualCollector<Result<(), Error<T>>, CanWithdraw>
1089    + VirtualCollector<Result<(), Error<T>>, HasDeposits>
1090    + VirtualCollector<Result<Cow<'a, Asset>, Error<T>>, TotalValue>
1091    + VirtualCollector<Result<Cow<'a, Asset>, Error<T>>, ReceiptActiveValue>
1092    + VirtualCollector<Result<Cow<'a, Asset>, Error<T>>, ReceiptDepositValue>
1093    + VirtualCollector<Result<Cow<'a, Asset>, Error<T>>, ReceiptDepositValue>
1094    + VirtualCollector<Result<Cow<'a, Limits>, Error<T>>, DepositLimits>
1095    + VirtualCollector<Result<Cow<'a, Limits>, Error<T>>, MintLimits>
1096    + VirtualCollector<Result<Cow<'a, Limits>, Error<T>>, ReapLimits>
1097where
1098    T: LazyBalance,
1099    Asset: 'a + Clone,
1100    Receipt: 'a + Clone,
1101    SnapShot: 'a + Clone,
1102    Time: 'a + Clone,
1103    Limits: 'a + Clone,
1104{
1105}
1106
1107impl<'a, T, Asset, Receipt, SnapShot, Time, Limits, B>
1108    LazyBalanceOutput<'a, Asset, Receipt, SnapShot, Time, Limits, B> for T
1109where
1110    B: LazyBalance,
1111    Asset: 'a + Clone,
1112    Receipt: 'a + Clone,
1113    SnapShot: 'a + Clone,
1114    Time: 'a + Clone,
1115    Limits: 'a + Clone,
1116    T: VirtualCollector<Result<(Cow<'a, Asset>, Cow<'a, Receipt>), Error<B>>, Deposit>
1117        + VirtualCollector<Result<Cow<'a, Asset>, Error<B>>, Mint>
1118        + VirtualCollector<Result<Cow<'a, Asset>, Error<B>>, Reap>
1119        + VirtualCollector<Result<Cow<'a, Asset>, Error<B>>, Drain>
1120        + VirtualCollector<Result<Cow<'a, Asset>, Error<B>>, Withdraw>
1121        + VirtualCollector<Result<(), Error<B>>, CanDeposit>
1122        + VirtualCollector<Result<(), Error<B>>, CanMint>
1123        + VirtualCollector<Result<(), Error<B>>, CanReap>
1124        + VirtualCollector<Result<(), Error<B>>, CanWithdraw>
1125        + VirtualCollector<Result<(), Error<B>>, HasDeposits>
1126        + VirtualCollector<Result<Cow<'a, Asset>, Error<B>>, TotalValue>
1127        + VirtualCollector<Result<Cow<'a, Asset>, Error<B>>, ReceiptActiveValue>
1128        + VirtualCollector<Result<Cow<'a, Asset>, Error<B>>, ReceiptDepositValue>
1129        + VirtualCollector<Result<Cow<'a, Limits>, Error<B>>, DepositLimits>
1130        + VirtualCollector<Result<Cow<'a, Limits>, Error<B>>, MintLimits>
1131        + VirtualCollector<Result<Cow<'a, Limits>, Error<B>>, ReapLimits>,
1132{
1133}
1134
1135/// A abstract-container representation of a structured value participating in the
1136/// lazy balance system (e.g. Balance, SnapShot, Receipt).
1137///
1138/// Look on to [`VirtualDynField`] or [`virtuals`](crate::virtuals) for contextual info.
1139///
1140/// It does not fix its internal representation directly. Instead, it:
1141/// - derives its core types (asset, rational, time) from context
1142/// - supports allocation via [`VirtualDynField`]
1143/// - allows extensibility through [`VirtualDynExtension`]
1144///
1145/// This keeps components lightweight, composable, and context-driven,
1146/// rather than self-contained or rigid.
1147pub trait LazyBalanceComponent<T, Asset, Rational, Time, Context, Addon>:
1148    Delimited
1149    + VirtualDynFieldWithDelegatedBounds<T::Asset, Context, Asset>
1150    + VirtualDynFieldWithDelegatedBounds<T::Rational, Context, Rational>
1151    + VirtualDynFieldWithDelegatedBounds<T::Time, Context, Time>
1152    + DelegateVirtualDynExtension<Context, Addon>
1153where
1154    T: LazyBalance,
1155    Context: VirtualDynExtensionSchema<Addon>
1156        + VirtualDynBound<Asset>
1157        + VirtualDynBound<Rational>
1158        + VirtualDynBound<Time>,
1159    Addon: DiscriminantTag,
1160    Rational: DiscriminantTag,
1161    Time: DiscriminantTag,
1162    Asset: DiscriminantTag,
1163{
1164}
1165
1166impl<B, T, Asset, Rational, Time, Context, Addon>
1167    LazyBalanceComponent<B, Asset, Rational, Time, Context, Addon> for T
1168where
1169    T: Delimited
1170        + VirtualDynFieldWithDelegatedBounds<B::Asset, Context, Asset>
1171        + VirtualDynFieldWithDelegatedBounds<B::Rational, Context, Rational>
1172        + VirtualDynFieldWithDelegatedBounds<B::Time, Context, Time>
1173        + DelegateVirtualDynExtension<Context, Addon>,
1174    B: LazyBalance,
1175    Context: VirtualDynExtensionSchema<Addon>
1176        + VirtualDynBound<Asset>
1177        + VirtualDynBound<Rational>
1178        + VirtualDynBound<Time>,
1179    Addon: DiscriminantTag,
1180    Rational: DiscriminantTag,
1181    Time: DiscriminantTag,
1182    Asset: DiscriminantTag,
1183{
1184}
1185
1186pub trait LazyBalanceLimits<T: LazyBalance>:
1187    Delimited
1188    + VirtualDynField<LimitsAsset, Some = T::Asset>
1189    + VirtualDynBound<LimitsAsset>
1190    + Extent<LimitsAsset, Scalar = T::Asset>
1191{
1192}
1193
1194impl<L, T> LazyBalanceLimits<L> for T
1195where
1196    T: Delimited
1197        + VirtualDynField<LimitsAsset, Some = L::Asset>
1198        + VirtualDynBound<LimitsAsset>
1199        + Extent<LimitsAsset, Scalar = L::Asset>,
1200    L: LazyBalance,
1201{
1202}
1203// ===============================================================================
1204// ```````````````````````````` VIRTUAL DISCRIMINANTS ````````````````````````````
1205// ===============================================================================
1206
1207discriminants! {
1208    /// Discriminant for the asset type within [`LazyBalance::Balance`].
1209    ///
1210    /// Used to resolve the concrete asset binding from context.
1211    BalanceAsset,
1212
1213    /// Discriminant for the numeric representation within [`LazyBalance::Balance`].
1214    ///
1215    /// Identifies how balance values are represented (e.g. fixed-point).
1216    BalanceRational,
1217
1218    /// Discriminant for the time dimension within [`LazyBalance::Balance`].
1219    ///
1220    /// Used to bind the temporal component associated with balance state.
1221    BalanceTime,
1222
1223    /// Discriminant for the asset type within [`LazyBalance::SnapShot`].
1224    ///
1225    /// Separates snapshot asset semantics from live balance state.
1226    SnapShotAsset,
1227
1228    /// Discriminant for the numeric representation within [`LazyBalance::SnapShot`].
1229    ///
1230    /// Defines how snapshot values are expressed.
1231    SnapShotRational,
1232
1233    /// Discriminant for the time dimension within [`LazyBalance::SnapShot`].
1234    ///
1235    /// Used to associate snapshots with specific points in time.
1236    SnapShotTime,
1237
1238    /// Discriminant for the asset type within [`LazyBalance::Receipt`].
1239    ///
1240    /// Identifies the asset representation for receipt claims.
1241    ReceiptAsset,
1242
1243    /// Discriminant for the numeric representation within [`LazyBalance::Receipt`].
1244    ///
1245    /// Defines how receipt values are quantified.
1246    ReceiptRational,
1247
1248    /// Discriminant for the time dimension within [`LazyBalance::Receipt`].
1249    ///
1250    /// Used to track temporal aspects of receipt validity or evolution.
1251    ReceiptTime,
1252
1253    /// Discriminant identifying the storage domain for snapshots within
1254    /// [`LazyBalance`] systems.
1255    ///
1256    /// Used to bind lazy storage behavior for snapshot data.
1257    SnapShotStorage,
1258
1259    /// Discriminant for error types within the [`LazyBalance`] system.
1260    ///
1261    /// Allows context to provide a concrete error type for all operations.
1262    LazyBalanceError,
1263
1264    /// Discriminant for additional balance-specific extensions.
1265    ///
1266    /// Used to inject custom logic or metadata into [`LazyBalance::Balance`].
1267    BalanceAddon,
1268
1269    /// Discriminant for additional snapshot-specific extensions.
1270    ///
1271    /// Used to extend snapshot behavior or attach auxiliary data.
1272    SnapShotAddon,
1273
1274    /// Discriminant for additional receipt-specific extensions.
1275    ///
1276    /// Used to extend receipt behavior or attach auxiliary data.
1277    ReceiptAddon,
1278
1279    /// Discriminant for the asset type used in operation limits.
1280    ///
1281    /// Binds the value representation used when deriving limits
1282    /// (e.g. minimum, maximum, optimal) for balance operations.
1283    LimitsAsset,
1284
1285}
1286
1287// ===============================================================================
1288// ````````````````````````` LAZY BALANCE MODEL CHECKER ``````````````````````````
1289// ===============================================================================
1290
1291#[cfg(feature = "std")]
1292pub use lazy_balance_model_checker::*;
1293
1294/// Model checker module for [`LazyBalance`] implementations.
1295///
1296/// Use [`LazyBalanceModelChecker`] as the **entrypoint** for all model
1297/// checking operations.
1298///
1299/// This module exposes the required types:
1300/// - [`ManualBalanceModel`]
1301/// - [`BalanceOp`]
1302/// - [`BalanceState`]
1303/// - [`BalanceGuards`]
1304/// - [`BalanceTraps`]
1305/// - [`BalanceModelResults`]
1306///
1307/// These types are used to define the testing environment:
1308/// - reference model behavior
1309/// - operation space
1310/// - state representation
1311/// - validity and trap conditions
1312///
1313/// The execution engine is internal; interact with it only through
1314/// the trait methods.
1315#[cfg(feature = "std")]
1316mod lazy_balance_model_checker {
1317
1318    // ===============================================================================
1319    // ``````````````````````````````````` IMPORTS ```````````````````````````````````
1320    // ===============================================================================
1321
1322    // --- Local crate imports ---
1323    use crate::{assets::*, base::Sortable};
1324
1325    // --- Core (Rust std replacement) ---
1326    use core::{fmt::Debug, hash::Hash};
1327
1328    // --- Substrate primitives ---
1329    use sp_runtime::{traits::One, Cow};
1330
1331    // --- Substrate std helpers ---
1332    use sp_std::collections::{btree_map::BTreeMap, btree_set::BTreeSet, vec_deque::VecDeque};
1333
1334    // --- Standard library ---
1335    use std::{
1336        fs::{create_dir_all, File},
1337        io::Write,
1338        path::PathBuf,
1339        time::{SystemTime, UNIX_EPOCH},
1340    };
1341
1342    // --- Randomness Provider ---
1343    use rand::{seq::SliceRandom, thread_rng};
1344
1345    // ===============================================================================
1346    // ```````````````````` LAZY BALANCE MODEL CHECKER ENTRYPOINT ````````````````````
1347    // ===============================================================================
1348
1349    /// Unified model checker interface over the lazy balance
1350    /// model checker module.
1351    ///
1352    /// This trait provides a **single entrypoint API** to access
1353    /// all model-checking utilities.
1354    ///
1355    /// By implementing this trait on a tester type, you bind:
1356    /// - the lazy model ([`Self::LazyBalance`]) under test
1357    /// - the manual reference model ([`Self::ManualBalance`])
1358    /// - guards, hashing, and execution semantics
1359    ///
1360    /// ## Underlying System
1361    ///
1362    /// - [`ManualBalanceModel`]: eager balance reference model
1363    /// - [`BalanceOp`]: balance operations set
1364    /// - [`BalanceState`]: execution state (lazy + manual + trace)
1365    /// - [`BalanceGuards`]: operation validity rules
1366    /// - [`BalanceTraps`]: controlled invalid execution
1367    /// - [`BalanceModelResults`]: result aggregation
1368    ///
1369    /// ## What happens during execution
1370    ///
1371    /// - sequences of [`BalanceOp`] are explored (breadth-first)
1372    /// - each step builds on a **trace** to form a [`BalanceState`]
1373    /// - operations are filtered using:
1374    ///   - [`BalanceGuards`] -> define normal validity
1375    ///   - [`BalanceTraps`] -> allow controlled invalid cases
1376    ///
1377    /// An operation is considered **valid for execution** if:
1378    ///   - it passes the `guard`, OR
1379    ///   - it is explicitly allowed by a `trap`
1380    ///
1381    /// Additionally, exploration is further controlled by `flow`:
1382    ///   - both `guard flow` and `trap flow` must allow the operation
1383    ///   - if either blocks it, the operation is not explored further
1384    ///
1385    /// - both models are executed:
1386    ///     - lazy: [`Self::LazyBalance`] via [`LazyBalance`]
1387    ///     - manual: [`Self::ManualBalance`] vi [`ManualBalanceModel`]
1388    ///
1389    /// - results are classified into:
1390    ///     - [`BalanceFailure`]: execution error
1391    ///     - [`BalanceExit`]: drift between models
1392    ///     - `pass`: successful sequence
1393    ///     - `trap`: expected failure via [`BalanceTraps`]
1394    ///
1395    /// ## Usage
1396    ///
1397    /// ```ignore
1398    /// struct Tester;
1399    ///
1400    /// impl LazyBalanceModelChecker for Tester {
1401    ///     type LazyBalance = Balance;
1402    ///     type ManualBalance = Manual;
1403    ///
1404    ///     type TrapFn = fn(
1405    ///         &BalanceState<Self::LazyBalance, Self::ManualBalance>,
1406    ///         &BalanceOp<Self::LazyBalance, Self::ManualBalance>,
1407    ///     ) -> bool;
1408    ///
1409    ///     type FlowFn = fn(
1410    ///         &BalanceState<Self::LazyBalance, Self::ManualBalance>,
1411    ///         &BalanceOp<Self::LazyBalance, Self::ManualBalance>,
1412    ///     ) -> bool;
1413    ///
1414    ///     type Hasher = fn(
1415    ///         &BalanceState<Self::LazyBalance, Self::ManualBalance>,
1416    ///     ) -> u64;
1417    /// }
1418    ///
1419    /// let mut results = Tester::initiate_results();
1420    ///
1421    /// Tester::explore(..., &mut results);
1422    /// // or
1423    /// Tester::explore_with_traps(..., Some(traps), &mut results);
1424    /// // or
1425    /// Tester::explore_custom(..., Some(traps), &mut results);
1426    ///
1427    /// Tester::write_reports(path, &results, false, false, false);
1428    /// ```
1429    pub trait LazyBalanceModelChecker: Sized {
1430        /// Lazy balance implementation.
1431        type LazyBalance: LazyBalanceMarker;
1432
1433        /// Manual (reference) model used for validation.
1434        ///
1435        /// This type provides an **eager execution layer** over the lazy model:
1436        /// - consumes resolved outputs from [`Self::LazyBalance`] via [`LazyBalance`]
1437        /// - maintains a concrete, user-facing state
1438        ///
1439        /// Unlike the lazy model, this operates as a **direct global
1440        /// state model** via [`ManualBalanceModel`] methods:
1441        /// - no deferred receipts
1442        /// - no lazy resolution
1443        /// - updates are applied immediately to a single state
1444        ///
1445        /// It must also define:
1446        /// - [`BalanceStateHasher`]: for state deduplication during exploration
1447        /// - [`BalanceGuards`]: for operation validity and invariants
1448        ///
1449        /// Acts as the **reference baseline** against which the lazy model
1450        /// is evaluated for correctness and precision.
1451        type ManualBalance: ManualBalanceModel<Self::LazyBalance>
1452            + BalanceStateHasher<Self::LazyBalance, Self::ManualBalance>
1453            + BalanceGuards<Self::LazyBalance, Self::ManualBalance>
1454            + Clone;
1455
1456        /// Trap predicate used for trap-based testing.
1457        ///
1458        /// Represents a **single trap condition** applied across all operations:
1459        /// - evaluated per `(state, op)`
1460        /// - returns `true` to override guards defined in [`BalanceGuards`]
1461        /// via [`Self::ManualBalance`]
1462        ///
1463        /// This trap is **injected globally** during exploration, so it will be
1464        /// evaluated for every operation ([`BalanceOp`]). Implementations should therefore:
1465        /// - precisely target the intended operation/condition
1466        /// - return `false` for all unrelated operations
1467        ///
1468        /// Unlike [`BalanceGuards`], which define validity for all operations at once,
1469        /// this is defined **per invocation** to inject a specific invalid scenario.
1470        ///
1471        /// Used to enable controlled violations:
1472        /// ```text
1473        /// guard(...) || trap(...)
1474        /// ```
1475        type TrapFn: Fn(
1476            &BalanceState<Self::LazyBalance, Self::ManualBalance>,
1477            &BalanceOp<Self::LazyBalance, Self::ManualBalance>,
1478        ) -> bool;
1479
1480        /// Flow predicate used to control exploration.
1481        ///
1482        /// Applied globally across all operations:
1483        /// - evaluated per `(state, op)`
1484        /// - returns `true` to allow the candidate operation [`BalanceOp`] to proceed
1485        /// - returns `false` to block exploration of that operation
1486        ///
1487        /// Combined with the base guard flow as:
1488        /// ```text
1489        /// guard_flow(...) && trap_flow(...)
1490        /// ```
1491        ///
1492        /// Unlike [`Self::TrapFn`], this does **not override validity**;
1493        /// it only restricts whether an operation is explored.
1494        ///
1495        /// Since this is combined using `&&`, returning `false` will
1496        /// **prevent the operation from being explored entirely**.
1497        /// Therefore implementations should:
1498        /// - return `true` by default
1499        /// - only return `false` when explicitly pruning a specific case
1500        ///
1501        /// Used to shape the search space (e.g., pruning, targeting).
1502        type FlowFn: Fn(
1503            &BalanceState<Self::LazyBalance, Self::ManualBalance>,
1504            &BalanceOp<Self::LazyBalance, Self::ManualBalance>,
1505        ) -> bool;
1506
1507        /// Optional additional custom state hasher.
1508        ///
1509        /// Provides an additional hashing layer over [`BalanceState`] during exploration:
1510        /// - evaluated alongside [`BalanceStateHasher`] from [`Self::ManualBalance`]
1511        /// - used for further deduplication of states
1512        ///
1513        /// Since [`Self::ManualBalance`] already defines a primary hashing strategy via
1514        /// [`BalanceStateHasher`], this acts as an **optional second-stage filter**.
1515        ///
1516        /// This is particularly useful when:
1517        /// - multiple exploration strategies or model checkers are run
1518        /// - additional pruning is needed beyond the primary state hash
1519        ///
1520        /// In such cases, this hasher can be applied **on top of the base hasher**
1521        /// to refine state uniqueness and avoid revisiting equivalent states.
1522        ///
1523        /// If provided, both hashes are used during exploration.
1524        type Hasher: Fn(&BalanceState<Self::LazyBalance, Self::ManualBalance>) -> u64;
1525
1526        /// Creates a new [`BalanceModelResults`] container.
1527        fn initiate_results() -> BalanceModelResults<Self::LazyBalance, Self::ManualBalance> {
1528            BalanceModelResults::new()
1529        }
1530
1531        /// Runs model exploration under valid conditions (guards only).
1532        ///
1533        /// Explores sequences of operations using [`BalanceGuards`] without any
1534        /// trap overrides.
1535        ///
1536        /// ## Parameters
1537        ///
1538        /// - `users`: set of users participating in operations
1539        /// - `deposits`: input values used for deposit operations
1540        /// - `adjustments`: values used for mint / reap operations
1541        /// - `subjects`: contextual inputs (e.g. precision, forced flags)
1542        /// - `max_depth`: maximum sequence length to explore
1543        /// - `allowed_bps`: maximum allowed drift in basis points
1544        /// (e.g. 10 = 0.10%, 100 = 1%)
1545        /// - `allowed_diff`: maximum allowed absolute value difference
1546        /// (not percentage-based)
1547        /// - `results`: mutable container collecting outcomes
1548        ///
1549        /// ## Behavior
1550        ///
1551        /// - generates operation sequences up to `max_depth`
1552        /// - filters using [`BalanceGuards`] (`guard(...)`)
1553        /// - executes both lazy and manual models
1554        /// - compares results and records:
1555        /// - `pass`: valid execution
1556        /// - `fail`: error
1557        /// - `exit`: withdrawals drifts between balance models
1558        ///
1559        /// This is the standard correctness check without testing invalid scenarios.
1560        fn explore(
1561            users: &[<Self::ManualBalance as ManualBalanceModel<Self::LazyBalance>>::User],
1562            deposits: &[<Self::LazyBalance as LazyBalance>::Asset],
1563            adjustments: &[<Self::LazyBalance as LazyBalance>::Asset],
1564            subjects: &[<Self::LazyBalance as LazyBalance>::Subject],
1565            max_depth: u32,
1566            allowed_bps: u32,
1567            allowed_diff: u32,
1568            results: &mut BalanceModelResults<Self::LazyBalance, Self::ManualBalance>,
1569        ) where
1570            <Self::LazyBalance as LazyBalance>::Id: Default,
1571            <Self::LazyBalance as LazyBalance>::Asset: From<<Self::LazyBalance as LazyBalance>::Asset>
1572                + Into<<Self::LazyBalance as LazyBalance>::Asset>,
1573        {
1574            explore_explore_balance_states_default::<Self::LazyBalance, Self::ManualBalance>(
1575                users,
1576                deposits,
1577                adjustments,
1578                subjects,
1579                max_depth,
1580                allowed_bps,
1581                allowed_diff,
1582                results,
1583            )
1584        }
1585
1586        /// Runs exploration with trap overrides (`guard || trap`).
1587        ///
1588        /// Extends [`Self::explore`] by allowing controlled violations
1589        /// of guards using [`BalanceTraps`].
1590        ///
1591        /// ## Parameters
1592        ///
1593        /// - `users`: set of users participating in operations
1594        /// - `deposits`: input values used for deposit operations
1595        /// - `adjustments`: values used for mint / reap operations
1596        /// - `subjects`: contextual inputs (e.g. precision, forced flags)
1597        /// - `max_depth`: maximum sequence length to explore
1598        /// - `allowed_bps`: maximum allowed drift in basis points
1599        ///   (e.g. 10 = 0.10%, 100 = 1%)
1600        /// - `allowed_diff`: maximum allowed absolute value difference
1601        /// - `traps`: optional trap configuration (trap, flow, reason)
1602        /// - `results`: mutable container collecting outcomes
1603        ///
1604        /// ## Behavior
1605        ///
1606        /// - operations are allowed using:
1607        /// ```text
1608        /// guard(...) || trap(...)
1609        /// ```
1610        ///
1611        /// - `trap` is applied globally to every operation:
1612        ///   - should return `true` only for the intended operation(s)
1613        ///   - should return `false` by default for all unrelated operations
1614        ///
1615        /// - `flow` further filters exploration:
1616        /// ```text
1617        /// guard_flow(...) && trap_flow(...)
1618        /// ```
1619        ///   - should return `true` for the intended operation(s) or by default
1620        ///   - return `false` **only to explicitly block** exploration of a case
1621        ///
1622        /// - typically used to:
1623        ///   - force invalid transitions
1624        ///   - trigger expected errors
1625        ///   - validate edge-case behavior
1626        ///
1627        /// - executes both lazy and manual models and records:
1628        ///   - pass / fail / exit / trap outcomes
1629        ///
1630        /// This is used for adversarial and trap-based testing.
1631        fn explore_traps(
1632            users: &[<Self::ManualBalance as ManualBalanceModel<Self::LazyBalance>>::User],
1633            deposits: &[<Self::LazyBalance as LazyBalance>::Asset],
1634            adjustments: &[<Self::LazyBalance as LazyBalance>::Asset],
1635            subjects: &[<Self::LazyBalance as LazyBalance>::Subject],
1636            max_depth: u32,
1637            allowed_bps: u32,
1638            allowed_diff: u32,
1639            traps: Option<BalanceTraps<Self::TrapFn, Self::FlowFn>>,
1640            results: &mut BalanceModelResults<Self::LazyBalance, Self::ManualBalance>,
1641        ) where
1642            <Self::LazyBalance as LazyBalance>::Id: Default,
1643            <Self::LazyBalance as LazyBalance>::Asset: From<<Self::LazyBalance as LazyBalance>::Asset>
1644                + Into<<Self::LazyBalance as LazyBalance>::Asset>,
1645        {
1646            explore_balance_trap_states_default::<
1647                Self::LazyBalance,
1648                Self::ManualBalance,
1649                Self::TrapFn,
1650                Self::FlowFn,
1651            >(
1652                users,
1653                deposits,
1654                adjustments,
1655                subjects,
1656                max_depth,
1657                allowed_bps,
1658                allowed_diff,
1659                traps,
1660                results,
1661            )
1662        }
1663
1664        /// Runs full custom exploration (custom traps + optional additional hasher).
1665        ///
1666        /// This is the most flexible entrypoint, allowing control over:
1667        /// - trap behavior
1668        /// - additional state hashing
1669        ///
1670        /// ## Parameters
1671        ///
1672        /// - `users`: set of users participating in operations
1673        /// - `deposits`: input values used for deposit operations
1674        /// - `adjustments`: values used for mint / reap operations
1675        /// - `subjects`: contextual inputs (e.g. precision, forced flags)
1676        /// - `max_depth`: maximum sequence length to explore
1677        /// - `allowed_bps`: maximum allowed drift in basis points
1678        ///   (e.g. `10` = 0.10%, `100` = 1%)
1679        /// - `allowed_diff`: maximum allowed absolute value difference
1680        /// - `traps`: optional trap configuration (`guard || trap`)
1681        /// - `hasher`: optional additional state hasher (second-stage pruning)
1682        /// - `results`: mutable container collecting outcomes
1683        ///
1684        /// ## Behavior
1685        ///
1686        /// - combines:
1687        ///   - [`BalanceGuards`] -> base validity
1688        ///   - [`BalanceTraps`] -> override (`guard || trap`)
1689        ///   - `flow` -> exploration control (`guard_flow && trap_flow`)
1690        ///
1691        /// - if `hasher` is provided:
1692        ///   - used alongside [`BalanceStateHasher`] from [`Self::ManualBalance`]
1693        ///   - enables additional state deduplication
1694        ///
1695        /// - executes both lazy and manual models and records:
1696        ///   - pass / fail / exit / trap outcomes
1697        ///
1698        /// Use this when:
1699        /// - testing complex trap scenarios
1700        /// - adding custom state pruning logic
1701        /// - requiring full control over exploration behavior
1702        fn explore_custom(
1703            users: &[<Self::ManualBalance as ManualBalanceModel<Self::LazyBalance>>::User],
1704            deposits: &[<Self::LazyBalance as LazyBalance>::Asset],
1705            adjustments: &[<Self::LazyBalance as LazyBalance>::Asset],
1706            subjects: &[<Self::LazyBalance as LazyBalance>::Subject],
1707            max_depth: u32,
1708            allowed_bps: u32,
1709            allowed_diff: u32,
1710            traps: Option<BalanceTraps<Self::TrapFn, Self::FlowFn>>,
1711            hasher: Option<Self::Hasher>,
1712            results: &mut BalanceModelResults<Self::LazyBalance, Self::ManualBalance>,
1713        ) where
1714            <Self::LazyBalance as LazyBalance>::Id: Default,
1715            <Self::LazyBalance as LazyBalance>::Asset: From<<Self::LazyBalance as LazyBalance>::Asset>
1716                + Into<<Self::LazyBalance as LazyBalance>::Asset>,
1717        {
1718            explore_balance_states::<
1719                Self::LazyBalance,
1720                Self::ManualBalance,
1721                Self::TrapFn,
1722                Self::FlowFn,
1723                Self::Hasher,
1724            >(
1725                users,
1726                deposits,
1727                adjustments,
1728                subjects,
1729                max_depth,
1730                allowed_bps,
1731                allowed_diff,
1732                traps,
1733                hasher,
1734                results,
1735            )
1736        }
1737
1738        /// Writes CSV reports for analysis.
1739        ///
1740        /// Exports the contents of [`BalanceModelResults`] into CSV files
1741        /// under the given directory.
1742        ///
1743        /// ## Parameters
1744        ///
1745        /// - `dir`: output directory where CSV files will be created
1746        /// - `results`: collected model checking results
1747        /// - `write_pass`: whether to write successful sequences
1748        /// - `write_exit`: whether to write drift cases ([`BalanceExit`])
1749        /// - `write_trap`: whether to write trapped sequences
1750        ///
1751        /// ## Behavior
1752        ///
1753        /// - always writes **failures**
1754        /// - optionally writes:
1755        ///   - `pass`: successful executions
1756        ///   - `exit`: drift between lazy and manual models
1757        ///   - `trap`: expected trap-triggered sequences
1758        ///
1759        /// - file names are timestamped to avoid overwriting
1760        /// - prints a summary of results after writing
1761        ///
1762        /// Useful for:
1763        /// - debugging failures
1764        /// - analyzing drift behavior
1765        /// - validating trap scenarios
1766        fn write_reports(
1767            dir: std::path::PathBuf,
1768            results: &BalanceModelResults<Self::LazyBalance, Self::ManualBalance>,
1769            write_pass: bool,
1770            write_exit: bool,
1771            write_trap: bool,
1772        ) {
1773            balance_states_csv_reports(dir, results, write_pass, write_exit, write_trap)
1774        }
1775    }
1776
1777    // ===============================================================================
1778    // ````````````````````` MODEL CHECKER ENUMS AND STRUCTURES ``````````````````````
1779    // ===============================================================================
1780
1781    /// Operation descriptor for driving both [`LazyBalance`] and
1782    /// [`ManualBalanceModel`] executions in a consistent manner.
1783    ///
1784    /// Each variant represents a high-level balance action along with
1785    /// the required inputs. These operations are typically used in
1786    /// testing or simulation to:
1787    /// - execute the lazy balance model
1788    /// - pass the resolved results into a manual (eager) model
1789    /// - compare and validate behavior across both systems
1790    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
1791    pub enum BalanceOp<T: LazyBalanceMarker, M: ManualBalanceModel<T>> {
1792        /// Deposit operation.
1793        ///
1794        /// `(user, amount, subject)` where:
1795        /// - `user` is the target account
1796        /// - `amount` is the requested asset value
1797        /// - `subject` defines execution intent (e.g., precision/behavior)
1798        Deposit(M::User, T::Asset, T::Subject),
1799
1800        /// Withdraw operation.
1801        ///
1802        /// `(user)` where:
1803        /// - `user` is the account performing withdrawal
1804        Withdraw(M::User),
1805
1806        /// Mint operation.
1807        ///
1808        /// `(amount, subject)` where:
1809        /// - `amount` is the asset value to introduce
1810        /// - `subject` defines execution intent
1811        Mint(T::Asset, T::Subject),
1812
1813        /// Reap operation.
1814        ///
1815        /// `(amount, subject)` where:
1816        /// - `amount` is the asset value to remove or adjust
1817        /// - `subject` defines execution intent
1818        Reap(T::Asset, T::Subject),
1819
1820        /// Drain (Full-Reap) operation.
1821        ///
1822        /// Resets the entire balance state.
1823        Drain,
1824    }
1825
1826    /// Represents a failure encountered during execution of an operation sequence.
1827    ///
1828    /// This structure is used for:
1829    /// - validating expected failures (trap testing), where a known sequence
1830    ///   must produce a specific error
1831    /// - diagnosing mismatches between lazy and manual models
1832    /// - providing reproducible execution traces for debugging
1833    #[derive(Clone, Debug, PartialEq, Eq)]
1834    pub struct BalanceFailure<T: LazyBalanceMarker, M: ManualBalanceModel<T>> {
1835        /// The error message, typically derived from the returned error
1836        /// (e.g. via debug formatting), describing why execution failed.
1837        pub reason: String,
1838        /// The trace of operations leading to the failure, where the
1839        /// final operation in the sequence is the one that triggered the error.
1840        pub sequence: Vec<BalanceOp<T, M>>,
1841    }
1842
1843    /// Represents a successful withdrawal where the lazy and manual models
1844    /// produce different outputs.
1845    ///
1846    /// A withdrawal has completed successfully in both models, but the
1847    /// resulting values are not equal, indicating **drift** between the
1848    /// lazy (deferred) model and the manual (eager reference) model.
1849    ///
1850    /// This occurs because withdrawals are state-dependent: prior balance
1851    /// adjustments (`mint`, `reap`) can change the effective value of a
1852    /// receipt, so the redeemed amount may differ from the original deposit.
1853    ///
1854    /// This structure captures:
1855    /// - the outputs from both models
1856    /// - their absolute difference (`diff`)
1857    /// - the difference in basis points (`bps`)
1858    /// - the full operation trace leading to the withdrawal
1859    ///
1860    /// The final operation in `sequence` is always the [`BalanceOp::Withdraw`] that
1861    /// produced this result.
1862    ///
1863    /// [`LazyBalanceModelChecker`] methods may apply tolerance thresholds (e.g. minimum
1864    /// `diff` or `bps`) to ignore negligible drift. If the deviation falls within
1865    /// acceptable bounds, this exit may be discarded.
1866    ///
1867    /// This is primarily used to evaluate whether the lazy model maintains
1868    /// acceptable precision relative to the manual reference model.
1869    #[derive(Clone, Debug, PartialEq, Eq)]
1870    pub struct BalanceExit<T: LazyBalanceMarker, M: ManualBalanceModel<T>> {
1871        /// Value returned by the lazy balance model.
1872        pub lazy: T::Asset,
1873
1874        /// Value returned by the manual (reference) model.
1875        pub manual: T::Asset,
1876
1877        /// Absolute difference between `lazy` and `manual`.
1878        pub diff: T::Asset,
1879
1880        /// Difference expressed in basis points.
1881        pub bps: T::Asset,
1882
1883        /// Trace of operations leading to this withdrawal.
1884        /// The last operation is the `Withdraw`.
1885        pub sequence: Vec<BalanceOp<T, M>>,
1886    }
1887
1888    /// Convenience container holding the owned state required to operate
1889    /// on a [`LazyBalance`] instance.
1890    ///
1891    /// This groups the core components needed for execution into a single
1892    /// structure with owned representations.
1893    ///
1894    /// Primarily used to simplify passing and managing balance state
1895    /// across operations and tests.
1896    #[derive(Clone, Debug, PartialEq, Eq)]
1897    pub struct LazyContainer<T: LazyBalanceMarker> {
1898        /// Underlying mutable balance state as owned.
1899        pub balance: T::Balance,
1900
1901        /// Logical partition of the balance.
1902        pub variant: T::Variant,
1903
1904        /// Identifier of the balance owner or context.
1905        pub id: T::Id,
1906    }
1907
1908    /// Primary state container for executing and tracking a sequence of operations.
1909    ///
1910    /// Holds both lazy and manual model states along with auxiliary data
1911    /// required for coordinated execution and validation.
1912    ///
1913    /// This can represent:
1914    /// - a full state snapshot at a given step in a sequence, or
1915    /// - a single evolving state where operations are applied incrementally
1916    ///
1917    /// Used by model checkers to:
1918    /// - execute operations step-by-step
1919    /// - maintain consistency between lazy and manual models
1920    /// - track execution history for validation and debugging
1921    #[derive(Clone, Debug, PartialEq, Eq)]
1922    pub struct BalanceState<T: LazyBalanceMarker, M: ManualBalanceModel<T>> {
1923        /// Lazy balance state container.
1924        pub lazy: LazyContainer<T>,
1925
1926        /// Manual (eager reference) model state.
1927        pub manual: M,
1928
1929        /// Latest receipts per user.
1930        pub receipts: BTreeMap<M::User, T::Receipt>,
1931
1932        /// Operation trace leading to this state.
1933        pub trace: Vec<BalanceOp<T, M>>,
1934    }
1935
1936    /// Defines trap behavior for state exploration.
1937    ///
1938    /// `BalanceTraps` provides mechanisms to intentionally allow and explore
1939    /// otherwise disallowed operations (as per [`BalanceGuards`]) for testing
1940    /// and validation purposes.
1941    ///
1942    /// - `trap` is a predicate over `(state, op)` that, when `true`,
1943    ///   overrides the guard and allows the operation:
1944    ///
1945    ///   ```text
1946    ///   guard(...) || trap(...)
1947    ///   ```
1948    ///
1949    ///   This enables controlled exploration of invalid transitions.
1950    ///
1951    /// - `flow` is an additional exploration filter combined with the
1952    ///   base flow using logical AND:
1953    ///
1954    ///   ```text
1955    ///   guard_flow && trap_flow
1956    ///   ```
1957    ///
1958    ///   Unlike `trap`, this does not override but restricts execution.
1959    ///
1960    /// - `reason` is the expected failure description i.e., debugged error
1961    ///   associated with the trap, typically used for validation or comparison
1962    ///   when the trapped operation results in an error.
1963    ///
1964    /// `G` and `F` are function/closure types using [`BalanceState`] and
1965    /// [`BalanceOp`]:
1966    /// - `G: Fn(&BalanceState<T, M>, &BalanceOp<T, M>) -> bool`
1967    /// - `F: Fn(&BalanceState<T, M>, &BalanceOp<T, M>) -> bool`
1968    pub struct BalanceTraps<G, F> {
1969        /// Predicate that allows an operation even if the guard rejects it.
1970        pub trap: G,
1971
1972        /// Additional flow filter applied with logical AND.
1973        pub flow: F,
1974
1975        /// Expected reason associated with the trapped execution.
1976        pub reason: String,
1977    }
1978
1979    // ===============================================================================
1980    // ```````````````````````````` MODEL CHECKER TRAITS `````````````````````````````
1981    // ===============================================================================
1982
1983    /// Convenience trait alias for a [`LazyBalance`] implementor used in
1984    /// model checking and testing.
1985    pub trait LazyBalanceMarker: LazyBalance + Clone + Debug {}
1986
1987    impl<T> LazyBalanceMarker for T where T: LazyBalance + Clone + Debug {}
1988
1989    /// A user-indexed **eager balance model** over a [`LazyBalance`] system,
1990    /// primarily intended for testing and validation.
1991    ///
1992    /// This model consumes the resolved outputs of lazy balance operations
1993    /// (`lazy_result`) and maintains a concrete, user-facing state without
1994    /// any deferred semantics (no receipts, no lazy evaluation).
1995    ///
1996    /// Unlike [`LazyBalance`], implementations are free to use the simplest
1997    /// possible accounting logic (e.g., a single global state machine or
1998    /// direct user balances), as correctness is derived from the provided
1999    /// results rather than internal computation.
2000    ///
2001    /// It is not a source of truth and must remain consistent with the
2002    /// underlying [`LazyBalance`] execution.
2003    ///
2004    /// This makes it suitable as a **reference model** for:
2005    /// - validating lazy execution behavior
2006    /// - testing invariants and edge cases
2007    /// - comparing expected vs actual outcomes
2008    pub trait ManualBalanceModel<T>: Clone + Debug
2009    where
2010        T: LazyBalanceMarker,
2011    {
2012        /// Simple user identifier type for the model.
2013        type User: Sortable + Hash + Copy;
2014
2015        /// Error type returned by model operations.
2016        type Error: Debug + Clone + 'static;
2017
2018        /// Creates a new instance with an empty manual balance state.
2019        fn new() -> Self;
2020
2021        /// Applies a deposit for `user`.
2022        ///
2023        /// `amount` is the requested input. The lazy model is assumed to have been
2024        /// executed with the same inputs, and `lazy_result` contains its resolved output:
2025        /// - actual deposited value
2026        /// - corresponding receipt
2027        ///
2028        /// This method consumes that result to update the manual balance state.
2029        fn deposit(
2030            &mut self,
2031            user: Self::User,
2032            amount: T::Asset,
2033            lazy_result: &(T::Asset, T::Receipt),
2034        ) -> Result<(), Self::Error>;
2035
2036        /// Applies a withdrawal for `user`.
2037        ///
2038        /// The lazy model is assumed to have been executed with the same inputs,
2039        /// and `lazy_result` contains the resolved asset produced by that withdrawal.
2040        ///
2041        /// This method consumes that result to update the manual balance state.
2042        ///
2043        /// Returns the withdrawn amount.
2044        fn withdraw(
2045            &mut self,
2046            user: Self::User,
2047            lazy_result: &T::Asset,
2048        ) -> Result<T::Asset, Self::Error>;
2049
2050        /// Applies a mint operation.
2051        ///
2052        /// The lazy model is assumed to have been executed with the same input,
2053        /// and `lazy_result` contains the resolved asset produced by that mint.
2054        ///
2055        /// This method consumes that result to update the manual balance state.
2056        fn mint(&mut self, amount: T::Asset, lazy_result: &T::Asset) -> Result<(), Self::Error>;
2057
2058        /// Applies a reap operation.
2059        ///
2060        /// The lazy model is assumed to have been executed with the same input,
2061        /// and `lazy_result` contains the resolved asset produced by that reap.
2062        ///
2063        /// This method consumes that result to update the manual balance state.
2064        fn reap(&mut self, amount: T::Asset, lazy_result: &T::Asset) -> Result<(), Self::Error>;
2065
2066        /// Applies a drain (full reap), resetting the manual balance state.
2067        ///
2068        /// Brings the model into a fully drained state, consistent with a
2069        /// corresponding lazy balance drain operation.
2070        fn drain(&mut self) -> Result<(), Self::Error>;
2071
2072        /// Returns the total aggregated value tracked by the model.
2073        ///
2074        /// This is a utility method for external use and is not used by
2075        /// the model checker during exploration.
2076        fn total(&self) -> T::Asset;
2077    }
2078
2079    /// Defines a primary hashing strategy for [`BalanceState`] instances.
2080    ///
2081    /// Used to uniquely identify a state during exploration or testing,
2082    /// allowing model checkers to avoid revisiting previously seen states.
2083    ///
2084    /// Implementors can choose any hashing scheme appropriate for their
2085    /// use case, as long as it provides sufficient uniqueness for pruning
2086    /// duplicate states during operation sequencing.
2087    ///
2088    /// This is typically used to:
2089    /// - detect already explored states
2090    /// - prevent redundant execution paths
2091    /// - guide search strategies over operation sequences
2092    pub trait BalanceStateHasher<T: LazyBalanceMarker, M: ManualBalanceModel<T>> {
2093        /// Computes a hash representing the given state.
2094        fn hash(state: &BalanceState<T, M>) -> u64;
2095    }
2096
2097    /// Defines validity checks for operations during state exploration.
2098    ///
2099    /// Each method determines whether a candidate operation ([`BalanceOp`]) can be
2100    /// appended to the current trace, i.e., whether the next step in the sequence is
2101    /// valid given the current state.
2102    ///
2103    /// - A **sequence** is an ordered list of operations (`Vec<BalanceOp>`) being explored.
2104    /// - The **trace** is the current prefix of that sequence already applied
2105    ///   to reach the current [`BalanceState`].
2106    ///
2107    /// Each guard method receives the current state (derived from the trace)
2108    /// and the inputs of the next operation, and returns:
2109    /// - `true`: operation is allowed
2110    /// - `false`: operation is disallowed (guarded)
2111    ///
2112    /// During execution, an operation ([`BalanceOp`]) is permitted if:
2113    ///
2114    /// ```text
2115    /// guard(...) || trap(...)
2116    /// ```
2117    ///
2118    /// - `guard(...)` is the normal allow/disallow check (this trait)
2119    /// - [`BalanceTraps`] may override and allow a disallowed operation
2120    ///
2121    /// ## Flow
2122    ///
2123    /// `flow` is an additional filter applied after guards:
2124    ///
2125    /// ```text
2126    /// guard_flow(...) && trap_flow(...)
2127    /// ```
2128    ///
2129    /// - Both must return `true` for the operation to proceed
2130    /// - Unlike guards, flow is combined (`&&`), not overridden
2131    /// - Used only to control exploration, not correctness
2132    ///
2133    /// ## Invariant
2134    ///
2135    /// `invariant` must hold for every state reached via the
2136    /// trace evaluated at each step
2137    ///
2138    /// ## Example
2139    ///
2140    /// ```text
2141    /// trace:   [Deposit(A, 100)]
2142    /// next:    Withdraw(A)
2143    ///
2144    /// guard_withdraw(state, A) = true -> allowed (valid transition)
2145    ///
2146    /// --------
2147    ///
2148    /// trace:   []
2149    /// next:    Withdraw(A)
2150    ///
2151    /// guard_withdraw(state, A) = false -> disallowed (no receipt)
2152    ///
2153    /// trap_override(...) = true -> allowed (forced invalid case for testing)
2154    ///
2155    /// flow(...) = false -> blocked regardless of guard/trap
2156    /// ```
2157    ///
2158    /// BalanceGuards should only express validity conditions for extending the
2159    /// sequence. They should not include exploration logic or testing behavior.
2160    pub trait BalanceGuards<T: LazyBalanceMarker, M: ManualBalanceModel<T>> {
2161        /// Returns `true` if [`BalanceOp::Deposit`] is allowed to be appended
2162        /// to the current trace, given the current state and inputs.
2163        ///
2164        /// `false` disallows the operation.
2165        fn deposit(
2166            _state: &BalanceState<T, M>,
2167            _user: &M::User,
2168            _amount: &T::Asset,
2169            _subject: &T::Subject,
2170        ) -> bool {
2171            true
2172        }
2173
2174        /// Returns `true` if [`BalanceOp::Withdraw`] is allowed to be appended
2175        /// to the current trace, given the current state and inputs.
2176        ///
2177        /// `false` disallows the operation.
2178        fn withdraw(_state: &BalanceState<T, M>, _user: &M::User) -> bool {
2179            true
2180        }
2181
2182        /// Returns `true` if [`BalanceOp::Mint`] is allowed to be appended
2183        /// to the current trace, given the current state and inputs.
2184        ///
2185        /// `false` disallows the operation.
2186        fn mint(_state: &BalanceState<T, M>, _value: &T::Asset, _subject: &T::Subject) -> bool {
2187            true
2188        }
2189
2190        /// Returns `true` if [`BalanceOp::Reap`] is allowed to be appended
2191        /// to the current trace, given the current state and inputs.
2192        ///
2193        /// `false` disallows the operation.
2194        fn reap(_state: &BalanceState<T, M>, _value: &T::Asset, _subject: &T::Subject) -> bool {
2195            true
2196        }
2197
2198        /// Returns `true` if [`BalanceOp::Drain`] is allowed to be appended
2199        /// to the current trace, given the current state and inputs.
2200        ///
2201        /// `false` disallows the operation.
2202        fn drain(_state: &BalanceState<T, M>) -> bool {
2203            true
2204        }
2205
2206        /// Additional exploration filter applied after guards.
2207        ///
2208        /// Returns `true` if the candidate operation should be explored further.
2209        /// Combined with [`BalanceTraps`] `flow` using logical AND (`&&`) if traps
2210        /// are initiated.
2211        fn flow(_state: &BalanceState<T, M>, _next: &BalanceOp<T, M>) -> bool {
2212            true
2213        }
2214
2215        /// Global invariant over the current state.
2216        ///
2217        /// Must hold for every state reached via the trace:
2218        /// - `Ok(())` -> state is valid
2219        /// - `Err(reason)` -> invariant violated, path terminates
2220        fn invariant(_state: &BalanceState<T, M>) -> Result<(), String> {
2221            Ok(())
2222        }
2223    }
2224
2225    // ===============================================================================
2226    // `````````````````````````````` MODEL CHECKER API ``````````````````````````````
2227    // ===============================================================================
2228
2229    /// Default state exploration entrypoint for [`LazyBalance`].
2230    ///
2231    /// Uses:
2232    /// - [`BalanceGuards`] for validity
2233    /// - no [`BalanceTraps`] (strict valid execution only)
2234    /// - Utilizes state hashing using [`BalanceStateHasher`]
2235    ///
2236    /// Explores sequences up to max_depth, applying operations that pass:
2237    /// - `guard(...)`
2238    /// - `flow(...)`
2239    ///
2240    /// Records:
2241    /// - successful sequences
2242    /// - failures
2243    /// - exits (drift between lazy and manual models)
2244    /// - asserts [`BalanceGuards::invariant`] at every state
2245    ///
2246    /// This is the standard model check under valid system behavior only.
2247    fn explore_explore_balance_states_default<T, M>(
2248        users: &[M::User],
2249        deposits: &[T::Asset],
2250        adjustments: &[T::Asset],
2251        subjects: &[T::Subject],
2252        max_depth: u32,
2253        allowed_bps: u32,
2254        allowed_diff: u32,
2255        results: &mut BalanceModelResults<T, M>,
2256    ) where
2257        T: LazyBalanceMarker + Clone,
2258        M: ManualBalanceModel<T> + BalanceStateHasher<T, M> + BalanceGuards<T, M> + Clone,
2259        T::Id: Default,
2260        T::Asset: From<T::Asset> + Into<T::Asset>,
2261    {
2262        explore_balance_states::<
2263            T,
2264            M,
2265            fn(&BalanceState<T, M>, &BalanceOp<T, M>) -> bool,
2266            fn(&BalanceState<T, M>, &BalanceOp<T, M>) -> bool,
2267            fn(&BalanceState<T, M>) -> u64,
2268        >(
2269            users,
2270            deposits,
2271            adjustments,
2272            subjects,
2273            max_depth,
2274            allowed_bps,
2275            allowed_diff,
2276            None,
2277            None,
2278            results,
2279        );
2280    }
2281
2282    // BalanceState exploration with trap overrides for [`LazyBalance`].
2283    ///
2284    /// Extends [`explore_explore_balance_states_default`] by allowing controlled
2285    /// violations of [`BalanceGuards`] via [`BalanceTraps`].
2286    ///
2287    /// Uses:
2288    /// - [`BalanceGuards`] for baseline validity
2289    /// - [`BalanceTraps`] to override guards (`guard || trap`)
2290    /// - trap-specific flow filtering (`guard_flow && trap_flow`)
2291    ///
2292    /// This enables exploration of:
2293    /// - invalid transitions
2294    /// - edge cases
2295    /// - expected failure scenarios
2296    ///
2297    /// `reason` from [`BalanceTraps`] is used to validate or tag resulting errors.
2298    ///
2299    /// Suitable for trap testing and adversarial validation.
2300    fn explore_balance_trap_states_default<T, M, G, F>(
2301        users: &[M::User],
2302        deposits: &[T::Asset],
2303        adjustments: &[T::Asset],
2304        subjects: &[T::Subject],
2305        max_depth: u32,
2306        allowed_bps: u32,
2307        allowed_diff: u32,
2308        overrides: Option<BalanceTraps<G, F>>,
2309        results: &mut BalanceModelResults<T, M>,
2310    ) where
2311        T: LazyBalanceMarker + Clone,
2312        M: ManualBalanceModel<T> + BalanceStateHasher<T, M> + BalanceGuards<T, M> + Clone,
2313        T::Id: Default,
2314        T::Asset: From<T::Asset> + Into<T::Asset>,
2315        G: Fn(&BalanceState<T, M>, &BalanceOp<T, M>) -> bool,
2316        F: Fn(&BalanceState<T, M>, &BalanceOp<T, M>) -> bool,
2317    {
2318        explore_balance_states::<T, M, G, F, fn(&BalanceState<T, M>) -> u64>(
2319            users,
2320            deposits,
2321            adjustments,
2322            subjects,
2323            max_depth,
2324            allowed_bps,
2325            allowed_diff,
2326            overrides,
2327            None,
2328            results,
2329        );
2330    }
2331
2332    /// Core state exploration engine for [`LazyBalance`].
2333    ///
2334    /// Performs **breadth-first** exploration over operation sequences,
2335    /// starting from an initial empty state.
2336    ///
2337    /// ```text
2338    /// -> [Deposit]   -> [Deposit, Mint] -> [Deposit, Mint, Withdraw]
2339    /// -> [Mint]      -> [Mint, Reap]
2340    /// -> [Reap]      -> ...
2341    /// -> [Withdraw]  -> ...
2342    /// -> [Drain]     -> ...
2343    /// ```
2344    ///
2345    /// Exploration proceeds level by level:
2346    /// - all sequences of length `n` are explored before `n+1`
2347    /// - each step extends the current **trace** by one [`BalanceOp`]
2348    /// - guards / traps / flow determine whether a branch continues
2349    ///
2350    /// At each step:
2351    /// - checks [`BalanceGuards::invariant`] on the current state
2352    /// - generates candidate operations (vector of [`BalanceOp`])
2353    /// - filters using:
2354    ///     - `guard_{op}(...) || trap(...)`
2355    ///     - `guard_flow_{op}(...) && trap_flow(...)`
2356    ///
2357    /// For each valid operation:
2358    /// - executes lazy model
2359    /// - applies manual model
2360    /// - updates state (receipts, trace)
2361    /// - evaluates withdrawal drift ([`compare_balances_values`])
2362    ///
2363    /// Uses:
2364    /// - visited set with [`BalanceStateHasher`] to avoid revisiting states
2365    /// - optional custom hasher for additional deduplication
2366    ///
2367    /// Records:
2368    /// - errors (failures)
2369    /// - exits (drift beyond tolerance)
2370    /// - successful paths
2371    ///
2372    /// This function defines the full execution semantics for:
2373    /// - guarded exploration
2374    /// - trap-based overrides
2375    /// - state-space traversal
2376    fn explore_balance_states<T, M, G, F, H>(
2377        users: &[M::User],
2378        deposits: &[T::Asset],
2379        adjustments: &[T::Asset],
2380        subjects: &[T::Subject],
2381        max_depth: u32,
2382        allowed_bps: u32,
2383        allowed_diff: u32,
2384        overrides: Option<BalanceTraps<G, F>>,
2385        hasher: Option<H>,
2386        results: &mut BalanceModelResults<T, M>,
2387    ) where
2388        T: LazyBalanceMarker + Clone,
2389        M: ManualBalanceModel<T> + BalanceStateHasher<T, M> + BalanceGuards<T, M> + Clone,
2390        T::Id: Default,
2391        T::Asset: From<T::Asset> + Into<T::Asset>,
2392        G: Fn(&BalanceState<T, M>, &BalanceOp<T, M>) -> bool,
2393        F: Fn(&BalanceState<T, M>, &BalanceOp<T, M>) -> bool,
2394        H: Fn(&BalanceState<T, M>) -> u64,
2395    {
2396        let mut visited = BTreeSet::new();
2397        let mut queue = VecDeque::new();
2398        let mut rng = thread_rng();
2399
2400        let traps = overrides.as_ref().map(|v| &v.reason);
2401
2402        let flow_ok = |state: &BalanceState<T, M>, op: &BalanceOp<T, M>| {
2403            let base = <M as BalanceGuards<T, M>>::flow(state, op);
2404
2405            if let Some(o) = &overrides {
2406                base && (o.flow)(state, op)
2407            } else {
2408                base
2409            }
2410        };
2411
2412        let guard_ok = |state: &BalanceState<T, M>, op: &BalanceOp<T, M>| {
2413            let base = match op {
2414                BalanceOp::Deposit(u, v, l) => <M as BalanceGuards<T, M>>::deposit(state, u, v, l),
2415                BalanceOp::Withdraw(u) => <M as BalanceGuards<T, M>>::withdraw(state, u),
2416                BalanceOp::Mint(v, l) => <M as BalanceGuards<T, M>>::mint(state, v, l),
2417                BalanceOp::Reap(v, l) => <M as BalanceGuards<T, M>>::reap(state, v, l),
2418                BalanceOp::Drain => <M as BalanceGuards<T, M>>::drain(state),
2419            };
2420
2421            let trap_override = overrides
2422                .as_ref()
2423                .map(|o| (o.trap)(state, op))
2424                .unwrap_or(false);
2425
2426            base || trap_override
2427        };
2428
2429        let push_trace = |trace: &[BalanceOp<T, M>], op| {
2430            let mut t = trace.to_vec();
2431            t.push(op);
2432            t
2433        };
2434
2435        queue.push_back(BalanceState {
2436            lazy: LazyContainer::<T> {
2437                balance: Default::default(),
2438                variant: Default::default(),
2439                id: Default::default(),
2440            },
2441            manual: M::new(),
2442            receipts: BTreeMap::new(),
2443            trace: Vec::new(),
2444        });
2445
2446        while let Some(state) = queue.pop_front() {
2447            if let Err(reason) = <M as BalanceGuards<T, M>>::invariant(&state) {
2448                let reason = reason.to_string();
2449                record_err(results, &state.trace, reason, traps);
2450                continue;
2451            }
2452
2453            if state.trace.len() == max_depth as usize {
2454                record_pass(results, &state.trace, traps);
2455                continue;
2456            }
2457
2458            if !visited.insert(<M as BalanceStateHasher<T, M>>::hash(&state)) {
2459                continue;
2460            }
2461
2462            if let Some(ref h) = hasher {
2463                if !visited.insert(h(&state)) {
2464                    continue;
2465                }
2466            }
2467
2468            // ---------- DEPOSIT ----------
2469            for &u in users {
2470                for _ in 0..deposits.len() {
2471                    if let Some(&amount) = deposits.choose(&mut rng) {
2472                        let sub = subjects.choose(&mut rng).cloned().unwrap_or_default();
2473                        if !guard_ok(&state, &BalanceOp::Deposit(u, amount, sub.clone())) {
2474                            continue;
2475                        }
2476
2477                        if !flow_ok(&state, &BalanceOp::Deposit(u, amount, sub.clone())) {
2478                            continue;
2479                        }
2480
2481                        let mut next = state.clone();
2482
2483                        match deposit::<T>(&mut next.lazy, amount, &sub) {
2484                            Ok(pass) => {
2485                                if let Err(e) = next.manual.deposit(u, amount, &pass) {
2486                                    let trace = push_trace(
2487                                        &state.trace,
2488                                        BalanceOp::Deposit(u, amount, sub),
2489                                    );
2490                                    record_err(results, &trace, format!("{:?}", e), traps);
2491                                    continue;
2492                                }
2493
2494                                let (actual, receipt) = pass;
2495                                next.receipts.insert(u, receipt);
2496                                next.trace.push(BalanceOp::Deposit(u, actual, sub));
2497                                queue.push_back(next);
2498                            }
2499
2500                            Err(e) => {
2501                                let trace =
2502                                    push_trace(&state.trace, BalanceOp::Deposit(u, amount, sub));
2503                                record_err(results, &trace, format!("{:?}", e), traps);
2504                            }
2505                        }
2506                    }
2507                }
2508            }
2509
2510            // ---------- WITHDRAW ----------
2511            for &u in users {
2512                if !guard_ok(&state, &BalanceOp::Withdraw(u)) {
2513                    continue;
2514                }
2515
2516                if !flow_ok(&state, &BalanceOp::Withdraw(u)) {
2517                    continue;
2518                }
2519
2520                let mut next = state.clone();
2521
2522                let Some(receipt) = next.receipts.remove(&u) else {
2523                    let trace = push_trace(&next.trace, BalanceOp::Withdraw(u));
2524                    record_err(
2525                        results,
2526                        &trace,
2527                        "ModelChecker::WithdrawReceiptMissing".into(),
2528                        traps,
2529                    );
2530                    continue;
2531                };
2532
2533                match withdraw::<T>(&mut next.lazy, receipt) {
2534                    Ok(lazy_value) => {
2535                        let manual_value = match next.manual.withdraw(u, &lazy_value) {
2536                            Ok(v) => v,
2537                            Err(e) => {
2538                                let trace = push_trace(&next.trace, BalanceOp::Withdraw(u));
2539                                record_err(results, &trace, format!("{:?}", e), traps);
2540                                continue;
2541                            }
2542                        };
2543
2544                        let trace = push_trace(&next.trace, BalanceOp::Withdraw(u));
2545
2546                        if let Err(exit) = compare_balances_values::<T, M>(
2547                            lazy_value,
2548                            manual_value,
2549                            &trace,
2550                            allowed_bps,
2551                            allowed_diff,
2552                        ) {
2553                            record_exit(results, exit);
2554                            continue;
2555                        }
2556
2557                        next.trace = trace;
2558                        queue.push_back(next);
2559                    }
2560
2561                    Err(e) => {
2562                        let trace = push_trace(&next.trace, BalanceOp::Withdraw(u));
2563                        record_err(results, &trace, format!("{:?}", e), traps);
2564                    }
2565                }
2566            }
2567
2568            // ---------- MINT ----------
2569            for _ in 0..adjustments.len() {
2570                if let Some(&v) = adjustments.choose(&mut rng) {
2571                    let sub = subjects.choose(&mut rng).cloned().unwrap_or_default();
2572                    if !guard_ok(&state, &BalanceOp::Mint(v, sub.clone())) {
2573                        continue;
2574                    }
2575
2576                    if !flow_ok(&state, &BalanceOp::Mint(v, sub.clone())) {
2577                        continue;
2578                    }
2579
2580                    let mut next = state.clone();
2581
2582                    match mint::<T>(&mut next.lazy, v, &sub) {
2583                        Ok(pass) => {
2584                            if let Err(e) = next.manual.mint(v, &pass) {
2585                                let trace = push_trace(&next.trace, BalanceOp::Mint(v, sub));
2586                                record_err(results, &trace, format!("{:?}", e), traps);
2587                                continue;
2588                            }
2589
2590                            next.trace.push(BalanceOp::Mint(pass, sub));
2591                            queue.push_back(next);
2592                        }
2593
2594                        Err(e) => {
2595                            let trace = push_trace(&next.trace, BalanceOp::Mint(v, sub));
2596                            record_err(results, &trace, format!("{:?}", e), traps);
2597                        }
2598                    }
2599                }
2600            }
2601
2602            // ---------- REAP ----------
2603            for _ in 0..adjustments.len() {
2604                if let Some(&v) = adjustments.choose(&mut rng) {
2605                    let sub = subjects.choose(&mut rng).cloned().unwrap_or_default();
2606                    if !guard_ok(&state, &BalanceOp::Reap(v, sub.clone())) {
2607                        continue;
2608                    }
2609
2610                    if !flow_ok(&state, &BalanceOp::Reap(v, sub.clone())) {
2611                        continue;
2612                    }
2613
2614                    let mut next = state.clone();
2615
2616                    match reap::<T>(&mut next.lazy, v, &sub) {
2617                        Ok(pass) => {
2618                            if let Err(e) = next.manual.reap(v, &pass) {
2619                                let trace = push_trace(&next.trace, BalanceOp::Reap(v, sub));
2620                                record_err(results, &trace, format!("{:?}", e), traps);
2621                                continue;
2622                            }
2623
2624                            next.trace.push(BalanceOp::Reap(pass, sub));
2625                            queue.push_back(next);
2626                        }
2627
2628                        Err(e) => {
2629                            let trace = push_trace(&next.trace, BalanceOp::Reap(v, sub));
2630                            record_err(results, &trace, format!("{:?}", e), traps);
2631                        }
2632                    }
2633                }
2634            }
2635
2636            // ---------- DRAIN ----------
2637            if flow_ok(&state, &BalanceOp::Drain) && guard_ok(&state, &BalanceOp::Drain) {
2638                let mut next = state.clone();
2639
2640                match drain::<T>(&mut next.lazy) {
2641                    Ok(_) => {
2642                        if let Err(e) = next.manual.drain() {
2643                            let trace = push_trace(&next.trace, BalanceOp::Drain);
2644                            record_err(results, &trace, format!("{:?}", e), traps);
2645                            continue;
2646                        }
2647
2648                        next.trace.push(BalanceOp::Drain);
2649                        queue.push_back(next);
2650                    }
2651
2652                    Err(e) => {
2653                        let trace = push_trace(&next.trace, BalanceOp::Drain);
2654                        record_err(results, &trace, format!("{:?}", e), traps);
2655                    }
2656                }
2657            }
2658        }
2659    }
2660
2661    /// Writes model exploration results to CSV files.
2662    ///
2663    /// Creates a directory (if not present) and writes categorized outputs:
2664    ///
2665    /// - **fail** (always written) sequences that resulted in errors
2666    /// - **pass** (optional) successful sequences
2667    /// - **trap** (optional) sequences matching expected trap failures
2668    /// - **exit** (optional) sequences with drift between models
2669    ///
2670    /// File names are timestamped to avoid overwrites.
2671    ///
2672    /// Each CSV contains:
2673    /// - a unique ID (`sl_no`)
2674    /// - relevant fields (reason, values, sequence)
2675    ///
2676    /// If all result categories are empty, no files are written.
2677    ///
2678    /// Also prints a summary of counts:
2679    /// - trap mode includes trapped count
2680    /// - normal mode includes fail / pass / exit counts
2681    fn balance_states_csv_reports<T: LazyBalanceMarker, M: ManualBalanceModel<T>>(
2682        dir: PathBuf,
2683        results: &BalanceModelResults<T, M>,
2684        write_pass: bool,
2685        write_exit: bool,
2686        write_trap: bool,
2687    ) {
2688        if results.fail.is_empty()
2689            && results.pass.is_empty()
2690            && results.trap.is_empty()
2691            && results.exit.is_empty()
2692        {
2693            return;
2694        }
2695
2696        let fuzz_dir = dir;
2697
2698        create_dir_all(&fuzz_dir).unwrap();
2699
2700        let now = SystemTime::now()
2701            .duration_since(UNIX_EPOCH)
2702            .unwrap()
2703            .as_secs();
2704
2705        // ---------- FAIL CSV (MANDATORY) ----------
2706        if !results.fail.is_empty() {
2707            let fail_path = fuzz_dir.join(format!("fail_{}.csv", now));
2708            let mut fail_file = File::create(&fail_path).unwrap();
2709
2710            writeln!(fail_file, "sl_no,reason,step,sequence").unwrap();
2711
2712            for (id, f) in &results.fail {
2713                writeln!(fail_file, "{},\"{}\",\"{:?}\"", id, f.reason, f.sequence).unwrap();
2714            }
2715
2716            // println!("Fail CSV: {}", fail_path.display());
2717        }
2718
2719        // ---------- PASS CSV (OPTIONAL) ----------
2720        if write_pass && !results.pass.is_empty() {
2721            let pass_path = fuzz_dir.join(format!("pass_{}.csv", now));
2722            let mut pass_file = File::create(&pass_path).unwrap();
2723
2724            writeln!(pass_file, "sl_no,sequence").unwrap();
2725
2726            for (id, seq) in &results.pass {
2727                writeln!(pass_file, "{},\"{:?}\"", id, seq).unwrap();
2728            }
2729
2730            // println!("Pass CSV: {}", pass_path.display());
2731        }
2732
2733        // ---------- TRAP CSV (OPTIONAL) ----------
2734        if write_trap && !results.trap.is_empty() {
2735            let trap_path = fuzz_dir.join(format!("trap_{}.csv", now));
2736            let mut trap_file = File::create(&trap_path).unwrap();
2737
2738            writeln!(trap_file, "sl_no,reason,sequence").unwrap();
2739
2740            for (id, reason, seq) in &results.trap {
2741                writeln!(trap_file, "{},\"{}\",\"{:?}\"", id, reason, seq).unwrap();
2742            }
2743
2744            // println!("Trap CSV: {}", trap_path.display());
2745        }
2746
2747        // ---------- EXIT CSV (OPTIONAL) ----------
2748        if write_exit && !results.exit.is_empty() {
2749            let exit_path = fuzz_dir.join(format!("exit_{}.csv", now));
2750            let mut exit_file = File::create(&exit_path).unwrap();
2751
2752            writeln!(exit_file, "sl_no,lazy,manual,diff,bps,sequence").unwrap();
2753
2754            for (id, exit) in &results.exit {
2755                writeln!(
2756                    exit_file,
2757                    "{},{:?},{:?},{:?},{:?},\"{:?}\"",
2758                    id, exit.lazy, exit.manual, exit.diff, exit.bps, exit.sequence
2759                )
2760                .unwrap();
2761            }
2762
2763            // println!("BalanceExit CSV: {}", exit_path.display());
2764        }
2765
2766        // ---------- SUMMARY ----------
2767        if !results.trap.is_empty() {
2768            println!(
2769                "Trap check completed: {} failed, {} passed, {} exited, {} trapped",
2770                results.fail.len(),
2771                results.pass.len(),
2772                results.exit.len(),
2773                results.trap.len(),
2774            );
2775        } else {
2776            println!(
2777                "Model check completed: {} failed, {} passed, {} exited",
2778                results.fail.len(),
2779                results.pass.len(),
2780                results.exit.len(),
2781            );
2782        }
2783    }
2784
2785    // ===============================================================================
2786    // ```````````````````````` MODEL CHECKER INTERNAL HELPERS ```````````````````````
2787    // ===============================================================================
2788
2789    /// Compares lazy and manual withdrawal results and evaluates drift.
2790    ///
2791    /// Computes the absolute difference (`diff`) between lazy and manual
2792    /// values and checks it against allowed tolerance thresholds:
2793    ///
2794    /// - Ignores negligible differences (diff <= 1) (rounding differences)
2795    /// - Computes basis points (bps) relative to the larger value
2796    /// - Accepts results within `allowed_bps` or `allowed_diff` thresholds
2797    ///
2798    /// If the deviation exceeds both thresholds, returns a [`BalanceExit`]
2799    /// containing:
2800    /// - lazy and manual values
2801    /// - absolute difference
2802    /// - basis points difference
2803    /// - execution trace (sequence)
2804    ///
2805    /// This is used to validate that the lazy model maintains acceptable
2806    /// precision relative to the manual reference model.
2807    fn compare_balances_values<T: LazyBalanceMarker, M: ManualBalanceModel<T>>(
2808        lazy: T::Asset,
2809        manual: T::Asset,
2810        seq: &[BalanceOp<T, M>],
2811        allowed_bps: u32,
2812        allowed_diff: u32,
2813    ) -> Result<(), BalanceExit<T, M>> {
2814        let lazy = lazy.into();
2815        let diff = if lazy > manual {
2816            lazy - manual
2817        } else {
2818            manual - lazy
2819        };
2820
2821        if diff <= One::one() {
2822            return Ok(());
2823        }
2824
2825        let base = lazy.max(manual).max(1u8.into());
2826
2827        if diff * 10_000u32.into() > base * allowed_bps.into() {
2828            if diff <= allowed_diff.into() {
2829                return Ok(());
2830            }
2831            return Err(BalanceExit::<T, M> {
2832                lazy,
2833                manual,
2834                diff,
2835                bps: diff * 10_000u32.into() / base,
2836                sequence: seq.to_vec(),
2837            });
2838        }
2839
2840        Ok(())
2841    }
2842
2843    /// Records a failed execution for the given trace.
2844    ///
2845    /// Wraps the reason and trace into a [`BalanceFailure`] and stores it.
2846    /// If traps are active, records it as a trapped failure.
2847    fn record_err<T: LazyBalanceMarker, M: ManualBalanceModel<T>>(
2848        results: &mut BalanceModelResults<T, M>,
2849        trace: &[BalanceOp<T, M>],
2850        reason: String,
2851        traps: Option<&String>,
2852    ) {
2853        let failure = BalanceFailure {
2854            reason,
2855            sequence: trace.to_vec(),
2856        };
2857
2858        match traps {
2859            Some(e) => results.record_trapped(trace, Err(failure), Some(e)),
2860            None => results.record(trace, Err(failure)),
2861        }
2862    }
2863
2864    /// Records a successful execution for the given trace.
2865    ///
2866    /// Marks the sequence as passed. Behavior is identical regardless of traps.
2867    fn record_pass<T: LazyBalanceMarker, M: ManualBalanceModel<T>>(
2868        results: &mut BalanceModelResults<T, M>,
2869        trace: &[BalanceOp<T, M>],
2870        traps: Option<&String>,
2871    ) {
2872        match traps {
2873            Some(_) => results.record(trace, Ok(())),
2874            None => results.record(trace, Ok(())),
2875        }
2876    }
2877
2878    /// Records an exit (drift) between lazy and manual models.
2879    ///
2880    /// Stores the [`BalanceExit`] for later analysis.
2881    fn record_exit<T: LazyBalanceMarker, M: ManualBalanceModel<T>>(
2882        results: &mut BalanceModelResults<T, M>,
2883        exit: BalanceExit<T, M>,
2884    ) {
2885        results.exit_record(exit)
2886    }
2887
2888    // ===============================================================================
2889    // ```````````````````````````` MODEL CHECKER RESULTS ````````````````````````````
2890    // ===============================================================================
2891
2892    /// Aggregates results produced during state exploration.
2893    ///
2894    /// Stores categorized outcomes for executed sequences:
2895    /// - `pass`: successful sequences
2896    /// - `fail`: sequences that resulted in an error ([`BalanceFailure`])
2897    /// - `exit`: sequences with drift between models ([`BalanceExit`])
2898    /// - `trap`: sequences that triggered trap conditions
2899    ///
2900    /// Each entry is paired with a unique identifier for tracking.
2901    ///
2902    /// The `next_*_id` fields maintain incremental IDs for each category.
2903    #[derive(Debug, Clone, Eq, PartialEq)]
2904    pub struct BalanceModelResults<T: LazyBalanceMarker, M: ManualBalanceModel<T>> {
2905        /// Successful sequences.
2906        pub pass: Vec<(usize, Vec<BalanceOp<T, M>>)>,
2907
2908        /// Failed sequences with associated error.
2909        pub fail: Vec<(usize, BalanceFailure<T, M>)>,
2910
2911        /// Drifted sequences between lazy and manual models.
2912        pub exit: Vec<(usize, BalanceExit<T, M>)>,
2913
2914        /// Trap-triggered sequences with reason.
2915        pub trap: Vec<(usize, String, Vec<BalanceOp<T, M>>)>,
2916
2917        /// Next identifier for pass entries.
2918        pub next_pass_id: usize,
2919
2920        /// Next identifier for exit entries.
2921        pub next_exit_id: usize,
2922
2923        /// Next identifier for fail entries.
2924        pub next_fail_id: usize,
2925
2926        /// Next identifier for trap entries.
2927        pub next_trap_id: usize,
2928    }
2929
2930    impl<T: LazyBalanceMarker, M: ManualBalanceModel<T>> BalanceModelResults<T, M> {
2931        /// Creates a new empty result container with initialized IDs.
2932        fn new() -> Self {
2933            Self {
2934                pass: Vec::new(),
2935                fail: Vec::new(),
2936                exit: Vec::new(),
2937                trap: Vec::new(),
2938                next_pass_id: 1,
2939                next_exit_id: 1,
2940                next_fail_id: 1,
2941                next_trap_id: 1,
2942            }
2943        }
2944
2945        /// Records an exit (drift) with a unique ID.
2946        fn exit_record(&mut self, exit: BalanceExit<T, M>) {
2947            let id = self.next_exit_id;
2948            self.next_exit_id += 1;
2949            self.exit.push((id, exit));
2950        }
2951
2952        /// Records a result without trap context.
2953        /// Delegates to [`Self::record_trapped`] with no trap expectation.
2954        fn record(&mut self, seq: &[BalanceOp<T, M>], result: Result<(), BalanceFailure<T, M>>) {
2955            self.record_trapped(seq, result, None);
2956        }
2957
2958        /// Records a result with optional trap expectation.
2959        /// - `Ok`: stored as pass
2960        /// - `Err`:
2961        ///     - if matches expected trap reason, stored as trap
2962        ///     - otherwise, stored as failure
2963        fn record_trapped(
2964            &mut self,
2965            seq: &[BalanceOp<T, M>],
2966            result: Result<(), BalanceFailure<T, M>>,
2967            traps: Option<&String>,
2968        ) {
2969            match result {
2970                Ok(_) => {
2971                    let id = self.next_pass_id;
2972                    self.next_pass_id += 1;
2973
2974                    self.pass.push((id, seq.to_vec()));
2975                }
2976
2977                Err(f) => {
2978                    if let Some(trap) = traps {
2979                        if *trap == f.reason {
2980                            // Expected trap
2981                            let id = self.next_trap_id;
2982                            self.next_trap_id += 1;
2983
2984                            self.trap.push((id, f.reason.clone(), seq.to_vec()));
2985
2986                            return;
2987                        }
2988                    }
2989
2990                    let id = self.next_fail_id;
2991                    self.next_fail_id += 1;
2992
2993                    self.fail.push((id, f.clone()));
2994                }
2995            }
2996        }
2997    }
2998
2999    // ===============================================================================
3000    // `````````````````````` LAZY BALANCE CONVENIENCE CALLERS ```````````````````````
3001    // ===============================================================================
3002
3003    /// Executes a deposit on the lazy balance model using the provided container.
3004    ///
3005    /// Constructs the required tagged input, invokes [`LazyBalance::deposit`], and
3006    /// extracts the resolved (asset, receipt) output.
3007    ///
3008    /// Returns the actual deposited value and issued receipt.
3009    fn deposit<'a, T: LazyBalanceMarker>(
3010        model: &'a mut LazyContainer<T>,
3011        value: T::Asset,
3012        subject: &'a T::Subject,
3013    ) -> Result<(T::Asset, T::Receipt), Error<T>> {
3014        let input = <T::Input<'_> as FromTag<_, Deposit>>::from_tag((
3015            MutHandle::Borrowed(&mut model.balance),
3016            Cow::Borrowed(&model.variant),
3017            Cow::Borrowed(&model.id),
3018            Cow::Owned(value),
3019            Cow::Borrowed(subject),
3020        ));
3021
3022        let raw = T::deposit(input);
3023
3024        let Ok(result) = TryIntoTag::<_, Deposit>::try_into_tag(raw) else {
3025            unreachable!()
3026        };
3027
3028        match result {
3029            Ok((asset, receipt)) => Ok((asset.into_owned(), receipt.into_owned())),
3030            Err(e) => Err(e),
3031        }
3032    }
3033
3034    /// Executes a withdrawal on the lazy balance model.
3035    ///
3036    /// Consumes the provided receipt, invokes [`LazyBalance::withdraw`], and
3037    /// returns the resolved asset value.
3038    fn withdraw<'a, T: LazyBalanceMarker>(
3039        model: &'a mut LazyContainer<T>,
3040        receipt: T::Receipt,
3041    ) -> Result<T::Asset, Error<T>> {
3042        let input = <T::Input<'_> as FromTag<_, Withdraw>>::from_tag((
3043            MutHandle::Borrowed(&mut model.balance),
3044            Cow::Borrowed(&model.variant),
3045            Cow::Borrowed(&model.id),
3046            Cow::Owned(receipt),
3047        ));
3048
3049        let raw = T::withdraw(input);
3050
3051        let Ok(result) = TryIntoTag::<_, Withdraw>::try_into_tag(raw) else {
3052            unreachable!()
3053        };
3054
3055        match result {
3056            Ok(v) => Ok(v.into_owned()),
3057            Err(e) => Err(e),
3058        }
3059    }
3060
3061    /// Executes a mint operation on the lazy balance model.
3062    ///
3063    /// Constructs the tagged input, invokes [`LazyBalance::mint`], and returns
3064    /// the resolved asset value added to the balance.
3065    fn mint<'a, T: LazyBalanceMarker>(
3066        model: &'a mut LazyContainer<T>,
3067        value: T::Asset,
3068        subject: &'a T::Subject,
3069    ) -> Result<T::Asset, Error<T>> {
3070        let input = <T::Input<'_> as FromTag<_, Mint>>::from_tag((
3071            MutHandle::Borrowed(&mut model.balance),
3072            Cow::Borrowed(&model.variant),
3073            Cow::Borrowed(&model.id),
3074            Cow::Owned(value),
3075            Cow::Borrowed(subject),
3076        ));
3077
3078        let raw = T::mint(input);
3079
3080        let Ok(result) = TryIntoTag::<_, Mint>::try_into_tag(raw) else {
3081            unreachable!()
3082        };
3083
3084        match result {
3085            Ok(v) => Ok(v.into_owned()),
3086            Err(e) => Err(e),
3087        }
3088    }
3089
3090    /// Executes a reap operation on the lazy balance model.
3091    ///
3092    /// Constructs the tagged input, invokes [`LazyBalance::reap`], and returns
3093    /// the resolved asset value removed or adjusted from the balance.
3094    fn reap<'a, T: LazyBalanceMarker>(
3095        model: &'a mut LazyContainer<T>,
3096        value: T::Asset,
3097        subject: &'a T::Subject,
3098    ) -> Result<T::Asset, Error<T>> {
3099        let input = <T::Input<'_> as FromTag<_, Reap>>::from_tag((
3100            MutHandle::Borrowed(&mut model.balance),
3101            Cow::Borrowed(&model.variant),
3102            Cow::Borrowed(&model.id),
3103            Cow::Owned(value),
3104            Cow::Borrowed(subject),
3105        ));
3106
3107        let raw = T::reap(input);
3108
3109        let Ok(result) = TryIntoTag::<_, Reap>::try_into_tag(raw) else {
3110            unreachable!()
3111        };
3112
3113        match result {
3114            Ok(v) => Ok(v.into_owned()),
3115            Err(e) => Err(e),
3116        }
3117    }
3118
3119    /// Executes a drain operation on the lazy balance model.
3120    ///
3121    /// Constructs the tagged input, invokes [`LazyBalance::drain`].
3122    ///
3123    /// Clears the balance state and returns the resolved drained value.
3124    fn drain<'a, T: LazyBalanceMarker>(
3125        model: &'a mut LazyContainer<T>,
3126    ) -> Result<T::Asset, Error<T>> {
3127        let input = <T::Input<'_> as FromTag<_, Drain>>::from_tag((
3128            MutHandle::Borrowed(&mut model.balance),
3129            Cow::Borrowed(&model.variant),
3130            Cow::Borrowed(&model.id),
3131        ));
3132
3133        let raw = <T as LazyBalance>::drain(input);
3134
3135        let Ok(result) = TryIntoTag::<_, Drain>::try_into_tag(raw) else {
3136            unreachable!()
3137        };
3138
3139        match result {
3140            Ok(v) => Ok(v.into_owned()),
3141            Err(e) => Err(e),
3142        }
3143    }
3144}