@@ -1982,7 +1982,6 @@ fn test_swap_subtoken_disabled() {
19821982 } ) ;
19831983}
19841984
1985- /// V3 path: protocol + user positions exist, fees accrued, everything must be removed.
19861985#[ test]
19871986fn test_liquidate_v3_removes_positions_ticks_and_state ( ) {
19881987 new_test_ext ( ) . execute_with ( || {
@@ -1992,7 +1991,7 @@ fn test_liquidate_v3_removes_positions_ticks_and_state() {
19921991 assert_ok ! ( Pallet :: <Test >:: maybe_initialize_v3( netuid) ) ;
19931992 assert ! ( SwapV3Initialized :: <Test >:: get( netuid) ) ;
19941993
1995- // Enable user LP (mock usually enables for 0..=100, but be explicit and consistent)
1994+ // Enable user LP
19961995 assert_ok ! ( Swap :: toggle_user_liquidity(
19971996 RuntimeOrigin :: root( ) ,
19981997 netuid. into( ) ,
@@ -2041,14 +2040,14 @@ fn test_liquidate_v3_removes_positions_ticks_and_state() {
20412040 assert ! ( Ticks :: <Test >:: get( netuid, TickIndex :: MAX ) . is_some( ) ) ;
20422041 assert ! ( CurrentLiquidity :: <Test >:: get( netuid) > 0 ) ;
20432042
2044- // There should be some bitmap words (active ticks) after adding a position.
20452043 let had_bitmap_words = TickIndexBitmapWords :: < Test > :: iter_prefix ( ( netuid, ) )
20462044 . next ( )
20472045 . is_some ( ) ;
20482046 assert ! ( had_bitmap_words) ;
20492047
2050- // ACT: Liquidate & reset swap state
2048+ // ACT: users-only liquidation then protocol clear
20512049 assert_ok ! ( Pallet :: <Test >:: do_dissolve_all_liquidity_providers( netuid) ) ;
2050+ assert_ok ! ( Pallet :: <Test >:: do_clear_protocol_liquidity( netuid) ) ;
20522051
20532052 // ASSERT: positions cleared (both user and protocol)
20542053 assert_eq ! (
@@ -2091,12 +2090,11 @@ fn test_liquidate_v3_removes_positions_ticks_and_state() {
20912090 } ) ;
20922091}
20932092
2094- /// V3 path with user liquidity disabled at teardown: must still remove all positions and clear state.
2093+ /// V3 path with user liquidity disabled at teardown:
2094+ /// must still remove positions and clear state (after protocol clear).
20952095#[ test]
20962096fn test_liquidate_v3_with_user_liquidity_disabled ( ) {
20972097 new_test_ext ( ) . execute_with ( || {
2098- // Pick a netuid the mock treats as "disabled" by default (per your comment >100),
2099- // then explicitly walk through enable -> add -> disable -> liquidate.
21002098 let netuid = NetUid :: from ( 101 ) ;
21012099
21022100 assert_ok ! ( Pallet :: <Test >:: maybe_initialize_v3( netuid) ) ;
@@ -2125,15 +2123,16 @@ fn test_liquidate_v3_with_user_liquidity_disabled() {
21252123 )
21262124 . expect ( "add liquidity" ) ;
21272125
2128- // Disable user LP *before* liquidation to validate that removal ignores this flag.
2126+ // Disable user LP *before* liquidation; removal must ignore this flag.
21292127 assert_ok ! ( Swap :: toggle_user_liquidity(
21302128 RuntimeOrigin :: root( ) ,
21312129 netuid. into( ) ,
21322130 false
21332131 ) ) ;
21342132
2135- // ACT
2133+ // Users-only dissolve, then clear protocol liquidity/state.
21362134 assert_ok ! ( Pallet :: <Test >:: do_dissolve_all_liquidity_providers( netuid) ) ;
2135+ assert_ok ! ( Pallet :: <Test >:: do_clear_protocol_liquidity( netuid) ) ;
21372136
21382137 // ASSERT: positions & ticks gone, state reset
21392138 assert_eq ! (
@@ -2158,7 +2157,7 @@ fn test_liquidate_v3_with_user_liquidity_disabled() {
21582157 assert ! ( !FeeGlobalTao :: <Test >:: contains_key( netuid) ) ;
21592158 assert ! ( !FeeGlobalAlpha :: <Test >:: contains_key( netuid) ) ;
21602159
2161- // `EnabledUserLiquidity` is removed by liquidation .
2160+ // `EnabledUserLiquidity` is removed by protocol clear stage .
21622161 assert ! ( !EnabledUserLiquidity :: <Test >:: contains_key( netuid) ) ;
21632162 } ) ;
21642163}
@@ -2205,7 +2204,6 @@ fn test_liquidate_non_v3_uninitialized_ok_and_clears() {
22052204 } ) ;
22062205}
22072206
2208- /// Idempotency: calling liquidation twice is safe (both V3 and non‑V3 flavors).
22092207#[ test]
22102208fn test_liquidate_idempotent ( ) {
22112209 // V3 flavor
@@ -2230,11 +2228,14 @@ fn test_liquidate_idempotent() {
22302228 123_456_789
22312229 ) ) ;
22322230
2233- // 1st liquidation
2231+ // Users-only liquidations are idempotent.
22342232 assert_ok ! ( Pallet :: <Test >:: do_dissolve_all_liquidity_providers( netuid) ) ;
2235- // 2nd liquidation (no state left) — must still succeed
22362233 assert_ok ! ( Pallet :: <Test >:: do_dissolve_all_liquidity_providers( netuid) ) ;
22372234
2235+ // Now clear protocol liquidity/state—also idempotent.
2236+ assert_ok ! ( Pallet :: <Test >:: do_clear_protocol_liquidity( netuid) ) ;
2237+ assert_ok ! ( Pallet :: <Test >:: do_clear_protocol_liquidity( netuid) ) ;
2238+
22382239 // State remains empty
22392240 assert ! (
22402241 Positions :: <Test >:: iter_prefix_values( ( netuid, OK_COLDKEY_ACCOUNT_ID ) )
@@ -2254,7 +2255,7 @@ fn test_liquidate_idempotent() {
22542255 new_test_ext ( ) . execute_with ( || {
22552256 let netuid = NetUid :: from ( 8 ) ;
22562257
2257- // Never initialize V3
2258+ // Never initialize V3; both calls no-op and succeed.
22582259 assert_ok ! ( Pallet :: <Test >:: do_dissolve_all_liquidity_providers( netuid) ) ;
22592260 assert_ok ! ( Pallet :: <Test >:: do_dissolve_all_liquidity_providers( netuid) ) ;
22602261
@@ -2286,7 +2287,7 @@ fn liquidate_v3_refunds_user_funds_and_clears_state() {
22862287 ) ) ;
22872288 assert_ok ! ( Pallet :: <Test >:: maybe_initialize_v3( netuid) ) ;
22882289
2289- // Use distinct cold/hot to demonstrate alpha refund goes to (owner, owner) .
2290+ // Use distinct cold/hot to demonstrate alpha refund/stake accounting .
22902291 let cold = OK_COLDKEY_ACCOUNT_ID ;
22912292 let hot = OK_HOTKEY_ACCOUNT_ID ;
22922293
@@ -2322,25 +2323,27 @@ fn liquidate_v3_refunds_user_funds_and_clears_state() {
23222323 <Test as Config >:: BalanceOps :: increase_provided_tao_reserve ( netuid. into ( ) , tao_taken) ;
23232324 <Test as Config >:: BalanceOps :: increase_provided_alpha_reserve ( netuid. into ( ) , alpha_taken) ;
23242325
2325- // Liquidate everything on the subnet .
2326+ // Users‑only liquidation .
23262327 assert_ok ! ( Pallet :: <Test >:: do_dissolve_all_liquidity_providers( netuid) ) ;
23272328
23282329 // Expect balances restored to BEFORE snapshots (no swaps ran -> zero fees).
2329- // TAO: we withdrew 'need_tao' above and liquidation refunded it, so we should be back to 'tao_before'.
23302330 let tao_after = <Test as Config >:: BalanceOps :: tao_balance ( & cold) ;
23312331 assert_eq ! ( tao_after, tao_before, "TAO principal must be refunded" ) ;
23322332
2333- // ALPHA: refund is credited to (coldkey=cold, hotkey=cold). Compare totals across both ledgers .
2333+ // ALPHA totals conserved to owner (distribution may differ) .
23342334 let alpha_after_hot =
23352335 <Test as Config >:: BalanceOps :: alpha_balance ( netuid. into ( ) , & cold, & hot) ;
23362336 let alpha_after_owner =
23372337 <Test as Config >:: BalanceOps :: alpha_balance ( netuid. into ( ) , & cold, & cold) ;
23382338 let alpha_after_total = alpha_after_hot + alpha_after_owner;
23392339 assert_eq ! (
23402340 alpha_after_total, alpha_before_total,
2341- "ALPHA principal must be refunded to the account (may be credited to (owner, owner) )"
2341+ "ALPHA principal must be refunded/staked for the account (check totals )"
23422342 ) ;
23432343
2344+ // Clear protocol liquidity and V3 state now.
2345+ assert_ok ! ( Pallet :: <Test >:: do_clear_protocol_liquidity( netuid) ) ;
2346+
23442347 // User position(s) are gone and all V3 state cleared.
23452348 assert_eq ! ( Pallet :: <Test >:: count_positions( netuid, & cold) , 0 ) ;
23462349 assert ! ( Ticks :: <Test >:: iter_prefix( netuid) . next( ) . is_none( ) ) ;
@@ -2386,20 +2389,23 @@ fn refund_alpha_single_provider_exact() {
23862389 . expect ( "decrease ALPHA" ) ;
23872390 <Test as Config >:: BalanceOps :: increase_provided_alpha_reserve ( netuid. into ( ) , alpha_taken) ;
23882391
2389- // --- Act: dissolve (calls refund_alpha inside) .
2392+ // --- Act: users‑only dissolve .
23902393 assert_ok ! ( Pallet :: <Test >:: do_dissolve_all_liquidity_providers( netuid) ) ;
23912394
2392- // --- Assert: refunded back to the owner (may credit to (cold,cold) ).
2395+ // --- Assert: total α conserved to owner (may be staked to validator ).
23932396 let alpha_after_hot =
23942397 <Test as Config >:: BalanceOps :: alpha_balance ( netuid. into ( ) , & cold, & hot) ;
23952398 let alpha_after_owner =
23962399 <Test as Config >:: BalanceOps :: alpha_balance ( netuid. into ( ) , & cold, & cold) ;
23972400 let alpha_after_total = alpha_after_hot + alpha_after_owner;
23982401 assert_eq ! (
23992402 alpha_after_total, alpha_before_total,
2400- "ALPHA principal must be conserved to the owner "
2403+ "ALPHA principal must be conserved to the account "
24012404 ) ;
24022405
2406+ // Clear protocol liquidity and V3 state now.
2407+ assert_ok ! ( Pallet :: <Test >:: do_clear_protocol_liquidity( netuid) ) ;
2408+
24032409 // --- State is cleared.
24042410 assert ! ( Ticks :: <Test >:: iter_prefix( netuid) . next( ) . is_none( ) ) ;
24052411 assert_eq ! ( Pallet :: <Test >:: count_positions( netuid, & cold) , 0 ) ;
@@ -2593,7 +2599,6 @@ fn test_dissolve_v3_green_path_refund_tao_stake_alpha_and_clear_state() {
25932599 <Test as Config >:: BalanceOps :: alpha_balance ( netuid. into ( ) , & cold, & validator_hotkey) ;
25942600
25952601 let alpha_before_total = if validator_hotkey == hot {
2596- // Avoid double counting when validator == user's hotkey.
25972602 alpha_before_hot + alpha_before_owner
25982603 } else {
25992604 alpha_before_hot + alpha_before_owner + alpha_before_val
@@ -2635,13 +2640,10 @@ fn test_dissolve_v3_green_path_refund_tao_stake_alpha_and_clear_state() {
26352640 ) ;
26362641
26372642 if validator_hotkey == hot {
2638- // Net effect: user's hot ledger returns to its original balance.
26392643 assert_eq ! (
26402644 alpha_after_hot, alpha_before_hot,
26412645 "When validator == hotkey, user's hot ledger must net back to its original balance"
26422646 ) ;
2643-
2644- // Totals without double-counting the same ledger.
26452647 let alpha_after_total = alpha_after_hot + alpha_after_owner;
26462648 assert_eq ! (
26472649 alpha_after_total, alpha_before_total,
@@ -2659,23 +2661,22 @@ fn test_dissolve_v3_green_path_refund_tao_stake_alpha_and_clear_state() {
26592661
26602662 let hot_loss = alpha_before_hot - alpha_after_hot;
26612663 let val_gain = alpha_after_val - alpha_before_val;
2662-
26632664 assert_eq ! (
26642665 val_gain, hot_loss,
26652666 "α that left the user's hot ledger must equal α credited to the validator ledger"
26662667 ) ;
26672668
2668- // Totals across distinct ledgers must be conserved.
26692669 let alpha_after_total = alpha_after_hot + alpha_after_owner + alpha_after_val;
26702670 assert_eq ! (
26712671 alpha_after_total, alpha_before_total,
26722672 "Total α for the coldkey must be conserved"
26732673 ) ;
26742674 }
26752675
2676- // --- Assert: All positions (user + protocol) removed and V3 state cleared ---
2677- let protocol_id = Pallet :: < Test > :: protocol_account_id ( ) ;
2676+ // Now clear protocol liquidity & state and assert full reset.
2677+ assert_ok ! ( Pallet :: <Test >:: do_clear_protocol_liquidity ( netuid ) ) ;
26782678
2679+ let protocol_id = Pallet :: < Test > :: protocol_account_id ( ) ;
26792680 assert_eq ! ( Pallet :: <Test >:: count_positions( netuid, & cold) , 0 ) ;
26802681 let prot_positions_after =
26812682 Positions :: < Test > :: iter_prefix_values ( ( netuid, protocol_id) ) . collect :: < Vec < _ > > ( ) ;
@@ -2684,7 +2685,6 @@ fn test_dissolve_v3_green_path_refund_tao_stake_alpha_and_clear_state() {
26842685 "protocol positions must be removed"
26852686 ) ;
26862687
2687- // Ticks / liquidity / price / flags cleared
26882688 assert ! ( Ticks :: <Test >:: iter_prefix( netuid) . next( ) . is_none( ) ) ;
26892689 assert ! ( Ticks :: <Test >:: get( netuid, TickIndex :: MIN ) . is_none( ) ) ;
26902690 assert ! ( Ticks :: <Test >:: get( netuid, TickIndex :: MAX ) . is_none( ) ) ;
@@ -2693,20 +2693,99 @@ fn test_dissolve_v3_green_path_refund_tao_stake_alpha_and_clear_state() {
26932693 assert ! ( !AlphaSqrtPrice :: <Test >:: contains_key( netuid) ) ;
26942694 assert ! ( !SwapV3Initialized :: <Test >:: contains_key( netuid) ) ;
26952695
2696- // Fee globals cleared
26972696 assert ! ( !FeeGlobalTao :: <Test >:: contains_key( netuid) ) ;
26982697 assert ! ( !FeeGlobalAlpha :: <Test >:: contains_key( netuid) ) ;
26992698
2700- // Active tick bitmap cleared
27012699 assert ! (
27022700 TickIndexBitmapWords :: <Test >:: iter_prefix( ( netuid, ) )
27032701 . next( )
27042702 . is_none( ) ,
27052703 "active tick bitmap words must be cleared"
27062704 ) ;
27072705
2706+ assert ! ( !FeeRate :: <Test >:: contains_key( netuid) ) ;
2707+ assert ! ( !EnabledUserLiquidity :: <Test >:: contains_key( netuid) ) ;
2708+ } ) ;
2709+ }
2710+
2711+ #[ test]
2712+ fn test_clear_protocol_liquidity_green_path ( ) {
2713+ new_test_ext ( ) . execute_with ( || {
2714+ // --- Arrange ---
2715+ let netuid = NetUid :: from ( 55 ) ;
2716+
2717+ // Ensure the "user liquidity enabled" flag exists so we can verify it's removed later.
2718+ assert_ok ! ( Pallet :: <Test >:: toggle_user_liquidity(
2719+ RuntimeOrigin :: root( ) ,
2720+ netuid,
2721+ true
2722+ ) ) ;
2723+
2724+ // Initialize V3 state; this should set price/tick flags and create a protocol position.
2725+ assert_ok ! ( Pallet :: <Test >:: maybe_initialize_v3( netuid) ) ;
2726+ assert ! (
2727+ SwapV3Initialized :: <Test >:: get( netuid) ,
2728+ "V3 must be initialized"
2729+ ) ;
2730+
2731+ // Sanity: protocol positions exist before clearing.
2732+ let protocol_id = Pallet :: < Test > :: protocol_account_id ( ) ;
2733+ let prot_positions_before =
2734+ Positions :: < Test > :: iter_prefix_values ( ( netuid, protocol_id) ) . collect :: < Vec < _ > > ( ) ;
2735+ assert ! (
2736+ !prot_positions_before. is_empty( ) ,
2737+ "protocol positions should exist after V3 init"
2738+ ) ;
2739+
2740+ // --- Act ---
2741+ // Green path: just clear protocol liquidity and wipe all V3 state.
2742+ assert_ok ! ( Pallet :: <Test >:: do_clear_protocol_liquidity( netuid) ) ;
2743+
2744+ // --- Assert: all protocol positions removed ---
2745+ let prot_positions_after =
2746+ Positions :: < Test > :: iter_prefix_values ( ( netuid, protocol_id) ) . collect :: < Vec < _ > > ( ) ;
2747+ assert ! (
2748+ prot_positions_after. is_empty( ) ,
2749+ "protocol positions must be removed by do_clear_protocol_liquidity"
2750+ ) ;
2751+
2752+ // --- Assert: V3 data wiped (idempotent even if some maps were empty) ---
2753+ // Ticks / active tick bitmap
2754+ assert ! ( Ticks :: <Test >:: iter_prefix( netuid) . next( ) . is_none( ) ) ;
2755+ assert ! (
2756+ TickIndexBitmapWords :: <Test >:: iter_prefix( ( netuid, ) )
2757+ . next( )
2758+ . is_none( ) ,
2759+ "active tick bitmap words must be cleared"
2760+ ) ;
2761+
2762+ // Fee globals
2763+ assert ! ( !FeeGlobalTao :: <Test >:: contains_key( netuid) ) ;
2764+ assert ! ( !FeeGlobalAlpha :: <Test >:: contains_key( netuid) ) ;
2765+
2766+ // Price / tick / liquidity / flags
2767+ assert ! ( !AlphaSqrtPrice :: <Test >:: contains_key( netuid) ) ;
2768+ assert ! ( !CurrentTick :: <Test >:: contains_key( netuid) ) ;
2769+ assert ! ( !CurrentLiquidity :: <Test >:: contains_key( netuid) ) ;
2770+ assert ! ( !SwapV3Initialized :: <Test >:: contains_key( netuid) ) ;
2771+
27082772 // Knobs removed
27092773 assert ! ( !FeeRate :: <Test >:: contains_key( netuid) ) ;
27102774 assert ! ( !EnabledUserLiquidity :: <Test >:: contains_key( netuid) ) ;
2775+
2776+ // --- And it's idempotent ---
2777+ assert_ok ! ( Pallet :: <Test >:: do_clear_protocol_liquidity( netuid) ) ;
2778+ assert ! (
2779+ Positions :: <Test >:: iter_prefix_values( ( netuid, protocol_id) )
2780+ . next( )
2781+ . is_none( )
2782+ ) ;
2783+ assert ! ( Ticks :: <Test >:: iter_prefix( netuid) . next( ) . is_none( ) ) ;
2784+ assert ! (
2785+ TickIndexBitmapWords :: <Test >:: iter_prefix( ( netuid, ) )
2786+ . next( )
2787+ . is_none( )
2788+ ) ;
2789+ assert ! ( !SwapV3Initialized :: <Test >:: contains_key( netuid) ) ;
27112790 } ) ;
27122791}
0 commit comments