Skip to content

feat: net TAO flow for emission allocation#2634

Merged
sam0x17 merged 1 commit into
opentensor:devnet-readyfrom
igoraxz:feat/net-tao-flow
May 4, 2026
Merged

feat: net TAO flow for emission allocation#2634
sam0x17 merged 1 commit into
opentensor:devnet-readyfrom
igoraxz:feat/net-tao-flow

Conversation

@igoraxz
Copy link
Copy Markdown

@igoraxz igoraxz commented May 4, 2026

Summary

Replace gross user flow ema(buys - sells) with net flow ema(buys - sells) - ema(emission + chain_buys - root_sells) for emission share computation.

Subnets only receive emissions when they generate positive net TAO inflow for the network.

Simulation results (30-day backtest):

  • Current gross flow: 55 subnets EMA > 0 → aggregate profit = −362 τ/day (network losing TAO)
  • Net flow: 33 subnets EMA > 0 → aggregate profit = +571 τ/day (network gaining TAO)

Rationale

Currently the network subsidizes subnets for gross TAO flow (buys − sells). This heavily misallocates incentives to subnets with negative net TAO flow for the network: e.g. a subnet gets 1 TAO in user buys → receives 4 TAO in protocol subsidy → net TAO flow ("subnet profit") is −3 TAO.

The fix changes the emission allocator to maximize subnet TAO inflow net of protocol subsidy.

The net protocol cost to a subnet per block is:

net_subsidy = (emission_injected + chain_buys) - root_sells
  • Emission injects TAO into pool → network gives
  • Chain buys (excess TAO swapped for alpha) inject TAO → network gives
  • Root sells (root validators claiming dividends as TAO) remove TAO → network takes back

This update also addresses the runaway effect of chain buys — runaway emission cannot be sustained unless a subnet continuously attracts MORE external TAO than the protocol gives it in emission + chain buys.

Flow separation (no cross-contamination)

Flow Code path SubnetTaoFlow (user) SubnetProtocolFlow (protocol)
User buy (stake) stake_into_subnetrecord_tao_inflow ✅ (+)
User sell (unstake) unstake_from_subnetrecord_tao_outflow ✅ (−)
Emission injection adjust_protocol_liquidity (direct) ✅ (+)
Chain buys (excess) swap_tao_for_alpha (direct, not via stake) ✅ (+)
Root sells swap_alpha_for_tao (direct, not via unstake) ✅ (−)

Changes (7 files, +130 lines)

New per-block storage (auditable base values):

  • SubnetExcessTao — actual excess TAO from swap result (recorded inside success path)
  • SubnetRootSellTao — TAO from root dividend sells per block

Protocol cost EMA:

  • SubnetProtocolFlow — per-block accumulator (emit + chain_buys − root_sells)
  • SubnetEmaProtocolFlow — EMA with same smoothing factor as user EMA

Share computation:

  • net_ema = ema(user_flow) - ema(protocol_cost) when toggle is on
  • Protocol EMA always computed regardless of toggle (stays warm for instant switching)

Sudo toggle:

  • NetTaoFlowEnabled (default: on) — sudo_set_net_tao_flow_enabled (call_index 91)
  • Set to false to instantly revert to gross flow behavior

Cleanup: all new storage items removed on subnet deregistration.

Test plan

  • Unit tests for get_ema_protocol_flow with known inputs
  • Verify get_shares_flow produces correct net shares when protocol EMA > 0
  • Verify toggle off reverts to gross flow behavior
  • Verify new storage items cleaned up on deregistration
  • Integration test: simulate emission + chain buys + root sells over multiple blocks
  • Verify no regression on existing SubnetEmaTaoFlow behavior

🤖 Generated with Claude Code

Replace gross user flow (buys - sells) with net flow (user flow - protocol cost)
for emission share computation. This ensures subnets only receive emissions when
they generate positive net TAO inflow for the network, preventing subsidized
extraction where a subnet receives more in protocol emissions than it attracts
in external capital.

New per-block storage (auditable base values):
- SubnetExcessTao: excess TAO swapped (chain buys) per block
- SubnetRootSellTao: TAO from root dividend sells per block

New protocol cost tracking:
- SubnetProtocolFlow: per-block accumulator (emit + chain_buys - root_sells)
- SubnetEmaProtocolFlow: EMA of protocol cost, same smoothing as user flow EMA

Emission share computation:
- net_ema = ema(user_flow) - ema(protocol_cost)
- Protocol EMA starts from zero and scales in over ~30 days
- Sudo toggle NetTaoFlowEnabled (default: on) to switch between net and gross flow

Sudo call: sudo_set_net_tao_flow_enabled (call_index 91)

Cleanup: all new storage items removed on subnet deregistration.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@JohnReedV JohnReedV added the skip-cargo-audit This PR fails cargo audit but needs to be merged anyway label May 4, 2026
@sam0x17 sam0x17 merged commit 7e52a40 into opentensor:devnet-ready May 4, 2026
209 of 217 checks passed
);

// Reset per-block root sell counters from the previous block.
// Root sells (step 8 in block_step) happen after coinbase, so their
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Step numbers in ai generated comments are fragile, I suggest we do not refer to them.

SubnetProtocolFlow::<T>::remove(netuid);
}

fn get_ema_protocol_flow(netuid: NetUid) -> I64F64 {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is a getter, it should not modify state

.iter()
.map(|netuid| (*netuid, Self::get_ema_flow(*netuid)))
.map(|netuid| {
let user_ema = Self::get_ema_flow(*netuid);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not call get_ema_flow if net_flow_enabled is false, it adds unnecessary state reads.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip-cargo-audit This PR fails cargo audit but needs to be merged anyway

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants