Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
59 changes: 46 additions & 13 deletions pallets/subtensor/src/subnets/subnet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,17 +209,50 @@ impl<T: Config> Pallet<T> {
let symbol = Self::get_next_available_symbol(netuid_to_register);
TokenSymbol::<T>::insert(netuid_to_register, symbol);

// Seed the new subnet pool at a 1:1 reserve ratio.
// Separately, grant the subnet owner outstanding alpha based on the TAO they actually spent
// on registration converted by the current median subnet alpha price.
let pool_initial_tao: TaoBalance = Self::get_network_min_lock();
let pool_initial_alpha: AlphaBalance = pool_initial_tao.to_u64().into();
let owner_alpha_stake: AlphaBalance =
U96F32::saturating_from_num(actual_tao_lock_amount.to_u64())
.safe_div(median_subnet_alpha_price)
.saturating_floor()
.saturating_to_num::<u64>()
.into();
// Registration seeding model:
// L = actual_tao_lock_amount
// P = median TAO price of alpha across active subnets
// A = L / P
//
// Inject:
// - L / 4 TAO into the TAO side of the pool
// - A / 2 total alpha, split evenly between:
// * pool alpha reserve
// * owner staked alpha
//
// This means the owner's alpha comes out of the seeded A / 2 rather than
// being minted on top of it. Subject to integer rounding, this initializes
// the pool at the median price and gives the owner ~12.5% of L if they
// immediately fully unstake into the fresh pool.
let two = U96F32::saturating_from_num(2_u64);
let four = U96F32::saturating_from_num(4_u64);
let lock_amount_as_float = U96F32::saturating_from_num(actual_tao_lock_amount.to_u64());

let alpha_at_median_price = lock_amount_as_float.safe_div(median_subnet_alpha_price);

let pool_initial_tao: TaoBalance = lock_amount_as_float
.safe_div(four)
.saturating_floor()
.saturating_to_num::<u64>()
.into();

let total_created_alpha: AlphaBalance = alpha_at_median_price
.safe_div(two)
.saturating_floor()
.saturating_to_num::<u64>()
.into();

let owner_alpha_stake: AlphaBalance = alpha_at_median_price
.safe_div(four)
.saturating_floor()
.saturating_to_num::<u64>()
.into();

// The owner's alpha must come out of the seeded A / 2 rather than be
// minted on top of it. Any rounding dust remains in the pool.
let pool_initial_alpha: AlphaBalance =
total_created_alpha.saturating_sub(owner_alpha_stake);

let actual_tao_lock_amount_less_pool_tao =
actual_tao_lock_amount.saturating_sub(pool_initial_tao);

Expand Down Expand Up @@ -252,7 +285,7 @@ impl<T: Config> Pallet<T> {
}

if actual_tao_lock_amount > TaoBalance::ZERO && pool_initial_tao > TaoBalance::ZERO {
// Record in TotalStake the initial TAO in the pool.
// Record in TotalStake only the TAO actually injected into the subnet pool.
Self::increase_total_stake(pool_initial_tao);
}

Expand Down Expand Up @@ -280,7 +313,7 @@ impl<T: Config> Pallet<T> {
log::info!("NetworkAdded( netuid:{netuid_to_register:?}, mechanism:{mechid:?} )");
Self::deposit_event(Event::NetworkAdded(netuid_to_register, mechid));

// --- 19. Return success.
// --- 20. Return success.
Ok(())
}

Expand Down
89 changes: 55 additions & 34 deletions pallets/subtensor/src/tests/coinbase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2394,28 +2394,42 @@ fn test_distribute_emission_no_miners_all_drained() {
new_test_ext(1).execute_with(|| {
let netuid = add_dynamic_network(&U256::from(1), &U256::from(2));
remove_owner_registration_stake(netuid);

// Restore the pre-change test invariant that this subnet starts with an exact 1:1 spot price.
mock::setup_reserves(
netuid,
TaoBalance::from(1_000_000_000_000_u64),
AlphaBalance::from(1_000_000_000_000_u64),
);

let hotkey = U256::from(3);
let coldkey = U256::from(4);
let init_stake = 1;
let init_stake = 1_u64;

SubtensorModule::set_burn(netuid, TaoBalance::from(0));
SubtensorModule::set_subnet_owner_cut(0_u16);

register_ok_neuron(netuid, hotkey, coldkey, 0);
// Give non-zero stake

// Give non-zero stake.
SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet(
&hotkey,
&coldkey,
netuid,
init_stake.into(),
);

assert_eq!(
SubtensorModule::get_total_stake_for_hotkey(&hotkey),
init_stake.into()
TaoBalance::from(init_stake)
);

// Set the weight of root TAO to be 0%, so only alpha is effective.
SubtensorModule::set_tao_weight(0);

// Set the emission to be 1 million.
let emission = AlphaBalance::from(1_000_000);
let emission = AlphaBalance::from(1_000_000_u64);

// Run drain pending without any miners.
SubtensorModule::distribute_emission(
netuid,
Expand All @@ -2425,14 +2439,12 @@ fn test_distribute_emission_no_miners_all_drained() {
AlphaBalance::ZERO,
);

// Get the new stake of the hotkey.
let new_stake = SubtensorModule::get_total_stake_for_hotkey(&hotkey);
// We expect this neuron to get *all* the emission.
// Slight epsilon due to rounding (hotkey_take).
let new_stake = SubtensorModule::get_total_stake_for_hotkey(&hotkey);
assert_abs_diff_eq!(
new_stake,
u64::from(emission + init_stake.into()).into(),
epsilon = 1.into()
TaoBalance::from(init_stake + emission.to_u64()),
epsilon = TaoBalance::from(1_u64)
);
});
}
Expand All @@ -2442,28 +2454,38 @@ fn test_distribute_emission_no_miners_all_drained() {
fn test_distribute_emission_zero_emission() {
new_test_ext(1).execute_with(|| {
let netuid = add_dynamic_network_disable_commit_reveal(&U256::from(1), &U256::from(2));
remove_owner_registration_stake(netuid);

// Restore the pre-change test invariant that this subnet starts with an exact 1:1 spot price.
mock::setup_reserves(
netuid,
TaoBalance::from(1_000_000_000_000_u64),
AlphaBalance::from(1_000_000_000_000_u64),
);

let hotkey = U256::from(3);
let coldkey = U256::from(4);
let miner_hk = U256::from(5);
let miner_ck = U256::from(6);
let init_stake: u64 = 100_000_000_000_000;
let tempo = 2;

SubtensorModule::set_tempo(netuid, tempo);
// Set weight-set limit to 0.
SubtensorModule::set_weights_set_rate_limit(netuid, 0);

register_ok_neuron(netuid, hotkey, coldkey, 0);
register_ok_neuron(netuid, miner_hk, miner_ck, 0);
// Give non-zero stake

SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet(
&hotkey,
&coldkey,
netuid,
init_stake.into(),
);

assert_eq!(
SubtensorModule::get_total_stake_for_hotkey(&hotkey),
init_stake.into()
TaoBalance::from(init_stake)
);

// Set the weight of root TAO to be 0%, so only alpha is effective.
Expand All @@ -2474,7 +2496,7 @@ fn test_distribute_emission_zero_emission() {
// Run epoch for initial setup.
SubtensorModule::epoch(netuid, AlphaBalance::ZERO);

// Set weights on miner
// Set weights on miner.
assert_ok!(SubtensorModule::set_weights(
RuntimeOrigin::signed(hotkey),
netuid,
Expand All @@ -2485,7 +2507,6 @@ fn test_distribute_emission_zero_emission() {

run_to_block_no_epoch(netuid, 50);

// Clear incentive and dividends.
Incentive::<Test>::remove(NetUidStorageIndex::from(netuid));
Dividends::<Test>::remove(netuid);

Expand All @@ -2498,12 +2519,11 @@ fn test_distribute_emission_zero_emission() {
AlphaBalance::ZERO,
);

// Get the new stake of the hotkey.
// Stake should remain unchanged.
let new_stake = SubtensorModule::get_total_stake_for_hotkey(&hotkey);
// We expect the stake to remain unchanged.
assert_eq!(new_stake, init_stake.into());
assert_eq!(new_stake, TaoBalance::from(init_stake));

// Check that the incentive and dividends are set by epoch.
// Epoch should still have rebuilt incentive/dividend state.
assert!(
Incentive::<Test>::get(NetUidStorageIndex::from(netuid))
.iter()
Expand Down Expand Up @@ -3968,32 +3988,33 @@ fn test_get_subnet_terms_alpha_emissions_cap() {
let owner_hotkey = U256::from(10);
let owner_coldkey = U256::from(11);
let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey);

// Restore the original "price = 1.0" precondition explicitly.
mock::setup_reserves(
netuid,
TaoBalance::from(1_000_000_000_u64),
AlphaBalance::from(1_000_000_000_u64),
);

let tao_block_emission: U96F32 = U96F32::saturating_from_num(
SubtensorModule::get_block_emission()
.unwrap_or(TaoBalance::ZERO)
.to_u64(),
);

// price = 1.0
// tao_block_emission = 1000000000
// tao_block_emission == alpha_emission_i
// alpha_in_i <= alpha_injection_cap
let emissions1 = U96F32::from_num(100_000_000);

let emissions1 = U96F32::from_num(100_000_000_u64);
let subnet_emissions1 = BTreeMap::from([(netuid, emissions1)]);
let (_, alpha_in, _, _) = SubtensorModule::get_subnet_terms(&subnet_emissions1);

assert_eq!(alpha_in.get(&netuid).copied().unwrap(), emissions1);
let (_, alpha_in_1, _, _) = SubtensorModule::get_subnet_terms(&subnet_emissions1);

// price = 1.0
// tao_block_emission = 1000000000
// tao_block_emission == alpha_emission_i
// alpha_in_i > alpha_injection_cap
let emissions2 = U96F32::from_num(10_000_000_000u64);
assert_eq!(alpha_in_1.get(&netuid).copied().unwrap(), emissions1);

let emissions2 = U96F32::from_num(10_000_000_000_u64);
let subnet_emissions2 = BTreeMap::from([(netuid, emissions2)]);
let (_, alpha_in, _, _) = SubtensorModule::get_subnet_terms(&subnet_emissions2);
let (_, alpha_in_2, _, _) = SubtensorModule::get_subnet_terms(&subnet_emissions2);

assert_eq!(alpha_in.get(&netuid).copied().unwrap(), tao_block_emission);
assert_eq!(
alpha_in_2.get(&netuid).copied().unwrap(),
tao_block_emission
);
});
}
Loading
Loading