Emission Mechanics
This deep-dive follows the complete journey of TAO from block creation to participant wallets. We trace the exact flow that happens every ~12 seconds in Subtensor.
This page traces the emission flow that happens during Phase 1: on_initialize. For epoch-specific consensus calculations, see Yuma Consensus.
dTAO Emission Flow (per block)
Example: Who Gets What (one block, one subnet)
Step 1: Calculate Block Emission
Every block (~12 seconds), new TAO is created. The amount follows a logarithmic decay based on total issuance:
// From run_coinbase.rs
fn get_block_emission() -> u64 {
let issuance = TotalIssuance::get();
let max_supply = 21_000_000 * 10^9; // 21M TAO in RAO
// Logarithmic decay: emission decreases as issuance approaches max
let remaining = max_supply - issuance;
let emission = remaining / DECAY_CONSTANT;
emission
}| Total Issuance | Approx. Emission/Block | Daily Emission |
|---|---|---|
| ~7.5M TAO (current) | ~0.5 TAO | ~3,600 TAO |
| ~10.5M TAO | ~0.25 TAO | ~1,800 TAO |
| ~15M TAO | ~0.125 TAO | ~900 TAO |
The first halving has already occurred. Current emission is approximately half of the initial rate.
Storage: BlockEmission stores the calculated value for the current block.
Step 2: Determine Subnet Shares (TAO Flow)
The total block emission is divided among subnets. With dTAO, shares are calculated using TAO Flow, a measure of actual TAO movement through each subnet's liquidity pool.
TAO Flow Algorithm
For each subnet, the system tracks net TAO inflows (staking) and outflows (unstaking):
// From subnet_emissions.rs
// Track when users stake/unstake
record_tao_inflow(netuid, tao_amount); // Staking adds TAO
record_tao_outflow(netuid, tao_amount); // Unstaking removes TAO
// Calculate EMA of flow for smoothing
net_flow = SubnetTaoFlow::get(netuid); // Running total
ema_flow = FlowEmaSmoothingFactor * net_flow
+ (1 - FlowEmaSmoothingFactor) * previous_ema;
// Power-law normalization across subnets
share[netuid] = max(ema_flow - cutoff, 0) ^ exponent
shares = normalize(shares) // Sum to 1Why TAO Flow?
TAO Flow measures actual demand for a subnet. A subnet with heavy staking activity indicates user interest and receives more emission – far more dynamic than using static weights from the root network.
Storage:
SubnetTaoFlow: Running net TAO flow per subnetSubnetEmaTaoFlow: EMA-smoothed flow for share calculation
Step 3: Convert to Alpha Tokens
With dTAO, each subnet has its own token called Alpha. Emission doesn't go directly to participants as TAO – it first passes through the subnet's liquidity pool.
The Liquidity Pool
Each subnet has a constant-product liquidity pool that enables swapping between TAO and Alpha:
| Pool Component | Storage | Purpose |
|---|---|---|
| TAO Reserves | SubnetTAO | TAO locked in the pool |
| Alpha Price | SubnetMovingPrice | EMA-smoothed exchange rate |
| Alpha Out | SubnetAlphaOut | Alpha available for distribution |
| Alpha In | SubnetAlphaIn | Alpha returned from operations |
How Alpha Gets Its Value
Alpha price is determined by the ratio of TAO to Alpha in the liquidity pool:
The result is a market dynamic:
- Staking TAO into a subnet adds TAO to the pool, increasing Alpha price
- Unstaking from a subnet removes TAO from the pool, decreasing Alpha price
- High demand subnets accumulate more TAO, making their Alpha more valuable
Example: Alpha Appreciation
Subnet 1 pool: 10,000 TAO ↔ 10,000 Alpha → Price = 1 TAO/α
After heavy staking: 20,000 TAO ↔ 15,000 Alpha → Price = 1.33 TAO/α
Early stakers' Alpha is now worth 33% more in TAO terms.
Dual Injection
Every block, the system injects both TAO and Alpha into the pool:
// From coinbase/run_coinbase.rs
fn get_dynamic_tao_emission(netuid, tao_emission, alpha_block_emission) {
let alpha_price = current_alpha_price(netuid);
// Alpha to inject = fixed per block
let alpha_in_emission = alpha_block_emission;
// TAO to inject = alpha_in × current_price
let tao_in_emission = alpha_in_emission * alpha_price;
// Alpha available for distribution
let alpha_out_emission = alpha_block_emission;
(tao_in_emission, alpha_in_emission, alpha_out_emission)
}The swap maintains pool liquidity while generating Alpha tokens for distribution.
Step 4: Split: Owner / Miners / Validators / Root
The Alpha tokens (alpha_out) are split in order:
Sequential Split Order
- Owner Cut (first): The subnet owner takes their configured percentage off the
top. Storage:
PendingOwnerCut - From the remainder:
- Root Dividends: If the total of all subnet Alpha EMA prices exceeds 1 TAO, root validators also receive dividends.
Example: 10% Owner Cut
If a block produces 1000 Alpha for a subnet with 10% owner cut:
Owner: 100α → Miners: 450α → Validators: 450α
The 50/50 split applies to the 900α remainder, not the full 1000α.
| Recipient | Share | Pending Storage |
|---|---|---|
| Owner | Configured % (first) | PendingOwnerCut |
| Miners | 50% of remainder | PendingServerEmission |
| Validators | 50% of remainder | PendingValidatorEmission |
| Root Validators | If total α EMA price > 1τ | PendingRootAlphaDivs |
Root Sell Flag
The root_sell_flag determines whether root validators receive Alpha dividends or if the
Alpha is recycled back to the subnet:
// If total of all subnet EMA alpha prices > 1 TAO
if total_ema_alpha_price > 1.0 {
// Root validators get alpha dividends
PendingRootAlphaDivs += root_share;
} else {
// Alpha is recycled back to subnet pool
recycle_alpha(netuid, root_share);
}Step 5: Epoch Distribution
Pending emission accumulates every block, but actual distribution to participant wallets happens at epoch boundaries. When a subnet's tempo triggers, the accumulated alpha passes through a three-stage distribution cascade before reaching wallets.
The Distribution Cascade
Each hotkey's dividend goes through two "take" deductions before reaching nominators:
- Stage 1 — Raw dividends: The epoch runs Yuma Consensus, producing a raw dividend amount per hotkey on the subnet.
- Stage 2 — Childkey take: If the hotkey has parent-child relationships, the child's configured take rate is applied to each parent's share. The child keeps their cut, a small portion is burned (recycled to the subnet), and the parent receives the remainder. If parent and child share the same coldkey owner, no take is deducted.
- Stage 3 — Validator take: The hotkey's own take rate (set by the validator) is deducted from the post-childkey dividends. The validator receives their take directly. The remainder is distributed proportionally to all nominators based on their stake share.
Worked Example
Suppose a hotkey earns 1000α in raw dividends. A parent hotkey (different coldkey owner) contributed 40% of the stake, so 400α of the dividend is attributable to the parent. The childkey take rate is 10%, and the validator take rate is 18%.
| Stage | Detail | Amount |
|---|---|---|
| Raw dividend | Hotkey's own portion | 600α |
| Parent's portion (40% of stake) | 400α | |
| Childkey take | Burn (~1% of parent share, recycled to subnet) | -4α |
| Child keeps (10% of parent share) | -40α | |
| Post-childkey total (600 + 356) | 956α | |
| Validator take | Validator keeps (18% of 956α) | -172α |
| Nominator pool (distributed by stake share) | 784α |
Pseudocode
// From distribute_emission() → epoch flow
fn distribute_emission(netuid) {
let server_alpha = PendingServerEmission::take(netuid);
let validator_alpha = PendingValidatorEmission::take(netuid);
// Run Yuma Consensus to get per-hotkey scores
let (incentive, dividends) = run_epoch(netuid);
// Miners: distributed by incentive score
for (uid, hotkey) in neurons(netuid) {
let miner_share = incentive[uid] * server_alpha;
increase_stake(hotkey, miner_share);
}
// Validators: three-stage cascade
for (uid, hotkey) in neurons(netuid) {
let raw_dividend = dividends[uid] * validator_alpha;
// Stage 2: Childkey take split
// For each parent relationship, deduct burn + child take
let post_childkey = get_parent_child_dividends_distribution(
hotkey, netuid, raw_dividend
);
// Stage 3: Validator/delegate take
let take_rate = get_hotkey_take(hotkey);
let validator_take = post_childkey * take_rate;
let nominator_pool = post_childkey - validator_take;
// Validator gets their take
increase_stake(hotkey, owner_coldkey, validator_take);
// Nominators get the rest, split by stake share
increase_stake_for_all_nominators(hotkey, nominator_pool);
// Record what nominators received (cleared each epoch)
AlphaDividendsPerSubnet::insert(netuid, hotkey, nominator_pool);
}
}After distribution, the chain records the final nominator amounts in AlphaDividendsPerSubnet. This value reflects the nominator portion only — after both childkey take and validator take
have been deducted. It is cleared and rewritten each epoch, so it always reflects the most
recent distribution. The equivalent for root validator dividends is RootAlphaDividendsPerSubnet (v361+).
Events emitted:
Storage:
AlphaDividendsPerSubnet— nominator alpha dividends per epochRootAlphaDividendsPerSubnet— root nominator dividends per epochChildkeyTake— childkey take rate per (hotkey, subnet)
Staking: Where TAO Flow Comes From
TAO Flow (Step 2) comes from user staking and unstaking operations:
Staking TAO → Alpha
// User calls add_stake(hotkey, netuid, amount)
fn stake_into_subnet(coldkey, hotkey, netuid, tao_amount) {
// 1. Deduct TAO from coldkey balance
Balances::transfer(coldkey, pool, tao_amount);
// 2. Swap TAO for Alpha through liquidity pool
let alpha = swap_tao_for_alpha(netuid, tao_amount);
// 3. Add alpha stake for this hotkey
Stake::insert((hotkey, coldkey, netuid), alpha);
// 4. Record the TAO inflow (affects subnet share)
record_tao_inflow(netuid, tao_amount);
// 5. Emit event
emit StakeAdded { coldkey, hotkey, netuid, tao: tao_amount, alpha };
}Unstaking Alpha → TAO
// User calls remove_stake(hotkey, netuid, alpha_amount)
fn unstake_from_subnet(coldkey, hotkey, netuid, alpha_amount) {
// 1. Remove alpha stake
Stake::decrease((hotkey, coldkey, netuid), alpha_amount);
// 2. Swap Alpha for TAO through liquidity pool
let tao = swap_alpha_for_tao(netuid, alpha_amount);
// 3. Credit TAO to coldkey balance
Balances::transfer(pool, coldkey, tao);
// 4. Record the TAO outflow (affects subnet share)
record_tao_outflow(netuid, tao);
// 5. Emit event
emit StakeRemoved { coldkey, hotkey, netuid, tao, alpha: alpha_amount };
}Related calls:
Worked Example: Following 0.5 TAO Through the System
Let's trace exactly how one block's emission gets distributed across a simplified network with 3 subnets. This shows the actual numbers at each step.
Setup
- Block emission: 0.5 TAO (500,000,000 RAO)
- 3 subnets with different TAO Flow shares
- Subnet 1: 50% TAO Flow, 10% owner cut, Alpha price = 1.2 TAO
- Subnet 2: 30% TAO Flow, 5% owner cut, Alpha price = 0.8 TAO
- Subnet 3: 20% TAO Flow, 0% owner cut, Alpha price = 1.0 TAO
Step-by-Step Distribution
| Step | Subnet 1 | Subnet 2 | Subnet 3 |
|---|---|---|---|
| TAO Share | 0.25 TAO (50%) | 0.15 TAO (30%) | 0.10 TAO (20%) |
| Alpha Minted | 0.208α (0.25÷1.2) | 0.188α (0.15÷0.8) | 0.100α (0.10÷1.0) |
| Owner Cut | 0.021α (10%) | 0.009α (5%) | 0α (0%) |
| Remainder | 0.187α | 0.179α | 0.100α |
| Miners (50%) | 0.094α | 0.089α | 0.050α |
| Validators (50%) | 0.094α | 0.089α | 0.050α |
Final Distribution (per block)
| Recipient | Alpha Received | TAO Value (approx) |
|---|---|---|
| Subnet 1 Owner | 0.021α | ~0.025 TAO |
| Subnet 2 Owner | 0.009α | ~0.007 TAO |
| All Miners (total) | 0.233α | ~0.232 TAO |
| All Validators (total) | 0.233α | ~0.232 TAO |
Note: Within each subnet, individual miner/validator shares are determined by their Incentive and Dividend scores from Yuma Consensus.
Interactive Calculator
Experiment with the emission parameters. Configure subnet TAO Flow shares, owner cuts, and pool sizes, then see how much emission reaches each participant type. Set your own position to calculate your personal earnings.