Guides
Etao
Release notes for Etao (runtime v480) — subnets choose how their alpha emission is split between owner, validators and miners, while root TAO stakers keep their baseline payout. What changed, the rules, the migration, and the CLI.
Etao (runtime v480) hands every subnet owner the dial that used to be welded shut: how the subnet's alpha emission is divided. For five years every subnet paid the same split — 18% to the owner, 41% to validators, 41% to miners. Now a subnet that lives on its miners can pay them 62%. A subnet that needs deep validation can push validators to the 50% cap. An owner who wants to signal commitment can set their own cut to zero. One extrinsic, one rate limit, five rules, and a guarantee underneath all of it: root TAO stakers are paid exactly what they would be on an untouched subnet, whatever the owner picks.
This page is the complete release note. It covers what changed and why, how the vector and its rules work, how root stays whole, the migration, the deprecated calls, every new storage item, event, runtime API, EVM function and SDK/CLI surface, worked CLI examples for owners, and what validators, miners, root stakers and integrators should expect.
Summary
- New: a per-subnet emission weight vector
[burn, recycle, owner, validator, miner]ofu64parts ofu64::MAXthat sums to exactly one. - New extrinsic:
AdminUtils.sudo_set_subnet_emission_weights(netuid, uids, values)— a sparse update callable by the subnet owner (once per 50,400 blocks per subnet) or root. - Rules (v1): burn = recycle = 0;
0 ≤ owner ≤ 33%(lease-created subnets≥ 18%);validator ≤ 50%and high enough that root's baseline share is reachable;miner ≥ 33%; the five slots sum to exactly one. - Root stays whole: every accepted set re-solves the subnet's
effective TAO weight
w_sonce, at that block, so root's share of the subnet's emission equals its baseline (default validator share, globalTaoWeight). The solve never reads the old vector, so changes cannot ratchet root's share in either direction. - Migration: every live subnet is seeded from its legacy owner-cut state. Payouts in the first post-upgrade block are identical to the last pre-upgrade block.
- Deprecated:
sudo_set_subnet_owner_cutandsudo_set_owner_cut_enablednow returnDeprecated; the owner slot replaces both. - Surfaces: runtime API
get_subnet_emission_weights, eventSubnetEmissionWeightsSet, three hyperparameters (owner_emission,validator_emission,miner_emission), EVMsetSubnetEmissionWeights/getSubnetEmissionWeights, theset_subnet_emission_weightsintent andsubnet_emission_weightsread in the Python SDK, andbtcli subnets emissionwithshow,set,set-value,increaseanddecrease. - Ships to mainnet as a sudo runtime upgrade from
main;spec_version480.
Why
Bittensor subnets are not alike. Some are miner-heavy compute markets where validation is cheap and the scarce resource is miner effort. Some are judgment-heavy and need validators to spend real money to score work. Some are owner-run products where the owner cut funds the team; others want the owner cut gone so every token flows to participants. A single fixed split cannot serve all of them, and until now the only knob — the owner cut — was a global sudo setting, the same for every subnet.
The obstacle to letting owners move the validator share was root. Root TAO stakers are paid from the validator slot of every subnet in proportion to their TAO-weighted stake. Let an owner cut the validator slot from 41% to 20% and, with nothing else changing, root's income from that subnet would halve. Owners would be able to tax a network-wide stakeholder to pay their own participants. Etao removes that coupling: the validator slot becomes a choice about validators, and root's payout is pinned to a baseline that the owner cannot move.
The emission weight vector
Every subnet carries a five-slot vector that says how each block's alpha emission is divided:
| Slot | Name | Paid to | v1 rule |
|---|---|---|---|
| 0 | burn | nothing — alpha is destroyed | must be 0 (reserved) |
| 1 | recycle | nothing — alpha returns to the unissued pool | must be 0 (reserved) |
| 2 | owner | the subnet owner's hotkey at each epoch | 0 ≤ owner ≤ 33%; lease-created subnets ≥ 18% |
| 3 | validator | validator dividends (root TAO stakers are paid from this slot) | ≤ 50%, and root's baseline share must be reachable |
| 4 | miner | miner incentive, split across the subnet's mechanisms | ≥ 33% |
Each slot is a u64 share of u64::MAX, so the five slots sum to exactly
one with no rounding dust. Per block, alpha_out × owner accrues to the
owner, alpha_out × validator to the validator bucket and
alpha_out × miner to the miner bucket. Burn and recycle are present so the
format does not change when they are switched on in a later runtime; today a
non-zero value in either is rejected.
The owner slot at zero means "no owner cut". The old owner_cut_enabled
reading is now simply owner > 0; the auto-lock of the owner cut
(owner_cut_auto_lock_enabled) is unchanged and applies to whatever the
owner slot pays.
Setting it
AdminUtils.sudo_set_subnet_emission_weights(netuid: u16, uids: Vec<u16>, values: Vec<u64>)The update is sparse: uids[i] names a slot and values[i] its new
share. Omitted owner / validator / miner slots keep their current value;
omitted burn / recycle are treated as zero. The merged vector must pass
every rule, and each rule has its own error so a rejection names the
constraint:
| Error | Rule |
|---|---|
BurnEmissionMustBeZero | slot 0 must be 0 |
RecycleEmissionMustBeZero | slot 1 must be 0 |
OwnerEmissionAboveMaximum | owner > 33% |
LeasedSubnetOwnerCutBelowMinimum | lease-created subnet with owner < 18% |
ValidatorEmissionAboveMaximum | validator > 50% |
ValidatorEmissionTooLowToPreserveRoot | no TAO weight ≤ 1 reaches root's baseline (see below) |
MinerEmissionBelowMinimum | miner < 33% |
EmissionWeightsDoNotSumToOne | the five slots do not sum to exactly u64::MAX |
EmissionWeightsNotSettableOnRoot | netuid 0 has no alpha emission to divide |
EmissionWeightIndexOutOfRange / EmissionWeightIndexDuplicated / EmissionWeightsLengthMismatch | malformed uids / values |
Callers: the subnet owner coldkey, or root. Both obey every rule including
the lease floor, and both respect the admin freeze window. Because the slots
must still sum to one, lowering one slot means raising another in the same
call; btcli does that arithmetic for you (below).
Rate limit
Owners may change a subnet's vector once every 50,400 blocks (about
seven days at 12 s). The limit is keyed by subnet, not by owner coldkey
(LastRateLimitedBlock[OwnerHyperparamUpdate(netuid, EmissionWeights)]), so
handing a subnet to a new owner does not reset it. A subnet's first change is
never rate-limited, root is exempt, and dissolving a subnet clears the key so
a reused netuid starts fresh. The window is a runtime constant
(SubnetEmissionWeightsRateLimit), not a hyperparameter.
How root stays whole
Root TAO is weighted against alpha by a TAO weight w. Each block, the
alpha root stakers receive from a subnet is
root_alpha = V × x / (x + A) × alpha_out with x = root_tao × wwhere V is the validator slot and A the subnet's alpha issuance. Before
Etao, w was the single global TaoWeight (0.18 on mainnet today,
governance-set) and V was fixed, so root's share of a subnet's emission was
the same formula everywhere.
Etao gives every subnet its own effective TAO weight w_s
(SubnetTaoWeight), used on the consensus side of that subnet only: the
root share carved out of the validator slot, the TAO-versus-alpha split of
validator dividends, and the stake weighting inside Yuma Consensus. A subnet
with no entry follows the global TaoWeight, exactly as before.
The set-time solve
Every accepted set call solves w_s once, at that block, from live chain
values and the new validator slot alone:
- Root's baseline share of this subnet's emission:
r = V0 × x_g / (x_g + A)withx_g = root_tao × TaoWeight.V0is the validator share of the default vector (0.41 on mainnet, derived from the default owner cut; one global constant, no per-subnet storage) andTaoWeightthe global knob. This is what root would earn from the subnet right now if the owner had never touched it. - The weight that pays
runder the new slot:x_new = r × A / (V_new − r),w_s = x_new / root_tao.
So the block after any change, root TAO stakers earn from the subnet exactly
what an untouched subnet would pay them at that block. Lowering V raises
w_s; raising V lowers it; the alpha root receives does not move.
Two properties follow from anchoring to a fresh baseline rather than to root's current share:
- No ratchet. The solve never reads the old vector or the old
w_s. An owner who raisesVwhile the subnet is young and lowers it after issuance grows (or the reverse) ends up with the samew_sand the same root share as an owner who made the final change in one step. Root's payout is a function of the chain's state, not of the owner's history. - Back to default means back to global. Setting
Vto exactlyV0clears the per-subnet entry, so the subnet follows the globalTaoWeightagain (the solve would land there anyway). A subnet that has never changed its validator share has no entry at all.
Between sets, w_s is frozen: it is never recomputed per block, and
everything scales with stake and issuance as usual. Setting only the owner
or miner slot still re-anchors w_s, because the solve runs on every
accepted set — in practice this means a subnet that already follows the
global weight keeps following it, and a subnet with a frozen weight gets it
refreshed to the current baseline.
When a change is rejected
The weight is capped at one — root TAO never counts more than 1:1 against
alpha — so not every V is reachable. The call fails with
ValidatorEmissionTooLowToPreserveRoot when V_new ≤ r or the solved
w_s would exceed one. Equivalently, the validator floor at this block is
V_min = r × (1 + A / root_tao)On a mainnet subnet with 5.44M TAO on root, 6.46M alpha issued and
TaoWeight = 0.18, the baseline r is about 5.4% of emission and the
validator slot can go down to about 11.8%. The floor moves with root
stake, the subnet's issuance and the global weight, so
btcli subnets emission show prints the live value, and every write checks
it locally before anything is signed. With no root stake or no alpha issued
there is nothing to preserve: any valid V is accepted and w_s is left
alone.
What does not change
The pool side never sees w_s. tao_in, alpha_in, the
alpha-injection cap and RootProp use the global TaoWeight, exactly as
before. Changing a subnet's vector changes how its alpha is distributed;
it does not change how much TAO or alpha is injected into its pool, and
it does not change prices, swaps or the emission the subnet receives.
Migration and compatibility
The runtime upgrade runs one migration, seed_subnet_emission_weights_v1,
which seeds every live subnet so that the first post-upgrade block pays out
exactly what the last pre-upgrade block did:
owner= the legacySubnetOwnerCutscaled tou64parts whenOwnerCutEnabledwas true, else 0;validator= half of the remainder (the old 50/50 split);miner= whatever is left, absorbing the rounding ulp;- no
SubnetTaoWeightentry, so every subnet keeps following the globalTaoWeightuntil its owner changes the vector; SubnetCreatedViaLease= true for subnets with a live lease mapping (the lease floor on the owner slot applies to them);SubnetOwnerCutAccrued=alpha_issuance × legacy SubnetOwnerCut— the exact deduction the legacy lock refund applied — so dissolving a subnet later pays no more than it would have. Subnets already mid-dissolution get this seeded too, so their pending refund is correct.
Newly registered subnets are seeded with the default vector
[0, 0, 18%, 41%, 41%] and no SubnetTaoWeight entry. Dissolving a subnet
clears its vector, weight, lease marker, accrued owner cut and rate-limit
key, so a reused netuid starts clean.
Deprecated extrinsics. AdminUtils.sudo_set_subnet_owner_cut and
AdminUtils.sudo_set_owner_cut_enabled keep their call indices and SCALE
signatures but always return Error::Deprecated. Set the owner slot
(including to zero) through sudo_set_subnet_emission_weights instead. The
SubnetOwnerCut and OwnerCutEnabled storage items remain readable as
legacy views but are no longer written.
New chain surface
Storage (pallet SubtensorModule):
| Item | Type | Meaning |
|---|---|---|
SubnetEmissionWeights | netuid → BoundedVec<u64, 5> | the vector; defaults to [0, 0, 18%, 41%, 41%] |
SubnetTaoWeight | netuid → Option<u64> | effective TAO weight w_s in u64::MAX parts; absent = follows global TaoWeight |
SubnetCreatedViaLease | netuid → bool | marks lease-created subnets (owner floor 18%) |
SubnetOwnerCutAccrued | netuid → alpha | owner cut paid out so far; deducted from the lock refund on dissolution |
Event: SubtensorModule.SubnetEmissionWeightsSet { netuid, weights }
carries the full post-merge five-slot vector.
Rate limit: TransactionType::OwnerHyperparamUpdate(Hyperparameter::EmissionWeights)
keyed by subnet in LastRateLimitedBlock; constant
SubnetEmissionWeightsRateLimit = 50_400.
Runtime API / RPC: SubnetInfoRuntimeApi.get_subnet_emission_weights(netuid) → Option<SubnetEmissionWeightsInfo { netuid, weights, burn, recycle, owner, validator, miner, effective_tao_weight, created_via_lease }> (API version 3).
get_subnet_hyperparams_v3 also reports owner_emission,
validator_emission and miner_emission as u64 parts.
EVM (subnet precompile, index 2051):
function setSubnetEmissionWeights(uint16 netuid, uint16[] calldata uids, uint64[] calldata values) external;
function getSubnetEmissionWeights(uint16 netuid) external view returns (uint64[] memory weights, bool createdViaLease);Shares are uint64 / type(uint64).max. The setter is dispatched from the
caller's mapped coldkey and must pass the same rules; a rejected set reverts
with the chain error name.
Python SDK:
- read
subnet_emission_weights(netuid)→ vector, shares, effective and global TAO weight, root's current and baseline share, the live validator floor, lease flag, rate-limit state; - intent
SetSubnetEmissionWeights(netuid, slots, values, renormalize=False)→ sparse set with an optional proportional renormalisation of the untouched slots; its plan lists every rule the chain would reject and a structuredpreview(before/after vector, root's share before and after, TAO weight before and after, rate limit, allowed ranges); - module
bittensor.emission_weightswith the pure rules (validate,renormalize,validator_floor,baseline_root_share,solve_tao_weight,preview) so you can check a vector offline.
CLI: btcli subnets emission show | set | set-value | increase | decrease.
For subnet owners: step by step
Every command below takes --json for a machine-readable record with stable
field names and --dry-run to preview a write without signing. Exit codes are
fixed: 0 done; 1 the subnet does not exist, the change is blocked
right now (owner rate limit) or the chain rejected it; 2 invalid input or
a vector the chain's rules would reject.
1. Look before you touch.
btcli subnets emission show --netuid 4subnet 4 emission weights
Vector
owner 18.00% 3320329489486196244 parts
validator 41.00% 7563207292111677685 parts
miner 41.00% 7563207292111677686 parts
burn 0.00% reserved, pinned to 0
recycle 0.00% reserved, pinned to 0
Root
root share of emission 5.4015% what root TAO stakers earn from this subnet now
root baseline share 5.4015% with the default validator share and the global TaoWeight; every change re-anchors root here
effective tao weight 18.00% follows the global TaoWeight (18.00%)
root tao 5,443,000 TAO
alpha issuance 6,457,000 alpha
next owner change now first change; owners may change once per 50,400 blocks
created via lease no
Allowed
owner 0.00% to 33.00% 0 turns the owner cut off
validator 11.8093% to 50.0000% the floor is where root's baseline share needs a TAO weight of 100%
miner >= 33.00% miner incentive floor
burn = 0% reserved for a future runtime; must stay 0
recycle = 0% reserved for a future runtime; must stay 0
sum = 100% all five slots add up to exactly 18446744073709551615 parts
help: change one slot with btcli subnets emission set-value --netuid 4 --slot owner --value 10% (the other two rescale to fill 100%); add --dry-run to previewThe Allowed table is live: the validator floor is computed from root stake, your subnet's issuance and the global weight at this block.
2. Preview the change. Move one slot; the other two rescale to fill 100%:
btcli subnets emission set-value --netuid 4 --slot validator --value 20% --dry-runrequires: subnet owner coldkey
dry run: set emission weights on netuid 4: validator=20.00% (renormalizing the rest)
signer owner (5F...)
est. fee τ0.000216036
effect set emission weights on netuid 4: validator=20.00% (renormalizing the rest)
effect vector: owner 18.00% / validator 41.00% / miner 41.00% -> owner 24.41% / validator 20.00% / miner 55.59%
effect root earns 5.4015% of this subnet's emission now and 5.4015% after (its baseline: default validator share, global TaoWeight); the subnet's TAO weight moves 18.00% -> 43.89%
run for real btcli subnets emission set-value --netuid 4 --slot validator --value 20%
see https://www.bittensor.com/docs/tx/set-subnet-emission-weightsNote the renormalisation: set-value keeps the ratio of the untouched
slots, so taking 21 points from validators hands them to owner and miner in
their current 18:41 proportion. To pay the whole difference to miners, write
the full split instead:
btcli subnets emission set --netuid 4 --owner 18% --validator 20% --miner 62% --dry-run3. Submit. Drop --dry-run; the review card shows each moving slot,
root's share before and after, the TAO weight move, and the block from which
the next owner change is allowed. Confirm to sign (--yes skips the prompt).
btcli subnets emission set --netuid 4 --owner 18% --validator 20% --miner 62%Emission weights
owner 18.00% -> 18.00%
validator 41.00% -> 20.00%
miner 41.00% -> 62.00%
root share of emission 5.4015% -> 5.4015% (baseline)
subnet tao weight 18.00% -> 43.89%
next owner change after this about block 7,463,280 (50,400 blocks after the one that includes this call)
Fees
estimated fee ~τ0.000216036
Signer
signer owner (5F...)
Transaction
operation set_subnet_emission_weights
netuid 4
slots [0, 1, 2, 3, 4]
values [0, 0, 3320413933267719290, 3689348814741910323, 11436981325699922002]
renormalize no
✓ set emission weights on netuid 4: owner=18.00% validator=20.00% miner=62.00%
Result
fee τ0.000216037
block 0x783b…8014f
extrinsic 7412880-0006With --json a real submit prints {"success", "message", "block_hash", "extrinsic_id", "fee_tao"} instead.
4. Nudge instead of rewrite.
btcli subnets emission increase --netuid 4 --slot miner --by 5%
btcli subnets emission decrease --netuid 4 --slot owner --by 0.02
btcli subnets emission set-value --netuid 4 --slot owner --value 0 # turn the owner cut off5. When it is rejected, the error tells you the rule and the range.
btcli subnets emission set-value --netuid 4 --slot validator --value 5%error: the chain would reject this change: ValidatorEmissionTooLowToPreserveRoot: validator (slot 3) is 5.0000%; every change re-solves this subnet's TAO weight so root earns its baseline share of emission, and that weight is capped at 100%, so right now the validator share must be at least about 11.8093%
note: allowed on subnet 4 right now: validator 11.8093% to 50.0000% (the floor is where root's baseline share needs a TAO weight of 100%)
help: btcli subnets emission show --netuid 4 prints the vector and every allowed rangeExit code 2, nothing signed. With --json the same three fields arrive as
{"error", "note", "help"} on stderr.
6. Inside the weekly window the write is blocked, not rejected.
error: the owner changed this vector at block 7,412,880; the chain allows the next owner change at block 7,463,280 (in 50,112 blocks, about 167.0 h); root is exempt
help: resolve this hard stop before submittingExit code 1. show prints the same as next owner change, and the JSON
record carries it under rate_limit. Signing from a coldkey that does not
own the subnet is stopped the same way, naming both the signer and the owner,
before anything reaches the chain.
Agents and scripts: btcli --json subnets emission show --netuid 4
returns {netuid, block, weights, shares, effective_tao_weight, global_tao_weight, root_share, baseline_root_share, root_tao, alpha_issuance, min_validator, created_via_lease, limits, rate_limit};
--json --dry-run on any write returns the plan with ok, violations,
args and preview{before, after, changed, root, rate_limit, limits}.
Notes by role
Validators. Your dividends come from the validator slot. On a subnet
whose owner lowers that slot, the alpha paid to validators and their alpha
stakers goes down and the subnet's effective TAO weight goes up. Within your
dividends the TAO-staker portion is τ × w_s / (α + τ × w_s), so a higher
w_s tilts the split toward TAO stakers. The validator floor guarantees the
slot can never fall to a level that starves root; nothing guarantees it stays
at 41%. Watch SubnetEmissionWeightsSet events or
btcli subnets emission show on the subnets you validate.
Miners. The miner slot is at least 33% and can grow to whatever is left after the owner slot and the validator floor — with the owner cut at zero and the validator slot at its floor, most of the subnet's emission. Nothing about weight setting, incentive or the epoch changes; only the size of the bucket.
Root TAO stakers. Your share of every subnet's emission is pinned to its
baseline — what the subnet would pay you with the default validator share
and the global TaoWeight — at every change the owner makes, and it cannot
be moved by sequencing changes. Between changes your payout scales with root
stake and issuance exactly as before. The global TaoWeight remains the
governance lever over root's weight; a subnet that has never changed its
validator share follows it directly.
Subnet owners. You get one change per week per subnet; plan it. Your
first change is free of the limit. Lease-created subnets cannot drop the
owner slot below 18%. Every btcli write validates locally and shows the
exact floor, so test with --dry-run first. The owner cut's auto-lock
setting is unaffected.
Integrators and explorers. Read the vector with the
get_subnet_emission_weights runtime API or the subnet_emission_weights
SDK read; the three hyperparameters are also in get_subnet_hyperparams_v3.
Treat SubnetOwnerCut / OwnerCutEnabled as legacy views. The effective TAO
weight a subnet actually uses on the consensus side is
SubnetTaoWeight[netuid] falling back to TaoWeight; the pool side still
uses TaoWeight alone. Expect SubnetEmissionWeightsSet events; the two
deprecated extrinsics now fail with Deprecated.
Release details
- Runtime
spec_version480;transaction_versionunchanged. - Delivered to mainnet as a sudo runtime upgrade from
main. - Migration
seed_subnet_emission_weights_v1, idempotent (HasMigrationRun). - Source:
pallets/subtensor/src/subnets/emission_weights.rs,pallets/admin-utils/src/lib.rs,pallets/subtensor/src/migrations/migrate_seed_subnet_emission_weights.rs.
Related
- Emission weights — the concept page
- Emissions — the full per-tempo flow
set-subnet-emission-weights— the intentsubnet-emission-weights— the readowner_emission,validator_emission,miner_emission- Release page