Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
18ad00a
owner set validator cut
open-junius Sep 12, 2025
3048554
commit Cargo.lock
open-junius Sep 12, 2025
5372967
commit Cargo.lock
open-junius Sep 12, 2025
6de0f23
commit Cargo.lock
open-junius Sep 12, 2025
634079e
fix unit test
open-junius Sep 12, 2025
15fd19c
precision improvement
open-junius Sep 14, 2025
ab1d2ec
rebase pr
open-junius Oct 2, 2025
6730799
commit Cargo.lock
open-junius Oct 2, 2025
31e62ba
commit Cargo.lock
open-junius Oct 2, 2025
35785da
clean up code
open-junius Oct 2, 2025
d5fdb55
Merge branch 'devnet-ready' into validator_cut
open-junius Oct 3, 2025
a04b2cc
fix unit test
open-junius Oct 3, 2025
ee6957a
add separate test file
open-junius Oct 7, 2025
e792af1
update emisson accroding to rate
open-junius Oct 7, 2025
8e07a52
add test file to mod
open-junius Oct 7, 2025
d5f44c5
more test cases
open-junius Oct 7, 2025
b2dd663
cargo clippy
open-junius Oct 7, 2025
1cb952e
cargo fix
open-junius Oct 7, 2025
a1bc8b5
cargo fix
open-junius Oct 7, 2025
279cdf3
cargo fix
open-junius Oct 7, 2025
7e48760
Merge branch 'devnet-ready' into validator_cut
open-junius Oct 7, 2025
e358179
bump version
open-junius Oct 7, 2025
6e55a8b
Merge branch 'devnet-ready' into validator_cut
open-junius Oct 8, 2025
cc9304d
auto-update benchmark weights
github-actions[bot] Oct 8, 2025
85b082a
minx max limit
open-junius Oct 8, 2025
43fce2e
add min/max value for validator cut
open-junius Oct 9, 2025
3d6180f
commit Cargo.lock
open-junius Oct 9, 2025
e6b2612
commit Cargo.lock
open-junius Oct 9, 2025
fd40e3d
commit Cargo.lock
open-junius Oct 9, 2025
5a9c7f9
cargo clippy
open-junius Oct 9, 2025
c3077aa
commit Cargo.lock
open-junius Oct 9, 2025
dbaf5c2
commit Cargo.lock
open-junius Oct 9, 2025
c2df16d
fix conflict
open-junius Oct 9, 2025
51e97e2
fix unit tests
open-junius Oct 9, 2025
c551ca2
update boundary
open-junius Oct 9, 2025
e974265
fix unit tests
open-junius Oct 9, 2025
466cf5d
remove invalid edge case
open-junius Oct 10, 2025
92a88ff
fix conflict
open-junius Oct 10, 2025
3c4a86e
Merge branch 'devnet-ready' into validator_cut
open-junius Oct 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions pallets/admin-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2145,6 +2145,33 @@ pub mod pallet {
);
Ok(())
}

/// Sets the validator cut for a subnet.
/// Only callable by subnet owner or root.
#[pallet::call_index(81)]
#[pallet::weight(Weight::from_parts(15_000_000, 0)
.saturating_add(<T as frame_system::Config>::DbWeight::get().reads(1_u64))
.saturating_add(<T as frame_system::Config>::DbWeight::get().writes(1_u64)))]
pub fn sudo_set_validator_cut(
origin: OriginFor<T>,
netuid: NetUid,
cut: u64,
) -> DispatchResult {
let maybe_owner = pallet_subtensor::Pallet::<T>::ensure_sn_owner_or_root_with_limits(
origin.clone(),
netuid,
&[TransactionType::SetValidatorCut],
)?;

pallet_subtensor::Pallet::<T>::set_validator_cut(netuid, cut)?;
log::debug!("ValidatorCutSet( netuid: {netuid:?}, cut: {cut:?} ) ");
pallet_subtensor::Pallet::<T>::record_owner_rl(
maybe_owner,
netuid,
&[TransactionType::SetValidatorCut],
);
Ok(())
}
}
}

Expand Down
126 changes: 126 additions & 0 deletions pallets/admin-utils/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2895,3 +2895,129 @@ fn test_sudo_set_min_allowed_uids() {
);
});
}

