@@ -5,7 +5,7 @@ use frame_support::storage::{TransactionOutcome, transactional};
55use frame_support:: { ensure, pallet_prelude:: DispatchError , traits:: Get } ;
66use safe_math:: * ;
77use sp_arithmetic:: helpers_128bit;
8- use sp_runtime:: { DispatchResult , traits:: AccountIdConversion } ;
8+ use sp_runtime:: { DispatchResult , Vec , traits:: AccountIdConversion } ;
99use substrate_fixed:: types:: { I64F64 , U64F64 , U96F32 } ;
1010use subtensor_runtime_common:: {
1111 AlphaCurrency , BalanceOps , Currency , NetUid , SubnetInfo , TaoCurrency ,
@@ -1216,80 +1216,176 @@ impl<T: Config> Pallet<T> {
12161216 /// Dissolve all LPs and clean state.
12171217 pub fn do_dissolve_all_liquidity_providers ( netuid : NetUid ) -> DispatchResult {
12181218 if SwapV3Initialized :: < T > :: get ( netuid) {
1219- // 1) Snapshot (owner, position_id).
1219+ // 1) Snapshot only *non‑protocol* positions: (owner, position_id).
12201220 struct CloseItem < A > {
12211221 owner : A ,
12221222 pos_id : PositionId ,
12231223 }
1224+ let protocol_account = Self :: protocol_account_id ( ) ;
1225+
12241226 let mut to_close: sp_std:: vec:: Vec < CloseItem < T :: AccountId > > = sp_std:: vec:: Vec :: new ( ) ;
12251227 for ( ( owner, pos_id) , _pos) in Positions :: < T > :: iter_prefix ( ( netuid, ) ) {
1226- to_close. push ( CloseItem { owner, pos_id } ) ;
1228+ if owner != protocol_account {
1229+ to_close. push ( CloseItem { owner, pos_id } ) ;
1230+ }
12271231 }
12281232
1229- let protocol_account = Self :: protocol_account_id ( ) ;
1233+ if to_close. is_empty ( ) {
1234+ log:: debug!(
1235+ "dissolve_all_lp: no user positions; netuid={netuid:?}, protocol liquidity untouched"
1236+ ) ;
1237+ return Ok ( ( ) ) ;
1238+ }
12301239
1231- // Non‑protocol first
1232- to_close
1233- . sort_by ( |a, b| ( a. owner == protocol_account) . cmp ( & ( b. owner == protocol_account) ) ) ;
1240+ let mut user_refunded_tao = TaoCurrency :: ZERO ;
1241+ let mut user_staked_alpha = AlphaCurrency :: ZERO ;
1242+
1243+ let trust: Vec < u16 > = T :: SubnetInfo :: get_validator_trust ( netuid. into ( ) ) ;
1244+ let permit: Vec < bool > = T :: SubnetInfo :: get_validator_permit ( netuid. into ( ) ) ;
1245+
1246+ // Helper: pick target validator uid, only among permitted validators, by highest trust.
1247+ let pick_target_uid = |trust : & Vec < u16 > , permit : & Vec < bool > | -> Option < u16 > {
1248+ let mut best_uid: Option < usize > = None ;
1249+ let mut best_trust: u16 = 0 ;
1250+ for ( i, ( & t, & p) ) in trust. iter ( ) . zip ( permit. iter ( ) ) . enumerate ( ) {
1251+ if p && ( best_uid. is_none ( ) || t > best_trust) {
1252+ best_uid = Some ( i) ;
1253+ best_trust = t;
1254+ }
1255+ }
1256+ best_uid. map ( |i| i as u16 )
1257+ } ;
12341258
12351259 for CloseItem { owner, pos_id } in to_close. into_iter ( ) {
12361260 match Self :: do_remove_liquidity ( netuid, & owner, pos_id) {
12371261 Ok ( rm) => {
1262+ // α withdrawn from the pool = principal + accrued fees
1263+ let alpha_total_from_pool: AlphaCurrency =
1264+ rm. alpha . saturating_add ( rm. fee_alpha ) ;
1265+
1266+ // ---------------- USER: refund τ and convert α → stake ----------------
1267+
1268+ // 1) Refund τ principal directly.
12381269 if rm. tao > TaoCurrency :: ZERO {
12391270 T :: BalanceOps :: increase_balance ( & owner, rm. tao ) ;
1240- }
1241- if owner != protocol_account {
1271+ user_refunded_tao = user_refunded_tao. saturating_add ( rm. tao ) ;
12421272 T :: BalanceOps :: decrease_provided_tao_reserve ( netuid, rm. tao ) ;
1243- let alpha_burn = rm. alpha . saturating_add ( rm. fee_alpha ) ;
1244- if alpha_burn > AlphaCurrency :: ZERO {
1245- T :: BalanceOps :: decrease_provided_alpha_reserve ( netuid, alpha_burn) ;
1273+ }
1274+
1275+ // 2) Stake ALL withdrawn α (principal + fees) to the best permitted validator.
1276+ if alpha_total_from_pool > AlphaCurrency :: ZERO {
1277+ if let Some ( target_uid) = pick_target_uid ( & trust, & permit) {
1278+ let validator_hotkey: T :: AccountId =
1279+ T :: SubnetInfo :: hotkey_of_uid ( netuid. into ( ) , target_uid) . ok_or (
1280+ sp_runtime:: DispatchError :: Other (
1281+ "validator_hotkey_missing" ,
1282+ ) ,
1283+ ) ?;
1284+
1285+ // Stake α from LP owner (coldkey) to chosen validator (hotkey).
1286+ T :: BalanceOps :: increase_stake (
1287+ & owner,
1288+ & validator_hotkey,
1289+ netuid,
1290+ alpha_total_from_pool,
1291+ ) ?;
1292+
1293+ user_staked_alpha =
1294+ user_staked_alpha. saturating_add ( alpha_total_from_pool) ;
1295+
1296+ log:: debug!(
1297+ "dissolve_all_lp: user dissolved & staked α: netuid={netuid:?}, owner={owner:?}, pos_id={pos_id:?}, α_staked={alpha_total_from_pool:?}, target_uid={target_uid}"
1298+ ) ;
1299+ } else {
1300+ // No permitted validators; burn to avoid balance drift.
1301+ log:: debug!(
1302+ "dissolve_all_lp: no permitted validators; α burned: netuid={netuid:?}, owner={owner:?}, pos_id={pos_id:?}, α_total={alpha_total_from_pool:?}"
1303+ ) ;
12461304 }
1305+
1306+ T :: BalanceOps :: decrease_provided_alpha_reserve (
1307+ netuid,
1308+ alpha_total_from_pool,
1309+ ) ;
12471310 }
12481311 }
12491312 Err ( e) => {
12501313 log:: debug!(
1251- "dissolve_all_lp: force-closing failed position : netuid={netuid:?}, owner={owner:?}, pos_id={pos_id:?}, err={e:?}"
1314+ "dissolve_all_lp: force-close failed: netuid={netuid:?}, owner={owner:?}, pos_id={pos_id:?}, err={e:?}"
12521315 ) ;
12531316 continue ;
12541317 }
12551318 }
12561319 }
12571320
1258- // 3) Clear active tick index entries, then all swap state.
1259- let active_ticks: sp_std:: vec:: Vec < TickIndex > =
1260- Ticks :: < T > :: iter_prefix ( netuid) . map ( |( ti, _) | ti) . collect ( ) ;
1261- for ti in active_ticks {
1262- ActiveTickIndexManager :: < T > :: remove ( netuid, ti) ;
1263- }
1321+ log:: debug!(
1322+ "dissolve_all_liquidity_providers (users-only): netuid={netuid:?}, users_refunded_total_τ={user_refunded_tao:?}, users_staked_total_α={user_staked_alpha:?}; protocol liquidity untouched"
1323+ ) ;
12641324
1265- let _ = Positions :: < T > :: clear_prefix ( ( netuid , ) , u32 :: MAX , None ) ;
1266- let _ = Ticks :: < T > :: clear_prefix ( netuid , u32 :: MAX , None ) ;
1325+ return Ok ( ( ) ) ;
1326+ }
12671327
1268- FeeGlobalTao :: < T > :: remove ( netuid) ;
1269- FeeGlobalAlpha :: < T > :: remove ( netuid) ;
1270- CurrentLiquidity :: < T > :: remove ( netuid) ;
1271- CurrentTick :: < T > :: remove ( netuid) ;
1272- AlphaSqrtPrice :: < T > :: remove ( netuid) ;
1273- SwapV3Initialized :: < T > :: remove ( netuid) ;
1328+ log:: debug!(
1329+ "dissolve_all_liquidity_providers: netuid={netuid:?}, mode=V2-or-nonV3, leaving all liquidity/state intact"
1330+ ) ;
12741331
1275- let _ = TickIndexBitmapWords :: < T > :: clear_prefix ( ( netuid, ) , u32:: MAX , None ) ;
1276- FeeRate :: < T > :: remove ( netuid) ;
1277- EnabledUserLiquidity :: < T > :: remove ( netuid) ;
1332+ Ok ( ( ) )
1333+ }
12781334
1279- log :: debug! (
1280- "dissolve_all_liquidity_providers: netuid={netuid:?}, mode=V3, positions closed; τ principal refunded; α burned; state cleared"
1281- ) ;
1335+ /// Clear **protocol-owned** liquidity and wipe all swap state for `netuid`.
1336+ pub fn do_clear_protocol_liquidity ( netuid : NetUid ) -> DispatchResult {
1337+ let protocol_account = Self :: protocol_account_id ( ) ;
12821338
1283- return Ok ( ( ) ) ;
1339+ // 1) Force-close only protocol positions, burning proceeds.
1340+ let mut burned_tao = TaoCurrency :: ZERO ;
1341+ let mut burned_alpha = AlphaCurrency :: ZERO ;
1342+
1343+ // Collect protocol position IDs first to avoid mutating while iterating.
1344+ let protocol_pos_ids: sp_std:: vec:: Vec < PositionId > = Positions :: < T > :: iter_prefix ( ( netuid, ) )
1345+ . filter_map ( |( ( owner, pos_id) , _) | {
1346+ if owner == protocol_account {
1347+ Some ( pos_id)
1348+ } else {
1349+ None
1350+ }
1351+ } )
1352+ . collect ( ) ;
1353+
1354+ for pos_id in protocol_pos_ids {
1355+ match Self :: do_remove_liquidity ( netuid, & protocol_account, pos_id) {
1356+ Ok ( rm) => {
1357+ let alpha_total_from_pool: AlphaCurrency =
1358+ rm. alpha . saturating_add ( rm. fee_alpha ) ;
1359+ let tao = rm. tao ;
1360+
1361+ if tao > TaoCurrency :: ZERO {
1362+ burned_tao = burned_tao. saturating_add ( tao) ;
1363+ }
1364+ if alpha_total_from_pool > AlphaCurrency :: ZERO {
1365+ burned_alpha = burned_alpha. saturating_add ( alpha_total_from_pool) ;
1366+ }
1367+
1368+ log:: debug!(
1369+ "clear_protocol_liquidity: burned protocol pos: netuid={netuid:?}, pos_id={pos_id:?}, τ={tao:?}, α_total={alpha_total_from_pool:?}"
1370+ ) ;
1371+ }
1372+ Err ( e) => {
1373+ log:: debug!(
1374+ "clear_protocol_liquidity: force-close failed: netuid={netuid:?}, pos_id={pos_id:?}, err={e:?}"
1375+ ) ;
1376+ continue ;
1377+ }
1378+ }
12841379 }
12851380
1286- // V2 / non‑V3: ensure V3 residues are cleared (safe no‑ops).
1287- let _ = Positions :: < T > :: clear_prefix ( ( netuid, ) , u32:: MAX , None ) ;
1381+ // 2) Clear active tick index entries, then all swap state (idempotent even if empty/non‑V3).
12881382 let active_ticks: sp_std:: vec:: Vec < TickIndex > =
12891383 Ticks :: < T > :: iter_prefix ( netuid) . map ( |( ti, _) | ti) . collect ( ) ;
12901384 for ti in active_ticks {
12911385 ActiveTickIndexManager :: < T > :: remove ( netuid, ti) ;
12921386 }
1387+
1388+ let _ = Positions :: < T > :: clear_prefix ( ( netuid, ) , u32:: MAX , None ) ;
12931389 let _ = Ticks :: < T > :: clear_prefix ( netuid, u32:: MAX , None ) ;
12941390
12951391 FeeGlobalTao :: < T > :: remove ( netuid) ;
@@ -1300,12 +1396,11 @@ impl<T: Config> Pallet<T> {
13001396 SwapV3Initialized :: < T > :: remove ( netuid) ;
13011397
13021398 let _ = TickIndexBitmapWords :: < T > :: clear_prefix ( ( netuid, ) , u32:: MAX , None ) ;
1303-
13041399 FeeRate :: < T > :: remove ( netuid) ;
13051400 EnabledUserLiquidity :: < T > :: remove ( netuid) ;
13061401
13071402 log:: debug!(
1308- "dissolve_all_liquidity_providers : netuid={netuid:?}, mode=V2-or-nonV3, state_cleared "
1403+ "clear_protocol_liquidity : netuid={netuid:?}, protocol_burned: τ={burned_tao:?}, α={burned_alpha:?}; state cleared "
13091404 ) ;
13101405
13111406 Ok ( ( ) )
@@ -1408,6 +1503,9 @@ impl<T: Config> SwapHandler<T::AccountId> for Pallet<T> {
14081503 fn toggle_user_liquidity ( netuid : NetUid , enabled : bool ) {
14091504 EnabledUserLiquidity :: < T > :: insert ( netuid, enabled)
14101505 }
1506+ fn clear_protocol_liquidity ( netuid : NetUid ) -> DispatchResult {
1507+ Self :: do_clear_protocol_liquidity ( netuid)
1508+ }
14111509}
14121510
14131511#[ derive( Debug , PartialEq ) ]
0 commit comments