Skip to content
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
b3de5f3
Root Reborn: reinvest root dividends into validator-curated baskets
unconst Jun 15, 2026
2aade21
Reuse Weights[ROOT] for basket vector instead of dedicated storage
unconst Jun 15, 2026
8f83d71
Fix dissolve misallocation: liquidate basket by owed, not current stake
unconst Jun 16, 2026
ced07a5
Beta basket: deposit at NAV so late stakers can't dilute or skim
unconst Jun 16, 2026
02378dd
Beta basket: lifecycle events + validator-weights RPC view
unconst Jun 16, 2026
2a6bdeb
Beta basket: symmetric flow accounting, escrow sweep guard, deprecate…
unconst Jun 16, 2026
6acb46f
Beta basket: allow root (UID 0) as a held-TAO basket slot
unconst Jun 19, 2026
f898989
Beta basket: allow setting root (UID 0) weight; dedup root reserve/se…
unconst Jun 19, 2026
c37d783
Beta basket: unify per-subnet slots into a per-validator fund (tradea…
unconst Jul 2, 2026
c802e51
Merge remote-tracking branch 'origin/root-reborn' into release-v438
unarbos Jul 23, 2026
1b28f55
Retry the srtool image build with backoff on transient CI egress fail…
unarbos Jul 23, 2026
1377f4a
Value beta baskets at realizable quotes instead of spot marks.
unarbos Jul 23, 2026
032d5fd
Ship the v438 root-reborn user surface: SDK, btcli, docs, release notes.
unarbos Jul 23, 2026
ffc6cc5
Expose basket info over RPC and clear stale root basket weights via m…
unarbos Jul 23, 2026
1f31865
Mint basket shares on realizable delta-NAV and add direct stake-into-…
unarbos Jul 23, 2026
1f44dab
Address thermo audit findings on the basket deposit paths.
unarbos Jul 23, 2026
38f92bc
Gate root unstakes behind a hold interval to stop epoch-boundary JIT …
unarbos Jul 25, 2026
861b1b0
Merge origin/main into release-v438 (root reborn)
unarbos Jul 28, 2026
47878cd
Burn-based root registration, unified register CLI, and root docs sweep
unarbos Jul 28, 2026
b987cdd
add rank-pinned emission gate bar as alternative to q-mass quantile
unarbos Jul 29, 2026
235e642
chore: auditor auto-fix
Jul 29, 2026
b035b1d
default emission bar rank to 64
unarbos Jul 29, 2026
d843418
activate rank-64 bar at upgrade: migration, tests, proxy group, SDK b…
unarbos Jul 29, 2026
e0d2fd5
Merge branch 'feat/emission-bar-rank' into release-v438
unarbos Jul 29, 2026
e17c382
regenerate namespaces.pyi for basket read registry
unarbos Jul 29, 2026
80a9c83
fix CI failures on release-v438: proxy inventory, clippy, docs, e2e
unarbos Jul 29, 2026
6828de2
sync golden metadata fixtures with main for digest pins
unarbos Jul 29, 2026
e189044
fix remaining CI: golden corpus sync, fmt, biome, e2e count
unarbos Jul 29, 2026
9fccd10
keep e2e matrix at 112 with offline root-claim-type stubs
unarbos Jul 29, 2026
c591ac6
fix SDK ledger pins and eco-tests for Root Reborn
unarbos Jul 29, 2026
962284d
fix: bound root basket claim accounting
UnArbosFive Jul 27, 2026
079a3f8
fix: align root claim threshold benchmark
UnArbosFive Jul 29, 2026
6e68272
fix: chunk migrate_seed_beta_basket_v2 across blocks
unarbos Jul 29, 2026
42cc7fe
style: rustfmt seed beta basket migration chunks
unarbos Jul 29, 2026
00c6946
chore: regenerate docs catalog for CI drift gate
unarbos Jul 29, 2026
3b73dff
Bound and make resumable the RootClaimed drain in migrate_seed_beta_b…
unarbos Jul 29, 2026
d0b3051
fix: bound basket migration watermarks and move protocol redeploy TAO
unarbos Jul 29, 2026
2339112
chore: regenerate claim-root docs after protocol redeploy fix
unarbos Jul 29, 2026
b5a0af6
fix: snapshot migration valuation inputs across resume passes
unarbos Jul 29, 2026
00cc09f
fix: pause basket deposits while seed migration is in progress
unarbos Jul 29, 2026
6efccee
fix: block root hotkey swaps during beta basket seed migration
unarbos Jul 29, 2026
daaf18c
fix: block coldkey swaps during beta basket seed migration
unarbos Jul 29, 2026
d79b811
chore: regenerate docs after coldkey swap migration gate
unarbos Jul 29, 2026
9f8e497
fix: bound BasketClaimed migration on root hotkey swap
unarbos Jul 29, 2026
ff364cf
fix: expect stake_into_basket in Staking proxy allow-list
unarbos Jul 29, 2026
cfa2d29
fix: pause root stake mutations during beta basket seed
unarbos Jul 29, 2026
8b59ab7
chore: regenerate docs after claim_root / coldkey swap line shifts
unarbos Jul 29, 2026
fd7d88a
fix: meter basket conversion on dissolve; allow unbounded root hotkey…
unarbos Jul 29, 2026
d712311
fix: kick off beta basket seed only in on_runtime_upgrade
unarbos Jul 29, 2026
ebfb642
fix: CI failures from basket seed gate, sudo wrap, and swap weight
unarbos Jul 29, 2026
2953c8e
fix: biome format in claim-root e2e test
unarbos Jul 29, 2026
3e8ec4f
fix: meter BasketClaimed migration on root hotkey swap
unarbos Jul 29, 2026
4367ee4
chore: regenerate docs after BasketClaimed storage line shifts
unarbos Jul 29, 2026
96f6cd7
fix: enforce stake_into_basket weight-envelope bounds
unarbos Jul 29, 2026
3b3fe1a
fix: allow first per-subnet hotkey swap without cooldown
unarbos Jul 29, 2026
dd0144a
fix: clone HotkeySwappedOnSubnet event; allow shares growth in e2e
unarbos Jul 29, 2026
4d8229a
fix: relax BasketRate/Shares equality on root-moving hotkey swaps
unarbos Jul 29, 2026
a02fb0d
fix: regenerate SDK wire layer for v441 basket metadata
unarbos Jul 30, 2026
8625c59
revert: single-block BasketClaimed hotkey swap, drop deposit caps
unarbos Jul 30, 2026
6da6e52
revert: drop claim_root MAX_ROOT_CLAIM_WORK hard rejects
unarbos Jul 30, 2026
de518bf
fix: default emission bar rank to 32
unarbos Jul 30, 2026
9e6ac4f
chore: update emission bar rank comment to 32
unarbos Jul 30, 2026
175040a
fix: clippy unused import and docs line anchors
unarbos Jul 30, 2026
7dc3ee6
style: rustfmt claim_root test imports
unarbos Jul 30, 2026
0c2e1bf
fix: default empty root weights to 1/n; require 8 destinations
unarbos Jul 30, 2026
c86f442
Add per-validator root claim while keeping claim_root(subnets) compat.
unarbos Jul 30, 2026
731fafc
fix: root-slot default, orphaned-dust sweep, work-priced claim fees
unarbos Jul 30, 2026
e890d24
fix: harden set_root_weights and drop unused version_key
unarbos Jul 30, 2026
5492bda
fix: skip claims that would forfeit valuable holdings; align 1/n docs
unarbos Jul 30, 2026
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
117 changes: 115 additions & 2 deletions pallets/subtensor/rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ use subtensor_runtime_common::{MechId, NetUid, TaoBalance};
use sp_api::ProvideRuntimeApi;

pub use subtensor_custom_rpc_runtime_api::{
DelegateInfoRuntimeApi, NeuronInfoRuntimeApi, StakeInfoRuntimeApi, SubnetInfoRuntimeApi,
SubnetRegistrationRuntimeApi,
BetaBasketRuntimeApi, DelegateInfoRuntimeApi, NeuronInfoRuntimeApi, StakeInfoRuntimeApi,
SubnetInfoRuntimeApi, SubnetRegistrationRuntimeApi,
};

#[rpc(client, server)]
Expand Down Expand Up @@ -121,6 +121,38 @@ pub trait SubtensorCustomApi<BlockHash> {
netuid: NetUid,
at: Option<BlockHash>,
) -> RpcResult<Vec<u8>>;

/// Total TAO a staker (coldkey) would realize by redeeming all its root beta baskets.
#[method(name = "betaBasket_getStakerOwed")]
fn get_root_basket_owed(
&self,
coldkey: AccountId32,
at: Option<BlockHash>,
) -> RpcResult<TaoBalance>;
/// A validator's beta basket net asset value, in TAO.
#[method(name = "betaBasket_getValidatorNav")]
fn get_validator_basket_nav(
&self,
hotkey: AccountId32,
at: Option<BlockHash>,
) -> RpcResult<TaoBalance>;
/// A validator's full basket breakdown: SCALE-encoded `Vec<(NetUid, AlphaBalance, TaoBalance)>`.
#[method(name = "betaBasket_getValidatorBasket")]
fn get_validator_basket(
&self,
hotkey: AccountId32,
at: Option<BlockHash>,
) -> RpcResult<Vec<u8>>;
/// Network-wide total beta basket NAV across all validators, in TAO.
#[method(name = "betaBasket_getTotalNav")]
fn get_root_basket_total_nav(&self, at: Option<BlockHash>) -> RpcResult<TaoBalance>;
/// A validator's basket weight vector: SCALE-encoded `Vec<(NetUid, u16)>` (its strategy).
#[method(name = "betaBasket_getValidatorWeights")]
fn get_validator_weights(
&self,
hotkey: AccountId32,
at: Option<BlockHash>,
) -> RpcResult<Vec<u8>>;
}