#[test]
fn test_get_validator_cut() {
new_test_ext().execute_with(|| {
let netuid = NetUid::from(1);
let expected_cut: u64 = u64::MAX / 2; // 50% cut

// Set up a network
add_network(netuid, 10);

// Set a validator cut value
assert_ok!(SubtensorModule::set_validator_cut(netuid, expected_cut));

// Test that we can retrieve the value
let retrieved_cut = SubtensorModule::get_validator_cut(netuid);
assert_eq!(retrieved_cut, expected_cut);
});
}

#[test]
fn test_set_validator_cut() {
new_test_ext().execute_with(|| {
let netuid = NetUid::from(2);
let initial_cut: u64 = pallet_subtensor::DefaultValidatorCut::<Test>::get();
let new_cut: u64 = u64::MAX / 3; // 33% cut

// Set up a network
add_network(netuid, 10);

// Verify initial value
assert_eq!(SubtensorModule::get_validator_cut(netuid), initial_cut);

// Set new validator cut
assert_ok!(SubtensorModule::set_validator_cut(netuid, new_cut));

// Verify the value was set correctly
assert_eq!(SubtensorModule::get_validator_cut(netuid), new_cut);
});
}

#[test]
fn test_sudo_set_validator_cut() {
new_test_ext().execute_with(|| {
let netuid = NetUid::from(3);
let to_be_set: u64 = u64::MAX / 3;

// Set up a network
add_network(netuid, 10);

let sn_owner = U256::from(1324);
// Set the Subnet Owner
SubnetOwner::<Test>::insert(netuid, sn_owner);

let init_value = SubtensorModule::get_validator_cut(netuid);

// Test that non-authorized origin fails (using a regular signed origin)
assert_eq!(
AdminUtils::sudo_set_validator_cut(
<<Test as Config>::RuntimeOrigin>::signed(U256::from(1)),
netuid,
to_be_set
),
Err(DispatchError::BadOrigin)
);
// Value should remain unchangeds
assert_eq!(SubtensorModule::get_validator_cut(netuid), init_value);

assert_ok!(AdminUtils::sudo_set_validator_cut(
<<Test as Config>::RuntimeOrigin>::signed(sn_owner),
netuid,
to_be_set
));

// Verify the value was set correctly
assert_eq!(SubtensorModule::get_validator_cut(netuid), to_be_set);
});
}

#[test]
fn test_sudo_set_validator_cut_root() {
new_test_ext().execute_with(|| {
let netuid = NetUid::from(4);
let to_be_set: u64 = u64::MAX / 3;

// Set up a network
add_network(netuid, 10);

// Test that root can set the validator cut successfully
assert_ok!(AdminUtils::sudo_set_validator_cut(
<<Test as Config>::RuntimeOrigin>::root(),
netuid,
to_be_set
));

// Verify the value was set correctly
assert_eq!(SubtensorModule::get_validator_cut(netuid), to_be_set);
});
}

#[test]
fn test_validator_cut_bounds() {
new_test_ext().execute_with(|| {
let netuid = NetUid::from(5);
let min_cut: u64 = 0; // 0% cut
let max_cut: u64 = u64::MAX; // 100% cut

// Set up a network
add_network(netuid, 10);

// Test minimum value
assert_err!(
SubtensorModule::set_validator_cut(netuid, min_cut),
DispatchError::from(pallet_subtensor::Error::<Test>::InvalidValidatorCut)
);

// Test maximum value
assert_err!(
SubtensorModule::set_validator_cut(netuid, max_cut),
DispatchError::from(pallet_subtensor::Error::<Test>::InvalidValidatorCut)
);
assert_eq!(
SubtensorModule::get_validator_cut(netuid),
DefaultValidatorCut::<Test>::get()
);
});
}
31 changes: 27 additions & 4 deletions pallets/subtensor/src/coinbase/run_coinbase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -676,11 +676,34 @@ impl<T: Config> Pallet<T> {
});
log::debug!("incentive_sum: {incentive_sum:?}");

let validator_cut = Self::get_validator_cut(netuid);
log::debug!("validator_cut: {validator_cut:?}");

let rate = U96F32::from(validator_cut).saturating_div(u64::MAX.into());
log::debug!("validator rate: {rate:?}");
let miner_rate = U96F32::from(1_u64)
.saturating_sub(rate)
.saturating_mul(U96F32::from(2_u64));

