@@ -5,7 +5,7 @@ use frame_support::storage::{TransactionOutcome, transactional};
5
5
use frame_support:: { ensure, pallet_prelude:: DispatchError , traits:: Get } ;
6
6
use safe_math:: * ;
7
7
use sp_arithmetic:: helpers_128bit;
8
- use sp_runtime:: { DispatchResult , traits:: AccountIdConversion } ;
8
+ use sp_runtime:: { DispatchResult , Vec , traits:: AccountIdConversion } ;
9
9
use substrate_fixed:: types:: { I64F64 , U64F64 , U96F32 } ;
10
10
use subtensor_runtime_common:: {
11
11
AlphaCurrency , BalanceOps , Currency , NetUid , SubnetInfo , TaoCurrency ,
@@ -1216,80 +1216,176 @@ impl<T: Config> Pallet<T> {
1216
1216
/// Dissolve all LPs and clean state.
1217
1217
pub fn do_dissolve_all_liquidity_providers ( netuid : NetUid ) -> DispatchResult {
1218
1218
if SwapV3Initialized :: < T > :: get ( netuid) {
1219
- // 1) Snapshot (owner, position_id).
1219
+ // 1) Snapshot only *non‑protocol* positions: (owner, position_id).
1220
1220
struct CloseItem < A > {
1221
1221
owner : A ,
1222
1222
pos_id : PositionId ,
1223
1223
}
1224
+ let protocol_account = Self :: protocol_account_id ( ) ;
1225
+
1224
1226
let mut to_close: sp_std:: vec:: Vec < CloseItem < T :: AccountId > > = sp_std:: vec:: Vec :: new ( ) ;
1225
1227
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
+ }
1227
1231
}
1228
1232
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
+ }
1230
1239
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
+ } ;
1234
1258
1235
1259
for CloseItem { owner, pos_id } in to_close. into_iter ( ) {
1236
1260
match Self :: do_remove_liquidity ( netuid, & owner, pos_id) {
1237
1261
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.
1238
1269
if rm. tao > TaoCurrency :: ZERO {
1239
1270
T :: BalanceOps :: increase_balance ( & owner, rm. tao ) ;
1240
- }
1241
- if owner != protocol_account {
1271
+ user_refunded_tao = user_refunded_tao. saturating_add ( rm. tao ) ;
1242
1272
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
+ ) ;
1246
1304
}
1305
+
1306
+ T :: BalanceOps :: decrease_provided_alpha_reserve (
1307
+ netuid,
1308
+ alpha_total_from_pool,
1309
+ ) ;
1247
1310
}
1248
1311
}
1249
1312
Err ( e) => {
1250
1313
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:?}"
1252
1315
) ;
1253
1316
continue ;
1254
1317
}
1255
1318
}
1256
1319
}
1257
1320
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
+ ) ;
1264
1324
1265
- let _ = Positions :: < T > :: clear_prefix ( ( netuid , ) , u32 :: MAX , None ) ;
1266
- let _ = Ticks :: < T > :: clear_prefix ( netuid , u32 :: MAX , None ) ;
1325
+ return Ok ( ( ) ) ;
1326
+ }
1267
1327
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
+ ) ;
1274
1331
1275
- let _ = TickIndexBitmapWords :: < T > :: clear_prefix ( ( netuid, ) , u32:: MAX , None ) ;
1276
- FeeRate :: < T > :: remove ( netuid) ;
1277
- EnabledUserLiquidity :: < T > :: remove ( netuid) ;
1332
+ Ok ( ( ) )
1333
+ }
1278
1334
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 ( ) ;
1282
1338
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
+ }
1284
1379
}
1285
1380
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).
1288
1382
let active_ticks: sp_std:: vec:: Vec < TickIndex > =
1289
1383
Ticks :: < T > :: iter_prefix ( netuid) . map ( |( ti, _) | ti) . collect ( ) ;
1290
1384
for ti in active_ticks {
1291
1385
ActiveTickIndexManager :: < T > :: remove ( netuid, ti) ;
1292
1386
}
1387
+
1388
+ let _ = Positions :: < T > :: clear_prefix ( ( netuid, ) , u32:: MAX , None ) ;
1293
1389
let _ = Ticks :: < T > :: clear_prefix ( netuid, u32:: MAX , None ) ;
1294
1390
1295
1391
FeeGlobalTao :: < T > :: remove ( netuid) ;
@@ -1300,12 +1396,11 @@ impl<T: Config> Pallet<T> {
1300
1396
SwapV3Initialized :: < T > :: remove ( netuid) ;
1301
1397
1302
1398
let _ = TickIndexBitmapWords :: < T > :: clear_prefix ( ( netuid, ) , u32:: MAX , None ) ;
1303
-
1304
1399
FeeRate :: < T > :: remove ( netuid) ;
1305
1400
EnabledUserLiquidity :: < T > :: remove ( netuid) ;
1306
1401
1307
1402
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 "
1309
1404
) ;
1310
1405
1311
1406
Ok ( ( ) )
@@ -1408,6 +1503,9 @@ impl<T: Config> SwapHandler<T::AccountId> for Pallet<T> {
1408
1503
fn toggle_user_liquidity ( netuid : NetUid , enabled : bool ) {
1409
1504
EnabledUserLiquidity :: < T > :: insert ( netuid, enabled)
1410
1505
}
1506
+ fn clear_protocol_liquidity ( netuid : NetUid ) -> DispatchResult {
1507
+ Self :: do_clear_protocol_liquidity ( netuid)
1508
+ }
1411
1509
}
1412
1510
1413
1511
#[ derive( Debug , PartialEq ) ]
0 commit comments