pub struct SubtensorCustom<C, P> {
Expand Down Expand Up @@ -170,6 +202,7 @@ where
C::Api: SubnetInfoRuntimeApi<Block>,
C::Api: StakeInfoRuntimeApi<Block>,
C::Api: SubnetRegistrationRuntimeApi<Block>,
C::Api: BetaBasketRuntimeApi<Block>,
{
fn get_delegates(&self, at: Option<<Block as BlockT>::Hash>) -> RpcResult<Vec<u8>> {
let api = self.client.runtime_api();
Expand Down Expand Up @@ -583,4 +616,84 @@ where
Err(e) => Err(Error::RuntimeError(format!("Unable to get coldkey lock: {e:?}")).into()),
}
}

fn get_root_basket_owed(
&self,
coldkey: AccountId32,
at: Option<<Block as BlockT>::Hash>,
) -> RpcResult<TaoBalance> {
let api = self.client.runtime_api();
let at = at.unwrap_or_else(|| self.client.info().best_hash);

match api.get_root_basket_owed(at, coldkey) {
Ok(result) => Ok(result),
Err(e) => {
Err(Error::RuntimeError(format!("Unable to get root basket owed: {e:?}")).into())
}
}
}

fn get_validator_basket_nav(
&self,
hotkey: AccountId32,
at: Option<<Block as BlockT>::Hash>,
) -> RpcResult<TaoBalance> {
let api = self.client.runtime_api();
let at = at.unwrap_or_else(|| self.client.info().best_hash);

match api.get_validator_basket_nav(at, hotkey) {
Ok(result) => Ok(result),
Err(e) => Err(Error::RuntimeError(format!(
"Unable to get validator basket NAV: {e:?}"
))
.into()),
}
}

fn get_validator_basket(
&self,
hotkey: AccountId32,
at: Option<<Block as BlockT>::Hash>,
) -> RpcResult<Vec<u8>> {
let api = self.client.runtime_api();
let at = at.unwrap_or_else(|| self.client.info().best_hash);

match api.get_validator_basket(at, hotkey) {
Ok(result) => Ok(result.encode()),
Err(e) => {
Err(Error::RuntimeError(format!("Unable to get validator basket: {e:?}")).into())
}
}
}

fn get_root_basket_total_nav(
&self,
at: Option<<Block as BlockT>::Hash>,
) -> RpcResult<TaoBalance> {
let api = self.client.runtime_api();
let at = at.unwrap_or_else(|| self.client.info().best_hash);

match api.get_root_basket_total_nav(at) {
Ok(result) => Ok(result),
Err(e) => {
Err(Error::RuntimeError(format!("Unable to get total basket NAV: {e:?}")).into())
}
}
}

fn get_validator_weights(
&self,
hotkey: AccountId32,
at: Option<<Block as BlockT>::Hash>,
) -> RpcResult<Vec<u8>> {
let api = self.client.runtime_api();
let at = at.unwrap_or_else(|| self.client.info().best_hash);

match api.get_validator_weights(at, hotkey) {
Ok(result) => Ok(result.encode()),
Err(e) => {
Err(Error::RuntimeError(format!("Unable to get validator weights: {e:?}")).into())
}
}
}
}
13 changes: 13 additions & 0 deletions pallets/subtensor/runtime-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,17 @@ sp_api::decl_runtime_apis! {
fn get_proxy_types() -> Vec<ProxyTypeInfo>;
fn get_proxy_filters(proxy_types: Option<Vec<u8>>) -> Vec<ProxyFilterInfo>;
}

