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
17 changes: 17 additions & 0 deletions pallets/admin-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1963,6 +1963,23 @@ pub mod pallet {
Ok(())
}

/// Enables or disables net TAO flow (protocol cost deduction from emission shares).
/// When enabled, emission shares use net flow = user flow - protocol cost.
/// When disabled, emission shares use gross user flow only (current behavior).
#[pallet::call_index(91)]
#[pallet::weight(Weight::from_parts(7_343_000, 0)
.saturating_add(<T as frame_system::Config>::DbWeight::get().reads(0))
.saturating_add(<T as frame_system::Config>::DbWeight::get().writes(1)))]
pub fn sudo_set_net_tao_flow_enabled(
origin: OriginFor<T>,
enabled: bool,
) -> DispatchResult {
ensure_root(origin)?;
pallet_subtensor::Pallet::<T>::set_net_tao_flow_enabled(enabled);
log::debug!("set_net_tao_flow_enabled( {enabled:?} ) ");
Ok(())
}

/// Sets the global maximum number of mechanisms in a subnet
#[pallet::call_index(88)]
#[pallet::weight(Weight::from_parts(15_000_000, 0)
Expand Down
4 changes: 4 additions & 0 deletions pallets/subtensor/src/coinbase/root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,10 @@ impl<T: Config> Pallet<T> {
SubnetMovingPrice::<T>::remove(netuid);
SubnetTaoFlow::<T>::remove(netuid);
SubnetEmaTaoFlow::<T>::remove(netuid);
SubnetProtocolFlow::<T>::remove(netuid);
SubnetEmaProtocolFlow::<T>::remove(netuid);
SubnetExcessTao::<T>::remove(netuid);
SubnetRootSellTao::<T>::remove(netuid);
SubnetTaoProvided::<T>::remove(netuid);

// --- 13. Token / mechanism / registration toggles.
Expand Down
14 changes: 14 additions & 0 deletions pallets/subtensor/src/coinbase/run_coinbase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ impl<T: Config> Pallet<T> {
log::debug!(
"Running coinbase for block {current_block:?} with block emission: {block_emission:?}"
);

// Reset per-block root sell counters from the previous block.
// Root sells (step 8 in block_step) happen after coinbase, so their
// accumulated values are consumed here at the start of the next block.
let _ = SubnetRootSellTao::<T>::clear(u32::MAX, None);

// --- 1. Get all subnets (excluding root).
let subnets: Vec<NetUid> = Self::get_all_subnet_netuids()
.into_iter()
Expand Down Expand Up @@ -97,6 +103,11 @@ impl<T: Config> Pallet<T> {
let bought_alpha: AlphaBalance =
buy_swap_result_ok.amount_paid_out.into();
Self::recycle_subnet_alpha(*netuid_i, bought_alpha);

// Record actual excess TAO that entered pool.
let actual_excess: TaoBalance = buy_swap_result_ok.amount_paid_in;
SubnetExcessTao::<T>::insert(*netuid_i, actual_excess);
Self::record_protocol_inflow(*netuid_i, actual_excess);
}
}
Err(remainder) => {
Expand Down Expand Up @@ -132,6 +143,9 @@ impl<T: Config> Pallet<T> {
TotalStake::<T>::mutate(|total| {
*total = total.saturating_add(injected_tao);
});

// Record emission injection as protocol inflow.
Self::record_protocol_inflow(*netuid_i, injected_tao);
}
Err(remainder) => {
remaining_credit = remainder;
Expand Down
58 changes: 54 additions & 4 deletions pallets/subtensor/src/coinbase/subnet_emissions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,45 @@ impl<T: Config> Pallet<T> {
SubnetTaoFlow::<T>::remove(netuid);
}

pub fn record_protocol_inflow(netuid: NetUid, tao: TaoBalance) {
SubnetProtocolFlow::<T>::mutate(netuid, |flow| {
*flow = flow.saturating_add(u64::from(tao) as i64);
});
}

pub fn record_protocol_outflow(netuid: NetUid, tao: TaoBalance) {
SubnetProtocolFlow::<T>::mutate(netuid, |flow| {
*flow = flow.saturating_sub(u64::from(tao) as i64);
});
}

pub fn reset_protocol_flow(netuid: NetUid) {
SubnetProtocolFlow::<T>::remove(netuid);
}

fn get_ema_protocol_flow(netuid: NetUid) -> I64F64 {
let current_block: u64 = Self::get_current_block_as_u64();

let block_flow = I64F64::saturating_from_num(SubnetProtocolFlow::<T>::get(netuid));
let (last_block, last_block_ema) =
SubnetEmaProtocolFlow::<T>::get(netuid).unwrap_or((0, I64F64::saturating_from_num(0)));

if last_block != current_block {
let flow_alpha = I64F64::saturating_from_num(FlowEmaSmoothingFactor::<T>::get())
.safe_div(I64F64::saturating_from_num(i64::MAX));
let one = I64F64::saturating_from_num(1);
let ema_flow = (one.saturating_sub(flow_alpha))
.saturating_mul(last_block_ema)
.saturating_add(flow_alpha.saturating_mul(block_flow));
SubnetEmaProtocolFlow::<T>::insert(netuid, (current_block, ema_flow));

Self::reset_protocol_flow(netuid);
ema_flow
} else {
last_block_ema
}
}

// Update SubnetEmaTaoFlow if needed and return its value for
// the current block
#[allow(dead_code)]
Expand Down Expand Up @@ -177,12 +216,23 @@ impl<T: Config> Pallet<T> {
// Implementation of shares that uses TAO flow
#[allow(dead_code)]
fn get_shares_flow(subnets_to_emit_to: &[NetUid]) -> BTreeMap<NetUid, U64F64> {
// Get raw flows
let ema_flows = subnets_to_emit_to
let net_flow_enabled = NetTaoFlowEnabled::<T>::get();

// Always update the protocol EMA (keeps it warm for when toggled on)
let ema_flows: BTreeMap<NetUid, I64F64> = subnets_to_emit_to
.iter()
.map(|netuid| (*netuid, Self::get_ema_flow(*netuid)))
.map(|netuid| {
let user_ema = Self::get_ema_flow(*netuid);
let protocol_ema = Self::get_ema_protocol_flow(*netuid);
let net = if net_flow_enabled {
user_ema.saturating_sub(protocol_ema)
} else {
user_ema
};
(*netuid, net)
})
.collect();
log::debug!("EMA flows: {ema_flows:?}");
log::debug!("EMA flows (net_flow_enabled={net_flow_enabled}): {ema_flows:?}");

// Clip the EMA flow with lower limit L
// z[i] = max{S[i] − L, 0}
Expand Down
29 changes: 29 additions & 0 deletions pallets/subtensor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1344,6 +1344,16 @@ pub mod pallet {
pub type SubnetTaoInEmission<T: Config> =
StorageMap<_, Identity, NetUid, TaoBalance, ValueQuery, DefaultZeroTao<T>>;

/// --- MAP ( netuid ) --> excess_tao | Returns the excess TAO swapped (chain buys) into this subnet on the last block.
#[pallet::storage]
pub type SubnetExcessTao<T: Config> =
StorageMap<_, Identity, NetUid, TaoBalance, ValueQuery, DefaultZeroTao<T>>;

/// --- MAP ( netuid ) --> root_sell_tao | Returns the TAO received from root dividend sells on this subnet on the last block.
#[pallet::storage]
pub type SubnetRootSellTao<T: Config> =
StorageMap<_, Identity, NetUid, TaoBalance, ValueQuery, DefaultZeroTao<T>>;

/// --- MAP ( netuid ) --> alpha_supply_in_pool | Returns the amount of alpha in the pool.
#[pallet::storage]
pub type SubnetAlphaIn<T: Config> =
Expand Down Expand Up @@ -1576,6 +1586,25 @@ pub mod pallet {
pub type SubnetEmaTaoFlow<T: Config> =
StorageMap<_, Identity, NetUid, (u64, I64F64), OptionQuery>;

/// --- ITEM --> net_tao_flow_enabled | When true, emission shares use net flow (user - protocol). When false, uses gross user flow only.
#[pallet::type_value]
pub fn DefaultNetTaoFlowEnabled<T: Config>() -> bool {
true
}
#[pallet::storage]
pub type NetTaoFlowEnabled<T: Config> =
StorageValue<_, bool, ValueQuery, DefaultNetTaoFlowEnabled<T>>;

/// --- MAP ( netuid ) --> subnet_protocol_flow | Per-block accumulator for protocol cost (emission + chain buys - root sells).
#[pallet::storage]
pub type SubnetProtocolFlow<T: Config> =
StorageMap<_, Identity, NetUid, i64, ValueQuery, DefaultZeroI64<T>>;

/// --- MAP ( netuid ) --> subnet_ema_protocol_flow | EMA of protocol cost flow, same smoothing as SubnetEmaTaoFlow.
#[pallet::storage]
pub type SubnetEmaProtocolFlow<T: Config> =
StorageMap<_, Identity, NetUid, (u64, I64F64), OptionQuery>;

/// Default value for flow cutoff.
#[pallet::type_value]
pub fn DefaultFlowCutoff<T: Config>() -> I64F64 {
Expand Down
7 changes: 7 additions & 0 deletions pallets/subtensor/src/staking/claim_root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,13 @@ impl<T: Config> Pallet<T> {
}
};

// Record root sell as protocol outflow (reduces protocol cost).
let root_sell_tao: TaoBalance = owed_tao.amount_paid_out;
SubnetRootSellTao::<T>::mutate(netuid, |total| {
*total = total.saturating_add(root_sell_tao);
});
Self::record_protocol_outflow(netuid, root_sell_tao);

// Transfer unstaked TAO from subnet account to the root subnet account
// and increase root stake.
if let Some(root_subnet_account_id) = Self::get_subnet_account_id(NetUid::ROOT)
Expand Down
5 changes: 5 additions & 0 deletions pallets/subtensor/src/utils/misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -908,6 +908,11 @@ impl<T: Config> Pallet<T> {
FlowEmaSmoothingFactor::<T>::set(smoothing_factor);
}

/// Enables or disables net TAO flow (protocol cost deduction from emission shares).
pub fn set_net_tao_flow_enabled(enabled: bool) {
NetTaoFlowEnabled::<T>::set(enabled);
}

/// Multiply an integer `value` by a Q32 fixed-point factor.
///
/// Q32 means:
Expand Down
Loading