From 2c68387fcdc569795680e74b4eb4304fc2fa91b9 Mon Sep 17 00:00:00 2001 From: unconst Date: Thu, 20 Nov 2025 17:34:36 -0600 Subject: [PATCH 01/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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 );