@@ -2536,3 +2536,177 @@ fn refund_alpha_same_cold_multiple_hotkeys_conserved_to_owner() {
25362536 ) ;
25372537 } ) ;
25382538}
2539+
2540+ #[ test]
2541+ fn test_dissolve_v3_green_path_refund_tao_stake_alpha_and_clear_state ( ) {
2542+ new_test_ext ( ) . execute_with ( || {
2543+ // --- Setup ---
2544+ let netuid = NetUid :: from ( 42 ) ;
2545+ let cold = OK_COLDKEY_ACCOUNT_ID ;
2546+ let hot = OK_HOTKEY_ACCOUNT_ID ;
2547+
2548+ assert_ok ! ( Swap :: toggle_user_liquidity(
2549+ RuntimeOrigin :: root( ) ,
2550+ netuid. into( ) ,
2551+ true
2552+ ) ) ;
2553+ assert_ok ! ( Pallet :: <Test >:: maybe_initialize_v3( netuid) ) ;
2554+ assert ! ( SwapV3Initialized :: <Test >:: get( netuid) ) ;
2555+
2556+ // Tight in‑range band so BOTH τ and α are required.
2557+ let ct = CurrentTick :: < Test > :: get ( netuid) ;
2558+ let tick_low = ct. saturating_sub ( 10 ) ;
2559+ let tick_high = ct. saturating_add ( 10 ) ;
2560+ let liquidity: u64 = 1_250_000 ;
2561+
2562+ // Add liquidity and capture required τ/α.
2563+ let ( _pos_id, tao_needed, alpha_needed) =
2564+ Pallet :: < Test > :: do_add_liquidity ( netuid, & cold, & hot, tick_low, tick_high, liquidity)
2565+ . expect ( "add in-range liquidity" ) ;
2566+ assert ! ( tao_needed > 0 , "in-range pos must require TAO" ) ;
2567+ assert ! ( alpha_needed > 0 , "in-range pos must require ALPHA" ) ;
2568+
2569+ // Determine the permitted validator with the highest trust (green path).
2570+ let trust = <Test as Config >:: SubnetInfo :: get_validator_trust ( netuid. into ( ) ) ;
2571+ let permit = <Test as Config >:: SubnetInfo :: get_validator_permit ( netuid. into ( ) ) ;
2572+ assert_eq ! ( trust. len( ) , permit. len( ) , "trust/permit must align" ) ;
2573+ let target_uid: u16 = trust
2574+ . iter ( )
2575+ . zip ( permit. iter ( ) )
2576+ . enumerate ( )
2577+ . filter ( |( _, ( _t, p) ) | * * p)
2578+ . max_by_key ( |( _, ( t, _) ) | * t)
2579+ . map ( |( i, _) | i as u16 )
2580+ . expect ( "at least one permitted validator" ) ;
2581+ let validator_hotkey: <Test as frame_system:: Config >:: AccountId =
2582+ <Test as Config >:: SubnetInfo :: hotkey_of_uid ( netuid. into ( ) , target_uid)
2583+ . expect ( "uid -> hotkey mapping must exist" ) ;
2584+
2585+ // --- Snapshot BEFORE we withdraw τ/α to fund the position ---
2586+ let tao_before = <Test as Config >:: BalanceOps :: tao_balance ( & cold) ;
2587+
2588+ let alpha_before_hot =
2589+ <Test as Config >:: BalanceOps :: alpha_balance ( netuid. into ( ) , & cold, & hot) ;
2590+ let alpha_before_owner =
2591+ <Test as Config >:: BalanceOps :: alpha_balance ( netuid. into ( ) , & cold, & cold) ;
2592+ let alpha_before_val =
2593+ <Test as Config >:: BalanceOps :: alpha_balance ( netuid. into ( ) , & cold, & validator_hotkey) ;
2594+
2595+ let alpha_before_total = if validator_hotkey == hot {
2596+ // Avoid double counting when validator == user's hotkey.
2597+ alpha_before_hot + alpha_before_owner
2598+ } else {
2599+ alpha_before_hot + alpha_before_owner + alpha_before_val
2600+ } ;
2601+
2602+ // --- Mirror extrinsic bookkeeping: withdraw τ & α; bump provided reserves ---
2603+ let tao_taken = <Test as Config >:: BalanceOps :: decrease_balance ( & cold, tao_needed. into ( ) )
2604+ . expect ( "decrease TAO" ) ;
2605+ let alpha_taken = <Test as Config >:: BalanceOps :: decrease_stake (
2606+ & cold,
2607+ & hot,
2608+ netuid. into ( ) ,
2609+ alpha_needed. into ( ) ,
2610+ )
2611+ . expect ( "decrease ALPHA" ) ;
2612+
2613+ <Test as Config >:: BalanceOps :: increase_provided_tao_reserve ( netuid. into ( ) , tao_taken) ;
2614+ <Test as Config >:: BalanceOps :: increase_provided_alpha_reserve ( netuid. into ( ) , alpha_taken) ;
2615+
2616+ // --- Act: dissolve (GREEN PATH: permitted validators exist) ---
2617+ assert_ok ! ( Pallet :: <Test >:: do_dissolve_all_liquidity_providers( netuid) ) ;
2618+
2619+ // --- Assert: τ principal refunded to user ---
2620+ let tao_after = <Test as Config >:: BalanceOps :: tao_balance ( & cold) ;
2621+ assert_eq ! ( tao_after, tao_before, "TAO principal must be refunded" ) ;
2622+
2623+ // --- α ledger assertions ---
2624+ let alpha_after_hot =
2625+ <Test as Config >:: BalanceOps :: alpha_balance ( netuid. into ( ) , & cold, & hot) ;
2626+ let alpha_after_owner =
2627+ <Test as Config >:: BalanceOps :: alpha_balance ( netuid. into ( ) , & cold, & cold) ;
2628+ let alpha_after_val =
2629+ <Test as Config >:: BalanceOps :: alpha_balance ( netuid. into ( ) , & cold, & validator_hotkey) ;
2630+
2631+ // Owner ledger must be unchanged in the green path.
2632+ assert_eq ! (
2633+ alpha_after_owner, alpha_before_owner,
2634+ "Owner α ledger must be unchanged (staked to validator, not refunded)"
2635+ ) ;
2636+
2637+ if validator_hotkey == hot {
2638+ // Net effect: user's hot ledger returns to its original balance.
2639+ assert_eq ! (
2640+ alpha_after_hot, alpha_before_hot,
2641+ "When validator == hotkey, user's hot ledger must net back to its original balance"
2642+ ) ;
2643+
2644+ // Totals without double-counting the same ledger.
2645+ let alpha_after_total = alpha_after_hot + alpha_after_owner;
2646+ assert_eq ! (
2647+ alpha_after_total, alpha_before_total,
2648+ "Total α for the coldkey must be conserved (validator==hotkey)"
2649+ ) ;
2650+ } else {
2651+ assert ! (
2652+ alpha_before_hot >= alpha_after_hot,
2653+ "hot ledger should not increase"
2654+ ) ;
2655+ assert ! (
2656+ alpha_after_val >= alpha_before_val,
2657+ "validator ledger should not decrease"
2658+ ) ;
2659+
2660+ let hot_loss = alpha_before_hot - alpha_after_hot;
2661+ let val_gain = alpha_after_val - alpha_before_val;
2662+
2663+ assert_eq ! (
2664+ val_gain, hot_loss,
2665+ "α that left the user's hot ledger must equal α credited to the validator ledger"
2666+ ) ;
2667+
2668+ // Totals across distinct ledgers must be conserved.
2669+ let alpha_after_total = alpha_after_hot + alpha_after_owner + alpha_after_val;
2670+ assert_eq ! (
2671+ alpha_after_total, alpha_before_total,
2672+ "Total α for the coldkey must be conserved"
2673+ ) ;
2674+ }
2675+
2676+ // --- Assert: All positions (user + protocol) removed and V3 state cleared ---
2677+ let protocol_id = Pallet :: < Test > :: protocol_account_id ( ) ;
2678+
2679+ assert_eq ! ( Pallet :: <Test >:: count_positions( netuid, & cold) , 0 ) ;
2680+ let prot_positions_after =
2681+ Positions :: < Test > :: iter_prefix_values ( ( netuid, protocol_id) ) . collect :: < Vec < _ > > ( ) ;
2682+ assert ! (
2683+ prot_positions_after. is_empty( ) ,
2684+ "protocol positions must be removed"
2685+ ) ;
2686+
2687+ // Ticks / liquidity / price / flags cleared
2688+ assert ! ( Ticks :: <Test >:: iter_prefix( netuid) . next( ) . is_none( ) ) ;
2689+ assert ! ( Ticks :: <Test >:: get( netuid, TickIndex :: MIN ) . is_none( ) ) ;
2690+ assert ! ( Ticks :: <Test >:: get( netuid, TickIndex :: MAX ) . is_none( ) ) ;
2691+ assert ! ( !CurrentLiquidity :: <Test >:: contains_key( netuid) ) ;
2692+ assert ! ( !CurrentTick :: <Test >:: contains_key( netuid) ) ;
2693+ assert ! ( !AlphaSqrtPrice :: <Test >:: contains_key( netuid) ) ;
2694+ assert ! ( !SwapV3Initialized :: <Test >:: contains_key( netuid) ) ;
2695+
2696+ // Fee globals cleared
2697+ assert ! ( !FeeGlobalTao :: <Test >:: contains_key( netuid) ) ;
2698+ assert ! ( !FeeGlobalAlpha :: <Test >:: contains_key( netuid) ) ;
2699+
2700+ // Active tick bitmap cleared
2701+ assert ! (
2702+ TickIndexBitmapWords :: <Test >:: iter_prefix( ( netuid, ) )
2703+ . next( )
2704+ . is_none( ) ,
2705+ "active tick bitmap words must be cleared"
2706+ ) ;
2707+
2708+ // Knobs removed
2709+ assert ! ( !FeeRate :: <Test >:: contains_key( netuid) ) ;
2710+ assert ! ( !EnabledUserLiquidity :: <Test >:: contains_key( netuid) ) ;
2711+ } ) ;
2712+ }
0 commit comments