pub trait BetaBasketRuntimeApi {
/// Total TAO a coldkey would realize by redeeming all its root beta baskets (marked).
fn get_root_basket_owed(coldkey: AccountId32) -> TaoBalance;
/// A validator's beta basket net asset value, in TAO (marked).
fn get_validator_basket_nav(hotkey: AccountId32) -> TaoBalance;
/// A validator's basket breakdown: (subnet, alpha held, TAO value) per subnet.
fn get_validator_basket(hotkey: AccountId32) -> Vec<(NetUid, AlphaBalance, TaoBalance)>;
/// Network-wide total beta basket NAV across all validators, in TAO (marked).
fn get_root_basket_total_nav() -> TaoBalance;
/// A validator's basket weight vector `w`: (subnet, weight) it deploys dividends into.
fn get_validator_weights(hotkey: AccountId32) -> Vec<(NetUid, u16)>;
}
}
48 changes: 23 additions & 25 deletions pallets/subtensor/src/benchmarks/benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use sp_runtime::{
BoundedVec, PerU16, Percent,
traits::{BlakeTwo256, Dispatchable, Hash},
};
use sp_std::collections::{btree_set::BTreeSet, vec_deque::VecDeque};
use sp_std::collections::vec_deque::VecDeque;
use sp_std::vec;
use substrate_fixed::types::{I96F32, U64F64};
use subtensor_runtime_common::{AlphaBalance, NetUid, NetUidStorageIndex, TaoBalance};
Expand Down Expand Up @@ -1961,14 +1961,6 @@ mod pallet_benchmarks {
_(RawOrigin::Signed(coldkey.clone()), netuid, hotkey.clone());
}