// Update incentive according to the validator cut
let hotkey_emission: Vec<(T::AccountId, AlphaCurrency, AlphaCurrency)> = hotkey_emission
.iter()
.map(|(hotkey, incentive, reward)| {
let result: AlphaCurrency = U96F32::from(incentive.to_u64())
.saturating_mul(miner_rate)
.saturating_to_num::<u64>()
.into();
(hotkey.clone(), result, *reward)
})
.collect();

let pending_validator_alpha = if !incentive_sum.is_zero() {
pending_alpha
.saturating_add(pending_swapped)
.saturating_div(2.into())
.saturating_sub(pending_swapped)
let pending_alpha_f = U96F32::from(pending_alpha.to_u64());
let result = pending_alpha_f
.saturating_add(U96F32::from(pending_swapped.to_u64()))
.saturating_mul(rate)
.saturating_sub(U96F32::from(pending_swapped.to_u64()));
result.saturating_to_num::<u64>().into()
} else {
// If the incentive is 0, then Validators get 100% of the alpha.
pending_alpha
Expand Down
23 changes: 23 additions & 0 deletions pallets/subtensor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1564,6 +1564,29 @@ pub mod pallet {
pub type ImmuneOwnerUidsLimit<T> =
StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultImmuneOwnerUidsLimit<T>>;

#[pallet::type_value]
/// Min validator cut 1%, placeholder final value TBD
pub fn MinValidatorCut<T: Config>() -> u64 {
u64::MAX / 100
}

#[pallet::type_value]
/// Max validator cut 99%, placeholder final value TBD
pub fn MaxValidatorCut<T: Config>() -> u64 {
u64::MAX / 100 * 99
}

#[pallet::type_value]
/// Default validator cut 50%
pub fn DefaultValidatorCut<T: Config>() -> u64 {
u64::MAX / 2
}

#[pallet::storage]
/// --- MAP ( netuid ) --> Validator cut
pub type ValidatorCut<T> =
StorageMap<_, Identity, NetUid, u64, ValueQuery, DefaultValidatorCut<T>>;

/// =======================================
/// ==== Subnetwork Consensus Storage ====
/// =======================================
Expand Down
2 changes: 1 addition & 1 deletion pallets/subtensor/src/macros/dispatches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2263,7 +2263,7 @@ mod dispatches {
/// * commit_reveal_version (`u16`):
/// - The client (bittensor-drand) version
#[pallet::call_index(113)]
#[pallet::weight((Weight::from_parts(63_160_000, 0)
#[pallet::weight((Weight::from_parts(92_350_000, 0)
.saturating_add(T::DbWeight::get().reads(10_u64))
.saturating_add(T::DbWeight::get().writes(2)), DispatchClass::Normal, Pays::No))]
pub fn commit_timelocked_weights(
Expand Down
2 changes: 2 additions & 0 deletions pallets/subtensor/src/macros/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,8 @@ mod errors {
UidMapCouldNotBeCleared,
/// Trimming would exceed the max immune neurons percentage
TrimmingWouldExceedMaxImmunePercentage,
/// Invalid validator cut
InvalidValidatorCut,
/// Violating the rules of Childkey-Parentkey consistency
ChildParentInconsistency,
}
Expand Down
25 changes: 25 additions & 0 deletions pallets/subtensor/src/staking/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,31 @@ impl<T: Config> Pallet<T> {
});
}

/// Gets the validator cut for a given subnet.
///
/// # Arguments
/// * `netuid` - The network UID.
///
/// # Returns
/// The validator cut value for the subnet.
pub fn get_validator_cut(netuid: NetUid) -> u64 {
ValidatorCut::<T>::get(netuid)
}

/// Sets the validator cut for a given subnet.
///
/// # Arguments
/// * `netuid` - The network UID.
/// * `cut` - The validator cut value to set.
pub fn set_validator_cut(netuid: NetUid, cut: u64) -> DispatchResult {
ensure!(
cut >= MinValidatorCut::<T>::get() && cut <= MaxValidatorCut::<T>::get(),
Error::<T>::InvalidValidatorCut
);
ValidatorCut::<T>::insert(netuid, cut);
Ok(())
}

pub fn burn_subnet_alpha(_netuid: NetUid, _amount: AlphaCurrency) {
// Do nothing; TODO: record burned alpha in a tracker
}
Expand Down
1 change: 1 addition & 0 deletions pallets/subtensor/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@ mod swap_coldkey;
mod swap_hotkey;
mod swap_hotkey_with_subnet;
mod uids;
mod validator_cut;
mod weights;
Loading
Loading