From 2c68387fcdc569795680e74b4eb4304fc2fa91b9 Mon Sep 17 00:00:00 2001 From: unconst Date: Thu, 20 Nov 2025 17:34:36 -0600 Subject: [PATCH 01/21] delegates can set the claim type for all their delegates --- pallets/subtensor/src/lib.rs | 11 ++++++++++ pallets/subtensor/src/macros/dispatches.rs | 23 +++++++++++++++++++++ pallets/subtensor/src/macros/events.rs | 9 ++++++++ pallets/subtensor/src/staking/claim_root.rs | 15 ++++++++++++-- 4 files changed, 56 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index bbec3ce934..c7277e90b2 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -2244,6 +2244,17 @@ pub mod pallet { ValueQuery, DefaultRootClaimType, >; + #[pallet::storage] // -- MAP ( hotkey, netuid ) --> delegate_claim_type enum + pub type DelegateClaimType = StorageMap< + _, + Blake2_128Concat, + T::AccountId, + Identity, + u16, + RootClaimTypeEnum, + ValueQuery, + DefaultRootClaimType, + >; #[pallet::storage] // --- MAP ( u64 ) --> coldkey | Maps coldkeys that have stake to an index pub type StakingColdkeysByIndex = StorageMap<_, Identity, u64, T::AccountId, OptionQuery>; diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index d534dbb0c6..af9c4c161a 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2421,5 +2421,28 @@ mod dispatches { Ok(()) } + + /// --- Sets delegate claim type for a hotkey on a subnet. + #[pallet::call_index(125)] + #[pallet::weight(( + Weight::from_parts(5_711_000, 0).saturating_add(T::DbWeight::get().writes(1_u64)), + DispatchClass::Operational, + Pays::Yes + ))] + pub fn set_delegate_claim_type( + origin: OriginFor, + hotkey: T::AccountId, + netuid: NetUid, + new_claim_type: RootClaimTypeEnum, + ) -> DispatchResult { + let coldkey: T::AccountId = ensure_signed(origin)?; + ensure!(Self::coldkey_owns_hotkey(coldkey.clone(), hotkey.clone()), Error::::NonAssociatedColdKey); + DelegateClaimType::::insert((hotkey.clone(), netuid), new_claim_type.clone()); + Self::deposit_event(Event::DelegateClaimTypeSet { + hotkey: hotkey.clone(), + root_claim_type: new_claim_type, + }); + Ok(()) + } } } diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index d015205d4d..0fe5d6317e 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -468,6 +468,15 @@ mod events { root_claim_type: RootClaimTypeEnum, }, + /// Root claim type for a coldkey has been set. + /// Parameters: + /// (coldkey, u8) + DelegateClaimTypeSet { + /// delegate hotkey + hotkeu: T::AccountId, + root_claim_type: RootClaimTypeEnum, + }, + /// Subnet lease dividends have been distributed. SubnetLeaseDividendsDistributed { /// The lease ID diff --git a/pallets/subtensor/src/staking/claim_root.rs b/pallets/subtensor/src/staking/claim_root.rs index 7071a1ad55..132f801e40 100644 --- a/pallets/subtensor/src/staking/claim_root.rs +++ b/pallets/subtensor/src/staking/claim_root.rs @@ -128,7 +128,7 @@ impl Pallet { hotkey: &T::AccountId, coldkey: &T::AccountId, netuid: NetUid, - root_claim_type: RootClaimTypeEnum, + mut root_claim_type: RootClaimTypeEnum, ignore_minimum_condition: bool, ) { // Subtract the root claimed. @@ -157,6 +157,11 @@ impl Pallet { return; // no-op } + // If root_claim_type is Delegated, switch to the delegate's actual claim type. + if let RootClaimTypeEnum::Delegated = root_claim_type { + root_claim_type = DelegateClaimType::::get(hotkey, netuid); + } + match root_claim_type { // Increase stake on root RootClaimTypeEnum::Swap => { @@ -189,7 +194,7 @@ impl Pallet { ); } RootClaimTypeEnum::Keep => { - // Increase the stake with the alpha owned + // Increase the stake with the alpha owed Self::increase_stake_for_hotkey_and_coldkey_on_subnet( hotkey, coldkey, @@ -197,6 +202,12 @@ impl Pallet { owed_u64.into(), ); } + // Add Delegated arm for completeness, but it should never reach here due to switch above. + RootClaimTypeEnum::Delegated => { + // Should not reach here. Added for completeness. + log::error!("Delegated root_claim_type should have been switched. Skipping."); + return; + } }; // Increase root claimed by owed amount. From 6c503129c5de440ada655702a5c0e8dfc8297e51 Mon Sep 17 00:00:00 2001 From: unconst Date: Thu, 20 Nov 2025 17:38:15 -0600 Subject: [PATCH 02/21] record flow --- pallets/subtensor/src/staking/claim_root.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pallets/subtensor/src/staking/claim_root.rs b/pallets/subtensor/src/staking/claim_root.rs index 132f801e40..662c47905d 100644 --- a/pallets/subtensor/src/staking/claim_root.rs +++ b/pallets/subtensor/src/staking/claim_root.rs @@ -180,6 +180,9 @@ impl Pallet { } }; + // Importantly measures swap as flow. + Self::record_tao_outflow( netuid, owed_tao ); + Self::increase_stake_for_hotkey_and_coldkey_on_subnet( hotkey, coldkey, From 12b7af2cc6ccb9de2293d5a470b38a0ce3a94e05 Mon Sep 17 00:00:00 2001 From: unconst Date: Thu, 20 Nov 2025 17:40:49 -0600 Subject: [PATCH 03/21] ran fmt --- pallets/subtensor/src/macros/dispatches.rs | 5 ++++- pallets/subtensor/src/staking/claim_root.rs | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index af9c4c161a..61b5a0138c 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2436,7 +2436,10 @@ mod dispatches { new_claim_type: RootClaimTypeEnum, ) -> DispatchResult { let coldkey: T::AccountId = ensure_signed(origin)?; - ensure!(Self::coldkey_owns_hotkey(coldkey.clone(), hotkey.clone()), Error::::NonAssociatedColdKey); + ensure!( + Self::coldkey_owns_hotkey(coldkey.clone(), hotkey.clone()), + Error::::NonAssociatedColdKey + ); DelegateClaimType::::insert((hotkey.clone(), netuid), new_claim_type.clone()); Self::deposit_event(Event::DelegateClaimTypeSet { hotkey: hotkey.clone(), diff --git a/pallets/subtensor/src/staking/claim_root.rs b/pallets/subtensor/src/staking/claim_root.rs index 662c47905d..50f36d1dcb 100644 --- a/pallets/subtensor/src/staking/claim_root.rs +++ b/pallets/subtensor/src/staking/claim_root.rs @@ -181,7 +181,7 @@ impl Pallet { }; // Importantly measures swap as flow. - Self::record_tao_outflow( netuid, owed_tao ); + Self::record_tao_outflow(netuid, owed_tao); Self::increase_stake_for_hotkey_and_coldkey_on_subnet( hotkey, From badae219816ff94b41c64a0a26ecc8d2dcd428d2 Mon Sep 17 00:00:00 2001 From: unconst Date: Thu, 20 Nov 2025 17:48:56 -0600 Subject: [PATCH 04/21] claim type --- pallets/subtensor/src/lib.rs | 8 +++++++- pallets/subtensor/src/macros/dispatches.rs | 10 ++++++++++ pallets/subtensor/src/macros/errors.rs | 2 ++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index c7277e90b2..9af977005c 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -334,6 +334,8 @@ pub mod pallet { Swap, /// Keep all alpha emission. Keep, + /// Delegate choice to subnet. + Delegated, } /// Enum for the per-coldkey root claim frequency setting. @@ -359,7 +361,11 @@ pub mod pallet { /// This is set by the user. Either swap to TAO or keep as alpha. #[pallet::type_value] pub fn DefaultRootClaimType() -> RootClaimTypeEnum { - RootClaimTypeEnum::default() + RootClaimTypeEnum::Delegated + } + #[pallet::type_value] + pub fn DefaultDelegateClaimType() -> RootClaimTypeEnum { + RootClaimTypeEnum::Swap } /// Default number of root claims per claim call. diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 61b5a0138c..229bf5d775 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2440,6 +2440,16 @@ mod dispatches { Self::coldkey_owns_hotkey(coldkey.clone(), hotkey.clone()), Error::::NonAssociatedColdKey ); + + // Ensure the delegate claim type is not Delegated. + ensure!( + matches!( + new_claim_type, + RootClaimTypeEnum::Swap | RootClaimTypeEnum::Keep + ), + Error::::InvalidRootClaimType + ); + DelegateClaimType::::insert((hotkey.clone(), netuid), new_claim_type.clone()); Self::deposit_event(Event::DelegateClaimTypeSet { hotkey: hotkey.clone(), diff --git a/pallets/subtensor/src/macros/errors.rs b/pallets/subtensor/src/macros/errors.rs index 5a15330075..c4456ee1fe 100644 --- a/pallets/subtensor/src/macros/errors.rs +++ b/pallets/subtensor/src/macros/errors.rs @@ -266,5 +266,7 @@ mod errors { InvalidRootClaimThreshold, /// Exceeded subnet limit number or zero. InvalidSubnetNumber, + /// Delegates cant set delegated as claim type + InvalidRootClaimType, } } From 03908e3d2b396f0371ddb2f117cfe63caf801adf Mon Sep 17 00:00:00 2001 From: unconst Date: Thu, 20 Nov 2025 17:50:39 -0600 Subject: [PATCH 05/21] fixes --- pallets/subtensor/src/macros/events.rs | 2 +- pallets/subtensor/src/staking/claim_root.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index 0fe5d6317e..eda33af514 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -473,7 +473,7 @@ mod events { /// (coldkey, u8) DelegateClaimTypeSet { /// delegate hotkey - hotkeu: T::AccountId, + hotkey: T::AccountId, root_claim_type: RootClaimTypeEnum, }, diff --git a/pallets/subtensor/src/staking/claim_root.rs b/pallets/subtensor/src/staking/claim_root.rs index 50f36d1dcb..eb9f7a4bd1 100644 --- a/pallets/subtensor/src/staking/claim_root.rs +++ b/pallets/subtensor/src/staking/claim_root.rs @@ -158,7 +158,7 @@ impl Pallet { } // If root_claim_type is Delegated, switch to the delegate's actual claim type. - if let RootClaimTypeEnum::Delegated = root_claim_type { + if (root_claim_type == RootClaimTypeEnum::Delegated) { root_claim_type = DelegateClaimType::::get(hotkey, netuid); } From 13b2ee147f775d5b80678de224454794d7e1e93f Mon Sep 17 00:00:00 2001 From: unconst Date: Thu, 20 Nov 2025 17:53:19 -0600 Subject: [PATCH 06/21] fmt --- pallets/subtensor/src/staking/claim_root.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/pallets/subtensor/src/staking/claim_root.rs b/pallets/subtensor/src/staking/claim_root.rs index eb9f7a4bd1..8561070ab2 100644 --- a/pallets/subtensor/src/staking/claim_root.rs +++ b/pallets/subtensor/src/staking/claim_root.rs @@ -175,7 +175,6 @@ impl Pallet { Ok(owed_tao) => owed_tao, Err(err) => { log::error!("Error swapping alpha for TAO: {err:?}"); - return; } }; From 2f70212ef639bca40662fcb43e6cfd95bf86eb43 Mon Sep 17 00:00:00 2001 From: unconst Date: Thu, 20 Nov 2025 18:15:10 -0600 Subject: [PATCH 07/21] Keep default --- pallets/subtensor/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 9af977005c..692365b9e0 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -365,7 +365,7 @@ pub mod pallet { } #[pallet::type_value] pub fn DefaultDelegateClaimType() -> RootClaimTypeEnum { - RootClaimTypeEnum::Swap + RootClaimTypeEnum::Keep } /// Default number of root claims per claim call. From d12bcfc3267862230804b18aa4e9821ad50b5b2e Mon Sep 17 00:00:00 2001 From: unconst Date: Fri, 21 Nov 2025 12:32:24 -0600 Subject: [PATCH 08/21] add tests fix errors --- pallets/subtensor/src/lib.rs | 7 +- pallets/subtensor/src/macros/dispatches.rs | 8 +- pallets/subtensor/src/macros/events.rs | 3 +- pallets/subtensor/src/staking/claim_root.rs | 6 +- pallets/subtensor/src/tests/claim_root.rs | 240 +++++++++++++++++++- 5 files changed, 252 insertions(+), 12 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 692365b9e0..a74b5e1843 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -363,8 +363,9 @@ pub mod pallet { pub fn DefaultRootClaimType() -> RootClaimTypeEnum { RootClaimTypeEnum::Delegated } + /// Default value for delegate claim type storage #[pallet::type_value] - pub fn DefaultDelegateClaimType() -> RootClaimTypeEnum { + pub fn DefaultValidatorClaimType() -> RootClaimTypeEnum { RootClaimTypeEnum::Keep } @@ -2251,12 +2252,12 @@ pub mod pallet { DefaultRootClaimType, >; #[pallet::storage] // -- MAP ( hotkey, netuid ) --> delegate_claim_type enum - pub type DelegateClaimType = StorageMap< + pub type ValidatorClaimType = StorageDoubleMap< _, Blake2_128Concat, T::AccountId, Identity, - u16, + NetUid, RootClaimTypeEnum, ValueQuery, DefaultRootClaimType, diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 229bf5d775..acbbb51d21 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2429,7 +2429,7 @@ mod dispatches { DispatchClass::Operational, Pays::Yes ))] - pub fn set_delegate_claim_type( + pub fn set_validator_claim_type( origin: OriginFor, hotkey: T::AccountId, netuid: NetUid, @@ -2437,7 +2437,7 @@ mod dispatches { ) -> DispatchResult { let coldkey: T::AccountId = ensure_signed(origin)?; ensure!( - Self::coldkey_owns_hotkey(coldkey.clone(), hotkey.clone()), + Self::coldkey_owns_hotkey(&coldkey, &hotkey), Error::::NonAssociatedColdKey ); @@ -2450,8 +2450,8 @@ mod dispatches { Error::::InvalidRootClaimType ); - DelegateClaimType::::insert((hotkey.clone(), netuid), new_claim_type.clone()); - Self::deposit_event(Event::DelegateClaimTypeSet { + ValidatorClaimType::::insert(hotkey.clone(), netuid, new_claim_type.clone()); + Self::deposit_event(Event::ValidatorClaimTypeSet { hotkey: hotkey.clone(), root_claim_type: new_claim_type, }); diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index eda33af514..2b66a14924 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -471,9 +471,10 @@ mod events { /// Root claim type for a coldkey has been set. /// Parameters: /// (coldkey, u8) - DelegateClaimTypeSet { + ValidatorClaimTypeSet { /// delegate hotkey hotkey: T::AccountId, + /// root claim type enum root_claim_type: RootClaimTypeEnum, }, diff --git a/pallets/subtensor/src/staking/claim_root.rs b/pallets/subtensor/src/staking/claim_root.rs index 8561070ab2..f346b6c084 100644 --- a/pallets/subtensor/src/staking/claim_root.rs +++ b/pallets/subtensor/src/staking/claim_root.rs @@ -158,8 +158,8 @@ impl Pallet { } // If root_claim_type is Delegated, switch to the delegate's actual claim type. - if (root_claim_type == RootClaimTypeEnum::Delegated) { - root_claim_type = DelegateClaimType::::get(hotkey, netuid); + if root_claim_type == RootClaimTypeEnum::Delegated { + root_claim_type = ValidatorClaimType::::get(hotkey, netuid); } match root_claim_type { @@ -180,7 +180,7 @@ impl Pallet { }; // Importantly measures swap as flow. - Self::record_tao_outflow(netuid, owed_tao); + Self::record_tao_outflow(netuid, owed_tao.amount_paid_out.into()); Self::increase_stake_for_hotkey_and_coldkey_on_subnet( hotkey, diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index c11d88033f..dc6a628a01 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -9,7 +9,7 @@ use crate::{ StakingColdkeys, StakingColdkeysByIndex, SubnetAlphaIn, SubnetMechanism, SubnetMovingPrice, SubnetTAO, SubtokenEnabled, Tempo, pallet, }; -use crate::{RootClaimType, RootClaimTypeEnum, RootClaimed}; +use crate::{RootClaimType, RootClaimTypeEnum, RootClaimed, ValidatorClaimType}; use approx::assert_abs_diff_eq; use frame_support::dispatch::RawOrigin; use frame_support::pallet_prelude::Weight; @@ -1554,3 +1554,241 @@ fn test_claim_root_with_unrelated_subnets() { assert_eq!(u128::from(new_stake), claimed); }); } + +#[test] +fn test_claim_root_with_delegated_claim_type() { + new_test_ext(1).execute_with(|| { + // Setup: Create network with validator (hotkey/owner_coldkey) and two stakers + let owner_coldkey = U256::from(1001); // Validator's coldkey + let other_coldkey = U256::from(10010); // Other staker (not tested) + let hotkey = U256::from(1002); // Validator's hotkey + let alice_coldkey = U256::from(1003); // Staker who will delegate claim type + let bob_coldkey = U256::from(1004); // Staker who will set explicit claim type + let netuid = add_dynamic_network(&hotkey, &owner_coldkey); + + // Configure TAO weight and subnet mechanism for swap functionality + SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 + SubnetMechanism::::insert(netuid, 1); // Enable subnet mechanism for swaps + + // Setup swap pool with reserves to enable Swap claim type + let tao_reserve = TaoCurrency::from(50_000_000_000); + let alpha_in = AlphaCurrency::from(100_000_000_000); + SubnetTAO::::insert(netuid, tao_reserve); + SubnetAlphaIn::::insert(netuid, alpha_in); + + // Verify the alpha-to-TAO exchange rate is 0.5 + let current_price = + ::SwapInterface::current_alpha_price(netuid.into()) + .saturating_to_num::(); + assert_eq!(current_price, 0.5f64); + + // Setup root network stakes: Alice and Bob each have 10% of total stake + let root_stake = 2_000_000u64; + let root_stake_rate = 0.1f64; // Each staker owns 10% of root stake + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &alice_coldkey, + NetUid::ROOT, + root_stake.into(), + ); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &bob_coldkey, + NetUid::ROOT, + root_stake.into(), + ); + // Other coldkey has remaining 80% of stake + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &other_coldkey, + NetUid::ROOT, + (8 * root_stake).into(), + ); + + // Setup subnet alpha stake for validator + let initial_total_hotkey_alpha = 10_000_000u64; + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &owner_coldkey, + netuid, + initial_total_hotkey_alpha.into(), + ); + + // SCENARIO 1: Validator sets Keep claim type, Alice uses default (Delegated) + // Alice should inherit the validator's Keep claim type and receive alpha stake + assert_ok!(SubtensorModule::set_validator_claim_type( + RuntimeOrigin::signed(owner_coldkey), + hotkey, + netuid, + RootClaimTypeEnum::Keep + ),); + assert_eq!(ValidatorClaimType::::get(hotkey, netuid), RootClaimTypeEnum::Keep); + + // Bob explicitly sets Keep claim type (same as validator, but not delegated) + assert_ok!(SubtensorModule::set_root_claim_type( + RuntimeOrigin::signed(bob_coldkey), + RootClaimTypeEnum::Keep + ),); + + // Alice has default Delegated claim type (not explicitly set) + assert_eq!(RootClaimType::::get(alice_coldkey), RootClaimTypeEnum::Delegated); + + // Distribute pending root alpha emissions to create claimable rewards + let pending_root_alpha = 10_000_000u64; + SubtensorModule::distribute_emission( + netuid, + AlphaCurrency::ZERO, + AlphaCurrency::ZERO, + pending_root_alpha.into(), + AlphaCurrency::ZERO, + ); + + // Alice claims with delegated claim type (should use validator's Keep) + assert_ok!(SubtensorModule::claim_root( + RuntimeOrigin::signed(alice_coldkey), + BTreeSet::from([netuid]) + )); + + // Bob claims with explicit Keep claim type + assert_ok!(SubtensorModule::claim_root( + RuntimeOrigin::signed(bob_coldkey), + BTreeSet::from([netuid]) + )); + + // Verify both stakers received alpha stake (Keep claim type behavior) + // With Keep, rewards are staked as alpha on the subnet + let validator_take_percent = 0.18f64; + let expected_stake_per_user = + (pending_root_alpha as f64) * (1f64 - validator_take_percent) * root_stake_rate; + + let alice_alpha_stake: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &alice_coldkey, + netuid, + ) + .into(); + + let bob_alpha_stake: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &bob_coldkey, + netuid, + ) + .into(); + + // Both should have equal alpha stakes since they both used Keep claim type + assert_eq!(alice_alpha_stake, bob_alpha_stake); + assert_abs_diff_eq!(alice_alpha_stake, expected_stake_per_user as u64, epsilon = 100u64); + + // Verify neither received TAO stake (would happen with Swap claim type) + let alice_tao_stake: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &alice_coldkey, + NetUid::ROOT, + ) + .into(); + let bob_tao_stake: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &bob_coldkey, + NetUid::ROOT, + ) + .into(); + // TAO stake should remain unchanged at initial amount + assert_eq!(alice_tao_stake, root_stake); + assert_eq!(bob_tao_stake, root_stake); + + // SCENARIO 2: Validator changes to Swap claim type + // Alice (with Delegated) should now use Swap, Bob (explicit Keep) stays with Keep + assert_ok!(SubtensorModule::set_validator_claim_type( + RuntimeOrigin::signed(owner_coldkey), + hotkey, + netuid, + RootClaimTypeEnum::Swap + ),); + + // Distribute more pending root alpha for second round of claims + SubtensorModule::distribute_emission( + netuid, + AlphaCurrency::ZERO, + AlphaCurrency::ZERO, + pending_root_alpha.into(), + AlphaCurrency::ZERO, + ); + + // Both stakers claim again + assert_ok!(SubtensorModule::claim_root( + RuntimeOrigin::signed(alice_coldkey), + BTreeSet::from([netuid]) + )); + assert_ok!(SubtensorModule::claim_root( + RuntimeOrigin::signed(bob_coldkey), + BTreeSet::from([netuid]) + )); + + // Alice's alpha stake should remain the same (Swap doesn't add alpha stake) + let alice_alpha_stake_round2: u64 = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &alice_coldkey, + netuid, + ) + .into(); + + // Bob's alpha stake should increase (Keep adds alpha stake) + let bob_alpha_stake_round2: u64 = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &bob_coldkey, + netuid, + ) + .into(); + + // Alice used Swap (delegated from validator), so no new alpha stake + assert_abs_diff_eq!( + alice_alpha_stake_round2, + alice_alpha_stake, + epsilon = 100u64 + ); + + // Bob used Keep (explicit), so alpha stake increased + assert_abs_diff_eq!( + bob_alpha_stake_round2, + alice_alpha_stake + expected_stake_per_user as u64, + epsilon = 100u64 + ); + + // Alice used Swap, so TAO stake should increase + let alice_tao_stake_round2: u64 = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &alice_coldkey, + NetUid::ROOT, + ) + .into(); + + // Bob used Keep, so TAO stake should remain the same + let bob_tao_stake_round2: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &bob_coldkey, + NetUid::ROOT, + ) + .into(); + + // Alice's TAO stake increased (swapped alpha to TAO and staked on root) + let expected_tao_increase = expected_stake_per_user * current_price; + assert_abs_diff_eq!( + alice_tao_stake_round2, + root_stake + expected_tao_increase as u64, + epsilon = 10000u64 + ); + + // Bob's TAO stake unchanged (used Keep, not Swap) + assert_eq!(bob_tao_stake_round2, root_stake); + + // SUMMARY: This test demonstrates that: + // 1. Stakers with Delegated claim type inherit the validator's claim type + // 2. Stakers with explicit claim types use their own setting regardless of validator + // 3. Keep claim type stakes rewards as alpha on the subnet + // 4. Swap claim type converts alpha to TAO and stakes on root network + // 5. Changing validator's claim type affects delegated stakers immediately + }); +} From 857e59e26f7a23148c3ad1d1a90e31ecc86fcf52 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 21 Nov 2025 14:03:50 -0500 Subject: [PATCH 09/21] commit Cargo.lock --- pallets/subtensor/src/tests/claim_root.rs | 33 +++++++++++++++-------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index dc6a628a01..f2daf3d477 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -1575,7 +1575,7 @@ fn test_claim_root_with_delegated_claim_type() { let alpha_in = AlphaCurrency::from(100_000_000_000); SubnetTAO::::insert(netuid, tao_reserve); SubnetAlphaIn::::insert(netuid, alpha_in); - + // Verify the alpha-to-TAO exchange rate is 0.5 let current_price = ::SwapInterface::current_alpha_price(netuid.into()) @@ -1622,16 +1622,22 @@ fn test_claim_root_with_delegated_claim_type() { netuid, RootClaimTypeEnum::Keep ),); - assert_eq!(ValidatorClaimType::::get(hotkey, netuid), RootClaimTypeEnum::Keep); + assert_eq!( + ValidatorClaimType::::get(hotkey, netuid), + RootClaimTypeEnum::Keep + ); // Bob explicitly sets Keep claim type (same as validator, but not delegated) assert_ok!(SubtensorModule::set_root_claim_type( RuntimeOrigin::signed(bob_coldkey), RootClaimTypeEnum::Keep ),); - + // Alice has default Delegated claim type (not explicitly set) - assert_eq!(RootClaimType::::get(alice_coldkey), RootClaimTypeEnum::Delegated); + assert_eq!( + RootClaimType::::get(alice_coldkey), + RootClaimTypeEnum::Delegated + ); // Distribute pending root alpha emissions to create claimable rewards let pending_root_alpha = 10_000_000u64; @@ -1677,7 +1683,11 @@ fn test_claim_root_with_delegated_claim_type() { // Both should have equal alpha stakes since they both used Keep claim type assert_eq!(alice_alpha_stake, bob_alpha_stake); - assert_abs_diff_eq!(alice_alpha_stake, expected_stake_per_user as u64, epsilon = 100u64); + assert_abs_diff_eq!( + alice_alpha_stake, + expected_stake_per_user as u64, + epsilon = 100u64 + ); // Verify neither received TAO stake (would happen with Swap claim type) let alice_tao_stake: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -1766,12 +1776,13 @@ fn test_claim_root_with_delegated_claim_type() { .into(); // Bob used Keep, so TAO stake should remain the same - let bob_tao_stake_round2: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey, - &bob_coldkey, - NetUid::ROOT, - ) - .into(); + let bob_tao_stake_round2: u64 = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &bob_coldkey, + NetUid::ROOT, + ) + .into(); // Alice's TAO stake increased (swapped alpha to TAO and staked on root) let expected_tao_increase = expected_stake_per_user * current_price; From ad3a0d1867aed00d52bf19820d5df96a61af6840 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 21 Nov 2025 15:12:42 -0500 Subject: [PATCH 10/21] fix --- pallets/subtensor/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index a74b5e1843..38ee587750 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -2260,7 +2260,7 @@ pub mod pallet { NetUid, RootClaimTypeEnum, ValueQuery, - DefaultRootClaimType, + DefaultValidatorClaimType, >; #[pallet::storage] // --- MAP ( u64 ) --> coldkey | Maps coldkeys that have stake to an index pub type StakingColdkeysByIndex = From 26c9a3efe65215087a6a2d060e8f8e414566d8a5 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 21 Nov 2025 15:17:55 -0500 Subject: [PATCH 11/21] bump spec version --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 9ece1dd025..2171708685 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 347, + spec_version: 348, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 665b72fdce7e3796324019bf919125fe60f5563f Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Sun, 23 Nov 2025 23:59:29 -0500 Subject: [PATCH 12/21] bump spec version --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 2171708685..8f46d2d8e2 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 348, + spec_version: 349, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 9b34a03112b55628c74cbf234aa84d5c56c85cae Mon Sep 17 00:00:00 2001 From: Shamil Gadelshin Date: Mon, 24 Nov 2025 16:01:24 +0400 Subject: [PATCH 13/21] Fix set_validator_claim_type --- pallets/subtensor/src/macros/dispatches.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index f84fedca77..0e5120127f 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2447,10 +2447,7 @@ mod dispatches { // Ensure the delegate claim type is not Delegated. ensure!( - matches!( - new_claim_type, - RootClaimTypeEnum::Swap | RootClaimTypeEnum::Keep - ), + !matches!(new_claim_type, RootClaimTypeEnum::Delegated), Error::::InvalidRootClaimType ); From 7ab6ade35d13dabc2eeca40a6b414b5d7b0eeb90 Mon Sep 17 00:00:00 2001 From: Shamil Gadelshin Date: Thu, 4 Dec 2025 15:46:00 +0300 Subject: [PATCH 14/21] Add SubnetTaoFlow test. --- pallets/subtensor/src/tests/claim_root.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index 48d0c0f798..18878cb097 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -653,6 +653,8 @@ fn test_claim_root_with_drain_emissions_and_swap_claim_type() { ),); assert_eq!(RootClaimType::::get(coldkey), RootClaimTypeEnum::Swap); + assert_eq!(SubnetTaoFlow::::get(netuid), 0); + assert_ok!(SubtensorModule::claim_root( RuntimeOrigin::signed(coldkey), BTreeSet::from([netuid]) @@ -678,6 +680,14 @@ fn test_claim_root_with_drain_emissions_and_swap_claim_type() { epsilon = 10000u64, ); + // Check tao flow + + assert_abs_diff_eq!( + SubnetTaoFlow::::get(netuid), + -estimated_stake_increment as i64, + epsilon = 10i64, + ); + // Distribute and claim pending root alpha (round 2) SubtensorModule::distribute_emission( From e55109d610c8badf4093a809dc24ccc818b78983 Mon Sep 17 00:00:00 2001 From: Shamil Gadelshin Date: Fri, 5 Dec 2025 15:13:46 +0300 Subject: [PATCH 15/21] Modify ValidatorClaimTypeSet event --- pallets/subtensor/src/macros/dispatches.rs | 1 + pallets/subtensor/src/macros/events.rs | 2 + pallets/subtensor/src/tests/claim_root.rs | 54 +++++++++++++++++++++- 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index eebb07a508..7d28c3e2e6 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2461,6 +2461,7 @@ mod dispatches { Self::deposit_event(Event::ValidatorClaimTypeSet { hotkey: hotkey.clone(), root_claim_type: new_claim_type, + netuid, }); Ok(()) } diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index 2b66a14924..a5628a0943 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -476,6 +476,8 @@ mod events { hotkey: T::AccountId, /// root claim type enum root_claim_type: RootClaimTypeEnum, + /// subnet UID + netuid: NetUid, }, /// Subnet lease dividends have been distributed. diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index 18878cb097..6b4cb0923b 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -1,8 +1,8 @@ #![allow(clippy::expect_used)] -use crate::RootAlphaDividendsPerSubnet; use crate::tests::mock::{ - RuntimeOrigin, SubtensorModule, Test, add_dynamic_network, new_test_ext, run_to_block, + RuntimeEvent, RuntimeOrigin, SubtensorModule, System, Test, add_dynamic_network, new_test_ext, + run_to_block, }; use crate::{ DefaultMinRootClaimAmount, Error, MAX_NUM_ROOT_CLAIMS, MAX_ROOT_CLAIM_THRESHOLD, NetworksAdded, @@ -10,6 +10,7 @@ use crate::{ StakingColdkeys, StakingColdkeysByIndex, SubnetAlphaIn, SubnetMechanism, SubnetMovingPrice, SubnetTAO, SubnetTaoFlow, SubtokenEnabled, Tempo, pallet, }; +use crate::{Event, RootAlphaDividendsPerSubnet}; use crate::{RootClaimType, RootClaimTypeEnum, RootClaimed, ValidatorClaimType}; use approx::assert_abs_diff_eq; use frame_support::dispatch::RawOrigin; @@ -1566,6 +1567,55 @@ fn test_claim_root_with_unrelated_subnets() { }); } +#[test] +fn test_claim_root_with_set_validator_claim_type() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1001); + let hotkey = U256::from(1002); + let netuid = add_dynamic_network(&hotkey, &coldkey); + let new_claim_type = RootClaimTypeEnum::Swap; + + // Default check + assert_eq!( + ValidatorClaimType::::get(hotkey, netuid), + RootClaimTypeEnum::Keep + ); + + // Set new type + assert_ok!(SubtensorModule::set_validator_claim_type( + RuntimeOrigin::signed(coldkey), + hotkey, + netuid, + new_claim_type.clone() + ),); + + // Result check + assert_eq!( + ValidatorClaimType::::get(hotkey, netuid), + new_claim_type + ); + + let event = System::events().into_iter().find(|e| { + matches!( + &e.event, + RuntimeEvent::SubtensorModule(Event::ValidatorClaimTypeSet { .. }) + ) + }); + assert!(event.is_some()); + + if let Some(RuntimeEvent::SubtensorModule(Event::ValidatorClaimTypeSet { + hotkey: ev_hotkey, + root_claim_type: ev_claim_type, + netuid: ev_netuid, + })) = event.map(|e| e.event.clone()) + { + assert_eq!(ev_hotkey, hotkey); + assert_eq!(ev_claim_type, new_claim_type); + assert_eq!(ev_netuid, netuid); + } + }); +} + #[test] fn test_claim_root_with_delegated_claim_type() { new_test_ext(1).execute_with(|| { From 7b53c20e83a9dde46bc2786ac1639ad3118ceec9 Mon Sep 17 00:00:00 2001 From: Shamil Gadelshin Date: Fri, 5 Dec 2025 16:36:57 +0300 Subject: [PATCH 16/21] Bump spec version. --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 9c63332a75..8036969927 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -237,7 +237,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 357, + spec_version: 358, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 709676cbdb80ce5f147ac009ecc91475a60d0000 Mon Sep 17 00:00:00 2001 From: Shamil Gadelshin Date: Tue, 9 Dec 2025 16:28:38 +0300 Subject: [PATCH 17/21] Add tao outflow root_prop multiplier --- pallets/subtensor/src/staking/claim_root.rs | 36 +++++++++++++++++++-- pallets/subtensor/src/tests/claim_root.rs | 11 +++++-- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/pallets/subtensor/src/staking/claim_root.rs b/pallets/subtensor/src/staking/claim_root.rs index dd72134988..1c8e23257b 100644 --- a/pallets/subtensor/src/staking/claim_root.rs +++ b/pallets/subtensor/src/staking/claim_root.rs @@ -2,7 +2,7 @@ use super::*; use frame_support::weights::Weight; use sp_core::Get; use sp_std::collections::btree_set::BTreeSet; -use substrate_fixed::types::I96F32; +use substrate_fixed::types::{I96F32, U96F32}; use subtensor_swap_interface::SwapHandler; impl Pallet { @@ -190,8 +190,11 @@ impl Pallet { } }; + let recorded_tao_outflow = + Self::calculate_tao_outflow(netuid, owed_tao.amount_paid_out); + // Importantly measures swap as flow. - Self::record_tao_outflow(netuid, owed_tao.amount_paid_out.into()); + Self::record_tao_outflow(netuid, recorded_tao_outflow.into()); Self::increase_stake_for_hotkey_and_coldkey_on_subnet( hotkey, @@ -223,6 +226,35 @@ impl Pallet { }); } + // Calculates root proportion for subnet, uses "1 / root_prop" (with 10 as an upper bound) as + // a multiplier for provided Tao amount. + pub(crate) fn calculate_tao_outflow(netuid: NetUid, amount: TaoCurrency) -> TaoCurrency { + let root_tao: U96F32 = U96F32::saturating_from_num(SubnetTAO::::get(NetUid::ROOT)); + let tao_weight: U96F32 = root_tao.saturating_mul(Self::get_tao_weight()); + let alpha_issuance: U96F32 = U96F32::saturating_from_num(Self::get_alpha_issuance(netuid)); + let root_proportion: U96F32 = tao_weight + .checked_div(tao_weight.saturating_add(alpha_issuance)) + .unwrap_or(U96F32::saturating_from_num(0)); + + let root_prop_multiplier = { + let root_prop_multiplier = U96F32::saturating_from_num(1) + .checked_div(root_proportion) + .unwrap_or(U96F32::saturating_from_num(0.0)); + let upper_bound = U96F32::saturating_from_num(10); + + if root_prop_multiplier < upper_bound { + root_prop_multiplier + } else { + upper_bound + } + }; + + let tao = U96F32::saturating_from_num(amount.to_u64()); + let recorded_tao_outflow: u64 = tao.saturating_mul(root_prop_multiplier).to_num(); + + recorded_tao_outflow.into() + } + fn root_claim_on_subnet_weight(_root_claim_type: RootClaimTypeEnum) -> Weight { Weight::from_parts(60_000_000, 6987) .saturating_add(T::DbWeight::get().reads(7_u64)) diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index 6b4cb0923b..3ab48bec35 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -653,9 +653,11 @@ fn test_claim_root_with_drain_emissions_and_swap_claim_type() { RootClaimTypeEnum::Swap ),); assert_eq!(RootClaimType::::get(coldkey), RootClaimTypeEnum::Swap); - assert_eq!(SubnetTaoFlow::::get(netuid), 0); + // Setup root prop + SubnetTAO::::insert(NetUid::ROOT, TaoCurrency::from(1_000_000_000_000u64)); + assert_ok!(SubtensorModule::claim_root( RuntimeOrigin::signed(coldkey), BTreeSet::from([netuid]) @@ -682,10 +684,15 @@ fn test_claim_root_with_drain_emissions_and_swap_claim_type() { ); // Check tao flow + let tao_outflow: u64 = SubtensorModule::calculate_tao_outflow( + netuid, + (estimated_stake_increment as u64).into(), + ) + .into(); assert_abs_diff_eq!( SubnetTaoFlow::::get(netuid), - -estimated_stake_increment as i64, + -(tao_outflow as i64), epsilon = 10i64, ); From fa61d93fc6922ba70140f5e0a9f4ff014cb3ec4a Mon Sep 17 00:00:00 2001 From: Shamil Gadelshin Date: Tue, 9 Dec 2025 16:50:14 +0300 Subject: [PATCH 18/21] Add test for calculate_tao_outflow() --- pallets/subtensor/src/tests/claim_root.rs | 38 +++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index 3ab48bec35..60f6beab7c 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -2132,3 +2132,41 @@ fn test_claim_root_keep_subnets_swap_claim_type() { ); }); } + +#[test] +fn test_claim_root_calculate_tao_outflow() { + new_test_ext(1).execute_with(|| { + let hotkey = U256::from(1002); + let coldkey = U256::from(1003); + let netuid = add_dynamic_network(&hotkey, &coldkey); + + // Setup root prop + SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 + let alpha_in = AlphaCurrency::from(100_000_000_000); + SubnetAlphaIn::::insert(netuid, alpha_in); + + let tao_amount = 10u64; + + // Check unbounded tao outflow (coefficient < 10) + let tao_reserve = TaoCurrency::from(1_000_000_000_000u64); + SubnetTAO::::insert(NetUid::ROOT, tao_reserve); + + let tao_outflow_unbounded: u64 = + SubtensorModule::calculate_tao_outflow(netuid, tao_amount.into()).into(); + + assert_abs_diff_eq!(tao_outflow_unbounded, tao_amount, epsilon = 1u64,); + + // Check bounded tao outflow (coefficient > 10). + let tao_reserve = TaoCurrency::from(10_000_000_000u64); + SubnetTAO::::insert(NetUid::ROOT, tao_reserve); + + let tao_outflow_bounded: u64 = + SubtensorModule::calculate_tao_outflow(netuid, tao_amount.into()).into(); + let bounded_root_prop_multiplier = 10u64; + + assert_eq!( + tao_outflow_bounded, + bounded_root_prop_multiplier * tao_amount, + ); + }); +} From ef8e46ddfffb03f5da45587ebb75964cac48587a Mon Sep 17 00:00:00 2001 From: Shamil Gadelshin Date: Wed, 10 Dec 2025 16:06:34 +0300 Subject: [PATCH 19/21] Fix set_validator_claim_type --- pallets/subtensor/src/macros/dispatches.rs | 5 +++- pallets/subtensor/src/tests/claim_root.rs | 34 ++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 7d28c3e2e6..f54e70249c 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2453,7 +2453,10 @@ mod dispatches { // Ensure the delegate claim type is not Delegated. ensure!( - !matches!(new_claim_type, RootClaimTypeEnum::Delegated), + matches!( + new_claim_type, + RootClaimTypeEnum::Keep | RootClaimTypeEnum::Swap + ), Error::::InvalidRootClaimType ); diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index 60f6beab7c..5d1551846f 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -1620,6 +1620,40 @@ fn test_claim_root_with_set_validator_claim_type() { assert_eq!(ev_claim_type, new_claim_type); assert_eq!(ev_netuid, netuid); } + + // Check possible options + assert_ok!(SubtensorModule::set_validator_claim_type( + RuntimeOrigin::signed(coldkey), + hotkey, + netuid, + RootClaimTypeEnum::Keep + ),); + assert_ok!(SubtensorModule::set_validator_claim_type( + RuntimeOrigin::signed(coldkey), + hotkey, + netuid, + RootClaimTypeEnum::Swap + ),); + assert_err!( + SubtensorModule::set_validator_claim_type( + RuntimeOrigin::signed(coldkey), + hotkey, + netuid, + RootClaimTypeEnum::Delegated + ), + Error::::InvalidRootClaimType + ); + assert_err!( + SubtensorModule::set_validator_claim_type( + RuntimeOrigin::signed(coldkey), + hotkey, + netuid, + RootClaimTypeEnum::KeepSubnets { + subnets: BTreeSet::from([netuid]) + } + ), + Error::::InvalidRootClaimType + ); }); } From 5e8b299ef044aac478e08d5be40abb34b6cd5a6b Mon Sep 17 00:00:00 2001 From: Shamil Gadelshin Date: Mon, 15 Dec 2025 14:13:27 +0300 Subject: [PATCH 20/21] Update root claim defaults. --- pallets/subtensor/src/lib.rs | 4 ++-- pallets/subtensor/src/tests/claim_root.rs | 22 +++++++++++++++++++--- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index b585afb3e2..3d05a3aa54 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -330,9 +330,9 @@ pub mod pallet { /// Enum for the per-coldkey root claim setting. pub enum RootClaimTypeEnum { /// Swap any alpha emission for TAO. + #[default] Swap, /// Keep all alpha emission. - #[default] Keep, /// Keep all alpha emission for specified subnets. KeepSubnets { @@ -361,7 +361,7 @@ pub mod pallet { /// Default value for delegate claim type storage #[pallet::type_value] pub fn DefaultValidatorClaimType() -> RootClaimTypeEnum { - RootClaimTypeEnum::Keep + RootClaimTypeEnum::Swap } /// Default number of root claims per claim call. diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index 42e868571b..c8945d28e6 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -1585,7 +1585,7 @@ fn test_claim_root_with_set_validator_claim_type() { // Default check assert_eq!( ValidatorClaimType::::get(hotkey, netuid), - RootClaimTypeEnum::Keep + RootClaimTypeEnum::Swap ); // Set new type @@ -2206,10 +2206,26 @@ fn test_claim_root_calculate_tao_outflow() { } #[test] -fn test_claim_root_default_mode_keep() { +fn test_claim_root_default_mode() { new_test_ext(1).execute_with(|| { let coldkey = U256::from(1003); - assert_eq!(RootClaimType::::get(coldkey), RootClaimTypeEnum::Keep); + assert_eq!( + RootClaimType::::get(coldkey), + RootClaimTypeEnum::Delegated + ); + }); +} + +#[test] +fn test_claim_root_default_validator_mode() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1003); + let netuid = NetUid::from(100u16); + + assert_eq!( + ValidatorClaimType::::get(coldkey, netuid), + RootClaimTypeEnum::Swap + ); }); } From 860234f6f960596d1cae8e4ddb8896cd720fc7cc Mon Sep 17 00:00:00 2001 From: Shamil Gadelshin Date: Wed, 17 Dec 2025 17:36:48 +0300 Subject: [PATCH 21/21] Refactor root_prop usage. --- pallets/subtensor/src/staking/claim_root.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/pallets/subtensor/src/staking/claim_root.rs b/pallets/subtensor/src/staking/claim_root.rs index 1c8e23257b..bc38be406b 100644 --- a/pallets/subtensor/src/staking/claim_root.rs +++ b/pallets/subtensor/src/staking/claim_root.rs @@ -229,12 +229,7 @@ impl Pallet { // Calculates root proportion for subnet, uses "1 / root_prop" (with 10 as an upper bound) as // a multiplier for provided Tao amount. pub(crate) fn calculate_tao_outflow(netuid: NetUid, amount: TaoCurrency) -> TaoCurrency { - let root_tao: U96F32 = U96F32::saturating_from_num(SubnetTAO::::get(NetUid::ROOT)); - let tao_weight: U96F32 = root_tao.saturating_mul(Self::get_tao_weight()); - let alpha_issuance: U96F32 = U96F32::saturating_from_num(Self::get_alpha_issuance(netuid)); - let root_proportion: U96F32 = tao_weight - .checked_div(tao_weight.saturating_add(alpha_issuance)) - .unwrap_or(U96F32::saturating_from_num(0)); + let root_proportion = Self::root_proportion(netuid); let root_prop_multiplier = { let root_prop_multiplier = U96F32::saturating_from_num(1)