diff --git a/pallets/subtensor/src/coinbase/block_step.rs b/pallets/subtensor/src/coinbase/block_step.rs index 6081edad19..1bcd25c851 100644 --- a/pallets/subtensor/src/coinbase/block_step.rs +++ b/pallets/subtensor/src/coinbase/block_step.rs @@ -31,7 +31,9 @@ impl Pallet { Self::try_set_pending_children(block_number); // --- 8. Run auto-claim root divs. Self::run_auto_claim_root_divs(last_block_hash); - // --- 9. Populate root coldkey maps. + // --- 9. Update root alpha flow EMAs for all subnets + Self::update_root_alpha_flow_emas(); + // --- 10. Populate root coldkey maps. Self::populate_root_coldkey_staking_maps(); // Return ok. @@ -311,4 +313,14 @@ impl Pallet { }; } } + + pub fn update_root_alpha_flow_emas() { + let subnets: Vec = Self::get_all_subnet_netuids(); + for netuid in subnets.into_iter().filter(|netuid| *netuid != NetUid::ROOT) { + // Update root alpha inflow EMA (updates if needed based on block) + Self::get_ema_root_alpha_inflow(netuid); + // Update root alpha outflow EMA (updates if needed based on block) + Self::get_ema_root_alpha_outflow(netuid); + } + } } diff --git a/pallets/subtensor/src/coinbase/root.rs b/pallets/subtensor/src/coinbase/root.rs index 328ce3805c..7f95b46992 100644 --- a/pallets/subtensor/src/coinbase/root.rs +++ b/pallets/subtensor/src/coinbase/root.rs @@ -300,6 +300,10 @@ impl Pallet { SubnetMovingPrice::::remove(netuid); SubnetTaoFlow::::remove(netuid); SubnetEmaTaoFlow::::remove(netuid); + SubnetRootAlphaInflow::::remove(netuid); + SubnetRootAlphaOutflow::::remove(netuid); + SubnetEmaRootAlphaInflow::::remove(netuid); + SubnetEmaRootAlphaOutflow::::remove(netuid); SubnetTaoProvided::::remove(netuid); // --- 13. Token / mechanism / registration toggles. diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 2091946598..0b3e2391f3 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -58,7 +58,9 @@ impl Pallet { tao_in: &BTreeMap, alpha_in: &BTreeMap, excess_tao: &BTreeMap, - ) { + ) -> BTreeMap { + let mut chain_bought_alpha: BTreeMap = BTreeMap::new(); + for netuid_i in subnets_to_emit_to.iter() { let tao_in_i: TaoCurrency = tou64!(*tao_in.get(netuid_i).unwrap_or(&asfloat!(0))).into(); @@ -78,7 +80,8 @@ impl Pallet { ); if let Ok(buy_swap_result_ok) = buy_swap_result { let bought_alpha: AlphaCurrency = buy_swap_result_ok.amount_paid_out.into(); - Self::recycle_subnet_alpha(*netuid_i, bought_alpha); + // Instead of recycling, add to chain_bought_alpha for distribution to root dividends + chain_bought_alpha.insert(*netuid_i, bought_alpha); } } @@ -109,6 +112,8 @@ impl Pallet { .saturating_add(difference_tao.into()); }); } + + chain_bought_alpha } pub fn get_subnet_terms( @@ -179,7 +184,7 @@ impl Pallet { log::debug!("excess_amount: {excess_amount:?}"); // --- 2. Inject TAO and ALPHA to pool and swap with excess TAO. - Self::inject_and_maybe_swap(subnets_to_emit_to, &tao_in, &alpha_in, &excess_amount); + let chain_bought_alpha = Self::inject_and_maybe_swap(&subnets_to_emit_to, &tao_in, &alpha_in, &excess_amount); // --- 3. Inject ALPHA for participants. let cut_percent: U96F32 = Self::get_float_subnet_owner_cut(); @@ -239,6 +244,13 @@ impl Pallet { PendingRootAlphaDivs::::mutate(*netuid_i, |total| { *total = total.saturating_add(tou64!(root_alpha).into()); }); + + // Add chain-bought alpha to pending root dividends + if let Some(chain_bought) = chain_bought_alpha.get(netuid_i) { + PendingRootAlphaDivs::::mutate(*netuid_i, |total| { + *total = total.saturating_add(*chain_bought); + }); + } } else { // If we are not selling the root alpha, we should recycle it. Self::recycle_subnet_alpha(*netuid_i, AlphaCurrency::from(tou64!(root_alpha))); diff --git a/pallets/subtensor/src/coinbase/subnet_emissions.rs b/pallets/subtensor/src/coinbase/subnet_emissions.rs index 477a678864..65eb403407 100644 --- a/pallets/subtensor/src/coinbase/subnet_emissions.rs +++ b/pallets/subtensor/src/coinbase/subnet_emissions.rs @@ -55,6 +55,70 @@ impl Pallet { SubnetTaoFlow::::remove(netuid); } + pub fn record_root_alpha_inflow(netuid: NetUid, amount: AlphaCurrency) { + SubnetRootAlphaInflow::::mutate(netuid, |flow| { + *flow = flow.saturating_add(u64::from(amount) as i64); + }); + } + + pub fn record_root_alpha_outflow(netuid: NetUid, amount: AlphaCurrency) { + SubnetRootAlphaOutflow::::mutate(netuid, |flow| { + *flow = flow.saturating_sub(u64::from(amount) as i64); + }); + } + + pub fn reset_root_alpha_inflow(netuid: NetUid) { + SubnetRootAlphaInflow::::remove(netuid); + } + + pub fn reset_root_alpha_outflow(netuid: NetUid) { + SubnetRootAlphaOutflow::::remove(netuid); + } + + // Update SubnetEmaRootAlphaInflow if needed and return its value for the current block + pub fn get_ema_root_alpha_inflow(netuid: NetUid) -> I64F64 { + let current_block: u64 = Self::get_current_block_as_u64(); + let block_flow = I64F64::saturating_from_num(SubnetRootAlphaInflow::::get(netuid)); + let (last_block, last_block_ema) = + SubnetEmaRootAlphaInflow::::get(netuid).unwrap_or((0, I64F64::saturating_from_num(0))); + + if last_block != current_block { + let flow_alpha = I64F64::saturating_from_num(RootAlphaFlowEmaSmoothingFactor::::get()) + .safe_div(I64F64::saturating_from_num(i64::MAX)); + let one = I64F64::saturating_from_num(1); + let ema_flow = (one.saturating_sub(flow_alpha)) + .saturating_mul(last_block_ema) + .saturating_add(flow_alpha.saturating_mul(block_flow)); + SubnetEmaRootAlphaInflow::::insert(netuid, (current_block, ema_flow)); + Self::reset_root_alpha_inflow(netuid); + ema_flow + } else { + last_block_ema + } + } + + // Update SubnetEmaRootAlphaOutflow if needed and return its value for the current block + pub fn get_ema_root_alpha_outflow(netuid: NetUid) -> I64F64 { + let current_block: u64 = Self::get_current_block_as_u64(); + let block_flow = I64F64::saturating_from_num(SubnetRootAlphaOutflow::::get(netuid)); + let (last_block, last_block_ema) = + SubnetEmaRootAlphaOutflow::::get(netuid).unwrap_or((0, I64F64::saturating_from_num(0))); + + if last_block != current_block { + let flow_alpha = I64F64::saturating_from_num(RootAlphaFlowEmaSmoothingFactor::::get()) + .safe_div(I64F64::saturating_from_num(i64::MAX)); + let one = I64F64::saturating_from_num(1); + let ema_flow = (one.saturating_sub(flow_alpha)) + .saturating_mul(last_block_ema) + .saturating_add(flow_alpha.saturating_mul(block_flow)); + SubnetEmaRootAlphaOutflow::::insert(netuid, (current_block, ema_flow)); + Self::reset_root_alpha_outflow(netuid); + ema_flow + } else { + last_block_ema + } + } + // Update SubnetEmaTaoFlow if needed and return its value for // the current block #[allow(dead_code)] diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index ef2d44e68b..32aae4afb2 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -329,16 +329,11 @@ pub mod pallet { )] /// Enum for the per-coldkey root claim setting. pub enum RootClaimTypeEnum { - /// Swap any alpha emission for TAO. + /// Keep all alpha emission (manual claim only). #[default] - Swap, - /// Keep all alpha emission. Keep, - /// Keep all alpha emission for specified subnets. - KeepSubnets { - /// Subnets to keep alpha emissions (swap everything else). - subnets: BTreeSet, - }, + /// Keep all alpha emission (auto-claim enabled). + AutoKeep, } /// Default minimum root claim amount. @@ -1457,6 +1452,45 @@ pub mod pallet { pub type SubnetEmaTaoFlow = StorageMap<_, Identity, NetUid, (u64, I64F64), OptionQuery>; + /// Temporary storage for root alpha inflow per subnet (reset each block after EMA update). + #[pallet::storage] + pub type SubnetRootAlphaInflow = + StorageMap<_, Identity, NetUid, i64, ValueQuery, DefaultZeroI64>; + + /// Temporary storage for root alpha outflow per subnet (reset each block after EMA update). + #[pallet::storage] + pub type SubnetRootAlphaOutflow = + StorageMap<_, Identity, NetUid, i64, ValueQuery, DefaultZeroI64>; + + /// EMA of root alpha inflow (claims) per subnet. + /// Tracks EMA of alpha claimed from root dividends (in alpha terms, no price multiplication). + #[pallet::storage] + pub type SubnetEmaRootAlphaInflow = + StorageMap<_, Identity, NetUid, (u64, I64F64), OptionQuery>; + + /// EMA of root alpha outflow (sells) per subnet. + /// Tracks EMA of claimed root alpha that is sold/transferred (in alpha terms, no price multiplication). + #[pallet::storage] + pub type SubnetEmaRootAlphaOutflow = + StorageMap<_, Identity, NetUid, (u64, I64F64), OptionQuery>; + + /// Tracks claimed root alpha per hotkey, coldkey, and netuid for EMA tracking. + /// Similar to RootClaimed, but unlike RootClaimed it is updated when actual alpha is given + /// to the coldkey (on claim_root) and when it leaves the wallet (unstake, transfer, etc.). + /// Unlike RootClaimed, we don't update it on staking in/out of hotkeys. + #[pallet::storage] + pub type RootClaimedForEma = StorageNMap< + _, + ( + NMapKey, // subnet + NMapKey, // hotkey + NMapKey, // coldkey + ), + AlphaCurrency, + ValueQuery, + DefaultZeroAlpha, + >; + /// Default value for flow cutoff. #[pallet::type_value] pub fn DefaultFlowCutoff() -> I64F64 { @@ -1484,6 +1518,15 @@ pub mod pallet { 29_597_889_189_277 } #[pallet::type_value] + /// Default value for root alpha flow EMA smoothing. + pub fn DefaultRootAlphaFlowEmaSmoothingFactor() -> u64 { + // Example values: + // half-life factor value i64 normalized (x 2^63) + // 216000 (1 month) --> 0.000003209009576 ( 29_597_889_189_277) + // 50400 (1 week) --> 0.000013752825678 (126_847_427_788_335) + 29_597_889_189_277 + } + #[pallet::type_value] /// Flow EMA smoothing half-life. pub fn FlowHalfLife() -> u64 { 216_000 @@ -1492,6 +1535,10 @@ pub mod pallet { /// --- ITEM --> Flow EMA smoothing factor (flow alpha), u64 normalized pub type FlowEmaSmoothingFactor = StorageValue<_, u64, ValueQuery, DefaultFlowEmaSmoothingFactor>; + #[pallet::storage] + /// --- ITEM --> Root alpha flow EMA smoothing factor (flow alpha), u64 normalized + pub type RootAlphaFlowEmaSmoothingFactor = + StorageValue<_, u64, ValueQuery, DefaultRootAlphaFlowEmaSmoothingFactor>; /// ============================ /// ==== Global Parameters ===== diff --git a/pallets/subtensor/src/macros/hooks.rs b/pallets/subtensor/src/macros/hooks.rs index 31be3e5e4f..0cbaf0154b 100644 --- a/pallets/subtensor/src/macros/hooks.rs +++ b/pallets/subtensor/src/macros/hooks.rs @@ -126,7 +126,9 @@ mod hooks { //Migrate CRV3 to TimelockedCommits .saturating_add(migrations::migrate_crv3_v2_to_timelocked::migrate_crv3_v2_to_timelocked::()) // Migrate to fix root counters - .saturating_add(migrations::migrate_fix_root_tao_and_alpha_in::migrate_fix_root_tao_and_alpha_in::()) + .saturating_add(migrations::migrate_fix_root_tao_and_alpha_in::migrate_fix_root_tao_and_alpha_in::()) + // Migrate root claim types to Keep + .saturating_add(migrations::migrate_root_claim_type_to_keep::migrate_root_claim_type_to_keep::()) // Migrate last block rate limiting storage items .saturating_add(migrations::migrate_rate_limiting_last_blocks::migrate_obsolete_rate_limiting_last_blocks_storage::()) // Re-encode rate limit keys after introducing OwnerHyperparamUpdate variant diff --git a/pallets/subtensor/src/migrations/migrate_root_claim_type_to_keep.rs b/pallets/subtensor/src/migrations/migrate_root_claim_type_to_keep.rs new file mode 100644 index 0000000000..0e8f692eb5 --- /dev/null +++ b/pallets/subtensor/src/migrations/migrate_root_claim_type_to_keep.rs @@ -0,0 +1,55 @@ +use alloc::string::String; +use frame_support::IterableStorageMap; +use frame_support::weights::Weight; + +use super::*; + +pub fn migrate_root_claim_type_to_keep() -> Weight { + let migration_name = b"migrate_root_claim_type_to_keep".to_vec(); + + // Initialize the weight with one read operation. + let mut weight = T::DbWeight::get().reads(1); + + // Check if the migration has already run + if HasMigrationRun::::get(&migration_name) { + log::info!( + "Migration '{:?}' has already run. Skipping.", + String::from_utf8_lossy(&migration_name) + ); + return weight; + } + log::info!( + "Running migration '{}'", + String::from_utf8_lossy(&migration_name) + ); + + // Iterate through all RootClaimType entries and convert to Keep + let mut reads = 0u64; + let mut writes = 0u64; + + for (coldkey, old_type) in RootClaimType::::iter() { + reads += 1; + // Convert any old type to Keep + // Old variants: Swap, Keep, KeepSubnets { subnets } + // New variants: Keep, AutoKeep + // All old types become Keep (default, manual claim only) + RootClaimType::::insert(coldkey, RootClaimTypeEnum::Keep); + writes += 1; + } + + weight = weight.saturating_add(T::DbWeight::get().reads_writes(reads, writes)); + + // Mark the migration as completed + HasMigrationRun::::insert(&migration_name, true); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + + log::info!( + "Migration '{:?}' completed. Converted {} root claim types to Keep.", + String::from_utf8_lossy(&migration_name), + writes + ); + + // Return the migration weight. + weight +} + diff --git a/pallets/subtensor/src/migrations/mod.rs b/pallets/subtensor/src/migrations/mod.rs index 4c9d5f01d1..a49e9f9595 100644 --- a/pallets/subtensor/src/migrations/mod.rs +++ b/pallets/subtensor/src/migrations/mod.rs @@ -32,6 +32,7 @@ pub mod migrate_rao; pub mod migrate_rate_limit_keys; pub mod migrate_rate_limiting_last_blocks; pub mod migrate_remove_commitments_rate_limit; +pub mod migrate_root_claim_type_to_keep; pub mod migrate_remove_network_modality; pub mod migrate_remove_old_identity_maps; pub mod migrate_remove_stake_map; diff --git a/pallets/subtensor/src/staking/claim_root.rs b/pallets/subtensor/src/staking/claim_root.rs index 24a26d154c..159e6c2725 100644 --- a/pallets/subtensor/src/staking/claim_root.rs +++ b/pallets/subtensor/src/staking/claim_root.rs @@ -157,56 +157,27 @@ impl Pallet { return; // no-op } - let swap = match root_claim_type { - RootClaimTypeEnum::Swap => true, - RootClaimTypeEnum::Keep => false, - RootClaimTypeEnum::KeepSubnets { subnets } => !subnets.contains(&netuid), - }; - - if swap { - // Increase stake on root. Swap the alpha owed to TAO - let owed_tao = match Self::swap_alpha_for_tao( - netuid, - owed_u64.into(), - T::SwapInterface::min_price::(), - true, - ) { - Ok(owed_tao) => owed_tao, - Err(err) => { - log::error!("Error swapping alpha for TAO: {err:?}"); - - return; - } - }; - - Self::increase_stake_for_hotkey_and_coldkey_on_subnet( - hotkey, - coldkey, - NetUid::ROOT, - owed_tao.amount_paid_out.to_u64().into(), - ); - - Self::add_stake_adjust_root_claimed_for_hotkey_and_coldkey( - hotkey, - coldkey, - owed_tao.amount_paid_out.into(), - ); - } else - /* Keep */ - { - // Increase the stake with the alpha owned - Self::increase_stake_for_hotkey_and_coldkey_on_subnet( - hotkey, - coldkey, - netuid, - owed_u64.into(), - ); - } + // Always keep alpha (no swap functionality) + // Increase the stake with the alpha owned + Self::increase_stake_for_hotkey_and_coldkey_on_subnet( + hotkey, + coldkey, + netuid, + owed_u64.into(), + ); // Increase root claimed by owed amount. RootClaimed::::mutate((netuid, hotkey, coldkey), |root_claimed| { *root_claimed = root_claimed.saturating_add(owed_u64.into()); }); + + // Track claimed root alpha for EMA (similar to RootClaimed, but for EMA tracking) + RootClaimedForEma::::mutate((netuid, hotkey, coldkey), |root_claimed| { + *root_claimed = root_claimed.saturating_add(owed_u64.into()); + }); + + // Record root alpha inflow for EMA calculation (similar to record_tao_inflow) + Self::record_root_alpha_inflow(netuid, owed_u64.into()); } fn root_claim_on_subnet_weight(_root_claim_type: RootClaimTypeEnum) -> Weight { @@ -338,7 +309,12 @@ impl Pallet { for i in coldkeys_to_claim.iter() { weight.saturating_accrue(T::DbWeight::get().reads(1)); if let Ok(coldkey) = StakingColdkeysByIndex::::try_get(i) { - weight.saturating_accrue(Self::do_root_claim(coldkey.clone(), None)); + // Only auto-claim for coldkeys with AutoKeep setting + let root_claim_type = RootClaimType::::get(&coldkey); + weight.saturating_accrue(T::DbWeight::get().reads(1)); + if root_claim_type == RootClaimTypeEnum::AutoKeep { + weight.saturating_accrue(Self::do_root_claim(coldkey.clone(), None)); + } } continue; @@ -369,6 +345,15 @@ impl Pallet { RootClaimed::::mutate((netuid, new_hotkey, new_coldkey), |new_root_claimed| { *new_root_claimed = old_root_claimed.saturating_add(*new_root_claimed); }); + + // Transfer root claimed for EMA (similar to RootClaimed transfer) + let old_root_claimed_for_ema = RootClaimedForEma::::get((netuid, old_hotkey, old_coldkey)); + if !old_root_claimed_for_ema.is_zero() { + RootClaimedForEma::::remove((netuid, old_hotkey, old_coldkey)); + RootClaimedForEma::::mutate((netuid, new_hotkey, new_coldkey), |new_root_claimed_for_ema| { + *new_root_claimed_for_ema = old_root_claimed_for_ema.saturating_add(*new_root_claimed_for_ema); + }); + } } pub fn transfer_root_claimable_for_new_hotkey( old_hotkey: &T::AccountId, @@ -399,5 +384,7 @@ impl Pallet { } let _ = RootClaimed::::clear_prefix((netuid,), u32::MAX, None); + // Clear root claimed for EMA (similar to RootClaimed) + let _ = RootClaimedForEma::::clear_prefix((netuid,), u32::MAX, None); } } diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index f61a8a6ce2..1eefa32651 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -712,6 +712,9 @@ impl Pallet { Self::increase_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid, refund); } + // Calculate actual outflow: if we got a refund, less claimed alpha was actually sold + let actual_sold = actual_alpha_decrease.saturating_sub(refund); + // If this is a root-stake if netuid == NetUid::ROOT { // Adjust root claimed value for this hotkey and coldkey. @@ -729,6 +732,21 @@ impl Pallet { // Record TAO outflow Self::record_tao_outflow(netuid, swap_result.amount_paid_out.into()); + // Record root alpha outflow for EMA calculation (only for non-root subnets) + // Decrease claimed alpha for EMA when alpha leaves wallet (FIFO-like: first alpha out is deemed claimed) + // Similar to how RootClaimed is adjusted, but we track actual alpha leaving + if netuid != NetUid::ROOT { + let claimed_outflow = Self::decrease_root_claimed_for_ema_when_alpha_leaves( + netuid, + hotkey, + coldkey, + actual_sold, + ); + if !claimed_outflow.is_zero() { + Self::record_root_alpha_outflow(netuid, claimed_outflow); + } + } + LastColdkeyHotkeyStakeBlock::::insert(coldkey, hotkey, Self::get_current_block_as_u64()); // Deposit and log the unstaking event. @@ -871,6 +889,27 @@ impl Pallet { actual_alpha_decrease, ); + // Transfer claimed root alpha for EMA tracking (only for non-root subnets) + // FIFO-like transfer of claimed alpha: first alpha out is deemed as claimed alpha + // When alpha is transferred, it moves from origin to destination wallet (not sold, so no outflow recorded) + // Similar to how RootClaimed is adjusted, but we track actual alpha movement + // Note: All decreases to RootClaimedForEma happen only inside decrease_root_claimed_for_ema_when_alpha_leaves + if netuid != NetUid::ROOT { + let claimed_to_transfer = Self::decrease_root_claimed_for_ema_when_alpha_leaves( + netuid, + origin_hotkey, + origin_coldkey, + actual_alpha_moved, + ); + if !claimed_to_transfer.is_zero() { + // Add to destination's claimed alpha for EMA (similar to RootClaimed transfer) + // No outflow recorded here since alpha is just moved between wallets, not sold + RootClaimedForEma::::mutate((netuid, destination_hotkey, destination_coldkey), |root_claimed| { + *root_claimed = root_claimed.saturating_add(claimed_to_transfer); + }); + } + } + // Calculate TAO equivalent based on current price (it is accurate because // there's no slippage in this move) let current_price = @@ -1264,6 +1303,31 @@ impl Pallet { Ok(()) } + + /// Decreases root claimed alpha for EMA when alpha leaves the wallet. + /// Similar to remove_stake_adjust_root_claimed_for_hotkey_and_coldkey, but tracks actual alpha leaving. + /// Uses FIFO-like logic: first alpha out is deemed as claimed alpha. + /// Returns the amount of claimed alpha that was decreased (for use in transfers and EMA tracking). + pub fn decrease_root_claimed_for_ema_when_alpha_leaves( + netuid: NetUid, + hotkey: &T::AccountId, + coldkey: &T::AccountId, + alpha_leaving: AlphaCurrency, + ) -> AlphaCurrency { + // Get claimed alpha for this specific hotkey-coldkey pair (similar to RootClaimed::get) + let hotkey_claimed = RootClaimedForEma::::get((netuid, hotkey, coldkey)); + // FIFO-like: first alpha out is deemed as claimed alpha + let actual_outflow = hotkey_claimed.min(alpha_leaving); + + if !actual_outflow.is_zero() { + // Decrease claimed alpha for EMA (similar to RootClaimed::mutate) + RootClaimedForEma::::mutate((netuid, hotkey, coldkey), |claimed| { + *claimed = claimed.saturating_sub(actual_outflow); + }); + } + + actual_outflow + } } /////////////////////////////////////////// diff --git a/pallets/subtensor/src/swap/swap_coldkey.rs b/pallets/subtensor/src/swap/swap_coldkey.rs index c81138b58c..ab7959433f 100644 --- a/pallets/subtensor/src/swap/swap_coldkey.rs +++ b/pallets/subtensor/src/swap/swap_coldkey.rs @@ -190,6 +190,7 @@ impl Pallet { Alpha::::remove((&hotkey, old_coldkey, netuid)); if new_alpha.saturating_add(old_alpha) > U64F64::from(0u64) { + // Transfer root claimed (both RootClaimed and RootClaimedForEma) using helper function Self::transfer_root_claimed_for_new_keys( netuid, &hotkey, diff --git a/pallets/subtensor/src/swap/swap_hotkey.rs b/pallets/subtensor/src/swap/swap_hotkey.rs index 4fdf87fb7b..dd9dc94720 100644 --- a/pallets/subtensor/src/swap/swap_hotkey.rs +++ b/pallets/subtensor/src/swap/swap_hotkey.rs @@ -507,6 +507,7 @@ impl Pallet { // 9.2. Insert the new alpha values. for ((coldkey, netuid_alpha), alpha) in old_alpha_values { if netuid == netuid_alpha { + // Transfer root claimed (both RootClaimed and RootClaimedForEma) using helper function Self::transfer_root_claimed_for_new_keys( netuid, old_hotkey, new_hotkey, &coldkey, &coldkey, );