#[benchmark]
fn set_root_claim_type() {
let coldkey: T::AccountId = whitelisted_caller();

#[extrinsic_call]
_(RawOrigin::Signed(coldkey.clone()), RootClaimTypeEnum::Keep);
}

#[benchmark]
fn claim_root() {
let coldkey: T::AccountId = whitelisted_caller();
Expand Down Expand Up @@ -2011,6 +2003,16 @@ mod pallet_benchmarks {
initial_total_hotkey_alpha.into(),
);

// Point the validator's basket weight vector at the subnet so the distributed root
// dividend is deposited into its fund (instead of being recycled for lack of weights).
if let Ok(root_uid) = Uids::<T>::try_get(NetUid::ROOT, &hotkey) {
Weights::<T>::insert(
NetUidStorageIndex::ROOT,
root_uid,
vec![(u16::from(netuid), 1u16)],
);
}

let pending_root_alpha = 10_000_000u64;
Subtensor::<T>::distribute_emission(
netuid,
Expand All @@ -2020,29 +2022,25 @@ mod pallet_benchmarks {
AlphaBalance::ZERO,
);

let initial_stake =
Subtensor::<T>::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid);

assert_ok!(Subtensor::<T>::set_root_claim_type(
RawOrigin::Signed(coldkey.clone()).into(),
RootClaimTypeEnum::Keep
));
let initial_stake = Subtensor::<T>::get_stake_for_hotkey_and_coldkey_on_subnet(
&hotkey,
&coldkey,
NetUid::ROOT,
);

#[extrinsic_call]
_(RawOrigin::Signed(coldkey.clone()), BTreeSet::from([netuid]));
_(RawOrigin::Signed(coldkey.clone()));

let new_stake =
Subtensor::<T>::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid);
let new_stake = Subtensor::<T>::get_stake_for_hotkey_and_coldkey_on_subnet(
&hotkey,
&coldkey,
NetUid::ROOT,
);

// The claim must actually pay out (strict: a no-op claim is a broken benchmark).
assert!(new_stake > initial_stake);
}

#[benchmark]
fn sudo_set_num_root_claims() {
#[extrinsic_call]
_(RawOrigin::Root, 40);
}

#[benchmark]
fn sudo_set_root_claim_threshold() {
let coldkey: T::AccountId = whitelisted_caller();
Expand Down
4 changes: 1 addition & 3 deletions pallets/subtensor/src/coinbase/block_step.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ impl<T: Config + pallet_drand::Config> Pallet<T> {
/// Executes the necessary operations for each block.
pub fn block_step() -> Result<(), &'static str> {
let block_number: u64 = Self::get_current_block_as_u64();
let last_block_hash: T::Hash = <frame_system::Pallet<T>>::parent_hash();

// --- 1. Update registration burn prices.
Self::update_registration_prices_for_networks();
Expand All @@ -25,8 +24,7 @@ impl<T: Config + pallet_drand::Config> Pallet<T> {
Self::update_root_prop();
// --- 7. Set pending children on the epoch; but only after the coinbase has been run.
Self::try_set_pending_children(block_number);
// --- 8. Run auto-claim root divs.
Self::run_auto_claim_root_divs(last_block_hash);
// --- 8. Beta baskets are redeemed on-demand by stakers via `claim_root`; no auto-swap.
// --- 9. Populate root coldkey maps.
Self::populate_root_coldkey_staking_maps();
Self::populate_root_coldkey_staking_maps_v2();
Expand Down
10 changes: 5 additions & 5 deletions pallets/subtensor/src/coinbase/run_coinbase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -837,11 +837,11 @@ impl<T: Config> Pallet<T> {

let root_claimable_alpha: AlphaBalance = tou64!(root_claimable).into();
if !root_claimable_alpha.is_zero() {
Self::increase_root_claimable_for_hotkey_and_subnet(
&hotkey,
netuid,
root_claimable_alpha,
);
// Distribute the validator's root dividend into its beta basket across subnets
// per the validator's root weight vector (set on subnet 0). The bought basket
// alpha is staked to the validator under the global escrow coldkey, so it counts
// toward the validator's stake and compounds; stakers accrue a claimable rate.
Self::distribute_root_alpha_to_basket(&hotkey, netuid, root_claimable_alpha);

RootAlphaDividendsPerSubnet::<T>::mutate(netuid, &hotkey, |divs| {
*divs = divs.saturating_add(root_claimable_alpha);
Expand Down
Loading
Loading