From d8bce81ef1753f8da604af42188392ead9609189 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Sat, 27 Sep 2025 18:45:39 -0400 Subject: [PATCH 001/210] proxy precompile init impl --- common/src/lib.rs | 27 ++++ evm-tests/src/contracts/proxy.ts | 148 +++++++++++++++++++ pallets/admin-utils/src/lib.rs | 4 +- precompiles/src/lib.rs | 8 +- precompiles/src/proxy.rs | 222 +++++++++++++++++++++++++++++ precompiles/src/solidity/proxy.sol | 30 ++++ 6 files changed, 436 insertions(+), 3 deletions(-) create mode 100644 evm-tests/src/contracts/proxy.ts create mode 100644 precompiles/src/proxy.rs create mode 100644 precompiles/src/solidity/proxy.sol diff --git a/common/src/lib.rs b/common/src/lib.rs index a5d09ad974..e3fd723679 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -161,6 +161,33 @@ pub enum ProxyType { SubnetLeaseBeneficiary, // Used to operate the leased subnet } +impl TryFrom for ProxyType { + type Error = (); + + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(Self::Any), + 1 => Ok(Self::Owner), + 2 => Ok(Self::NonCritical), + 3 => Ok(Self::NonTransfer), + 4 => Ok(Self::Senate), + 5 => Ok(Self::NonFungibile), + 6 => Ok(Self::Triumvirate), + 7 => Ok(Self::Governance), + 8 => Ok(Self::Staking), + 9 => Ok(Self::Registration), + 10 => Ok(Self::Transfer), + 11 => Ok(Self::SmallTransfer), + 12 => Ok(Self::RootWeights), + 13 => Ok(Self::ChildKeys), + 14 => Ok(Self::SudoUncheckedSetCode), + 15 => Ok(Self::SwapHotkey), + 16 => Ok(Self::SubnetLeaseBeneficiary), + _ => Err(()), + } + } +} + impl Default for ProxyType { // allow all Calls; required to be most permissive fn default() -> Self { diff --git a/evm-tests/src/contracts/proxy.ts b/evm-tests/src/contracts/proxy.ts new file mode 100644 index 0000000000..4059409276 --- /dev/null +++ b/evm-tests/src/contracts/proxy.ts @@ -0,0 +1,148 @@ +export const IPROXY_ADDRESS = "0x000000000000000000000000000000000000080b"; + +export const IProxyABI = [ + { + "inputs": [ + { + "internalType": "uint8", + "name": "proxy_type", + "type": "uint8" + }, + { + "internalType": "uint32", + "name": "delay", + "type": "uint32" + }, + { + "internalType": "uint16", + "name": "index", + "type": "uint16" + } + ], + "name": "createPureProxy", + "outputs": [ + { + "internalType": "bytes32", + "name": "proxy", + "type": "bytes32" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "spawner", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "proxy_type", + "type": "uint8" + }, + { + "internalType": "uint16", + "name": "index", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "height", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "ext_index", + "type": "uint32" + } + ], + "name": "killPureProxy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "real", + "type": "bytes32" + }, + { + "internalType": "uint8[]", + "name": "force_proxy_type", // optional + "type": "uint8[]" + }, + { + "internalType": "uint8[]", + "name": "call", + "type": "uint8[]" + } + ], + "name": "proxyCall", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "removeProxies", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + },{ + "inputs": [], + "name": "pokeDeposit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "delegate", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "proxy_type", + "type": "uint8" + }, + { + "internalType": "uint32", + "name": "delay", + "type": "uint32" + } + ], + "name": "removeProxy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "delegate", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "proxy_type", + "type": "uint8" + }, + { + "internalType": "uint32", + "name": "delay", + "type": "uint32" + } + ], + "name": "addProxy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +]; diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 61e4b8a49b..e6332e398e 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -132,8 +132,8 @@ pub mod pallet { Alpha, /// Enum for crowdloan precompile Crowdloan, - /// Pure proxy precompile - PureProxy, + /// Proxy precompile + Proxy, /// Leasing precompile Leasing, } diff --git a/precompiles/src/lib.rs b/precompiles/src/lib.rs index f4de789dbf..95273dc724 100644 --- a/precompiles/src/lib.rs +++ b/precompiles/src/lib.rs @@ -33,6 +33,7 @@ use crate::extensions::*; use crate::leasing::*; use crate::metagraph::*; use crate::neuron::*; +use crate::proxy::*; use crate::sr25519::*; use crate::staking::*; use crate::storage_query::*; @@ -47,6 +48,7 @@ mod extensions; mod leasing; mod metagraph; mod neuron; +mod proxy; mod sr25519; mod staking; mod storage_query; @@ -107,7 +109,7 @@ where Self(Default::default()) } - pub fn used_addresses() -> [H160; 24] { + pub fn used_addresses() -> [H160; 25] { [ hash(1), hash(2), @@ -133,6 +135,7 @@ where hash(AlphaPrecompile::::INDEX), hash(CrowdloanPrecompile::::INDEX), hash(LeasingPrecompile::::INDEX), + hash(ProxyPrecompile::::INDEX), ] } } @@ -219,6 +222,9 @@ where a if a == hash(LeasingPrecompile::::INDEX) => { LeasingPrecompile::::try_execute::(handle, PrecompileEnum::Leasing) } + a if a == hash(ProxyPrecompile::::INDEX) => { + ProxyPrecompile::::try_execute::(handle, PrecompileEnum::Proxy) + } _ => None, } } diff --git a/precompiles/src/proxy.rs b/precompiles/src/proxy.rs new file mode 100644 index 0000000000..b243c4c161 --- /dev/null +++ b/precompiles/src/proxy.rs @@ -0,0 +1,222 @@ +use core::marker::PhantomData; + +use crate::{PrecompileExt, PrecompileHandleExt}; + +use fp_evm::{ExitError, PrecompileFailure}; +use frame_support::dispatch::{GetDispatchInfo, PostDispatchInfo}; +use frame_system::RawOrigin; +use pallet_evm::{AddressMapping, PrecompileHandle}; +use precompile_utils::EvmResult; +use sp_core::H256; +use sp_runtime::{ + codec::DecodeLimit, + traits::{Dispatchable, StaticLookup}, +}; +use sp_std::boxed::Box; +use sp_std::vec::Vec; +use subtensor_runtime_common::ProxyType; +pub struct ProxyPrecompile(PhantomData); +const MAX_DECODE_DEPTH: u32 = 8; + +impl PrecompileExt for ProxyPrecompile +where + R: frame_system::Config + + pallet_evm::Config + + pallet_subtensor::Config + + pallet_proxy::Config, + R::AccountId: From<[u8; 32]> + Into<[u8; 32]>, + ::AddressMapping: AddressMapping, + ::RuntimeCall: From> + + From> + + GetDispatchInfo + + Dispatchable, + ::AddressMapping: AddressMapping, + <::Lookup as StaticLookup>::Source: From, +{ + const INDEX: u64 = 2059; +} + +#[precompile_utils::precompile] +impl ProxyPrecompile +where + R: frame_system::Config + + pallet_evm::Config + + pallet_subtensor::Config + + pallet_proxy::Config, + R::AccountId: From<[u8; 32]> + Into<[u8; 32]>, + ::AddressMapping: AddressMapping, + ::RuntimeCall: From> + + From> + + GetDispatchInfo + + Dispatchable, + <::Lookup as StaticLookup>::Source: From, +{ + #[precompile::public("createPureProxy(uint8,uint32,uint16)")] + pub fn create_pure_proxy( + handle: &mut impl PrecompileHandle, + proxy_type_: u8, + delay: u32, + index: u16, + ) -> EvmResult { + let account_id = handle.caller_account_id::(); + let proxy_type = + ProxyType::try_from(proxy_type_).map_err(|_| PrecompileFailure::Error { + exit_status: ExitError::Other("Invalid proxy type".into()), + })?; + + let call = pallet_proxy::Call::::create_pure { + proxy_type, + delay: delay.into(), + index, + }; + + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id.clone()))?; + + // Success! + // Try to get proxy address + let proxy_address: [u8; 32] = + pallet_proxy::pallet::Pallet::::pure_account(&account_id, &proxy_type, index, None) + .into(); + + // Check if in the proxies map + let proxy_entry = pallet_proxy::pallet::Pallet::::proxies(account_id.clone()); + if proxy_entry + .0 + .iter() + .any(|p| account_id == p.delegate && proxy_type == p.proxy_type) + { + return Ok(proxy_address.into()); + } + + Err(PrecompileFailure::Error { + exit_status: ExitError::Other("Proxy not found".into()), + }) + } + + #[precompile::public("killPureProxy(bytes32,uint8,uint16,uint32,uint32)")] + pub fn kill_pure_proxy( + handle: &mut impl PrecompileHandle, + spawner: H256, + proxy_type: u8, + index: u16, + height: u32, + ext_index: u32, + ) -> EvmResult<()> { + let account_id = handle.caller_account_id::(); + let proxy_type = ProxyType::try_from(proxy_type).map_err(|_| PrecompileFailure::Error { + exit_status: ExitError::Other("Invalid proxy type".into()), + })?; + + let call = pallet_proxy::Call::::kill_pure { + spawner: <::Lookup as StaticLookup>::Source::from( + spawner.0.into(), + ), + proxy_type, + index, + height: height.into(), + ext_index: ext_index.into(), + }; + + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id)) + } + + #[precompile::public("proxyCall(bytes32,uint8[],uint8[])")] + pub fn proxy_call( + handle: &mut impl PrecompileHandle, + real: H256, + force_proxy_type: Vec, + call: Vec, + ) -> EvmResult<()> { + let account_id = handle.caller_account_id::(); + + let call = ::RuntimeCall::decode_with_depth_limit( + MAX_DECODE_DEPTH, + &mut &call[..], + ) + .map_err(|_| PrecompileFailure::Error { + exit_status: ExitError::Other("The raw call data not correctly encoded".into()), + })?; + + let mut proxy_type: Option = None; + if let Some(p) = force_proxy_type.first() { + let proxy_type_ = ProxyType::try_from(*p).map_err(|_| PrecompileFailure::Error { + exit_status: ExitError::Other("Invalid proxy type".into()), + })?; + proxy_type = Some(proxy_type_); + }; + + let call = pallet_proxy::Call::::proxy { + real: <::Lookup as StaticLookup>::Source::from( + real.0.into(), + ), + force_proxy_type: proxy_type, + call: Box::new(call), + }; + + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id)) + } + + #[precompile::public("addProxy(bytes32,uint8,uint32)")] + pub fn add_proxy( + handle: &mut impl PrecompileHandle, + delegate: H256, + proxy_type: u8, + delay: u32, + ) -> EvmResult<()> { + let account_id = handle.caller_account_id::(); + let proxy_type = ProxyType::try_from(proxy_type).map_err(|_| PrecompileFailure::Error { + exit_status: ExitError::Other("Invalid proxy type".into()), + })?; + + let call = pallet_proxy::Call::::add_proxy { + delegate: <::Lookup as StaticLookup>::Source::from( + delegate.0.into(), + ), + proxy_type, + delay: delay.into(), + }; + + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id)) + } + + #[precompile::public("removeProxy(bytes32,uint8,uint32)")] + pub fn remove_proxy( + handle: &mut impl PrecompileHandle, + delegate: H256, + proxy_type: u8, + delay: u32, + ) -> EvmResult<()> { + let account_id = handle.caller_account_id::(); + let proxy_type = ProxyType::try_from(proxy_type).map_err(|_| PrecompileFailure::Error { + exit_status: ExitError::Other("Invalid proxy type".into()), + })?; + + let call = pallet_proxy::Call::::remove_proxy { + delegate: <::Lookup as StaticLookup>::Source::from( + delegate.0.into(), + ), + proxy_type, + delay: delay.into(), + }; + + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id)) + } + + #[precompile::public("removeProxies()")] + pub fn remove_proxies(handle: &mut impl PrecompileHandle) -> EvmResult<()> { + let account_id = handle.caller_account_id::(); + + let call = pallet_proxy::Call::::remove_proxies {}; + + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id)) + } + + #[precompile::public("pokeDeposit()")] + pub fn poke_deposit(handle: &mut impl PrecompileHandle) -> EvmResult<()> { + let account_id = handle.caller_account_id::(); + + let call = pallet_proxy::Call::::poke_deposit {}; + + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id)) + } +} diff --git a/precompiles/src/solidity/proxy.sol b/precompiles/src/solidity/proxy.sol new file mode 100644 index 0000000000..b0e03031bf --- /dev/null +++ b/precompiles/src/solidity/proxy.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +address constant IPROXY_ADDRESS = 0x000000000000000000000000000000000000080b; + +interface IProxy { + function createPureProxy( + uint8 proxy_type, + uint32 delay, + uint16 index + ) external; + + function proxyCall(bytes32 real, uint8[] memory force_proxy_type, bytes memory call) external; + + function killPureProxy( + bytes32 spawner, + uint8 proxy_type, + uint16 index, + uint16 height, + uint32 ext_index + ) external; + + function addProxy(bytes32 delegate, uint8 proxy_type, uint32 delay) external; + + function removeProxy(bytes32 delegate, uint8 proxy_type, uint32 delay) external; + + function removeProxies() external; + + function pokeDeposit() external; +} From da18abc00773af43a6b5e050ed085178170c29fe Mon Sep 17 00:00:00 2001 From: open-junius Date: Mon, 20 Oct 2025 18:40:31 +0800 Subject: [PATCH 002/210] fix doc for storage --- pallets/subtensor/src/lib.rs | 118 +++++++++++++++++++++++------------ 1 file changed, 79 insertions(+), 39 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index ca55a0ab52..8277cda91d 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -898,9 +898,10 @@ pub mod pallet { 128 } - #[pallet::storage] - pub type MinActivityCutoff = - StorageValue<_, u16, ValueQuery, DefaultMinActivityCutoff>; + /// Global minimum activity cutoff, not used anymore. + // #[pallet::storage] + // pub type MinActivityCutoff = + // StorageValue<_, u16, ValueQuery, DefaultMinActivityCutoff>; #[pallet::storage] /// Global window (in blocks) at the end of each tempo where admin ops are disallowed @@ -913,14 +914,17 @@ pub mod pallet { StorageValue<_, u16, ValueQuery, DefaultOwnerHyperparamRateLimit>; #[pallet::storage] + /// Duration of coldkey swap schedule before execution pub type ColdkeySwapScheduleDuration = StorageValue<_, BlockNumberFor, ValueQuery, DefaultColdkeySwapScheduleDuration>; #[pallet::storage] + /// Duration of coldkey swap reschedule before execution pub type ColdkeySwapRescheduleDuration = StorageValue<_, BlockNumberFor, ValueQuery, DefaultColdkeySwapRescheduleDuration>; #[pallet::storage] + /// Duration of dissolve network schedule before execution pub type DissolveNetworkScheduleDuration = StorageValue<_, BlockNumberFor, ValueQuery, DefaultDissolveNetworkScheduleDuration>; @@ -1026,7 +1030,8 @@ pub mod pallet { ValueQuery, DefaultAccountLinkage, >; - #[pallet::storage] // --- DMAP ( netuid, hotkey ) --> u64 | Last total dividend this hotkey got on tempo. + #[pallet::storage] + /// --- DMAP ( netuid, hotkey ) --> u64 | Last total dividend this hotkey got on tempo. pub type AlphaDividendsPerSubnet = StorageDoubleMap< _, Identity, @@ -1037,7 +1042,8 @@ pub mod pallet { ValueQuery, DefaultZeroAlpha, >; - #[pallet::storage] // --- DMAP ( netuid, hotkey ) --> u64 | Last total root dividend paid to this hotkey on this subnet. + #[pallet::storage] + /// --- DMAP ( netuid, hotkey ) --> u64 | Last total root dividend paid to this hotkey on this subnet. pub type TaoDividendsPerSubnet = StorageDoubleMap< _, Identity, @@ -1081,52 +1087,68 @@ pub mod pallet { /// Eventually, Bittensor should migrate to using Holds afterwhich time we will not require this /// separate accounting. - #[pallet::storage] // --- ITEM ( maximum_number_of_networks ) + #[pallet::storage] + /// --- ITEM ( maximum_number_of_networks ) pub type SubnetLimit = StorageValue<_, u16, ValueQuery, DefaultSubnetLimit>; - #[pallet::storage] // --- ITEM ( total_issuance ) + #[pallet::storage] + /// --- ITEM ( total_issuance ) pub type TotalIssuance = StorageValue<_, TaoCurrency, ValueQuery, DefaultTotalIssuance>; - #[pallet::storage] // --- ITEM ( total_stake ) + #[pallet::storage] + /// --- ITEM ( total_stake ) pub type TotalStake = StorageValue<_, TaoCurrency, ValueQuery>; - #[pallet::storage] // --- ITEM ( moving_alpha ) -- subnet moving alpha. + #[pallet::storage] + /// --- ITEM ( moving_alpha ) -- subnet moving alpha. pub type SubnetMovingAlpha = StorageValue<_, I96F32, ValueQuery, DefaultMovingAlpha>; - #[pallet::storage] // --- MAP ( netuid ) --> moving_price | The subnet moving price. + #[pallet::storage] + /// --- MAP ( netuid ) --> moving_price | The subnet moving price. pub type SubnetMovingPrice = StorageMap<_, Identity, NetUid, I96F32, ValueQuery, DefaultMovingPrice>; - #[pallet::storage] // --- MAP ( netuid ) --> total_volume | The total amount of TAO bought and sold since the start of the network. + #[pallet::storage] + /// --- MAP ( netuid ) --> total_volume | The total amount of TAO bought and sold since the start of the network. pub type SubnetVolume = StorageMap<_, Identity, NetUid, u128, ValueQuery, DefaultZeroU128>; - #[pallet::storage] // --- MAP ( netuid ) --> tao_in_subnet | Returns the amount of TAO in the subnet. + #[pallet::storage] + /// --- MAP ( netuid ) --> tao_in_subnet | Returns the amount of TAO in the subnet. pub type SubnetTAO = StorageMap<_, Identity, NetUid, TaoCurrency, ValueQuery, DefaultZeroTao>; - #[pallet::storage] // --- MAP ( netuid ) --> tao_in_user_subnet | Returns the amount of TAO in the subnet reserve provided by users as liquidity. + #[pallet::storage] + /// --- MAP ( netuid ) --> tao_in_user_subnet | Returns the amount of TAO in the subnet reserve provided by users as liquidity. pub type SubnetTaoProvided = StorageMap<_, Identity, NetUid, TaoCurrency, ValueQuery, DefaultZeroTao>; - #[pallet::storage] // --- MAP ( netuid ) --> alpha_in_emission | Returns the amount of alph in emission into the pool per block. + #[pallet::storage] + /// --- MAP ( netuid ) --> alpha_in_emission | Returns the amount of alph in emission into the pool per block. pub type SubnetAlphaInEmission = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultZeroAlpha>; - #[pallet::storage] // --- MAP ( netuid ) --> alpha_out_emission | Returns the amount of alpha out emission into the network per block. + #[pallet::storage] + /// --- MAP ( netuid ) --> alpha_out_emission | Returns the amount of alpha out emission into the network per block. pub type SubnetAlphaOutEmission = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultZeroAlpha>; - #[pallet::storage] // --- MAP ( netuid ) --> tao_in_emission | Returns the amount of tao emitted into this subent on the last block. + #[pallet::storage] + /// --- MAP ( netuid ) --> tao_in_emission | Returns the amount of tao emitted into this subent on the last block. pub type SubnetTaoInEmission = StorageMap<_, Identity, NetUid, TaoCurrency, ValueQuery, DefaultZeroTao>; - #[pallet::storage] // --- MAP ( netuid ) --> alpha_supply_in_pool | Returns the amount of alpha in the pool. + #[pallet::storage] + /// --- MAP ( netuid ) --> alpha_supply_in_pool | Returns the amount of alpha in the pool. pub type SubnetAlphaIn = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultZeroAlpha>; - #[pallet::storage] // --- MAP ( netuid ) --> alpha_supply_user_in_pool | Returns the amount of alpha in the pool provided by users as liquidity. + #[pallet::storage] + /// --- MAP ( netuid ) --> alpha_supply_user_in_pool | Returns the amount of alpha in the pool provided by users as liquidity. pub type SubnetAlphaInProvided = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultZeroAlpha>; #[pallet::storage] /// --- MAP ( netuid ) --> alpha_supply_in_subnet | Returns the amount of alpha in the subnet. pub type SubnetAlphaOut = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultZeroAlpha>; - #[pallet::storage] // --- MAP ( cold ) --> Vec | Maps coldkey to hotkeys that stake to it + #[pallet::storage] + /// --- MAP ( cold ) --> Vec | Maps coldkey to hotkeys that stake to it pub type StakingHotkeys = StorageMap<_, Blake2_128Concat, T::AccountId, Vec, ValueQuery>; - #[pallet::storage] // --- MAP ( cold ) --> Vec | Returns the vector of hotkeys controlled by this coldkey. + #[pallet::storage] + /// --- MAP ( cold ) --> Vec | Returns the vector of hotkeys controlled by this coldkey. pub type OwnedHotkeys = StorageMap<_, Blake2_128Concat, T::AccountId, Vec, ValueQuery>; - #[pallet::storage] // --- DMAP ( cold, netuid )--> hot | Returns the hotkey a coldkey will autostake to with mining rewards. + #[pallet::storage] + /// --- DMAP ( cold, netuid )--> hot | Returns the hotkey a coldkey will autostake to with mining rewards. pub type AutoStakeDestination = StorageDoubleMap< _, Blake2_128Concat, @@ -1136,7 +1158,8 @@ pub mod pallet { T::AccountId, OptionQuery, >; - #[pallet::storage] // --- DMAP ( hot, netuid )--> Vec | Returns a list of coldkeys that are autostaking to a hotkey. + #[pallet::storage] + /// --- DMAP ( hot, netuid )--> Vec | Returns a list of coldkeys that are autostaking to a hotkey. pub type AutoStakeDestinationColdkeys = StorageDoubleMap< _, Blake2_128Concat, @@ -1147,7 +1170,8 @@ pub mod pallet { ValueQuery, >; - #[pallet::storage] // --- DMAP ( cold ) --> (block_expected, new_coldkey) | Maps coldkey to the block to swap at and new coldkey. + #[pallet::storage] + /// --- DMAP ( cold ) --> (block_expected, new_coldkey) | Maps coldkey to the block to swap at and new coldkey. pub type ColdkeySwapScheduled = StorageMap< _, Blake2_128Concat, @@ -1157,7 +1181,8 @@ pub mod pallet { DefaultColdkeySwapScheduled, >; - #[pallet::storage] // --- DMAP ( hot, netuid ) --> alpha | Returns the total amount of alpha a hotkey owns. + #[pallet::storage] + /// --- DMAP ( hot, netuid ) --> alpha | Returns the total amount of alpha a hotkey owns. pub type TotalHotkeyAlpha = StorageDoubleMap< _, Blake2_128Concat, @@ -1168,7 +1193,8 @@ pub mod pallet { ValueQuery, DefaultZeroAlpha, >; - #[pallet::storage] // --- DMAP ( hot, netuid ) --> alpha | Returns the total amount of alpha a hotkey owned in the last epoch. + #[pallet::storage] + /// --- DMAP ( hot, netuid ) --> alpha | Returns the total amount of alpha a hotkey owned in the last epoch. pub type TotalHotkeyAlphaLastEpoch = StorageDoubleMap< _, Blake2_128Concat, @@ -1191,7 +1217,8 @@ pub mod pallet { ValueQuery, DefaultSharePoolZero, >; - #[pallet::storage] // --- NMAP ( hot, cold, netuid ) --> alpha | Returns the alpha shares for a hotkey, coldkey, netuid triplet. + #[pallet::storage] + /// --- NMAP ( hot, cold, netuid ) --> alpha | Returns the alpha shares for a hotkey, coldkey, netuid triplet. pub type Alpha = StorageNMap< _, ( @@ -1202,7 +1229,8 @@ pub mod pallet { U64F64, // Shares ValueQuery, >; - #[pallet::storage] // --- MAP ( netuid ) --> token_symbol | Returns the token symbol for a subnet. + #[pallet::storage] + /// --- MAP ( netuid ) --> token_symbol | Returns the token symbol for a subnet. pub type TokenSymbol = StorageMap<_, Identity, NetUid, Vec, ValueQuery, DefaultUnicodeVecU8>; @@ -1261,20 +1289,24 @@ pub mod pallet { /// ============================ /// ==== Subnet Locks ===== /// ============================ - #[pallet::storage] // --- MAP ( netuid ) --> transfer_toggle + #[pallet::storage] + /// --- MAP ( netuid ) --> transfer_toggle pub type TransferToggle = StorageMap<_, Identity, NetUid, bool, ValueQuery, DefaultTrue>; - #[pallet::storage] // --- MAP ( netuid ) --> total_subnet_locked + #[pallet::storage] + /// --- MAP ( netuid ) --> total_subnet_locked pub type SubnetLocked = StorageMap<_, Identity, NetUid, TaoCurrency, ValueQuery, DefaultZeroTao>; - #[pallet::storage] // --- MAP ( netuid ) --> largest_locked + #[pallet::storage] + /// --- MAP ( netuid ) --> largest_locked pub type LargestLocked = StorageMap<_, Identity, NetUid, u64, ValueQuery, DefaultZeroU64>; /// ================= /// ==== Tempos ===== /// ================= - #[pallet::storage] // --- MAP ( netuid ) --> tempo + #[pallet::storage] + /// --- MAP ( netuid ) --> tempo pub type Tempo = StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultTempo>; /// ============================ @@ -1553,7 +1585,8 @@ pub mod pallet { /// ======================================= /// ==== Subnetwork Consensus Storage ==== /// ======================================= - #[pallet::storage] // --- DMAP ( netuid ) --> stake_weight | weight for stake used in YC. + #[pallet::storage] + /// --- DMAP ( netuid ) --> stake_weight | weight for stake used in YC. pub type StakeWeight = StorageMap<_, Identity, NetUid, Vec, ValueQuery, EmptyU16Vec>; #[pallet::storage] @@ -1688,30 +1721,36 @@ pub mod pallet { PrometheusInfoOf, OptionQuery, >; - #[pallet::storage] // --- MAP ( coldkey ) --> identity. (DEPRECATED for V2) + #[pallet::storage] + /// --- MAP ( coldkey ) --> identity. (DEPRECATED for V2) pub type Identities = StorageMap<_, Blake2_128Concat, T::AccountId, ChainIdentityOf, OptionQuery>; - #[pallet::storage] // --- MAP ( coldkey ) --> identity + #[pallet::storage] + /// --- MAP ( coldkey ) --> identity pub type IdentitiesV2 = StorageMap<_, Blake2_128Concat, T::AccountId, ChainIdentityOfV2, OptionQuery>; - #[pallet::storage] // --- MAP ( netuid ) --> identity. (DEPRECATED for V2) + #[pallet::storage] + /// --- MAP ( netuid ) --> identity. (DEPRECATED for V2) pub type SubnetIdentities = StorageMap<_, Blake2_128Concat, NetUid, SubnetIdentityOf, OptionQuery>; - #[pallet::storage] // --- MAP ( netuid ) --> identityV2 (DEPRECATED for V3) + #[pallet::storage] + /// --- MAP ( netuid ) --> identityV2 (DEPRECATED for V3) pub type SubnetIdentitiesV2 = StorageMap<_, Blake2_128Concat, NetUid, SubnetIdentityOfV2, OptionQuery>; - #[pallet::storage] // --- MAP ( netuid ) --> SubnetIdentityOfV3 + #[pallet::storage] + /// --- MAP ( netuid ) --> SubnetIdentityOfV3 pub type SubnetIdentitiesV3 = StorageMap<_, Blake2_128Concat, NetUid, SubnetIdentityOfV3, OptionQuery>; /// ================================= /// ==== Axon / Promo Endpoints ===== /// ================================= - #[pallet::storage] // --- NMAP ( hot, netuid, name ) --> last_block | Returns the last block of a transaction for a given key, netuid, and name. + #[pallet::storage] + /// --- NMAP ( hot, netuid, name ) --> last_block | Returns the last block of a transaction for a given key, netuid, and name. pub type TransactionKeyLastBlock = StorageNMap< _, ( @@ -1913,7 +1952,8 @@ pub mod pallet { /// ================== /// ==== Genesis ===== /// ================== - #[pallet::storage] // --- Storage for migration run status + #[pallet::storage] + /// --- Storage for migration run status pub type HasMigrationRun = StorageMap<_, Identity, Vec, bool, ValueQuery>; #[pallet::type_value] From 0ee6da16d5c43a2395bbd9f3aa9e19e4c734cc5b Mon Sep 17 00:00:00 2001 From: open-junius Date: Mon, 20 Oct 2025 19:05:56 +0800 Subject: [PATCH 003/210] fix all --- pallets/subtensor/src/lib.rs | 111 +++++++++++++++++++++++------------ 1 file changed, 72 insertions(+), 39 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 8277cda91d..39a5e8ab78 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -898,10 +898,10 @@ pub mod pallet { 128 } + #[pallet::storage] /// Global minimum activity cutoff, not used anymore. - // #[pallet::storage] - // pub type MinActivityCutoff = - // StorageValue<_, u16, ValueQuery, DefaultMinActivityCutoff>; + pub type MinActivityCutoff = + StorageValue<_, u16, ValueQuery, DefaultMinActivityCutoff>; #[pallet::storage] /// Global window (in blocks) at the end of each tempo where admin ops are disallowed @@ -957,34 +957,42 @@ pub mod pallet { /// /// Eventually, Bittensor should migrate to using Holds afterwhich time we will not require this /// separate accounting. - #[pallet::storage] /// --- ITEM --> Global weight - pub type TaoWeight = StorageValue<_, u64, ValueQuery, DefaultTaoWeight>; #[pallet::storage] + pub type TaoWeight = StorageValue<_, u64, ValueQuery, DefaultTaoWeight>; + /// --- ITEM --> CK burn - pub type CKBurn = StorageValue<_, u64, ValueQuery, DefaultCKBurn>; #[pallet::storage] + pub type CKBurn = StorageValue<_, u64, ValueQuery, DefaultCKBurn>; + /// --- ITEM ( default_delegate_take ) - pub type MaxDelegateTake = StorageValue<_, u16, ValueQuery, DefaultDelegateTake>; #[pallet::storage] + pub type MaxDelegateTake = StorageValue<_, u16, ValueQuery, DefaultDelegateTake>; + /// --- ITEM ( min_delegate_take ) - pub type MinDelegateTake = StorageValue<_, u16, ValueQuery, DefaultMinDelegateTake>; #[pallet::storage] + pub type MinDelegateTake = StorageValue<_, u16, ValueQuery, DefaultMinDelegateTake>; + /// --- ITEM ( default_childkey_take ) - pub type MaxChildkeyTake = StorageValue<_, u16, ValueQuery, DefaultMaxChildKeyTake>; #[pallet::storage] + pub type MaxChildkeyTake = StorageValue<_, u16, ValueQuery, DefaultMaxChildKeyTake>; + /// --- ITEM ( min_childkey_take ) + #[pallet::storage] pub type MinChildkeyTake = StorageValue<_, u16, ValueQuery, DefaultMinChildKeyTake>; + + /// MAP ( hot ) --> cold, Returns the controlling coldkey for a hotkey. #[pallet::storage] - /// MAP ( hot ) --> cold | Returns the controlling coldkey for a hotkey. pub type Owner = StorageMap<_, Blake2_128Concat, T::AccountId, T::AccountId, ValueQuery, DefaultAccount>; + + /// MAP ( hot ) --> take, Returns the hotkey delegation take. And signals that this key is open for delegation. #[pallet::storage] - /// MAP ( hot ) --> take | Returns the hotkey delegation take. And signals that this key is open for delegation. pub type Delegates = StorageMap<_, Blake2_128Concat, T::AccountId, u16, ValueQuery, DefaultDelegateTake>; + + /// DMAP ( hot, netuid ) --> take, Returns the hotkey childkey take for a specific subnet #[pallet::storage] - /// DMAP ( hot, netuid ) --> take | Returns the hotkey childkey take for a specific subnet pub type ChildkeyTake = StorageDoubleMap< _, Blake2_128Concat, @@ -994,8 +1002,9 @@ pub mod pallet { u16, // Value: take ValueQuery, >; - #[pallet::storage] + /// DMAP ( netuid, parent ) --> (Vec<(proportion,child)>, cool_down_block) + #[pallet::storage] pub type PendingChildKeys = StorageDoubleMap< _, Identity, @@ -1006,8 +1015,9 @@ pub mod pallet { ValueQuery, DefaultPendingChildkeys, >; - #[pallet::storage] + /// DMAP ( parent, netuid ) --> Vec<(proportion,child)> + #[pallet::storage] pub type ChildKeys = StorageDoubleMap< _, Blake2_128Concat, @@ -1018,8 +1028,9 @@ pub mod pallet { ValueQuery, DefaultAccountLinkage, >; - #[pallet::storage] + /// DMAP ( child, netuid ) --> Vec<(proportion,parent)> + #[pallet::storage] pub type ParentKeys = StorageDoubleMap< _, Blake2_128Concat, @@ -1030,8 +1041,9 @@ pub mod pallet { ValueQuery, DefaultAccountLinkage, >; - #[pallet::storage] + /// --- DMAP ( netuid, hotkey ) --> u64 | Last total dividend this hotkey got on tempo. + #[pallet::storage] pub type AlphaDividendsPerSubnet = StorageDoubleMap< _, Identity, @@ -1042,8 +1054,9 @@ pub mod pallet { ValueQuery, DefaultZeroAlpha, >; - #[pallet::storage] + /// --- DMAP ( netuid, hotkey ) --> u64 | Last total root dividend paid to this hotkey on this subnet. + #[pallet::storage] pub type TaoDividendsPerSubnet = StorageDoubleMap< _, Identity, @@ -1058,11 +1071,13 @@ pub mod pallet { /// ================== /// ==== Coinbase ==== /// ================== - #[pallet::storage] + /// --- ITEM ( global_block_emission ) - pub type BlockEmission = StorageValue<_, u64, ValueQuery, DefaultBlockEmission>; #[pallet::storage] + pub type BlockEmission = StorageValue<_, u64, ValueQuery, DefaultBlockEmission>; + /// --- DMap ( hot, netuid ) --> emission | last hotkey emission on network. + #[pallet::storage] pub type LastHotkeyEmissionOnNetuid = StorageDoubleMap< _, Blake2_128Concat, @@ -1089,66 +1104,83 @@ pub mod pallet { #[pallet::storage] /// --- ITEM ( maximum_number_of_networks ) + pub type SubnetLimit = StorageValue<_, u16, ValueQuery, DefaultSubnetLimit>; + #[pallet::storage] /// --- ITEM ( total_issuance ) pub type TotalIssuance = StorageValue<_, TaoCurrency, ValueQuery, DefaultTotalIssuance>; + #[pallet::storage] /// --- ITEM ( total_stake ) - pub type TotalStake = StorageValue<_, TaoCurrency, ValueQuery>; + pub type TotalStake = StorageValue<_, TaoCurrency, ValueQuery, DefaultZeroTao>; + #[pallet::storage] - /// --- ITEM ( moving_alpha ) -- subnet moving alpha. + /// --- ITEM ( moving_alpha ) -- subnet moving alpha. pub type SubnetMovingAlpha = StorageValue<_, I96F32, ValueQuery, DefaultMovingAlpha>; + #[pallet::storage] /// --- MAP ( netuid ) --> moving_price | The subnet moving price. pub type SubnetMovingPrice = StorageMap<_, Identity, NetUid, I96F32, ValueQuery, DefaultMovingPrice>; + #[pallet::storage] /// --- MAP ( netuid ) --> total_volume | The total amount of TAO bought and sold since the start of the network. pub type SubnetVolume = StorageMap<_, Identity, NetUid, u128, ValueQuery, DefaultZeroU128>; + #[pallet::storage] - /// --- MAP ( netuid ) --> tao_in_subnet | Returns the amount of TAO in the subnet. + /// --- MAP ( netuid ) --> tao_in_subnet, Returns the amount of TAO in the subnet. pub type SubnetTAO = StorageMap<_, Identity, NetUid, TaoCurrency, ValueQuery, DefaultZeroTao>; + #[pallet::storage] - /// --- MAP ( netuid ) --> tao_in_user_subnet | Returns the amount of TAO in the subnet reserve provided by users as liquidity. + /// --- MAP ( netuid ) --> tao_in_user_subnet, Returns the amount of TAO in the subnet reserve provided by users as liquidity. pub type SubnetTaoProvided = StorageMap<_, Identity, NetUid, TaoCurrency, ValueQuery, DefaultZeroTao>; + #[pallet::storage] - /// --- MAP ( netuid ) --> alpha_in_emission | Returns the amount of alph in emission into the pool per block. + /// --- MAP ( netuid ) --> alpha_in_emission, Returns the amount of alph in emission into the pool per block. pub type SubnetAlphaInEmission = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultZeroAlpha>; + #[pallet::storage] - /// --- MAP ( netuid ) --> alpha_out_emission | Returns the amount of alpha out emission into the network per block. + /// --- MAP ( netuid ) --> alpha_out_emission, Returns the amount of alpha out emission into the network per block. pub type SubnetAlphaOutEmission = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultZeroAlpha>; + #[pallet::storage] - /// --- MAP ( netuid ) --> tao_in_emission | Returns the amount of tao emitted into this subent on the last block. + /// --- MAP ( netuid ) --> tao_in_emission, Returns the amount of tao emitted into this subent on the last block. pub type SubnetTaoInEmission = StorageMap<_, Identity, NetUid, TaoCurrency, ValueQuery, DefaultZeroTao>; + #[pallet::storage] - /// --- MAP ( netuid ) --> alpha_supply_in_pool | Returns the amount of alpha in the pool. + /// --- MAP ( netuid ) --> alpha_supply_in_pool, Returns the amount of alpha in the pool. pub type SubnetAlphaIn = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultZeroAlpha>; + #[pallet::storage] - /// --- MAP ( netuid ) --> alpha_supply_user_in_pool | Returns the amount of alpha in the pool provided by users as liquidity. + /// --- MAP ( netuid ) --> alpha_supply_user_in_pool, Returns the amount of alpha in the pool provided by users as liquidity. pub type SubnetAlphaInProvided = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultZeroAlpha>; + #[pallet::storage] - /// --- MAP ( netuid ) --> alpha_supply_in_subnet | Returns the amount of alpha in the subnet. + /// --- MAP ( netuid ) --> alpha_supply_in_subnet, Returns the amount of alpha in the subnet. pub type SubnetAlphaOut = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultZeroAlpha>; + #[pallet::storage] /// --- MAP ( cold ) --> Vec | Maps coldkey to hotkeys that stake to it pub type StakingHotkeys = StorageMap<_, Blake2_128Concat, T::AccountId, Vec, ValueQuery>; + #[pallet::storage] - /// --- MAP ( cold ) --> Vec | Returns the vector of hotkeys controlled by this coldkey. + /// --- MAP ( cold ) --> Vec, Returns the vector of hotkeys controlled by this coldkey. pub type OwnedHotkeys = StorageMap<_, Blake2_128Concat, T::AccountId, Vec, ValueQuery>; + #[pallet::storage] - /// --- DMAP ( cold, netuid )--> hot | Returns the hotkey a coldkey will autostake to with mining rewards. + /// --- DMAP ( cold, netuid )--> hot, Returns the hotkey a coldkey will autostake to with mining rewards. pub type AutoStakeDestination = StorageDoubleMap< _, Blake2_128Concat, @@ -1158,8 +1190,9 @@ pub mod pallet { T::AccountId, OptionQuery, >; + #[pallet::storage] - /// --- DMAP ( hot, netuid )--> Vec | Returns a list of coldkeys that are autostaking to a hotkey. + /// --- DMAP ( hot, netuid )--> Vec, Returns a list of coldkeys that are autostaking to a hotkey pub type AutoStakeDestinationColdkeys = StorageDoubleMap< _, Blake2_128Concat, @@ -1171,7 +1204,7 @@ pub mod pallet { >; #[pallet::storage] - /// --- DMAP ( cold ) --> (block_expected, new_coldkey) | Maps coldkey to the block to swap at and new coldkey. + /// --- DMAP ( cold ) --> (block_expected, new_coldkey), Maps coldkey to the block to swap at and new coldkey. pub type ColdkeySwapScheduled = StorageMap< _, Blake2_128Concat, @@ -1182,7 +1215,7 @@ pub mod pallet { >; #[pallet::storage] - /// --- DMAP ( hot, netuid ) --> alpha | Returns the total amount of alpha a hotkey owns. + /// --- DMAP ( hot, netuid ) --> alpha, Returns the total amount of alpha a hotkey owns. pub type TotalHotkeyAlpha = StorageDoubleMap< _, Blake2_128Concat, @@ -1194,7 +1227,7 @@ pub mod pallet { DefaultZeroAlpha, >; #[pallet::storage] - /// --- DMAP ( hot, netuid ) --> alpha | Returns the total amount of alpha a hotkey owned in the last epoch. + /// --- DMAP ( hot, netuid ) --> alpha, Returns the total amount of alpha a hotkey owned in the last epoch. pub type TotalHotkeyAlphaLastEpoch = StorageDoubleMap< _, Blake2_128Concat, @@ -1206,7 +1239,7 @@ pub mod pallet { DefaultZeroAlpha, >; #[pallet::storage] - /// DMAP ( hot, netuid ) --> total_alpha_shares | Returns the number of alpha shares for a hotkey on a subnet. + /// DMAP ( hot, netuid ) --> total_alpha_shares, Returns the number of alpha shares for a hotkey on a subnet. pub type TotalHotkeyShares = StorageDoubleMap< _, Blake2_128Concat, @@ -1218,7 +1251,7 @@ pub mod pallet { DefaultSharePoolZero, >; #[pallet::storage] - /// --- NMAP ( hot, cold, netuid ) --> alpha | Returns the alpha shares for a hotkey, coldkey, netuid triplet. + /// --- NMAP ( hot, cold, netuid ) --> alpha, Returns the alpha shares for a hotkey, coldkey, netuid triplet. pub type Alpha = StorageNMap< _, ( @@ -1230,7 +1263,7 @@ pub mod pallet { ValueQuery, >; #[pallet::storage] - /// --- MAP ( netuid ) --> token_symbol | Returns the token symbol for a subnet. + /// --- MAP ( netuid ) --> token_symbol, Returns the token symbol for a subnet. pub type TokenSymbol = StorageMap<_, Identity, NetUid, Vec, ValueQuery, DefaultUnicodeVecU8>; @@ -1750,7 +1783,7 @@ pub mod pallet { /// ==== Axon / Promo Endpoints ===== /// ================================= #[pallet::storage] - /// --- NMAP ( hot, netuid, name ) --> last_block | Returns the last block of a transaction for a given key, netuid, and name. + /// --- NMAP ( hot, netuid, name ) --> last_block, Returns the last block of a transaction for a given key, netuid, and name. pub type TransactionKeyLastBlock = StorageNMap< _, ( From 0d204411a085a45f1bb953d49d7b2c00abd82e31 Mon Sep 17 00:00:00 2001 From: open-junius Date: Mon, 20 Oct 2025 20:32:16 +0800 Subject: [PATCH 004/210] fix doc --- pallets/subtensor/src/lib.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 39a5e8ab78..a943892d7f 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1071,7 +1071,6 @@ pub mod pallet { /// ================== /// ==== Coinbase ==== /// ================== - /// --- ITEM ( global_block_emission ) #[pallet::storage] pub type BlockEmission = StorageValue<_, u64, ValueQuery, DefaultBlockEmission>; @@ -1101,10 +1100,8 @@ pub mod pallet { /// /// Eventually, Bittensor should migrate to using Holds afterwhich time we will not require this /// separate accounting. - #[pallet::storage] /// --- ITEM ( maximum_number_of_networks ) - pub type SubnetLimit = StorageValue<_, u16, ValueQuery, DefaultSubnetLimit>; #[pallet::storage] From ff549c8354e3d1deffb0ca11b076e8e9a1051af6 Mon Sep 17 00:00:00 2001 From: open-junius Date: Mon, 20 Oct 2025 20:54:23 +0800 Subject: [PATCH 005/210] unify doc placement --- pallets/subtensor/src/lib.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index a943892d7f..bfd321d09c 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -957,42 +957,42 @@ pub mod pallet { /// /// Eventually, Bittensor should migrate to using Holds afterwhich time we will not require this /// separate accounting. - /// --- ITEM --> Global weight #[pallet::storage] + /// --- ITEM --> Global weight pub type TaoWeight = StorageValue<_, u64, ValueQuery, DefaultTaoWeight>; - /// --- ITEM --> CK burn #[pallet::storage] + /// --- ITEM --> CK burn pub type CKBurn = StorageValue<_, u64, ValueQuery, DefaultCKBurn>; - /// --- ITEM ( default_delegate_take ) #[pallet::storage] + /// --- ITEM ( default_delegate_take ) pub type MaxDelegateTake = StorageValue<_, u16, ValueQuery, DefaultDelegateTake>; - /// --- ITEM ( min_delegate_take ) #[pallet::storage] + /// --- ITEM ( min_delegate_take ) pub type MinDelegateTake = StorageValue<_, u16, ValueQuery, DefaultMinDelegateTake>; - /// --- ITEM ( default_childkey_take ) #[pallet::storage] + /// --- ITEM ( default_childkey_take ) pub type MaxChildkeyTake = StorageValue<_, u16, ValueQuery, DefaultMaxChildKeyTake>; - /// --- ITEM ( min_childkey_take ) #[pallet::storage] + /// --- ITEM ( min_childkey_take ) pub type MinChildkeyTake = StorageValue<_, u16, ValueQuery, DefaultMinChildKeyTake>; - /// MAP ( hot ) --> cold, Returns the controlling coldkey for a hotkey. #[pallet::storage] + /// MAP ( hot ) --> cold, Returns the controlling coldkey for a hotkey. Default is empty account. pub type Owner = StorageMap<_, Blake2_128Concat, T::AccountId, T::AccountId, ValueQuery, DefaultAccount>; - /// MAP ( hot ) --> take, Returns the hotkey delegation take. And signals that this key is open for delegation. #[pallet::storage] + /// MAP ( hot ) --> take, Returns the hotkey delegation take. And signals that this key is open for delegation. Default is 0. pub type Delegates = StorageMap<_, Blake2_128Concat, T::AccountId, u16, ValueQuery, DefaultDelegateTake>; - /// DMAP ( hot, netuid ) --> take, Returns the hotkey childkey take for a specific subnet #[pallet::storage] + /// DMAP ( hot, netuid ) --> take, Returns the hotkey childkey take for a specific subnet pub type ChildkeyTake = StorageDoubleMap< _, Blake2_128Concat, @@ -1003,8 +1003,8 @@ pub mod pallet { ValueQuery, >; - /// DMAP ( netuid, parent ) --> (Vec<(proportion,child)>, cool_down_block) #[pallet::storage] + /// DMAP ( netuid, parent ) --> (Vec<(proportion,child)>, cool_down_block) pub type PendingChildKeys = StorageDoubleMap< _, Identity, @@ -1016,8 +1016,8 @@ pub mod pallet { DefaultPendingChildkeys, >; - /// DMAP ( parent, netuid ) --> Vec<(proportion,child)> #[pallet::storage] + /// DMAP ( child, netuid ) --> Vec<(proportion,parent)> Default is empty Vec. pub type ChildKeys = StorageDoubleMap< _, Blake2_128Concat, @@ -1029,8 +1029,8 @@ pub mod pallet { DefaultAccountLinkage, >; - /// DMAP ( child, netuid ) --> Vec<(proportion,parent)> #[pallet::storage] + /// DMAP ( child, netuid ) --> Vec<(proportion,parent)> pub type ParentKeys = StorageDoubleMap< _, Blake2_128Concat, @@ -1042,8 +1042,8 @@ pub mod pallet { DefaultAccountLinkage, >; - /// --- DMAP ( netuid, hotkey ) --> u64 | Last total dividend this hotkey got on tempo. #[pallet::storage] + /// --- DMAP ( netuid, hotkey ) --> u64 | Last total dividend this hotkey got on tempo. pub type AlphaDividendsPerSubnet = StorageDoubleMap< _, Identity, @@ -1055,8 +1055,8 @@ pub mod pallet { DefaultZeroAlpha, >; - /// --- DMAP ( netuid, hotkey ) --> u64 | Last total root dividend paid to this hotkey on this subnet. #[pallet::storage] + /// --- DMAP ( netuid, hotkey ) --> u64 | Last total root dividend paid to this hotkey on this subnet. pub type TaoDividendsPerSubnet = StorageDoubleMap< _, Identity, @@ -1075,8 +1075,8 @@ pub mod pallet { #[pallet::storage] pub type BlockEmission = StorageValue<_, u64, ValueQuery, DefaultBlockEmission>; - /// --- DMap ( hot, netuid ) --> emission | last hotkey emission on network. #[pallet::storage] + /// --- DMap ( hot, netuid ) --> emission | last hotkey emission on network. pub type LastHotkeyEmissionOnNetuid = StorageDoubleMap< _, Blake2_128Concat, From 7c285bbe50fe5a4e5b7323fe563aa7285ba31371 Mon Sep 17 00:00:00 2001 From: open-junius Date: Mon, 20 Oct 2025 21:02:15 +0800 Subject: [PATCH 006/210] revert unnecessary changes --- pallets/subtensor/src/lib.rs | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index bfd321d09c..e2d12acb5e 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -960,37 +960,29 @@ pub mod pallet { #[pallet::storage] /// --- ITEM --> Global weight pub type TaoWeight = StorageValue<_, u64, ValueQuery, DefaultTaoWeight>; - #[pallet::storage] /// --- ITEM --> CK burn pub type CKBurn = StorageValue<_, u64, ValueQuery, DefaultCKBurn>; - #[pallet::storage] /// --- ITEM ( default_delegate_take ) pub type MaxDelegateTake = StorageValue<_, u16, ValueQuery, DefaultDelegateTake>; - #[pallet::storage] /// --- ITEM ( min_delegate_take ) pub type MinDelegateTake = StorageValue<_, u16, ValueQuery, DefaultMinDelegateTake>; - #[pallet::storage] /// --- ITEM ( default_childkey_take ) pub type MaxChildkeyTake = StorageValue<_, u16, ValueQuery, DefaultMaxChildKeyTake>; - #[pallet::storage] /// --- ITEM ( min_childkey_take ) pub type MinChildkeyTake = StorageValue<_, u16, ValueQuery, DefaultMinChildKeyTake>; - #[pallet::storage] - /// MAP ( hot ) --> cold, Returns the controlling coldkey for a hotkey. Default is empty account. + /// MAP ( hot ) --> cold, Returns the controlling coldkey for a hotkey pub type Owner = StorageMap<_, Blake2_128Concat, T::AccountId, T::AccountId, ValueQuery, DefaultAccount>; - #[pallet::storage] - /// MAP ( hot ) --> take, Returns the hotkey delegation take. And signals that this key is open for delegation. Default is 0. + /// MAP ( hot ) --> take, Returns the hotkey delegation take. And signals that this key is open for delegation pub type Delegates = StorageMap<_, Blake2_128Concat, T::AccountId, u16, ValueQuery, DefaultDelegateTake>; - #[pallet::storage] /// DMAP ( hot, netuid ) --> take, Returns the hotkey childkey take for a specific subnet pub type ChildkeyTake = StorageDoubleMap< @@ -1002,7 +994,6 @@ pub mod pallet { u16, // Value: take ValueQuery, >; - #[pallet::storage] /// DMAP ( netuid, parent ) --> (Vec<(proportion,child)>, cool_down_block) pub type PendingChildKeys = StorageDoubleMap< @@ -1015,9 +1006,8 @@ pub mod pallet { ValueQuery, DefaultPendingChildkeys, >; - #[pallet::storage] - /// DMAP ( child, netuid ) --> Vec<(proportion,parent)> Default is empty Vec. + /// DMAP ( parent, netuid ) --> Vec<(proportion,child)> pub type ChildKeys = StorageDoubleMap< _, Blake2_128Concat, @@ -1028,7 +1018,6 @@ pub mod pallet { ValueQuery, DefaultAccountLinkage, >; - #[pallet::storage] /// DMAP ( child, netuid ) --> Vec<(proportion,parent)> pub type ParentKeys = StorageDoubleMap< @@ -1041,7 +1030,6 @@ pub mod pallet { ValueQuery, DefaultAccountLinkage, >; - #[pallet::storage] /// --- DMAP ( netuid, hotkey ) --> u64 | Last total dividend this hotkey got on tempo. pub type AlphaDividendsPerSubnet = StorageDoubleMap< @@ -1054,7 +1042,6 @@ pub mod pallet { ValueQuery, DefaultZeroAlpha, >; - #[pallet::storage] /// --- DMAP ( netuid, hotkey ) --> u64 | Last total root dividend paid to this hotkey on this subnet. pub type TaoDividendsPerSubnet = StorageDoubleMap< @@ -1071,10 +1058,9 @@ pub mod pallet { /// ================== /// ==== Coinbase ==== /// ================== - /// --- ITEM ( global_block_emission ) #[pallet::storage] + /// --- ITEM ( global_block_emission ) pub type BlockEmission = StorageValue<_, u64, ValueQuery, DefaultBlockEmission>; - #[pallet::storage] /// --- DMap ( hot, netuid ) --> emission | last hotkey emission on network. pub type LastHotkeyEmissionOnNetuid = StorageDoubleMap< @@ -1087,7 +1073,6 @@ pub mod pallet { ValueQuery, DefaultZeroAlpha, >; - /// ========================== /// ==== Staking Counters ==== /// ========================== From 81d2713420627ae729d1e6a7d592b600288abf41 Mon Sep 17 00:00:00 2001 From: open-junius Date: Mon, 20 Oct 2025 21:33:20 +0800 Subject: [PATCH 007/210] fix doc --- pallets/subtensor/src/lib.rs | 42 ++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index e2d12acb5e..0e9eeeef3a 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -976,15 +976,15 @@ pub mod pallet { /// --- ITEM ( min_childkey_take ) pub type MinChildkeyTake = StorageValue<_, u16, ValueQuery, DefaultMinChildKeyTake>; #[pallet::storage] - /// MAP ( hot ) --> cold, Returns the controlling coldkey for a hotkey + /// MAP ( hot ) --> cold | Returns the controlling coldkey for a hotkey pub type Owner = StorageMap<_, Blake2_128Concat, T::AccountId, T::AccountId, ValueQuery, DefaultAccount>; #[pallet::storage] - /// MAP ( hot ) --> take, Returns the hotkey delegation take. And signals that this key is open for delegation + /// MAP ( hot ) --> take | Returns the hotkey delegation take. And signals that this key is open for delegation pub type Delegates = StorageMap<_, Blake2_128Concat, T::AccountId, u16, ValueQuery, DefaultDelegateTake>; #[pallet::storage] - /// DMAP ( hot, netuid ) --> take, Returns the hotkey childkey take for a specific subnet + /// DMAP ( hot, netuid ) --> take | Returns the hotkey childkey take for a specific subnet pub type ChildkeyTake = StorageDoubleMap< _, Blake2_128Concat, @@ -1112,42 +1112,42 @@ pub mod pallet { StorageMap<_, Identity, NetUid, u128, ValueQuery, DefaultZeroU128>; #[pallet::storage] - /// --- MAP ( netuid ) --> tao_in_subnet, Returns the amount of TAO in the subnet. + /// --- MAP ( netuid ) --> tao_in_subnet | Returns the amount of TAO in the subnet. pub type SubnetTAO = StorageMap<_, Identity, NetUid, TaoCurrency, ValueQuery, DefaultZeroTao>; #[pallet::storage] - /// --- MAP ( netuid ) --> tao_in_user_subnet, Returns the amount of TAO in the subnet reserve provided by users as liquidity. + /// --- MAP ( netuid ) --> tao_in_user_subnet | Returns the amount of TAO in the subnet reserve provided by users as liquidity. pub type SubnetTaoProvided = StorageMap<_, Identity, NetUid, TaoCurrency, ValueQuery, DefaultZeroTao>; #[pallet::storage] - /// --- MAP ( netuid ) --> alpha_in_emission, Returns the amount of alph in emission into the pool per block. + /// --- MAP ( netuid ) --> alpha_in_emission | Returns the amount of alph in emission into the pool per block. pub type SubnetAlphaInEmission = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultZeroAlpha>; #[pallet::storage] - /// --- MAP ( netuid ) --> alpha_out_emission, Returns the amount of alpha out emission into the network per block. + /// --- MAP ( netuid ) --> alpha_out_emission | Returns the amount of alpha out emission into the network per block. pub type SubnetAlphaOutEmission = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultZeroAlpha>; #[pallet::storage] - /// --- MAP ( netuid ) --> tao_in_emission, Returns the amount of tao emitted into this subent on the last block. + /// --- MAP ( netuid ) --> tao_in_emission | Returns the amount of tao emitted into this subent on the last block. pub type SubnetTaoInEmission = StorageMap<_, Identity, NetUid, TaoCurrency, ValueQuery, DefaultZeroTao>; #[pallet::storage] - /// --- MAP ( netuid ) --> alpha_supply_in_pool, Returns the amount of alpha in the pool. + /// --- MAP ( netuid ) --> alpha_supply_in_pool | Returns the amount of alpha in the pool. pub type SubnetAlphaIn = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultZeroAlpha>; #[pallet::storage] - /// --- MAP ( netuid ) --> alpha_supply_user_in_pool, Returns the amount of alpha in the pool provided by users as liquidity. + /// --- MAP ( netuid ) --> alpha_supply_user_in_pool | Returns the amount of alpha in the pool provided by users as liquidity. pub type SubnetAlphaInProvided = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultZeroAlpha>; #[pallet::storage] - /// --- MAP ( netuid ) --> alpha_supply_in_subnet, Returns the amount of alpha in the subnet. + /// --- MAP ( netuid ) --> alpha_supply_in_subnet | Returns the amount of alpha in the subnet. pub type SubnetAlphaOut = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultZeroAlpha>; @@ -1157,12 +1157,12 @@ pub mod pallet { StorageMap<_, Blake2_128Concat, T::AccountId, Vec, ValueQuery>; #[pallet::storage] - /// --- MAP ( cold ) --> Vec, Returns the vector of hotkeys controlled by this coldkey. + /// --- MAP ( cold ) --> Vec | Returns the vector of hotkeys controlled by this coldkey. pub type OwnedHotkeys = StorageMap<_, Blake2_128Concat, T::AccountId, Vec, ValueQuery>; #[pallet::storage] - /// --- DMAP ( cold, netuid )--> hot, Returns the hotkey a coldkey will autostake to with mining rewards. + /// --- DMAP ( cold, netuid )--> hot | Returns the hotkey a coldkey will autostake to with mining rewards. pub type AutoStakeDestination = StorageDoubleMap< _, Blake2_128Concat, @@ -1174,7 +1174,7 @@ pub mod pallet { >; #[pallet::storage] - /// --- DMAP ( hot, netuid )--> Vec, Returns a list of coldkeys that are autostaking to a hotkey + /// --- DMAP ( hot, netuid )--> Vec | Returns a list of coldkeys that are autostaking to a hotkey pub type AutoStakeDestinationColdkeys = StorageDoubleMap< _, Blake2_128Concat, @@ -1197,7 +1197,7 @@ pub mod pallet { >; #[pallet::storage] - /// --- DMAP ( hot, netuid ) --> alpha, Returns the total amount of alpha a hotkey owns. + /// --- DMAP ( hot, netuid ) --> alpha | Returns the total amount of alpha a hotkey owns. pub type TotalHotkeyAlpha = StorageDoubleMap< _, Blake2_128Concat, @@ -1209,7 +1209,7 @@ pub mod pallet { DefaultZeroAlpha, >; #[pallet::storage] - /// --- DMAP ( hot, netuid ) --> alpha, Returns the total amount of alpha a hotkey owned in the last epoch. + /// --- DMAP ( hot, netuid ) --> alpha | Returns the total amount of alpha a hotkey owned in the last epoch. pub type TotalHotkeyAlphaLastEpoch = StorageDoubleMap< _, Blake2_128Concat, @@ -1221,7 +1221,7 @@ pub mod pallet { DefaultZeroAlpha, >; #[pallet::storage] - /// DMAP ( hot, netuid ) --> total_alpha_shares, Returns the number of alpha shares for a hotkey on a subnet. + /// DMAP ( hot, netuid ) --> total_alpha_shares | Returns the number of alpha shares for a hotkey on a subnet. pub type TotalHotkeyShares = StorageDoubleMap< _, Blake2_128Concat, @@ -1233,7 +1233,7 @@ pub mod pallet { DefaultSharePoolZero, >; #[pallet::storage] - /// --- NMAP ( hot, cold, netuid ) --> alpha, Returns the alpha shares for a hotkey, coldkey, netuid triplet. + /// --- NMAP ( hot, cold, netuid ) --> alpha | Returns the alpha shares for a hotkey, coldkey, netuid triplet. pub type Alpha = StorageNMap< _, ( @@ -1245,7 +1245,7 @@ pub mod pallet { ValueQuery, >; #[pallet::storage] - /// --- MAP ( netuid ) --> token_symbol, Returns the token symbol for a subnet. + /// --- MAP ( netuid ) --> token_symbol | Returns the token symbol for a subnet. pub type TokenSymbol = StorageMap<_, Identity, NetUid, Vec, ValueQuery, DefaultUnicodeVecU8>; @@ -1765,7 +1765,7 @@ pub mod pallet { /// ==== Axon / Promo Endpoints ===== /// ================================= #[pallet::storage] - /// --- NMAP ( hot, netuid, name ) --> last_block, Returns the last block of a transaction for a given key, netuid, and name. + /// --- NMAP ( hot, netuid, name ) --> last_block | Returns the last block of a transaction for a given key, netuid, and name. pub type TransactionKeyLastBlock = StorageNMap< _, ( @@ -1909,7 +1909,7 @@ pub mod pallet { StorageDoubleMap<_, Twox64Concat, LeaseId, Identity, T::AccountId, U64F64, ValueQuery>; #[pallet::storage] - // --- MAP ( netuid ) --> lease_id | The lease id for a given netuid. + /// --- MAP ( netuid ) --> lease_id | The lease id for a given netuid. pub type SubnetUidToLeaseId = StorageMap<_, Twox64Concat, NetUid, LeaseId, OptionQuery>; From 7ea940ef8e912603628d884fc23581f66b4e518c Mon Sep 17 00:00:00 2001 From: open-junius Date: Mon, 20 Oct 2025 21:53:50 +0800 Subject: [PATCH 008/210] bump version --- pallets/subtensor/src/lib.rs | 2 +- runtime/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 0e9eeeef3a..aa9785ce19 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -899,7 +899,7 @@ pub mod pallet { } #[pallet::storage] - /// Global minimum activity cutoff, not used anymore. + /// Global minimum activity cutoff value pub type MinActivityCutoff = StorageValue<_, u16, ValueQuery, DefaultMinActivityCutoff>; diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index b19b7ddf6f..286c3312b6 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 330, + spec_version: 331, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 78359ccebab02edf76505f25e73e6c49e580d867 Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 21 Oct 2025 18:43:20 +0800 Subject: [PATCH 009/210] update add doc with correct position --- pallets/subtensor/src/lib.rs | 770 ++++++++++++++++++++++------------- 1 file changed, 496 insertions(+), 274 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index aa9785ce19..9d554f9866 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -314,102 +314,116 @@ pub mod pallet { /// ==== Staking + Accounts ==== /// ============================ - #[pallet::type_value] /// Default value for zero. + #[pallet::type_value] pub fn DefaultZeroU64() -> u64 { 0 } + /// Default value for Alpha currency. #[pallet::type_value] pub fn DefaultZeroAlpha() -> AlphaCurrency { AlphaCurrency::ZERO } + /// Default value for Tao currency. #[pallet::type_value] pub fn DefaultZeroTao() -> TaoCurrency { TaoCurrency::ZERO } - #[pallet::type_value] + /// Default value for zero. + #[pallet::type_value] pub fn DefaultZeroU128() -> u128 { 0 } - #[pallet::type_value] + /// Default value for zero. + #[pallet::type_value] pub fn DefaultZeroU16() -> u16 { 0 } - #[pallet::type_value] + /// Default value for false. + #[pallet::type_value] pub fn DefaultFalse() -> bool { false } - #[pallet::type_value] + /// Default value for false. + #[pallet::type_value] pub fn DefaultTrue() -> bool { true } - #[pallet::type_value] + /// Total Rao in circulation. + #[pallet::type_value] pub fn TotalSupply() -> u64 { 21_000_000_000_000_000 } - #[pallet::type_value] + /// Default Delegate Take. + #[pallet::type_value] pub fn DefaultDelegateTake() -> u16 { T::InitialDefaultDelegateTake::get() } - #[pallet::type_value] /// Default childkey take. + #[pallet::type_value] pub fn DefaultChildKeyTake() -> u16 { T::InitialDefaultChildKeyTake::get() } - #[pallet::type_value] + /// Default minimum delegate take. + #[pallet::type_value] pub fn DefaultMinDelegateTake() -> u16 { T::InitialMinDelegateTake::get() } - #[pallet::type_value] /// Default minimum childkey take. + #[pallet::type_value] pub fn DefaultMinChildKeyTake() -> u16 { T::InitialMinChildKeyTake::get() } - #[pallet::type_value] /// Default maximum childkey take. + #[pallet::type_value] pub fn DefaultMaxChildKeyTake() -> u16 { T::InitialMaxChildKeyTake::get() } - #[pallet::type_value] /// Default account take. + #[pallet::type_value] pub fn DefaultAccountTake() -> u64 { 0 } - #[pallet::type_value] + /// Default value for global weight. + #[pallet::type_value] pub fn DefaultTaoWeight() -> u64 { T::InitialTaoWeight::get() } - #[pallet::type_value] + /// Default emission per block. + #[pallet::type_value] pub fn DefaultBlockEmission() -> u64 { 1_000_000_000 } - #[pallet::type_value] + /// Default allowed delegation. + #[pallet::type_value] pub fn DefaultAllowsDelegation() -> bool { false } - #[pallet::type_value] + /// Default total issuance. + #[pallet::type_value] pub fn DefaultTotalIssuance() -> TaoCurrency { T::InitialIssuance::get().into() } - #[pallet::type_value] + /// Default account, derived from zero trailing bytes. + #[pallet::type_value] pub fn DefaultAccount() -> T::AccountId { #[allow(clippy::expect_used)] T::AccountId::decode(&mut TrailingZeroInput::zeroes()) @@ -418,329 +432,391 @@ pub mod pallet { // pub fn DefaultStakeInterval() -> u64 { // 360 // } (DEPRECATED) - #[pallet::type_value] + /// Default account linkage + #[pallet::type_value] pub fn DefaultAccountLinkage() -> Vec<(u64, T::AccountId)> { vec![] } - #[pallet::type_value] + /// Default pending childkeys + #[pallet::type_value] pub fn DefaultPendingChildkeys() -> (Vec<(u64, T::AccountId)>, u64) { (vec![], 0) } - #[pallet::type_value] + /// Default account linkage + #[pallet::type_value] pub fn DefaultProportion() -> u64 { 0 } - #[pallet::type_value] + /// Default accumulated emission for a hotkey + #[pallet::type_value] pub fn DefaultAccumulatedEmission() -> u64 { 0 } - #[pallet::type_value] + /// Default last adjustment block. + #[pallet::type_value] pub fn DefaultLastAdjustmentBlock() -> u64 { 0 } - #[pallet::type_value] + /// Default last adjustment block. + #[pallet::type_value] pub fn DefaultRegistrationsThisBlock() -> u16 { 0 } - #[pallet::type_value] + /// Default EMA price halving blocks + #[pallet::type_value] pub fn DefaultEMAPriceMovingBlocks() -> u64 { T::InitialEmaPriceHalvingPeriod::get() } - #[pallet::type_value] + /// Default registrations this block. + #[pallet::type_value] pub fn DefaultBurn() -> TaoCurrency { T::InitialBurn::get().into() } - #[pallet::type_value] /// Default burn token. + #[pallet::type_value] pub fn DefaultMinBurn() -> TaoCurrency { T::InitialMinBurn::get().into() } - #[pallet::type_value] + /// Default min burn token. + #[pallet::type_value] pub fn DefaultMaxBurn() -> TaoCurrency { T::InitialMaxBurn::get().into() } - #[pallet::type_value] + /// Default max burn token. + #[pallet::type_value] pub fn DefaultDifficulty() -> u64 { T::InitialDifficulty::get() } - #[pallet::type_value] + /// Default difficulty value. + #[pallet::type_value] pub fn DefaultMinDifficulty() -> u64 { T::InitialMinDifficulty::get() } - #[pallet::type_value] + /// Default min difficulty value. + #[pallet::type_value] pub fn DefaultMaxDifficulty() -> u64 { T::InitialMaxDifficulty::get() } - #[pallet::type_value] + /// Default max difficulty value. + #[pallet::type_value] pub fn DefaultMaxRegistrationsPerBlock() -> u16 { T::InitialMaxRegistrationsPerBlock::get() } - #[pallet::type_value] + /// Default max registrations per block. + #[pallet::type_value] pub fn DefaultRAORecycledForRegistration() -> TaoCurrency { T::InitialRAORecycledForRegistration::get().into() } - #[pallet::type_value] + /// Default number of networks. + #[pallet::type_value] pub fn DefaultN() -> u16 { 0 } - #[pallet::type_value] + /// Default value for hotkeys. + #[pallet::type_value] pub fn DefaultHotkeys() -> Vec { vec![] } - #[pallet::type_value] + /// Default value if network is added. + #[pallet::type_value] pub fn DefaultNeworksAdded() -> bool { false } - #[pallet::type_value] + /// Default value for network member. + #[pallet::type_value] pub fn DefaultIsNetworkMember() -> bool { false } - #[pallet::type_value] + /// Default value for registration allowed. + #[pallet::type_value] pub fn DefaultRegistrationAllowed() -> bool { true } - #[pallet::type_value] + /// Default value for network registered at. + #[pallet::type_value] pub fn DefaultNetworkRegisteredAt() -> u64 { 0 } - #[pallet::type_value] + /// Default value for network immunity period. + #[pallet::type_value] pub fn DefaultNetworkImmunityPeriod() -> u64 { T::InitialNetworkImmunityPeriod::get() } - #[pallet::type_value] + /// Default value for network min lock cost. + #[pallet::type_value] pub fn DefaultNetworkMinLockCost() -> TaoCurrency { T::InitialNetworkMinLockCost::get().into() } - #[pallet::type_value] + /// Default value for network lock reduction interval. + #[pallet::type_value] pub fn DefaultNetworkLockReductionInterval() -> u64 { T::InitialNetworkLockReductionInterval::get() } - #[pallet::type_value] + /// Default value for subnet owner cut. + #[pallet::type_value] pub fn DefaultSubnetOwnerCut() -> u16 { T::InitialSubnetOwnerCut::get() } - #[pallet::type_value] + /// Default value for recycle or burn. + #[pallet::type_value] pub fn DefaultRecycleOrBurn() -> RecycleOrBurnEnum { RecycleOrBurnEnum::Burn // default to burn } - #[pallet::type_value] + /// Default value for network rate limit. + #[pallet::type_value] pub fn DefaultNetworkRateLimit() -> u64 { if cfg!(feature = "pow-faucet") { return 0; } T::InitialNetworkRateLimit::get() } - #[pallet::type_value] + /// Default value for network rate limit. + #[pallet::type_value] pub fn DefaultNetworkRegistrationStartBlock() -> u64 { 0 } - #[pallet::type_value] + /// Default value for weights version key rate limit. /// In units of tempos. + #[pallet::type_value] pub fn DefaultWeightsVersionKeyRateLimit() -> u64 { 5 // 5 tempos } - #[pallet::type_value] + /// Default value for pending emission. + #[pallet::type_value] pub fn DefaultPendingEmission() -> AlphaCurrency { 0.into() } - #[pallet::type_value] + /// Default value for blocks since last step. + #[pallet::type_value] pub fn DefaultBlocksSinceLastStep() -> u64 { 0 } - #[pallet::type_value] + /// Default value for last mechanism step block. + #[pallet::type_value] pub fn DefaultLastMechanismStepBlock() -> u64 { 0 } - #[pallet::type_value] + /// Default value for subnet owner. + #[pallet::type_value] pub fn DefaultSubnetOwner() -> T::AccountId { #[allow(clippy::expect_used)] T::AccountId::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes()) .expect("trailing zeroes always produce a valid account ID; qed") } - #[pallet::type_value] + /// Default value for subnet locked. + #[pallet::type_value] pub fn DefaultSubnetLocked() -> u64 { 0 } - #[pallet::type_value] + /// Default value for network tempo + #[pallet::type_value] pub fn DefaultTempo() -> u16 { T::InitialTempo::get() } - #[pallet::type_value] + /// Default value for weights set rate limit. + #[pallet::type_value] pub fn DefaultWeightsSetRateLimit() -> u64 { 100 } - #[pallet::type_value] + /// Default block number at registration. + #[pallet::type_value] pub fn DefaultBlockAtRegistration() -> u64 { 0 } - #[pallet::type_value] + /// Default value for rho parameter. + #[pallet::type_value] pub fn DefaultRho() -> u16 { T::InitialRho::get() } - #[pallet::type_value] + /// Default value for alpha sigmoid steepness. + #[pallet::type_value] pub fn DefaultAlphaSigmoidSteepness() -> i16 { T::InitialAlphaSigmoidSteepness::get() } - #[pallet::type_value] + /// Default value for kappa parameter. + #[pallet::type_value] pub fn DefaultKappa() -> u16 { T::InitialKappa::get() } - #[pallet::type_value] + /// Default value for network min allowed UIDs. + #[pallet::type_value] pub fn DefaultMinAllowedUids() -> u16 { T::InitialMinAllowedUids::get() } - #[pallet::type_value] + /// Default maximum allowed UIDs. + #[pallet::type_value] pub fn DefaultMaxAllowedUids() -> u16 { T::InitialMaxAllowedUids::get() } - #[pallet::type_value] + /// -- Rate limit for set max allowed UIDs + #[pallet::type_value] pub fn MaxUidsTrimmingRateLimit() -> u64 { prod_or_fast!(30 * 7200, 1) } - #[pallet::type_value] + /// Default immunity period. + #[pallet::type_value] pub fn DefaultImmunityPeriod() -> u16 { T::InitialImmunityPeriod::get() } - #[pallet::type_value] + /// Default activity cutoff. + #[pallet::type_value] pub fn DefaultActivityCutoff() -> u16 { T::InitialActivityCutoff::get() } - #[pallet::type_value] + /// Default weights version key. + #[pallet::type_value] pub fn DefaultWeightsVersionKey() -> u64 { T::InitialWeightsVersionKey::get() } - #[pallet::type_value] + /// Default minimum allowed weights. + #[pallet::type_value] pub fn DefaultMinAllowedWeights() -> u16 { T::InitialMinAllowedWeights::get() } - #[pallet::type_value] /// Default maximum allowed validators. + #[pallet::type_value] pub fn DefaultMaxAllowedValidators() -> u16 { T::InitialMaxAllowedValidators::get() } - #[pallet::type_value] + /// Default adjustment interval. + #[pallet::type_value] pub fn DefaultAdjustmentInterval() -> u16 { T::InitialAdjustmentInterval::get() } - #[pallet::type_value] + /// Default bonds moving average. + #[pallet::type_value] pub fn DefaultBondsMovingAverage() -> u64 { T::InitialBondsMovingAverage::get() } + /// Default bonds penalty. #[pallet::type_value] pub fn DefaultBondsPenalty() -> u16 { T::InitialBondsPenalty::get() } + /// Default value for bonds reset - will not reset bonds #[pallet::type_value] pub fn DefaultBondsResetOn() -> bool { T::InitialBondsResetOn::get() } + /// Default validator prune length. #[pallet::type_value] pub fn DefaultValidatorPruneLen() -> u64 { T::InitialValidatorPruneLen::get() } - #[pallet::type_value] + /// Default scaling law power. + #[pallet::type_value] pub fn DefaultScalingLawPower() -> u16 { T::InitialScalingLawPower::get() } - #[pallet::type_value] + /// Default target registrations per interval. + #[pallet::type_value] pub fn DefaultTargetRegistrationsPerInterval() -> u16 { T::InitialTargetRegistrationsPerInterval::get() } - #[pallet::type_value] + /// Default adjustment alpha. + #[pallet::type_value] pub fn DefaultAdjustmentAlpha() -> u64 { T::InitialAdjustmentAlpha::get() } - #[pallet::type_value] + /// Default minimum stake for weights. + #[pallet::type_value] pub fn DefaultStakeThreshold() -> u64 { 0 } - #[pallet::type_value] + /// Default Reveal Period Epochs + #[pallet::type_value] pub fn DefaultRevealPeriodEpochs() -> u64 { 1 } - #[pallet::type_value] + /// Value definition for vector of u16. + #[pallet::type_value] pub fn EmptyU16Vec() -> Vec { vec![] } - #[pallet::type_value] + /// Value definition for vector of u64. + #[pallet::type_value] pub fn EmptyU64Vec() -> Vec { vec![] } - #[pallet::type_value] + /// Value definition for vector of bool. + #[pallet::type_value] pub fn EmptyBoolVec() -> Vec { vec![] } - #[pallet::type_value] + /// Value definition for bonds with type vector of (u16, u16). + #[pallet::type_value] pub fn DefaultBonds() -> Vec<(u16, u16)> { vec![] } - #[pallet::type_value] + /// Value definition for weights with vector of (u16, u16). + #[pallet::type_value] pub fn DefaultWeights() -> Vec<(u16, u16)> { vec![] } - #[pallet::type_value] + /// Default value for key with type T::AccountId derived from trailing zeroes. + #[pallet::type_value] pub fn DefaultKey() -> T::AccountId { #[allow(clippy::expect_used)] T::AccountId::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes()) @@ -749,118 +825,131 @@ pub mod pallet { // pub fn DefaultHotkeyEmissionTempo() -> u64 { // T::InitialHotkeyEmissionTempo::get() // } (DEPRECATED) - #[pallet::type_value] + /// Default value for rate limiting + #[pallet::type_value] pub fn DefaultTxRateLimit() -> u64 { T::InitialTxRateLimit::get() } - #[pallet::type_value] + /// Default value for delegate take rate limiting + #[pallet::type_value] pub fn DefaultTxDelegateTakeRateLimit() -> u64 { T::InitialTxDelegateTakeRateLimit::get() } - #[pallet::type_value] + /// Default value for chidlkey take rate limiting + #[pallet::type_value] pub fn DefaultTxChildKeyTakeRateLimit() -> u64 { T::InitialTxChildKeyTakeRateLimit::get() } - #[pallet::type_value] + /// Default value for last extrinsic block. + #[pallet::type_value] pub fn DefaultLastTxBlock() -> u64 { 0 } - #[pallet::type_value] + /// Default value for serving rate limit. + #[pallet::type_value] pub fn DefaultServingRateLimit() -> u64 { T::InitialServingRateLimit::get() } - #[pallet::type_value] + /// Default value for weight commit/reveal enabled. + #[pallet::type_value] pub fn DefaultCommitRevealWeightsEnabled() -> bool { true } - #[pallet::type_value] + /// Default value for weight commit/reveal version. + #[pallet::type_value] pub fn DefaultCommitRevealWeightsVersion() -> u16 { 4 } - #[pallet::type_value] + /// -- ITEM (switches liquid alpha on) + #[pallet::type_value] pub fn DefaultLiquidAlpha() -> bool { false } - #[pallet::type_value] + /// -- ITEM (switches liquid alpha on) + #[pallet::type_value] pub fn DefaultYuma3() -> bool { false } - #[pallet::type_value] + /// (alpha_low: 0.7, alpha_high: 0.9) + #[pallet::type_value] pub fn DefaultAlphaValues() -> (u16, u16) { (45875, 58982) } - #[pallet::type_value] + /// Default value for coldkey swap schedule duration + #[pallet::type_value] pub fn DefaultColdkeySwapScheduleDuration() -> BlockNumberFor { T::InitialColdkeySwapScheduleDuration::get() } - #[pallet::type_value] /// Default value for coldkey swap reschedule duration + #[pallet::type_value] pub fn DefaultColdkeySwapRescheduleDuration() -> BlockNumberFor { T::InitialColdkeySwapRescheduleDuration::get() } - #[pallet::type_value] /// Default value for applying pending items (e.g. childkeys). + #[pallet::type_value] pub fn DefaultPendingCooldown() -> u64 { prod_or_fast!(7_200, 15) } - #[pallet::type_value] /// Default minimum stake. + #[pallet::type_value] pub fn DefaultMinStake() -> TaoCurrency { 2_000_000.into() } - #[pallet::type_value] /// Default unicode vector for tau symbol. + #[pallet::type_value] pub fn DefaultUnicodeVecU8() -> Vec { b"\xF0\x9D\x9C\x8F".to_vec() // Unicode for tau (𝜏) } - #[pallet::type_value] /// Default value for dissolve network schedule duration + #[pallet::type_value] pub fn DefaultDissolveNetworkScheduleDuration() -> BlockNumberFor { T::InitialDissolveNetworkScheduleDuration::get() } - #[pallet::type_value] /// Default moving alpha for the moving price. + #[pallet::type_value] pub fn DefaultMovingAlpha() -> I96F32 { // Moving average take 30 days to reach 50% of the price // and 3.5 months to reach 90%. I96F32::saturating_from_num(0.000003) } - #[pallet::type_value] + /// Default subnet moving price. + #[pallet::type_value] pub fn DefaultMovingPrice() -> I96F32 { I96F32::saturating_from_num(0.0) } - #[pallet::type_value] + /// Default value for Share Pool variables + #[pallet::type_value] pub fn DefaultSharePoolZero() -> U64F64 { U64F64::saturating_from_num(0) } - #[pallet::type_value] /// Default value for minimum activity cutoff + #[pallet::type_value] pub fn DefaultMinActivityCutoff() -> u16 { 360 } - #[pallet::type_value] /// Default value for coldkey swap scheduled + #[pallet::type_value] pub fn DefaultColdkeySwapScheduled() -> (BlockNumberFor, T::AccountId) { #[allow(clippy::expect_used)] let default_account = T::AccountId::decode(&mut TrailingZeroInput::zeroes()) @@ -868,68 +957,68 @@ pub mod pallet { (BlockNumberFor::::from(0_u32), default_account) } - #[pallet::type_value] /// Default value for setting subnet owner hotkey rate limit + #[pallet::type_value] pub fn DefaultSetSNOwnerHotkeyRateLimit() -> u64 { 50400 } - #[pallet::type_value] /// Default number of terminal blocks in a tempo during which admin operations are prohibited + #[pallet::type_value] pub fn DefaultAdminFreezeWindow() -> u16 { 10 } - #[pallet::type_value] /// Default number of tempos for owner hyperparameter update rate limit + #[pallet::type_value] pub fn DefaultOwnerHyperparamRateLimit() -> u16 { 2 } - #[pallet::type_value] /// Default value for ck burn, 18%. + #[pallet::type_value] pub fn DefaultCKBurn() -> u64 { 0 } - #[pallet::type_value] /// Default value for subnet limit. + #[pallet::type_value] pub fn DefaultSubnetLimit() -> u16 { 128 } - #[pallet::storage] /// Global minimum activity cutoff value + #[pallet::storage] pub type MinActivityCutoff = StorageValue<_, u16, ValueQuery, DefaultMinActivityCutoff>; - #[pallet::storage] /// Global window (in blocks) at the end of each tempo where admin ops are disallowed + #[pallet::storage] pub type AdminFreezeWindow = StorageValue<_, u16, ValueQuery, DefaultAdminFreezeWindow>; - #[pallet::storage] /// Global number of epochs used to rate limit subnet owner hyperparameter updates + #[pallet::storage] pub type OwnerHyperparamRateLimit = StorageValue<_, u16, ValueQuery, DefaultOwnerHyperparamRateLimit>; - #[pallet::storage] /// Duration of coldkey swap schedule before execution + #[pallet::storage] pub type ColdkeySwapScheduleDuration = StorageValue<_, BlockNumberFor, ValueQuery, DefaultColdkeySwapScheduleDuration>; - #[pallet::storage] /// Duration of coldkey swap reschedule before execution + #[pallet::storage] pub type ColdkeySwapRescheduleDuration = StorageValue<_, BlockNumberFor, ValueQuery, DefaultColdkeySwapRescheduleDuration>; - #[pallet::storage] /// Duration of dissolve network schedule before execution + #[pallet::storage] pub type DissolveNetworkScheduleDuration = StorageValue<_, BlockNumberFor, ValueQuery, DefaultDissolveNetworkScheduleDuration>; - #[pallet::storage] /// --- DMap ( netuid, coldkey ) --> blocknumber | last hotkey swap on network. + #[pallet::storage] pub type LastHotkeySwapOnNetuid = StorageDoubleMap< _, Identity, @@ -941,8 +1030,8 @@ pub mod pallet { DefaultZeroU64, >; - #[pallet::storage] /// Ensures unique IDs for StakeJobs storage map + #[pallet::storage] pub type NextStakeJobId = StorageValue<_, u64, ValueQuery, DefaultZeroU64>; /// ============================ @@ -957,34 +1046,43 @@ pub mod pallet { /// /// Eventually, Bittensor should migrate to using Holds afterwhich time we will not require this /// separate accounting. - #[pallet::storage] + /// --- ITEM --> Global weight - pub type TaoWeight = StorageValue<_, u64, ValueQuery, DefaultTaoWeight>; #[pallet::storage] + pub type TaoWeight = StorageValue<_, u64, ValueQuery, DefaultTaoWeight>; + /// --- ITEM --> CK burn - pub type CKBurn = StorageValue<_, u64, ValueQuery, DefaultCKBurn>; #[pallet::storage] + pub type CKBurn = StorageValue<_, u64, ValueQuery, DefaultCKBurn>; + /// --- ITEM ( default_delegate_take ) - pub type MaxDelegateTake = StorageValue<_, u16, ValueQuery, DefaultDelegateTake>; #[pallet::storage] + pub type MaxDelegateTake = StorageValue<_, u16, ValueQuery, DefaultDelegateTake>; + /// --- ITEM ( min_delegate_take ) - pub type MinDelegateTake = StorageValue<_, u16, ValueQuery, DefaultMinDelegateTake>; #[pallet::storage] + pub type MinDelegateTake = StorageValue<_, u16, ValueQuery, DefaultMinDelegateTake>; + /// --- ITEM ( default_childkey_take ) - pub type MaxChildkeyTake = StorageValue<_, u16, ValueQuery, DefaultMaxChildKeyTake>; #[pallet::storage] + pub type MaxChildkeyTake = StorageValue<_, u16, ValueQuery, DefaultMaxChildKeyTake>; + /// --- ITEM ( min_childkey_take ) - pub type MinChildkeyTake = StorageValue<_, u16, ValueQuery, DefaultMinChildKeyTake>; #[pallet::storage] + pub type MinChildkeyTake = StorageValue<_, u16, ValueQuery, DefaultMinChildKeyTake>; + /// MAP ( hot ) --> cold | Returns the controlling coldkey for a hotkey + #[pallet::storage] pub type Owner = StorageMap<_, Blake2_128Concat, T::AccountId, T::AccountId, ValueQuery, DefaultAccount>; - #[pallet::storage] + /// MAP ( hot ) --> take | Returns the hotkey delegation take. And signals that this key is open for delegation + #[pallet::storage] pub type Delegates = StorageMap<_, Blake2_128Concat, T::AccountId, u16, ValueQuery, DefaultDelegateTake>; - #[pallet::storage] + /// DMAP ( hot, netuid ) --> take | Returns the hotkey childkey take for a specific subnet + #[pallet::storage] pub type ChildkeyTake = StorageDoubleMap< _, Blake2_128Concat, @@ -994,8 +1092,9 @@ pub mod pallet { u16, // Value: take ValueQuery, >; - #[pallet::storage] + /// DMAP ( netuid, parent ) --> (Vec<(proportion,child)>, cool_down_block) + #[pallet::storage] pub type PendingChildKeys = StorageDoubleMap< _, Identity, @@ -1006,8 +1105,9 @@ pub mod pallet { ValueQuery, DefaultPendingChildkeys, >; - #[pallet::storage] + /// DMAP ( parent, netuid ) --> Vec<(proportion,child)> + #[pallet::storage] pub type ChildKeys = StorageDoubleMap< _, Blake2_128Concat, @@ -1018,8 +1118,9 @@ pub mod pallet { ValueQuery, DefaultAccountLinkage, >; - #[pallet::storage] + /// DMAP ( child, netuid ) --> Vec<(proportion,parent)> + #[pallet::storage] pub type ParentKeys = StorageDoubleMap< _, Blake2_128Concat, @@ -1030,8 +1131,9 @@ pub mod pallet { ValueQuery, DefaultAccountLinkage, >; - #[pallet::storage] + /// --- DMAP ( netuid, hotkey ) --> u64 | Last total dividend this hotkey got on tempo. + #[pallet::storage] pub type AlphaDividendsPerSubnet = StorageDoubleMap< _, Identity, @@ -1042,8 +1144,9 @@ pub mod pallet { ValueQuery, DefaultZeroAlpha, >; - #[pallet::storage] + /// --- DMAP ( netuid, hotkey ) --> u64 | Last total root dividend paid to this hotkey on this subnet. + #[pallet::storage] pub type TaoDividendsPerSubnet = StorageDoubleMap< _, Identity, @@ -1058,11 +1161,13 @@ pub mod pallet { /// ================== /// ==== Coinbase ==== /// ================== - #[pallet::storage] + /// --- ITEM ( global_block_emission ) - pub type BlockEmission = StorageValue<_, u64, ValueQuery, DefaultBlockEmission>; #[pallet::storage] + pub type BlockEmission = StorageValue<_, u64, ValueQuery, DefaultBlockEmission>; + /// --- DMap ( hot, netuid ) --> emission | last hotkey emission on network. + #[pallet::storage] pub type LastHotkeyEmissionOnNetuid = StorageDoubleMap< _, Blake2_128Concat, @@ -1085,84 +1190,85 @@ pub mod pallet { /// /// Eventually, Bittensor should migrate to using Holds afterwhich time we will not require this /// separate accounting. - #[pallet::storage] + /// --- ITEM ( maximum_number_of_networks ) + #[pallet::storage] pub type SubnetLimit = StorageValue<_, u16, ValueQuery, DefaultSubnetLimit>; - #[pallet::storage] /// --- ITEM ( total_issuance ) + #[pallet::storage] pub type TotalIssuance = StorageValue<_, TaoCurrency, ValueQuery, DefaultTotalIssuance>; - #[pallet::storage] /// --- ITEM ( total_stake ) + #[pallet::storage] pub type TotalStake = StorageValue<_, TaoCurrency, ValueQuery, DefaultZeroTao>; - #[pallet::storage] /// --- ITEM ( moving_alpha ) -- subnet moving alpha. + #[pallet::storage] pub type SubnetMovingAlpha = StorageValue<_, I96F32, ValueQuery, DefaultMovingAlpha>; - #[pallet::storage] /// --- MAP ( netuid ) --> moving_price | The subnet moving price. + #[pallet::storage] pub type SubnetMovingPrice = StorageMap<_, Identity, NetUid, I96F32, ValueQuery, DefaultMovingPrice>; - #[pallet::storage] /// --- MAP ( netuid ) --> total_volume | The total amount of TAO bought and sold since the start of the network. + #[pallet::storage] pub type SubnetVolume = StorageMap<_, Identity, NetUid, u128, ValueQuery, DefaultZeroU128>; - #[pallet::storage] /// --- MAP ( netuid ) --> tao_in_subnet | Returns the amount of TAO in the subnet. + #[pallet::storage] pub type SubnetTAO = StorageMap<_, Identity, NetUid, TaoCurrency, ValueQuery, DefaultZeroTao>; - #[pallet::storage] /// --- MAP ( netuid ) --> tao_in_user_subnet | Returns the amount of TAO in the subnet reserve provided by users as liquidity. + #[pallet::storage] pub type SubnetTaoProvided = StorageMap<_, Identity, NetUid, TaoCurrency, ValueQuery, DefaultZeroTao>; - #[pallet::storage] /// --- MAP ( netuid ) --> alpha_in_emission | Returns the amount of alph in emission into the pool per block. + #[pallet::storage] pub type SubnetAlphaInEmission = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultZeroAlpha>; - #[pallet::storage] /// --- MAP ( netuid ) --> alpha_out_emission | Returns the amount of alpha out emission into the network per block. + #[pallet::storage] pub type SubnetAlphaOutEmission = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultZeroAlpha>; - #[pallet::storage] /// --- MAP ( netuid ) --> tao_in_emission | Returns the amount of tao emitted into this subent on the last block. + #[pallet::storage] pub type SubnetTaoInEmission = StorageMap<_, Identity, NetUid, TaoCurrency, ValueQuery, DefaultZeroTao>; - #[pallet::storage] /// --- MAP ( netuid ) --> alpha_supply_in_pool | Returns the amount of alpha in the pool. + #[pallet::storage] pub type SubnetAlphaIn = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultZeroAlpha>; - #[pallet::storage] /// --- MAP ( netuid ) --> alpha_supply_user_in_pool | Returns the amount of alpha in the pool provided by users as liquidity. + #[pallet::storage] pub type SubnetAlphaInProvided = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultZeroAlpha>; - #[pallet::storage] /// --- MAP ( netuid ) --> alpha_supply_in_subnet | Returns the amount of alpha in the subnet. + #[pallet::storage] pub type SubnetAlphaOut = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultZeroAlpha>; - #[pallet::storage] /// --- MAP ( cold ) --> Vec | Maps coldkey to hotkeys that stake to it + #[pallet::storage] pub type StakingHotkeys = StorageMap<_, Blake2_128Concat, T::AccountId, Vec, ValueQuery>; - #[pallet::storage] /// --- MAP ( cold ) --> Vec | Returns the vector of hotkeys controlled by this coldkey. + #[pallet::storage] pub type OwnedHotkeys = StorageMap<_, Blake2_128Concat, T::AccountId, Vec, ValueQuery>; - #[pallet::storage] /// --- DMAP ( cold, netuid )--> hot | Returns the hotkey a coldkey will autostake to with mining rewards. + #[pallet::storage] pub type AutoStakeDestination = StorageDoubleMap< _, Blake2_128Concat, @@ -1173,8 +1279,8 @@ pub mod pallet { OptionQuery, >; - #[pallet::storage] /// --- DMAP ( hot, netuid )--> Vec | Returns a list of coldkeys that are autostaking to a hotkey + #[pallet::storage] pub type AutoStakeDestinationColdkeys = StorageDoubleMap< _, Blake2_128Concat, @@ -1185,8 +1291,8 @@ pub mod pallet { ValueQuery, >; - #[pallet::storage] /// --- DMAP ( cold ) --> (block_expected, new_coldkey), Maps coldkey to the block to swap at and new coldkey. + #[pallet::storage] pub type ColdkeySwapScheduled = StorageMap< _, Blake2_128Concat, @@ -1196,8 +1302,8 @@ pub mod pallet { DefaultColdkeySwapScheduled, >; - #[pallet::storage] /// --- DMAP ( hot, netuid ) --> alpha | Returns the total amount of alpha a hotkey owns. + #[pallet::storage] pub type TotalHotkeyAlpha = StorageDoubleMap< _, Blake2_128Concat, @@ -1208,8 +1314,9 @@ pub mod pallet { ValueQuery, DefaultZeroAlpha, >; - #[pallet::storage] + /// --- DMAP ( hot, netuid ) --> alpha | Returns the total amount of alpha a hotkey owned in the last epoch. + #[pallet::storage] pub type TotalHotkeyAlphaLastEpoch = StorageDoubleMap< _, Blake2_128Concat, @@ -1220,8 +1327,9 @@ pub mod pallet { ValueQuery, DefaultZeroAlpha, >; - #[pallet::storage] + /// DMAP ( hot, netuid ) --> total_alpha_shares | Returns the number of alpha shares for a hotkey on a subnet. + #[pallet::storage] pub type TotalHotkeyShares = StorageDoubleMap< _, Blake2_128Concat, @@ -1232,8 +1340,9 @@ pub mod pallet { ValueQuery, DefaultSharePoolZero, >; - #[pallet::storage] + /// --- NMAP ( hot, cold, netuid ) --> alpha | Returns the alpha shares for a hotkey, coldkey, netuid triplet. + #[pallet::storage] pub type Alpha = StorageNMap< _, ( @@ -1244,51 +1353,63 @@ pub mod pallet { U64F64, // Shares ValueQuery, >; - #[pallet::storage] + /// --- MAP ( netuid ) --> token_symbol | Returns the token symbol for a subnet. + #[pallet::storage] pub type TokenSymbol = StorageMap<_, Identity, NetUid, Vec, ValueQuery, DefaultUnicodeVecU8>; /// ============================ /// ==== Global Parameters ===== /// ============================ - #[pallet::storage] + /// --- StorageItem Global Used Work. - pub type UsedWork = StorageMap<_, Identity, Vec, u64, ValueQuery>; #[pallet::storage] + pub type UsedWork = StorageMap<_, Identity, Vec, u64, ValueQuery>; + /// --- ITEM( global_max_registrations_per_block ) + #[pallet::storage] pub type MaxRegistrationsPerBlock = StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultMaxRegistrationsPerBlock>; - #[pallet::storage] + /// --- ITEM( total_number_of_existing_networks ) - pub type TotalNetworks = StorageValue<_, u16, ValueQuery>; #[pallet::storage] + pub type TotalNetworks = StorageValue<_, u16, ValueQuery>; + /// ITEM( network_immunity_period ) + #[pallet::storage] pub type NetworkImmunityPeriod = StorageValue<_, u64, ValueQuery, DefaultNetworkImmunityPeriod>; - #[pallet::storage] + /// ITEM( min_network_lock_cost ) + #[pallet::storage] pub type NetworkMinLockCost = StorageValue<_, TaoCurrency, ValueQuery, DefaultNetworkMinLockCost>; - #[pallet::storage] + /// ITEM( last_network_lock_cost ) + #[pallet::storage] pub type NetworkLastLockCost = StorageValue<_, TaoCurrency, ValueQuery, DefaultNetworkMinLockCost>; - #[pallet::storage] + /// ITEM( network_lock_reduction_interval ) + #[pallet::storage] pub type NetworkLockReductionInterval = StorageValue<_, u64, ValueQuery, DefaultNetworkLockReductionInterval>; - #[pallet::storage] + /// ITEM( subnet_owner_cut ) - pub type SubnetOwnerCut = StorageValue<_, u16, ValueQuery, DefaultSubnetOwnerCut>; #[pallet::storage] + pub type SubnetOwnerCut = StorageValue<_, u16, ValueQuery, DefaultSubnetOwnerCut>; + /// ITEM( network_rate_limit ) - pub type NetworkRateLimit = StorageValue<_, u64, ValueQuery, DefaultNetworkRateLimit>; #[pallet::storage] + pub type NetworkRateLimit = StorageValue<_, u64, ValueQuery, DefaultNetworkRateLimit>; + /// --- ITEM( nominator_min_required_stake ) --- Factor of DefaultMinStake in per-mill format. - pub type NominatorMinRequiredStake = StorageValue<_, u64, ValueQuery, DefaultZeroU64>; #[pallet::storage] + pub type NominatorMinRequiredStake = StorageValue<_, u64, ValueQuery, DefaultZeroU64>; + /// ITEM( weights_version_key_rate_limit ) --- Rate limit in tempos. + #[pallet::storage] pub type WeightsVersionKeyRateLimit = StorageValue<_, u64, ValueQuery, DefaultWeightsVersionKeyRateLimit>; @@ -1296,54 +1417,63 @@ pub mod pallet { /// ==== Rate Limiting ===== /// ============================ - #[pallet::storage] /// --- MAP ( RateLimitKey ) --> Block number in which the last rate limited operation occured + #[pallet::storage] pub type LastRateLimitedBlock = StorageMap<_, Identity, RateLimitKey, u64, ValueQuery, DefaultZeroU64>; /// ============================ /// ==== Subnet Locks ===== /// ============================ - #[pallet::storage] + /// --- MAP ( netuid ) --> transfer_toggle + #[pallet::storage] pub type TransferToggle = StorageMap<_, Identity, NetUid, bool, ValueQuery, DefaultTrue>; - #[pallet::storage] + /// --- MAP ( netuid ) --> total_subnet_locked + #[pallet::storage] pub type SubnetLocked = StorageMap<_, Identity, NetUid, TaoCurrency, ValueQuery, DefaultZeroTao>; - #[pallet::storage] + /// --- MAP ( netuid ) --> largest_locked + #[pallet::storage] pub type LargestLocked = StorageMap<_, Identity, NetUid, u64, ValueQuery, DefaultZeroU64>; /// ================= /// ==== Tempos ===== /// ================= - #[pallet::storage] + /// --- MAP ( netuid ) --> tempo + #[pallet::storage] pub type Tempo = StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultTempo>; /// ============================ /// ==== Subnet Parameters ===== /// ============================ + /// --- MAP ( netuid ) --> block number of first emission #[pallet::storage] pub type FirstEmissionBlockNumber = StorageMap<_, Identity, NetUid, u64, OptionQuery>; + /// --- MAP ( netuid ) --> subnet mechanism #[pallet::storage] pub type SubnetMechanism = StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultZeroU16>; - #[pallet::storage] + /// --- MAP ( netuid ) --> subnetwork_n (Number of UIDs in the network). - pub type SubnetworkN = StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultN>; #[pallet::storage] + pub type SubnetworkN = StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultN>; + /// --- MAP ( netuid ) --> network_is_added + #[pallet::storage] pub type NetworksAdded = StorageMap<_, Identity, NetUid, bool, ValueQuery, DefaultNeworksAdded>; - #[pallet::storage] + /// --- DMAP ( hotkey, netuid ) --> bool + #[pallet::storage] pub type IsNetworkMember = StorageDoubleMap< _, Blake2_128Concat, @@ -1354,94 +1484,117 @@ pub mod pallet { ValueQuery, DefaultIsNetworkMember, >; - #[pallet::storage] + /// --- MAP ( netuid ) --> network_registration_allowed + #[pallet::storage] pub type NetworkRegistrationAllowed = StorageMap<_, Identity, NetUid, bool, ValueQuery, DefaultRegistrationAllowed>; - #[pallet::storage] + /// --- MAP ( netuid ) --> network_pow_allowed + #[pallet::storage] pub type NetworkPowRegistrationAllowed = StorageMap<_, Identity, NetUid, bool, ValueQuery, DefaultRegistrationAllowed>; - #[pallet::storage] + /// --- MAP ( netuid ) --> block_created + #[pallet::storage] pub type NetworkRegisteredAt = StorageMap<_, Identity, NetUid, u64, ValueQuery, DefaultNetworkRegisteredAt>; - #[pallet::storage] + /// --- MAP ( netuid ) --> pending_emission + #[pallet::storage] pub type PendingEmission = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultPendingEmission>; - #[pallet::storage] + /// --- MAP ( netuid ) --> pending_root_emission + #[pallet::storage] pub type PendingRootDivs = StorageMap<_, Identity, NetUid, TaoCurrency, ValueQuery, DefaultZeroTao>; - #[pallet::storage] + /// --- MAP ( netuid ) --> pending_alpha_swapped + #[pallet::storage] pub type PendingAlphaSwapped = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultZeroAlpha>; - #[pallet::storage] + /// --- MAP ( netuid ) --> pending_owner_cut + #[pallet::storage] pub type PendingOwnerCut = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultZeroAlpha>; - #[pallet::storage] + /// --- MAP ( netuid ) --> blocks_since_last_step + #[pallet::storage] pub type BlocksSinceLastStep = StorageMap<_, Identity, NetUid, u64, ValueQuery, DefaultBlocksSinceLastStep>; - #[pallet::storage] + /// --- MAP ( netuid ) --> last_mechanism_step_block + #[pallet::storage] pub type LastMechansimStepBlock = StorageMap<_, Identity, NetUid, u64, ValueQuery, DefaultLastMechanismStepBlock>; - #[pallet::storage] + /// --- MAP ( netuid ) --> subnet_owner + #[pallet::storage] pub type SubnetOwner = StorageMap<_, Identity, NetUid, T::AccountId, ValueQuery, DefaultSubnetOwner>; - #[pallet::storage] + /// --- MAP ( netuid ) --> subnet_owner_hotkey + #[pallet::storage] pub type SubnetOwnerHotkey = StorageMap<_, Identity, NetUid, T::AccountId, ValueQuery, DefaultSubnetOwner>; - #[pallet::storage] + /// --- MAP ( netuid ) --> recycle_or_burn + #[pallet::storage] pub type RecycleOrBurn = StorageMap<_, Identity, NetUid, RecycleOrBurnEnum, ValueQuery, DefaultRecycleOrBurn>; - #[pallet::storage] + /// --- MAP ( netuid ) --> serving_rate_limit + #[pallet::storage] pub type ServingRateLimit = StorageMap<_, Identity, NetUid, u64, ValueQuery, DefaultServingRateLimit>; - #[pallet::storage] + /// --- MAP ( netuid ) --> Rho - pub type Rho = StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultRho>; #[pallet::storage] + pub type Rho = StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultRho>; + /// --- MAP ( netuid ) --> AlphaSigmoidSteepness + #[pallet::storage] pub type AlphaSigmoidSteepness = StorageMap<_, Identity, NetUid, i16, ValueQuery, DefaultAlphaSigmoidSteepness>; - #[pallet::storage] + /// --- MAP ( netuid ) --> Kappa - pub type Kappa = StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultKappa>; #[pallet::storage] + pub type Kappa = StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultKappa>; + /// --- MAP ( netuid ) --> registrations_this_interval + #[pallet::storage] pub type RegistrationsThisInterval = StorageMap<_, Identity, NetUid, u16, ValueQuery>; - #[pallet::storage] + /// --- MAP ( netuid ) --> pow_registrations_this_interval + #[pallet::storage] pub type POWRegistrationsThisInterval = StorageMap<_, Identity, NetUid, u16, ValueQuery>; - #[pallet::storage] + /// --- MAP ( netuid ) --> burn_registrations_this_interval + #[pallet::storage] pub type BurnRegistrationsThisInterval = StorageMap<_, Identity, NetUid, u16, ValueQuery>; - #[pallet::storage] + /// --- MAP ( netuid ) --> min_allowed_uids + #[pallet::storage] pub type MinAllowedUids = StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultMinAllowedUids>; - #[pallet::storage] + /// --- MAP ( netuid ) --> max_allowed_uids + #[pallet::storage] pub type MaxAllowedUids = StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultMaxAllowedUids>; - #[pallet::storage] + /// --- MAP ( netuid ) --> immunity_period + #[pallet::storage] pub type ImmunityPeriod = StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultImmunityPeriod>; - #[pallet::storage] + /// --- MAP ( netuid ) --> activity_cutoff + #[pallet::storage] pub type ActivityCutoff = StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultActivityCutoff>; #[pallet::type_value] @@ -1449,98 +1602,122 @@ pub mod pallet { pub fn DefaultMaxWeightsLimit() -> u16 { u16::MAX } - #[pallet::storage] + /// --- MAP ( netuid ) --> max_weight_limit + #[pallet::storage] pub type MaxWeightsLimit = StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultMaxWeightsLimit>; - #[pallet::storage] + /// --- MAP ( netuid ) --> weights_version_key + #[pallet::storage] pub type WeightsVersionKey = StorageMap<_, Identity, NetUid, u64, ValueQuery, DefaultWeightsVersionKey>; - #[pallet::storage] + /// --- MAP ( netuid ) --> min_allowed_weights + #[pallet::storage] pub type MinAllowedWeights = StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultMinAllowedWeights>; - #[pallet::storage] + /// --- MAP ( netuid ) --> max_allowed_validators + #[pallet::storage] pub type MaxAllowedValidators = StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultMaxAllowedValidators>; - #[pallet::storage] + /// --- MAP ( netuid ) --> adjustment_interval + #[pallet::storage] pub type AdjustmentInterval = StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultAdjustmentInterval>; - #[pallet::storage] + /// --- MAP ( netuid ) --> bonds_moving_average + #[pallet::storage] pub type BondsMovingAverage = StorageMap<_, Identity, NetUid, u64, ValueQuery, DefaultBondsMovingAverage>; - #[pallet::storage] + /// --- MAP ( netuid ) --> bonds_penalty + #[pallet::storage] pub type BondsPenalty = StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultBondsPenalty>; - #[pallet::storage] + /// --- MAP ( netuid ) --> bonds_reset + #[pallet::storage] pub type BondsResetOn = StorageMap<_, Identity, NetUid, bool, ValueQuery, DefaultBondsResetOn>; + /// --- MAP ( netuid ) --> weights_set_rate_limit #[pallet::storage] pub type WeightsSetRateLimit = StorageMap<_, Identity, NetUid, u64, ValueQuery, DefaultWeightsSetRateLimit>; - #[pallet::storage] + /// --- MAP ( netuid ) --> validator_prune_len + #[pallet::storage] pub type ValidatorPruneLen = StorageMap<_, Identity, NetUid, u64, ValueQuery, DefaultValidatorPruneLen>; - #[pallet::storage] + /// --- MAP ( netuid ) --> scaling_law_power + #[pallet::storage] pub type ScalingLawPower = StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultScalingLawPower>; - #[pallet::storage] + /// --- MAP ( netuid ) --> target_registrations_this_interval + #[pallet::storage] pub type TargetRegistrationsPerInterval = StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultTargetRegistrationsPerInterval>; - #[pallet::storage] + /// --- MAP ( netuid ) --> adjustment_alpha + #[pallet::storage] pub type AdjustmentAlpha = StorageMap<_, Identity, NetUid, u64, ValueQuery, DefaultAdjustmentAlpha>; - #[pallet::storage] + /// --- MAP ( netuid ) --> commit reveal v2 weights are enabled + #[pallet::storage] pub type CommitRevealWeightsEnabled = StorageMap<_, Identity, NetUid, bool, ValueQuery, DefaultCommitRevealWeightsEnabled>; - #[pallet::storage] + /// --- MAP ( netuid ) --> Burn - pub type Burn = StorageMap<_, Identity, NetUid, TaoCurrency, ValueQuery, DefaultBurn>; #[pallet::storage] + pub type Burn = StorageMap<_, Identity, NetUid, TaoCurrency, ValueQuery, DefaultBurn>; + /// --- MAP ( netuid ) --> Difficulty - pub type Difficulty = StorageMap<_, Identity, NetUid, u64, ValueQuery, DefaultDifficulty>; #[pallet::storage] + pub type Difficulty = StorageMap<_, Identity, NetUid, u64, ValueQuery, DefaultDifficulty>; + /// --- MAP ( netuid ) --> MinBurn + #[pallet::storage] pub type MinBurn = StorageMap<_, Identity, NetUid, TaoCurrency, ValueQuery, DefaultMinBurn>; - #[pallet::storage] + /// --- MAP ( netuid ) --> MaxBurn + #[pallet::storage] pub type MaxBurn = StorageMap<_, Identity, NetUid, TaoCurrency, ValueQuery, DefaultMaxBurn>; - #[pallet::storage] + /// --- MAP ( netuid ) --> MinDifficulty + #[pallet::storage] pub type MinDifficulty = StorageMap<_, Identity, NetUid, u64, ValueQuery, DefaultMinDifficulty>; - #[pallet::storage] + /// --- MAP ( netuid ) --> MaxDifficulty + #[pallet::storage] pub type MaxDifficulty = StorageMap<_, Identity, NetUid, u64, ValueQuery, DefaultMaxDifficulty>; - #[pallet::storage] + /// --- MAP ( netuid ) --> Block at last adjustment. + #[pallet::storage] pub type LastAdjustmentBlock = StorageMap<_, Identity, NetUid, u64, ValueQuery, DefaultLastAdjustmentBlock>; - #[pallet::storage] + /// --- MAP ( netuid ) --> Registrations of this Block. + #[pallet::storage] pub type RegistrationsThisBlock = StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultRegistrationsThisBlock>; - #[pallet::storage] + /// --- MAP ( netuid ) --> Halving time of average moving price. + #[pallet::storage] pub type EMAPriceHalvingBlocks = StorageMap<_, Identity, NetUid, u64, ValueQuery, DefaultEMAPriceMovingBlocks>; - #[pallet::storage] + /// --- MAP ( netuid ) --> global_RAO_recycled_for_registration + #[pallet::storage] pub type RAORecycledForRegistration = StorageMap< _, Identity, @@ -1549,31 +1726,38 @@ pub mod pallet { ValueQuery, DefaultRAORecycledForRegistration, >; - #[pallet::storage] + /// --- ITEM ( tx_rate_limit ) - pub type TxRateLimit = StorageValue<_, u64, ValueQuery, DefaultTxRateLimit>; #[pallet::storage] + pub type TxRateLimit = StorageValue<_, u64, ValueQuery, DefaultTxRateLimit>; + /// --- ITEM ( tx_delegate_take_rate_limit ) + #[pallet::storage] pub type TxDelegateTakeRateLimit = StorageValue<_, u64, ValueQuery, DefaultTxDelegateTakeRateLimit>; - #[pallet::storage] + /// --- ITEM ( tx_childkey_take_rate_limit ) + #[pallet::storage] pub type TxChildkeyTakeRateLimit = StorageValue<_, u64, ValueQuery, DefaultTxChildKeyTakeRateLimit>; - #[pallet::storage] + /// --- MAP ( netuid ) --> Whether or not Liquid Alpha is enabled + #[pallet::storage] pub type LiquidAlphaOn = StorageMap<_, Blake2_128Concat, NetUid, bool, ValueQuery, DefaultLiquidAlpha>; - #[pallet::storage] + /// --- MAP ( netuid ) --> Whether or not Yuma3 is enabled + #[pallet::storage] pub type Yuma3On = StorageMap<_, Blake2_128Concat, NetUid, bool, ValueQuery, DefaultYuma3>; - #[pallet::storage] + /// MAP ( netuid ) --> (alpha_low, alpha_high) + #[pallet::storage] pub type AlphaValues = StorageMap<_, Identity, NetUid, (u16, u16), ValueQuery, DefaultAlphaValues>; - #[pallet::storage] + /// --- MAP ( netuid ) --> If subtoken trading enabled + #[pallet::storage] pub type SubtokenEnabled = StorageMap<_, Identity, NetUid, bool, ValueQuery, DefaultFalse>; @@ -1592,24 +1776,28 @@ pub mod pallet { pub fn MinImmuneOwnerUidsLimit() -> u16 { 1 } - #[pallet::storage] + /// --- MAP ( netuid ) --> Burn key limit + #[pallet::storage] pub type ImmuneOwnerUidsLimit = StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultImmuneOwnerUidsLimit>; /// ======================================= /// ==== Subnetwork Consensus Storage ==== /// ======================================= - #[pallet::storage] + /// --- DMAP ( netuid ) --> stake_weight | weight for stake used in YC. + #[pallet::storage] pub type StakeWeight = StorageMap<_, Identity, NetUid, Vec, ValueQuery, EmptyU16Vec>; - #[pallet::storage] + /// --- DMAP ( netuid, hotkey ) --> uid + #[pallet::storage] pub type Uids = StorageDoubleMap<_, Identity, NetUid, Blake2_128Concat, T::AccountId, u16, OptionQuery>; - #[pallet::storage] + /// --- DMAP ( netuid, uid ) --> hotkey + #[pallet::storage] pub type Keys = StorageDoubleMap< _, Identity, @@ -1620,55 +1808,68 @@ pub mod pallet { ValueQuery, DefaultKey, >; - #[pallet::storage] + /// --- MAP ( netuid ) --> (hotkey, se, ve) + #[pallet::storage] pub type LoadedEmission = StorageMap<_, Identity, NetUid, Vec<(T::AccountId, u64, u64)>, OptionQuery>; - #[pallet::storage] + /// --- MAP ( netuid ) --> active + #[pallet::storage] pub type Active = StorageMap<_, Identity, NetUid, Vec, ValueQuery, EmptyBoolVec>; - #[pallet::storage] + /// --- MAP ( netuid ) --> rank + #[pallet::storage] pub type Rank = StorageMap<_, Identity, NetUid, Vec, ValueQuery, EmptyU16Vec>; - #[pallet::storage] + /// --- MAP ( netuid ) --> trust + #[pallet::storage] pub type Trust = StorageMap<_, Identity, NetUid, Vec, ValueQuery, EmptyU16Vec>; - #[pallet::storage] + /// --- MAP ( netuid ) --> consensus + #[pallet::storage] pub type Consensus = StorageMap<_, Identity, NetUid, Vec, ValueQuery, EmptyU16Vec>; - #[pallet::storage] + /// --- MAP ( netuid ) --> incentive + #[pallet::storage] pub type Incentive = StorageMap<_, Identity, NetUidStorageIndex, Vec, ValueQuery, EmptyU16Vec>; - #[pallet::storage] + /// --- MAP ( netuid ) --> dividends + #[pallet::storage] pub type Dividends = StorageMap<_, Identity, NetUid, Vec, ValueQuery, EmptyU16Vec>; - #[pallet::storage] + /// --- MAP ( netuid ) --> emission - pub type Emission = StorageMap<_, Identity, NetUid, Vec, ValueQuery>; #[pallet::storage] + pub type Emission = StorageMap<_, Identity, NetUid, Vec, ValueQuery>; + /// --- MAP ( netuid ) --> last_update + #[pallet::storage] pub type LastUpdate = StorageMap<_, Identity, NetUidStorageIndex, Vec, ValueQuery, EmptyU64Vec>; - #[pallet::storage] + /// --- MAP ( netuid ) --> validator_trust + #[pallet::storage] pub type ValidatorTrust = StorageMap<_, Identity, NetUid, Vec, ValueQuery, EmptyU16Vec>; - #[pallet::storage] + /// --- MAP ( netuid ) --> pruning_scores + #[pallet::storage] pub type PruningScores = StorageMap<_, Identity, NetUid, Vec, ValueQuery, EmptyU16Vec>; - #[pallet::storage] + /// --- MAP ( netuid ) --> validator_permit + #[pallet::storage] pub type ValidatorPermit = StorageMap<_, Identity, NetUid, Vec, ValueQuery, EmptyBoolVec>; - #[pallet::storage] + /// --- DMAP ( netuid, uid ) --> weights + #[pallet::storage] pub type Weights = StorageDoubleMap< _, Identity, @@ -1679,8 +1880,9 @@ pub mod pallet { ValueQuery, DefaultWeights, >; - #[pallet::storage] + /// --- DMAP ( netuid, uid ) --> bonds + #[pallet::storage] pub type Bonds = StorageDoubleMap< _, Identity, @@ -1691,8 +1893,9 @@ pub mod pallet { ValueQuery, DefaultBonds, >; - #[pallet::storage] + /// --- DMAP ( netuid, uid ) --> block_at_registration + #[pallet::storage] pub type BlockAtRegistration = StorageDoubleMap< _, Identity, @@ -1703,8 +1906,9 @@ pub mod pallet { ValueQuery, DefaultBlockAtRegistration, >; - #[pallet::storage] + /// --- MAP ( netuid, hotkey ) --> axon_info + #[pallet::storage] pub type Axons = StorageDoubleMap< _, Identity, @@ -1714,6 +1918,7 @@ pub mod pallet { AxonInfoOf, OptionQuery, >; + /// --- MAP ( netuid, hotkey ) --> certificate #[pallet::storage] pub type NeuronCertificates = StorageDoubleMap< @@ -1725,8 +1930,9 @@ pub mod pallet { NeuronCertificateOf, OptionQuery, >; - #[pallet::storage] + /// --- MAP ( netuid, hotkey ) --> prometheus_info + #[pallet::storage] pub type Prometheus = StorageDoubleMap< _, Identity, @@ -1736,36 +1942,38 @@ pub mod pallet { PrometheusInfoOf, OptionQuery, >; - #[pallet::storage] + /// --- MAP ( coldkey ) --> identity. (DEPRECATED for V2) + #[pallet::storage] pub type Identities = StorageMap<_, Blake2_128Concat, T::AccountId, ChainIdentityOf, OptionQuery>; - #[pallet::storage] /// --- MAP ( coldkey ) --> identity + #[pallet::storage] pub type IdentitiesV2 = StorageMap<_, Blake2_128Concat, T::AccountId, ChainIdentityOfV2, OptionQuery>; - #[pallet::storage] /// --- MAP ( netuid ) --> identity. (DEPRECATED for V2) + #[pallet::storage] pub type SubnetIdentities = StorageMap<_, Blake2_128Concat, NetUid, SubnetIdentityOf, OptionQuery>; - #[pallet::storage] /// --- MAP ( netuid ) --> identityV2 (DEPRECATED for V3) + #[pallet::storage] pub type SubnetIdentitiesV2 = StorageMap<_, Blake2_128Concat, NetUid, SubnetIdentityOfV2, OptionQuery>; - #[pallet::storage] /// --- MAP ( netuid ) --> SubnetIdentityOfV3 + #[pallet::storage] pub type SubnetIdentitiesV3 = StorageMap<_, Blake2_128Concat, NetUid, SubnetIdentityOfV3, OptionQuery>; /// ================================= /// ==== Axon / Promo Endpoints ===== /// ================================= - #[pallet::storage] + /// --- NMAP ( hot, netuid, name ) --> last_block | Returns the last block of a transaction for a given key, netuid, and name. + #[pallet::storage] pub type TransactionKeyLastBlock = StorageNMap< _, ( @@ -1776,27 +1984,32 @@ pub mod pallet { u64, ValueQuery, >; + + /// --- MAP ( key ) --> last_block #[deprecated] #[pallet::storage] - /// --- MAP ( key ) --> last_block pub type LastTxBlock = StorageMap<_, Identity, T::AccountId, u64, ValueQuery, DefaultLastTxBlock>; + + /// --- MAP ( key ) --> last_tx_block_childkey_take #[deprecated] #[pallet::storage] - /// --- MAP ( key ) --> last_tx_block_childkey_take pub type LastTxBlockChildKeyTake = StorageMap<_, Identity, T::AccountId, u64, ValueQuery, DefaultLastTxBlock>; + + /// --- MAP ( key ) --> last_tx_block_delegate_take #[deprecated] #[pallet::storage] - /// --- MAP ( key ) --> last_tx_block_delegate_take pub type LastTxBlockDelegateTake = StorageMap<_, Identity, T::AccountId, u64, ValueQuery, DefaultLastTxBlock>; + + /// ITEM( weights_min_stake ) // FIXME: this storage is used interchangably for alpha/tao #[pallet::storage] - /// ITEM( weights_min_stake ) pub type StakeThreshold = StorageValue<_, u64, ValueQuery, DefaultStakeThreshold>; - #[pallet::storage] + /// --- MAP (netuid, who) --> VecDeque<(hash, commit_block, first_reveal_block, last_reveal_block)> | Stores a queue of commits for an account on a given netuid. + #[pallet::storage] pub type WeightCommits = StorageDoubleMap< _, Twox64Concat, @@ -1806,9 +2019,10 @@ pub mod pallet { VecDeque<(H256, u64, u64, u64)>, OptionQuery, >; - #[pallet::storage] + /// MAP (netuid, epoch) → VecDeque<(who, commit_block, ciphertext, reveal_round)> /// Stores a queue of weight commits for an account on a given subnet. + #[pallet::storage] pub type TimelockedWeightCommits = StorageDoubleMap< _, Twox64Concat, @@ -1823,9 +2037,10 @@ pub mod pallet { )>, ValueQuery, >; - #[pallet::storage] + /// MAP (netuid, epoch) → VecDeque<(who, ciphertext, reveal_round)> /// DEPRECATED for CRV3WeightCommitsV2 + #[pallet::storage] pub type CRV3WeightCommits = StorageDoubleMap< _, Twox64Concat, @@ -1839,9 +2054,10 @@ pub mod pallet { )>, ValueQuery, >; - #[pallet::storage] + /// MAP (netuid, epoch) → VecDeque<(who, commit_block, ciphertext, reveal_round)> /// DEPRECATED for TimelockedWeightCommits + #[pallet::storage] pub type CRV3WeightCommitsV2 = StorageDoubleMap< _, Twox64Concat, @@ -1856,13 +2072,14 @@ pub mod pallet { )>, ValueQuery, >; - #[pallet::storage] + /// --- Map (netuid) --> Number of epochs allowed for commit reveal periods + #[pallet::storage] pub type RevealPeriodEpochs = StorageMap<_, Twox64Concat, NetUid, u64, ValueQuery, DefaultRevealPeriodEpochs>; - #[pallet::storage] /// --- Map (coldkey, hotkey) --> u64 the last block at which stake was added/removed. + #[pallet::storage] pub type LastColdkeyHotkeyStakeBlock = StorageDoubleMap< _, Twox64Concat, @@ -1873,9 +2090,9 @@ pub mod pallet { OptionQuery, >; - #[pallet::storage] /// DMAP ( hot, cold, netuid ) --> rate limits for staking operations /// Value contains just a marker: we use this map as a set. + #[pallet::storage] pub type StakingOperationRateLimiter = StorageNMap< _, ( @@ -1890,45 +2107,47 @@ pub mod pallet { /// ============================= /// ==== EVM related storage ==== /// ============================= - #[pallet::storage] + /// --- DMAP (netuid, uid) --> (H160, last_block_where_ownership_was_proven) + #[pallet::storage] pub type AssociatedEvmAddress = StorageDoubleMap<_, Twox64Concat, NetUid, Twox64Concat, u16, (H160, u64), OptionQuery>; /// ======================== /// ==== Subnet Leasing ==== /// ======================== - #[pallet::storage] + /// --- MAP ( lease_id ) --> subnet lease | The subnet lease for a given lease id. + #[pallet::storage] pub type SubnetLeases = StorageMap<_, Twox64Concat, LeaseId, SubnetLeaseOf, OptionQuery>; - #[pallet::storage] /// --- DMAP ( lease_id, contributor ) --> shares | The shares of a contributor for a given lease. + #[pallet::storage] pub type SubnetLeaseShares = StorageDoubleMap<_, Twox64Concat, LeaseId, Identity, T::AccountId, U64F64, ValueQuery>; - #[pallet::storage] /// --- MAP ( netuid ) --> lease_id | The lease id for a given netuid. + #[pallet::storage] pub type SubnetUidToLeaseId = StorageMap<_, Twox64Concat, NetUid, LeaseId, OptionQuery>; - #[pallet::storage] /// --- ITEM ( next_lease_id ) | The next lease id. + #[pallet::storage] pub type NextSubnetLeaseId = StorageValue<_, LeaseId, ValueQuery, ConstU32<0>>; - #[pallet::storage] /// --- MAP ( lease_id ) --> accumulated_dividends | The accumulated dividends for a given lease that needs to be distributed. + #[pallet::storage] pub type AccumulatedLeaseDividends = StorageMap<_, Twox64Concat, LeaseId, AlphaCurrency, ValueQuery, DefaultZeroAlpha>; - #[pallet::storage] /// --- ITEM ( CommitRevealWeightsVersion ) + #[pallet::storage] pub type CommitRevealWeightsVersion = StorageValue<_, u16, ValueQuery, DefaultCommitRevealWeightsVersion>; - #[pallet::storage] /// ITEM( NetworkRegistrationStartBlock ) + #[pallet::storage] pub type NetworkRegistrationStartBlock = StorageValue<_, u64, ValueQuery, DefaultNetworkRegistrationStartBlock>; @@ -1955,20 +2174,23 @@ pub mod pallet { pub fn MechanismEmissionRateLimit() -> u64 { prod_or_fast!(7_200, 1) } - #[pallet::storage] + /// --- MAP ( netuid ) --> Current number of subnet mechanisms + #[pallet::storage] pub type MechanismCountCurrent = StorageMap<_, Twox64Concat, NetUid, MechId, ValueQuery, DefaultMechanismCount>; - #[pallet::storage] + /// --- MAP ( netuid ) --> Normalized vector of emission split proportion between subnet mechanisms + #[pallet::storage] pub type MechanismEmissionSplit = StorageMap<_, Twox64Concat, NetUid, Vec, OptionQuery>; /// ================== /// ==== Genesis ===== /// ================== - #[pallet::storage] + /// --- Storage for migration run status + #[pallet::storage] pub type HasMigrationRun = StorageMap<_, Identity, Vec, bool, ValueQuery>; #[pallet::type_value] @@ -1977,8 +2199,8 @@ pub mod pallet { 0 } - #[pallet::storage] /// Storage value for pending childkey cooldown, settable by root. + #[pallet::storage] pub type PendingChildKeyCooldown = StorageValue<_, u64, ValueQuery, DefaultPendingChildKeyCooldown>; From b7f776b3c9be9445b58a1f46d9579e48f649ea1c Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 21 Oct 2025 20:06:11 +0800 Subject: [PATCH 010/210] manually update sth missed --- pallets/subtensor/src/lib.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 9d554f9866..916ce48b18 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -480,6 +480,7 @@ pub mod pallet { pub fn DefaultBurn() -> TaoCurrency { T::InitialBurn::get().into() } + /// Default burn token. #[pallet::type_value] pub fn DefaultMinBurn() -> TaoCurrency { @@ -1761,18 +1762,19 @@ pub mod pallet { pub type SubtokenEnabled = StorageMap<_, Identity, NetUid, bool, ValueQuery, DefaultFalse>; - #[pallet::type_value] /// Default value for burn keys limit + #[pallet::type_value] pub fn DefaultImmuneOwnerUidsLimit() -> u16 { 1 } - #[pallet::type_value] + /// Maximum value for burn keys limit pub fn MaxImmuneOwnerUidsLimit() -> u16 { 10 } - #[pallet::type_value] + /// Minimum value for burn keys limit + #[pallet::type_value] pub fn MinImmuneOwnerUidsLimit() -> u16 { 1 } @@ -2193,8 +2195,8 @@ pub mod pallet { #[pallet::storage] pub type HasMigrationRun = StorageMap<_, Identity, Vec, bool, ValueQuery>; - #[pallet::type_value] /// Default value for pending childkey cooldown (settable by root, default 0) + #[pallet::type_value] pub fn DefaultPendingChildKeyCooldown() -> u64 { 0 } From 891a788346e331a31c9a6d05a13d510bdf09b8d3 Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 22 Oct 2025 13:47:27 +0800 Subject: [PATCH 011/210] fix format --- pallets/subtensor/src/lib.rs | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 65f4b4ad9a..94568988f1 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -313,7 +313,6 @@ pub mod pallet { /// ============================ /// ==== Staking + Accounts ==== /// ============================ - /// Default value for zero. #[pallet::type_value] pub fn DefaultZeroU64() -> u64 { @@ -1047,7 +1046,6 @@ pub mod pallet { /// /// Eventually, Bittensor should migrate to using Holds afterwhich time we will not require this /// separate accounting. - /// --- ITEM --> Global weight #[pallet::storage] pub type TaoWeight = StorageValue<_, u64, ValueQuery, DefaultTaoWeight>; @@ -1162,7 +1160,6 @@ pub mod pallet { /// ================== /// ==== Coinbase ==== /// ================== - /// --- ITEM ( global_block_emission ) #[pallet::storage] pub type BlockEmission = StorageValue<_, u64, ValueQuery, DefaultBlockEmission>; @@ -1191,7 +1188,6 @@ pub mod pallet { /// /// Eventually, Bittensor should migrate to using Holds afterwhich time we will not require this /// separate accounting. - /// --- ITEM ( maximum_number_of_networks ) #[pallet::storage] pub type SubnetLimit = StorageValue<_, u16, ValueQuery, DefaultSubnetLimit>; @@ -1363,7 +1359,6 @@ pub mod pallet { /// ============================ /// ==== Global Parameters ===== /// ============================ - /// --- StorageItem Global Used Work. #[pallet::storage] pub type UsedWork = StorageMap<_, Identity, Vec, u64, ValueQuery>; @@ -1417,7 +1412,6 @@ pub mod pallet { /// ============================ /// ==== Rate Limiting ===== /// ============================ - /// --- MAP ( RateLimitKey ) --> Block number in which the last rate limited operation occured #[pallet::storage] pub type LastRateLimitedBlock = @@ -1426,7 +1420,6 @@ pub mod pallet { /// ============================ /// ==== Subnet Locks ===== /// ============================ - /// --- MAP ( netuid ) --> transfer_toggle #[pallet::storage] pub type TransferToggle = @@ -1445,7 +1438,6 @@ pub mod pallet { /// ================= /// ==== Tempos ===== /// ================= - /// --- MAP ( netuid ) --> tempo #[pallet::storage] pub type Tempo = StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultTempo>; @@ -1453,7 +1445,6 @@ pub mod pallet { /// ============================ /// ==== Subnet Parameters ===== /// ============================ - /// --- MAP ( netuid ) --> block number of first emission #[pallet::storage] pub type FirstEmissionBlockNumber = @@ -1769,6 +1760,7 @@ pub mod pallet { } /// Maximum value for burn keys limit + #[pallet::type_value] pub fn MaxImmuneOwnerUidsLimit() -> u16 { 10 } @@ -1787,7 +1779,6 @@ pub mod pallet { /// ======================================= /// ==== Subnetwork Consensus Storage ==== /// ======================================= - /// --- DMAP ( netuid ) --> stake_weight | weight for stake used in YC. #[pallet::storage] pub type StakeWeight = @@ -1973,7 +1964,6 @@ pub mod pallet { /// ================================= /// ==== Axon / Promo Endpoints ===== /// ================================= - /// --- NMAP ( hot, netuid, name ) --> last_block | Returns the last block of a transaction for a given key, netuid, and name. #[pallet::storage] pub type TransactionKeyLastBlock = StorageNMap< @@ -2109,7 +2099,6 @@ pub mod pallet { /// ============================= /// ==== EVM related storage ==== /// ============================= - /// --- DMAP (netuid, uid) --> (H160, last_block_where_ownership_was_proven) #[pallet::storage] pub type AssociatedEvmAddress = @@ -2118,7 +2107,6 @@ pub mod pallet { /// ======================== /// ==== Subnet Leasing ==== /// ======================== - /// --- MAP ( lease_id ) --> subnet lease | The subnet lease for a given lease id. #[pallet::storage] pub type SubnetLeases = @@ -2156,23 +2144,26 @@ pub mod pallet { /// ============================ /// ==== Subnet Mechanisms ===== /// ============================ - #[pallet::type_value] /// -- ITEM (Default number of sub-subnets) + #[pallet::type_value] pub fn DefaultMechanismCount() -> MechId { MechId::from(1) } - #[pallet::type_value] + /// -- ITEM (Maximum number of sub-subnets) + #[pallet::type_value] pub fn MaxMechanismCount() -> MechId { MechId::from(2) } - #[pallet::type_value] + /// -- ITEM (Rate limit for mechanism count updates) + #[pallet::type_value] pub fn MechanismCountSetRateLimit() -> u64 { prod_or_fast!(7_200, 1) } - #[pallet::type_value] + /// -- ITEM (Rate limit for mechanism emission distribution updates) + #[pallet::type_value] pub fn MechanismEmissionRateLimit() -> u64 { prod_or_fast!(7_200, 1) } @@ -2190,7 +2181,6 @@ pub mod pallet { /// ================== /// ==== Genesis ===== /// ================== - /// --- Storage for migration run status #[pallet::storage] pub type HasMigrationRun = StorageMap<_, Identity, Vec, bool, ValueQuery>; From 72f052b2dd2d64526d7dda3d58f944d13e81da4d Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 30 Oct 2025 15:40:02 +0800 Subject: [PATCH 012/210] commit Cargo.lock --- pallets/subtensor/src/lib.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 4d4cf33584..2bd4240636 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1627,16 +1627,6 @@ pub mod pallet { pub type PendingEmission = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultPendingEmission>; - /// --- MAP ( netuid ) --> pending_root_emission - #[pallet::storage] - pub type PendingRootDivs = - StorageMap<_, Identity, NetUid, TaoCurrency, ValueQuery, DefaultZeroTao>; - - /// --- MAP ( netuid ) --> pending_alpha_swapped - #[pallet::storage] - pub type PendingAlphaSwapped = - StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultZeroAlpha>; - /// --- MAP ( netuid ) --> pending_root_alpha_emission #[pallet::storage] pub type PendingRootAlphaDivs = From 426d4321266669dc6bd05abe76799c4452967d33 Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 30 Oct 2025 15:42:18 +0800 Subject: [PATCH 013/210] remove obsolete storage --- pallets/subtensor/src/lib.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 2bd4240636..62641c0a94 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1220,19 +1220,6 @@ pub mod pallet { DefaultZeroAlpha, >; - /// --- DMAP ( netuid, hotkey ) --> u64 | Last total root dividend paid to this hotkey on this subnet. - #[pallet::storage] - pub type TaoDividendsPerSubnet = StorageDoubleMap< - _, - Identity, - NetUid, - Blake2_128Concat, - T::AccountId, - TaoCurrency, - ValueQuery, - DefaultZeroTao, - >; - /// ================== /// ==== Coinbase ==== /// ================== From ab5b272a303d45454457ff264fba6074e64bcaca Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 30 Oct 2025 15:50:30 +0800 Subject: [PATCH 014/210] bump version --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index a4ee4a3b93..d8277164e2 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 333, + spec_version: 334, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 742c4aa41e52999a1b02f4e90f6ee817675cfae9 Mon Sep 17 00:00:00 2001 From: Shamil Gadelshin Date: Fri, 31 Oct 2025 12:02:04 +0300 Subject: [PATCH 015/210] Disable root claim fees. --- pallets/subtensor/src/staking/claim_root.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/staking/claim_root.rs b/pallets/subtensor/src/staking/claim_root.rs index 0ffbeb4b54..a87cb8812a 100644 --- a/pallets/subtensor/src/staking/claim_root.rs +++ b/pallets/subtensor/src/staking/claim_root.rs @@ -165,7 +165,7 @@ impl Pallet { netuid, owed_u64.into(), T::SwapInterface::min_price::(), - false, + true, ) { Ok(owed_tao) => owed_tao, Err(err) => { From d69790edb42599dc11dfdccf816aeb0442482691 Mon Sep 17 00:00:00 2001 From: Pawel Polewicz Date: Fri, 31 Oct 2025 22:41:54 +0000 Subject: [PATCH 016/210] Add subnet deregistration priority flag --- pallets/subtensor/src/coinbase/root.rs | 15 +++++++++++---- pallets/subtensor/src/lib.rs | 4 ++++ pallets/subtensor/src/tests/networks.rs | 22 ++++++++++++++++++++++ 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/pallets/subtensor/src/coinbase/root.rs b/pallets/subtensor/src/coinbase/root.rs index 642a7f18ac..d89db2a514 100644 --- a/pallets/subtensor/src/coinbase/root.rs +++ b/pallets/subtensor/src/coinbase/root.rs @@ -244,6 +244,7 @@ impl Pallet { // --- 5. Remove various network-related storages. NetworkRegisteredAt::::remove(netuid); + SubnetDeregistrationPriority::::remove(netuid); // --- 6. Remove incentive mechanism memory. let _ = Uids::::clear_prefix(netuid, u32::MAX, None); @@ -593,6 +594,7 @@ impl Pallet { let current_block: u64 = Self::get_current_block_as_u64(); let mut candidate_netuid: Option = None; + let mut candidate_priority: bool = false; let mut candidate_price: U96F32 = U96F32::saturating_from_num(u128::MAX); let mut candidate_timestamp: u64 = u64::MAX; @@ -609,12 +611,17 @@ impl Pallet { } let price: U96F32 = Self::get_moving_alpha_price(netuid); - - // If tie on price, earliest registration wins. - if price < candidate_price - || (price == candidate_price && registered_at < candidate_timestamp) + let priority = SubnetDeregistrationPriority::::get(netuid); + + // Prioritize higher deregistration priority, then lowest price, then earliest registration. + if candidate_netuid.is_none() + || (priority && !candidate_priority) + || (priority == candidate_priority + && (price < candidate_price + || (price == candidate_price && registered_at < candidate_timestamp))) { candidate_netuid = Some(netuid); + candidate_priority = priority; candidate_price = price; candidate_timestamp = registered_at; } diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 93d082f4a6..876344628c 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1434,6 +1434,10 @@ pub mod pallet { pub type NetworkRegisteredAt = StorageMap<_, Identity, NetUid, u64, ValueQuery, DefaultNetworkRegisteredAt>; #[pallet::storage] + /// --- MAP ( netuid ) --> deregistration priority flag (true pruned before price-based selection) + pub type SubnetDeregistrationPriority = + StorageMap<_, Identity, NetUid, bool, ValueQuery, DefaultFalse>; + #[pallet::storage] /// --- MAP ( netuid ) --> pending_emission pub type PendingEmission = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultPendingEmission>; diff --git a/pallets/subtensor/src/tests/networks.rs b/pallets/subtensor/src/tests/networks.rs index 0449c67f86..b0eba4356c 100644 --- a/pallets/subtensor/src/tests/networks.rs +++ b/pallets/subtensor/src/tests/networks.rs @@ -55,6 +55,8 @@ fn dissolve_no_stakers_no_alpha_no_emission() { SubtensorModule::set_subnet_locked_balance(net, TaoCurrency::from(0)); SubnetTAO::::insert(net, TaoCurrency::from(0)); Emission::::insert(net, Vec::::new()); + SubnetDeregistrationPriority::::insert(net, true); + assert!(SubnetDeregistrationPriority::::contains_key(net)); let before = SubtensorModule::get_coldkey_balance(&cold); assert_ok!(SubtensorModule::do_dissolve_network(net)); @@ -63,6 +65,7 @@ fn dissolve_no_stakers_no_alpha_no_emission() { // Balance should be unchanged (whatever the network-lock bookkeeping left there) assert_eq!(after, before); assert!(!SubtensorModule::if_subnet_exist(net)); + assert!(!SubnetDeregistrationPriority::::contains_key(net)); }); } @@ -1144,6 +1147,25 @@ fn prune_tie_on_price_earlier_registration_wins() { }); } +#[test] +fn prune_prefers_higher_priority_over_price() { + new_test_ext(0).execute_with(|| { + let n1 = add_dynamic_network(&U256::from(210), &U256::from(201)); + let n2 = add_dynamic_network(&U256::from(420), &U256::from(401)); + + let imm = SubtensorModule::get_network_immunity_period(); + System::set_block_number(imm + 5); + + SubnetMovingPrice::::insert(n1, I96F32::from_num(100)); + SubnetMovingPrice::::insert(n2, I96F32::from_num(1)); + + SubnetDeregistrationPriority::::insert(n1, true); + SubnetDeregistrationPriority::::insert(n2, false); + + assert_eq!(SubtensorModule::get_network_to_prune(), Some(n1)); + }); +} + #[test] fn prune_selection_complex_state_exhaustive() { new_test_ext(0).execute_with(|| { From 1e458059ffa08bf1c422037ecec79880ee2ee6ce Mon Sep 17 00:00:00 2001 From: Pawel Polewicz Date: Fri, 31 Oct 2025 22:46:08 +0000 Subject: [PATCH 017/210] Add extrinsic to clear subnet deregistration flag --- pallets/subtensor/src/macros/dispatches.rs | 27 +++++++++++++ pallets/subtensor/src/macros/events.rs | 2 + pallets/subtensor/src/tests/networks.rs | 46 ++++++++++++++++++++++ 3 files changed, 75 insertions(+) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index af497958a2..2ad5ac44c7 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2152,6 +2152,33 @@ mod dispatches { Ok(()) } + /// Clears the deregistration priority flag for a subnet. + /// + /// Accessible by the subnet owner or root origin. This resets the flag used to force + /// deregistration ordering, allowing the network to fall back to price-based pruning. + /// + /// # Errors + /// * [`Error::SubnetNotExists`] if the subnet does not exist. + #[pallet::call_index(121)] + #[pallet::weight(( + Weight::from_parts(20_000_000, 0) + .saturating_add(T::DbWeight::get().reads_writes(2, 1)), + DispatchClass::Normal, + Pays::Yes + ))] + pub fn clear_deregistration_priority( + origin: OriginFor, + netuid: NetUid, + ) -> DispatchResult { + ensure!(Self::if_subnet_exist(netuid), Error::::SubnetNotExists); + Self::ensure_subnet_owner_or_root(origin, netuid)?; + + SubnetDeregistrationPriority::::remove(netuid); + + Self::deposit_event(Event::SubnetDeregistrationPriorityCleared(netuid)); + Ok(()) + } + /// ---- Used to commit timelock encrypted commit-reveal weight values to later be revealed. /// /// # Args: diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index c2931024ee..8804024707 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -13,6 +13,8 @@ mod events { NetworkAdded(NetUid, u16), /// a network is removed. NetworkRemoved(NetUid), + /// network deregistration priority flag cleared. + SubnetDeregistrationPriorityCleared(NetUid), /// stake has been transferred from the a coldkey account onto the hotkey staking account. StakeAdded( T::AccountId, diff --git a/pallets/subtensor/src/tests/networks.rs b/pallets/subtensor/src/tests/networks.rs index b0eba4356c..d34086916a 100644 --- a/pallets/subtensor/src/tests/networks.rs +++ b/pallets/subtensor/src/tests/networks.rs @@ -7,6 +7,7 @@ use frame_support::{assert_err, assert_ok}; use frame_system::Config; use sp_core::U256; use sp_std::collections::{btree_map::BTreeMap, vec_deque::VecDeque}; +use sp_runtime::DispatchError; use substrate_fixed::types::{I96F32, U64F64, U96F32}; use subtensor_runtime_common::{MechId, NetUidStorageIndex, TaoCurrency}; use subtensor_swap_interface::{Order, SwapHandler}; @@ -69,6 +70,51 @@ fn dissolve_no_stakers_no_alpha_no_emission() { }); } +#[test] +fn clear_priority_by_root_resets_flag() { + new_test_ext(0).execute_with(|| { + let cold = U256::from(11); + let hot = U256::from(22); + let net = add_dynamic_network(&hot, &cold); + + SubnetDeregistrationPriority::::insert(net, true); + assert!(SubnetDeregistrationPriority::::get(net)); + + assert_ok!(SubtensorModule::clear_deregistration_priority( + RuntimeOrigin::root(), + net + )); + + assert!(!SubnetDeregistrationPriority::::get(net)); + assert!(!SubnetDeregistrationPriority::::contains_key(net)); + }); +} + +#[test] +fn clear_priority_requires_owner_or_root() { + new_test_ext(0).execute_with(|| { + let owner_cold = U256::from(13); + let owner_hot = U256::from(26); + let net = add_dynamic_network(&owner_hot, &owner_cold); + + SubnetDeregistrationPriority::::insert(net, true); + let intruder = U256::from(999); + + assert_err!( + SubtensorModule::clear_deregistration_priority(RuntimeOrigin::signed(intruder), net), + DispatchError::BadOrigin + ); + + assert_ok!(SubtensorModule::clear_deregistration_priority( + RuntimeOrigin::signed(owner_cold), + net + )); + + assert!(!SubnetDeregistrationPriority::::get(net)); + assert!(!SubnetDeregistrationPriority::::contains_key(net)); + }); +} + #[test] fn dissolve_refunds_full_lock_cost_when_no_emission() { new_test_ext(0).execute_with(|| { From 4c57621f3d223752ef25963bb9da55fbdb51b6a4 Mon Sep 17 00:00:00 2001 From: Pawel Polewicz Date: Fri, 31 Oct 2025 22:55:51 +0000 Subject: [PATCH 018/210] Add scheduling controls for subnet deregistration priority --- pallets/subtensor/src/lib.rs | 4 + pallets/subtensor/src/macros/dispatches.rs | 84 +++++++++++++++++-- pallets/subtensor/src/macros/errors.rs | 2 + pallets/subtensor/src/macros/events.rs | 5 ++ pallets/subtensor/src/tests/networks.rs | 98 ++++++++++++++++++---- pallets/subtensor/src/utils/misc.rs | 16 ++++ 6 files changed, 186 insertions(+), 23 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 876344628c..c7f5693dbb 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1438,6 +1438,10 @@ pub mod pallet { pub type SubnetDeregistrationPriority = StorageMap<_, Identity, NetUid, bool, ValueQuery, DefaultFalse>; #[pallet::storage] + /// --- MAP ( netuid ) --> scheduled block for enabling deregistration priority + pub type SubnetDeregistrationPrioritySchedule = + StorageMap<_, Identity, NetUid, BlockNumberFor, OptionQuery>; + #[pallet::storage] /// --- MAP ( netuid ) --> pending_emission pub type PendingEmission = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultPendingEmission>; diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 2ad5ac44c7..a56144305f 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -1411,6 +1411,7 @@ mod dispatches { .map_err(|_| Error::::FailedToSchedule)?; ColdkeySwapScheduled::::insert(&who, (when, new_coldkey.clone())); + Self::clear_deregistration_priority_for_owner(&who); // Emit the SwapScheduled event Self::deposit_event(Event::ColdkeySwapScheduled { old_coldkey: who.clone(), @@ -2152,30 +2153,97 @@ mod dispatches { Ok(()) } - /// Clears the deregistration priority flag for a subnet. + /// Manages the deregistration priority flag for a subnet. /// - /// Accessible by the subnet owner or root origin. This resets the flag used to force - /// deregistration ordering, allowing the network to fall back to price-based pruning. + /// When `schedule_set` is `true`, the flag is scheduled to be set after approximately + /// five days using the scheduler pallet. When `schedule_set` is `false`, the flag and any + /// pending schedule are cleared immediately. + /// + /// Accessible by the subnet owner or root origin. /// /// # Errors /// * [`Error::SubnetNotExists`] if the subnet does not exist. + /// * [`Error::SubnetDeregistrationPriorityAlreadyScheduled`] if a set operation is already + /// scheduled. #[pallet::call_index(121)] #[pallet::weight(( - Weight::from_parts(20_000_000, 0) - .saturating_add(T::DbWeight::get().reads_writes(2, 1)), + Weight::from_parts(40_000_000, 0) + .saturating_add(T::DbWeight::get().reads_writes(4, 2)), DispatchClass::Normal, Pays::Yes ))] - pub fn clear_deregistration_priority( + pub fn manage_deregistration_priority( origin: OriginFor, netuid: NetUid, + schedule_set: bool, ) -> DispatchResult { ensure!(Self::if_subnet_exist(netuid), Error::::SubnetNotExists); Self::ensure_subnet_owner_or_root(origin, netuid)?; - SubnetDeregistrationPriority::::remove(netuid); + if schedule_set { + ensure!( + SubnetDeregistrationPrioritySchedule::::get(netuid).is_none(), + Error::::SubnetDeregistrationPriorityAlreadyScheduled + ); + + let current_block: BlockNumberFor = >::block_number(); + let delay: BlockNumberFor = ColdkeySwapScheduleDuration::::get(); + let when: BlockNumberFor = current_block.saturating_add(delay); + + let call = Call::::force_set_deregistration_priority { netuid }; + let bound_call = ::Preimages::bound(LocalCallOf::::from(call)) + .map_err(|_| Error::::FailedToSchedule)?; + + T::Scheduler::schedule( + DispatchTime::At(when), + None, + 63, + frame_system::RawOrigin::Root.into(), + bound_call, + ) + .map_err(|_| Error::::FailedToSchedule)?; + + SubnetDeregistrationPrioritySchedule::::insert(netuid, when); + + Self::deposit_event(Event::SubnetDeregistrationPriorityScheduled(netuid, when)); + } else { + let was_flagged = SubnetDeregistrationPriority::::get(netuid); + SubnetDeregistrationPriority::::remove(netuid); + let had_schedule = SubnetDeregistrationPrioritySchedule::::take(netuid).is_some(); + + if was_flagged || had_schedule { + Self::deposit_event(Event::SubnetDeregistrationPriorityCleared(netuid)); + } + } + + Ok(()) + } + + /// Sets the deregistration priority flag immediately. + /// + /// This call is intended to be used by the scheduler and requires root origin. + #[pallet::call_index(122)] + #[pallet::weight(( + Weight::from_parts(15_000_000, 0) + .saturating_add(T::DbWeight::get().reads_writes(3, 2)), + DispatchClass::Operational, + Pays::No + ))] + pub fn force_set_deregistration_priority( + origin: OriginFor, + netuid: NetUid, + ) -> DispatchResult { + ensure_root(origin)?; + ensure!(Self::if_subnet_exist(netuid), Error::::SubnetNotExists); + + if SubnetDeregistrationPrioritySchedule::::take(netuid).is_none() { + // Schedule was cleared, nothing to do. + return Ok(()); + } + + SubnetDeregistrationPriority::::insert(netuid, true); + Self::deposit_event(Event::SubnetDeregistrationPrioritySet(netuid)); - Self::deposit_event(Event::SubnetDeregistrationPriorityCleared(netuid)); Ok(()) } diff --git a/pallets/subtensor/src/macros/errors.rs b/pallets/subtensor/src/macros/errors.rs index 5a15330075..d87d0f5938 100644 --- a/pallets/subtensor/src/macros/errors.rs +++ b/pallets/subtensor/src/macros/errors.rs @@ -152,6 +152,8 @@ mod errors { TxRateLimitExceeded, /// Swap already scheduled. SwapAlreadyScheduled, + /// Deregistration priority already scheduled. + SubnetDeregistrationPriorityAlreadyScheduled, /// failed to swap coldkey FailedToSchedule, /// New coldkey is hotkey diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index 8804024707..27093829f3 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -5,6 +5,7 @@ use frame_support::pallet_macros::pallet_section; #[pallet_section] mod events { use codec::Compact; + use frame_system::pallet_prelude::BlockNumberFor; #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] @@ -13,6 +14,10 @@ mod events { NetworkAdded(NetUid, u16), /// a network is removed. NetworkRemoved(NetUid), + /// a subnet's deregistration priority flag was scheduled to be set. + SubnetDeregistrationPriorityScheduled(NetUid, BlockNumberFor), + /// a subnet's deregistration priority flag was set. + SubnetDeregistrationPrioritySet(NetUid), /// network deregistration priority flag cleared. SubnetDeregistrationPriorityCleared(NetUid), /// stake has been transferred from the a coldkey account onto the hotkey staking account. diff --git a/pallets/subtensor/src/tests/networks.rs b/pallets/subtensor/src/tests/networks.rs index d34086916a..ed9d5b5e34 100644 --- a/pallets/subtensor/src/tests/networks.rs +++ b/pallets/subtensor/src/tests/networks.rs @@ -71,47 +71,115 @@ fn dissolve_no_stakers_no_alpha_no_emission() { } #[test] -fn clear_priority_by_root_resets_flag() { +fn manage_priority_schedule_and_force_set() { new_test_ext(0).execute_with(|| { - let cold = U256::from(11); - let hot = U256::from(22); - let net = add_dynamic_network(&hot, &cold); + let owner_cold = U256::from(11); + let owner_hot = U256::from(22); + let net = add_dynamic_network(&owner_hot, &owner_cold); - SubnetDeregistrationPriority::::insert(net, true); - assert!(SubnetDeregistrationPriority::::get(net)); + assert_ok!(SubtensorModule::manage_deregistration_priority( + RuntimeOrigin::signed(owner_cold), + net, + true + )); - assert_ok!(SubtensorModule::clear_deregistration_priority( + assert_eq!(SubnetDeregistrationPriority::::get(net), false); + let when = SubnetDeregistrationPrioritySchedule::::get(net).unwrap(); + assert!(when > 0); + + assert_ok!(SubtensorModule::force_set_deregistration_priority( RuntimeOrigin::root(), net )); - assert!(!SubnetDeregistrationPriority::::get(net)); - assert!(!SubnetDeregistrationPriority::::contains_key(net)); + assert!(SubnetDeregistrationPriority::::get(net)); + assert!(!SubnetDeregistrationPrioritySchedule::::contains_key(net)); }); } #[test] -fn clear_priority_requires_owner_or_root() { +fn manage_priority_clear_now() { new_test_ext(0).execute_with(|| { let owner_cold = U256::from(13); let owner_hot = U256::from(26); let net = add_dynamic_network(&owner_hot, &owner_cold); SubnetDeregistrationPriority::::insert(net, true); + SubnetDeregistrationPrioritySchedule::::insert(net, 42); + + assert_ok!(SubtensorModule::manage_deregistration_priority( + RuntimeOrigin::signed(owner_cold), + net, + false + )); + + assert_eq!(SubnetDeregistrationPriority::::get(net), false); + assert!(!SubnetDeregistrationPrioritySchedule::::contains_key(net)); + }); +} + +#[test] +fn manage_priority_requires_owner_or_root() { + new_test_ext(0).execute_with(|| { + let owner_cold = U256::from(15); + let owner_hot = U256::from(30); + let net = add_dynamic_network(&owner_hot, &owner_cold); let intruder = U256::from(999); assert_err!( - SubtensorModule::clear_deregistration_priority(RuntimeOrigin::signed(intruder), net), + SubtensorModule::manage_deregistration_priority( + RuntimeOrigin::signed(intruder), + net, + false + ), DispatchError::BadOrigin ); - assert_ok!(SubtensorModule::clear_deregistration_priority( - RuntimeOrigin::signed(owner_cold), + assert_ok!(SubtensorModule::manage_deregistration_priority( + RuntimeOrigin::root(), + net, + false + )); + }); +} + +#[test] +fn force_set_deregistration_priority_is_noop_without_schedule() { + new_test_ext(0).execute_with(|| { + let owner_cold = U256::from(17); + let owner_hot = U256::from(34); + let net = add_dynamic_network(&owner_hot, &owner_cold); + + assert_ok!(SubtensorModule::force_set_deregistration_priority( + RuntimeOrigin::root(), net )); - assert!(!SubnetDeregistrationPriority::::get(net)); - assert!(!SubnetDeregistrationPriority::::contains_key(net)); + assert_eq!(SubnetDeregistrationPriority::::get(net), false); + }); +} + +#[test] +fn schedule_swap_coldkey_clears_priority() { + new_test_ext(0).execute_with(|| { + let owner_cold = U256::from(21); + let owner_hot = U256::from(42); + let new_cold = U256::from(84); + let net = add_dynamic_network(&owner_hot, &owner_cold); + + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&owner_cold, swap_cost.to_u64() + 1_000); + + SubnetDeregistrationPriority::::insert(net, true); + SubnetDeregistrationPrioritySchedule::::insert(net, 5); + + assert_ok!(SubtensorModule::schedule_swap_coldkey( + RuntimeOrigin::signed(owner_cold), + new_cold + )); + + assert_eq!(SubnetDeregistrationPriority::::get(net), false); + assert!(!SubnetDeregistrationPrioritySchedule::::contains_key(net)); }); } diff --git a/pallets/subtensor/src/utils/misc.rs b/pallets/subtensor/src/utils/misc.rs index 0ba3df1103..a7a75c6ce4 100644 --- a/pallets/subtensor/src/utils/misc.rs +++ b/pallets/subtensor/src/utils/misc.rs @@ -121,6 +121,22 @@ impl Pallet { Self::deposit_event(Event::OwnerHyperparamRateLimitSet(epochs)); } + pub fn clear_deregistration_priority_for_owner(owner: &T::AccountId) { + let nets: sp_std::vec::Vec = SubnetOwner::::iter() + .filter_map(|(netuid, acct)| if acct == *owner { Some(netuid) } else { None }) + .collect(); + + for netuid in nets { + let was_flagged = SubnetDeregistrationPriority::::get(netuid); + let had_schedule = SubnetDeregistrationPrioritySchedule::::take(netuid).is_some(); + SubnetDeregistrationPriority::::remove(netuid); + + if was_flagged || had_schedule { + Self::deposit_event(Event::SubnetDeregistrationPriorityCleared(netuid)); + } + } + } + /// If owner is `Some`, record last-blocks for the provided `TransactionType`s. pub fn record_owner_rl( maybe_owner: Option<::AccountId>, From 0314ae9c1971b7aca4e7dc9e478074a744f52c9c Mon Sep 17 00:00:00 2001 From: Pawel Polewicz Date: Sun, 2 Nov 2025 04:19:24 +0000 Subject: [PATCH 019/210] Restrict deregistration flag clearing to root --- pallets/subtensor/src/macros/dispatches.rs | 29 ++++++++------ pallets/subtensor/src/tests/networks.rs | 45 ++++++++++++++++++---- pallets/subtensor/src/utils/misc.rs | 8 +--- 3 files changed, 57 insertions(+), 25 deletions(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index a56144305f..8e6e6f0814 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -1411,7 +1411,7 @@ mod dispatches { .map_err(|_| Error::::FailedToSchedule)?; ColdkeySwapScheduled::::insert(&who, (when, new_coldkey.clone())); - Self::clear_deregistration_priority_for_owner(&who); + Self::cancel_deregistration_priority_schedule_for_owner(&who); // Emit the SwapScheduled event Self::deposit_event(Event::ColdkeySwapScheduled { old_coldkey: who.clone(), @@ -2156,10 +2156,11 @@ mod dispatches { /// Manages the deregistration priority flag for a subnet. /// /// When `schedule_set` is `true`, the flag is scheduled to be set after approximately - /// five days using the scheduler pallet. When `schedule_set` is `false`, the flag and any - /// pending schedule are cleared immediately. + /// five days using the scheduler pallet. When `schedule_set` is `false`, any pending + /// schedule is cleared. Only the root origin may clear the flag itself. /// - /// Accessible by the subnet owner or root origin. + /// Accessible by the subnet owner (for scheduling or canceling a pending schedule) or root + /// origin (full control). /// /// # Errors /// * [`Error::SubnetNotExists`] if the subnet does not exist. @@ -2178,7 +2179,7 @@ mod dispatches { schedule_set: bool, ) -> DispatchResult { ensure!(Self::if_subnet_exist(netuid), Error::::SubnetNotExists); - Self::ensure_subnet_owner_or_root(origin, netuid)?; + let maybe_owner = Self::ensure_subnet_owner_or_root(origin, netuid)?; if schedule_set { ensure!( @@ -2207,12 +2208,18 @@ mod dispatches { Self::deposit_event(Event::SubnetDeregistrationPriorityScheduled(netuid, when)); } else { - let was_flagged = SubnetDeregistrationPriority::::get(netuid); - SubnetDeregistrationPriority::::remove(netuid); - let had_schedule = SubnetDeregistrationPrioritySchedule::::take(netuid).is_some(); - - if was_flagged || had_schedule { - Self::deposit_event(Event::SubnetDeregistrationPriorityCleared(netuid)); + if maybe_owner.is_some() { + if SubnetDeregistrationPrioritySchedule::::take(netuid).is_some() { + Self::deposit_event(Event::SubnetDeregistrationPriorityCleared(netuid)); + } + } else { + let was_flagged = SubnetDeregistrationPriority::::take(netuid); + let had_schedule = + SubnetDeregistrationPrioritySchedule::::take(netuid).is_some(); + + if was_flagged || had_schedule { + Self::deposit_event(Event::SubnetDeregistrationPriorityCleared(netuid)); + } } } diff --git a/pallets/subtensor/src/tests/networks.rs b/pallets/subtensor/src/tests/networks.rs index ed9d5b5e34..f4ba420964 100644 --- a/pallets/subtensor/src/tests/networks.rs +++ b/pallets/subtensor/src/tests/networks.rs @@ -6,8 +6,8 @@ use crate::*; use frame_support::{assert_err, assert_ok}; use frame_system::Config; use sp_core::U256; -use sp_std::collections::{btree_map::BTreeMap, vec_deque::VecDeque}; use sp_runtime::DispatchError; +use sp_std::collections::{btree_map::BTreeMap, vec_deque::VecDeque}; use substrate_fixed::types::{I96F32, U64F64, U96F32}; use subtensor_runtime_common::{MechId, NetUidStorageIndex, TaoCurrency}; use subtensor_swap_interface::{Order, SwapHandler}; @@ -93,12 +93,14 @@ fn manage_priority_schedule_and_force_set() { )); assert!(SubnetDeregistrationPriority::::get(net)); - assert!(!SubnetDeregistrationPrioritySchedule::::contains_key(net)); + assert!(!SubnetDeregistrationPrioritySchedule::::contains_key( + net + )); }); } #[test] -fn manage_priority_clear_now() { +fn manage_priority_owner_cancels_schedule_only() { new_test_ext(0).execute_with(|| { let owner_cold = U256::from(13); let owner_hot = U256::from(26); @@ -113,8 +115,33 @@ fn manage_priority_clear_now() { false )); - assert_eq!(SubnetDeregistrationPriority::::get(net), false); - assert!(!SubnetDeregistrationPrioritySchedule::::contains_key(net)); + assert!(SubnetDeregistrationPriority::::get(net)); + assert!(!SubnetDeregistrationPrioritySchedule::::contains_key( + net + )); + }); +} + +#[test] +fn manage_priority_root_clears_flag() { + new_test_ext(0).execute_with(|| { + let owner_cold = U256::from(19); + let owner_hot = U256::from(38); + let net = add_dynamic_network(&owner_hot, &owner_cold); + + SubnetDeregistrationPriority::::insert(net, true); + SubnetDeregistrationPrioritySchedule::::insert(net, 55); + + assert_ok!(SubtensorModule::manage_deregistration_priority( + RuntimeOrigin::root(), + net, + false + )); + + assert!(!SubnetDeregistrationPriority::::get(net)); + assert!(!SubnetDeregistrationPrioritySchedule::::contains_key( + net + )); }); } @@ -160,7 +187,7 @@ fn force_set_deregistration_priority_is_noop_without_schedule() { } #[test] -fn schedule_swap_coldkey_clears_priority() { +fn schedule_swap_coldkey_cancels_priority_schedule() { new_test_ext(0).execute_with(|| { let owner_cold = U256::from(21); let owner_hot = U256::from(42); @@ -178,8 +205,10 @@ fn schedule_swap_coldkey_clears_priority() { new_cold )); - assert_eq!(SubnetDeregistrationPriority::::get(net), false); - assert!(!SubnetDeregistrationPrioritySchedule::::contains_key(net)); + assert!(SubnetDeregistrationPriority::::get(net)); + assert!(!SubnetDeregistrationPrioritySchedule::::contains_key( + net + )); }); } diff --git a/pallets/subtensor/src/utils/misc.rs b/pallets/subtensor/src/utils/misc.rs index a7a75c6ce4..2184a4a5d7 100644 --- a/pallets/subtensor/src/utils/misc.rs +++ b/pallets/subtensor/src/utils/misc.rs @@ -121,17 +121,13 @@ impl Pallet { Self::deposit_event(Event::OwnerHyperparamRateLimitSet(epochs)); } - pub fn clear_deregistration_priority_for_owner(owner: &T::AccountId) { + pub fn cancel_deregistration_priority_schedule_for_owner(owner: &T::AccountId) { let nets: sp_std::vec::Vec = SubnetOwner::::iter() .filter_map(|(netuid, acct)| if acct == *owner { Some(netuid) } else { None }) .collect(); for netuid in nets { - let was_flagged = SubnetDeregistrationPriority::::get(netuid); - let had_schedule = SubnetDeregistrationPrioritySchedule::::take(netuid).is_some(); - SubnetDeregistrationPriority::::remove(netuid); - - if was_flagged || had_schedule { + if SubnetDeregistrationPrioritySchedule::::take(netuid).is_some() { Self::deposit_event(Event::SubnetDeregistrationPriorityCleared(netuid)); } } From 989944be7e2dbde60d8333fdaeed71a99ee9e8f1 Mon Sep 17 00:00:00 2001 From: Shamil Gadelshin Date: Fri, 31 Oct 2025 14:07:46 +0300 Subject: [PATCH 020/210] Remove claim root on network deregistration - change RootClaimed key order --- pallets/subtensor/src/lib.rs | 2 +- pallets/subtensor/src/staking/claim_root.rs | 51 ++++++------------ pallets/subtensor/src/tests/claim_root.rs | 58 +++++++++------------ runtime/src/lib.rs | 2 +- 4 files changed, 42 insertions(+), 71 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 93d082f4a6..edf1a5d1cc 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1962,9 +1962,9 @@ pub mod pallet { pub type RootClaimed = StorageNMap< _, ( + NMapKey, // subnet NMapKey, // hot NMapKey, // cold - NMapKey, // subnet ), u128, ValueQuery, diff --git a/pallets/subtensor/src/staking/claim_root.rs b/pallets/subtensor/src/staking/claim_root.rs index 0ffbeb4b54..7d69bced72 100644 --- a/pallets/subtensor/src/staking/claim_root.rs +++ b/pallets/subtensor/src/staking/claim_root.rs @@ -2,7 +2,7 @@ use super::*; use frame_support::weights::Weight; use sp_core::Get; use sp_std::collections::btree_set::BTreeSet; -use substrate_fixed::types::{I96F32, U64F64}; +use substrate_fixed::types::I96F32; use subtensor_swap_interface::SwapHandler; impl Pallet { @@ -99,7 +99,7 @@ impl Pallet { // Attain the root claimed to avoid overclaiming. let root_claimed: I96F32 = - I96F32::saturating_from_num(RootClaimed::::get((hotkey, coldkey, netuid))); + I96F32::saturating_from_num(RootClaimed::::get((netuid, hotkey, coldkey))); // Subtract the already claimed alpha. let owed: I96F32 = claimable.saturating_sub(root_claimed); @@ -200,7 +200,7 @@ impl Pallet { }; // Increase root claimed by owed amount. - RootClaimed::::mutate((hotkey, coldkey, netuid), |root_claimed| { + RootClaimed::::mutate((netuid, hotkey, coldkey), |root_claimed| { *root_claimed = root_claimed.saturating_add(owed_u64.into()); }); } @@ -250,7 +250,7 @@ impl Pallet { let root_claimable = RootClaimable::::get(hotkey); for (netuid, claimable_rate) in root_claimable.iter() { // Get current staker root claimed value. - let root_claimed: u128 = RootClaimed::::get((hotkey, coldkey, netuid)); + let root_claimed: u128 = RootClaimed::::get((netuid, hotkey, coldkey)); // Increase root claimed based on the claimable rate. let new_root_claimed = root_claimed.saturating_add( @@ -260,7 +260,7 @@ impl Pallet { ); // Set the new root claimed value. - RootClaimed::::insert((hotkey, coldkey, netuid), new_root_claimed); + RootClaimed::::insert((netuid, hotkey, coldkey), new_root_claimed); } } @@ -277,7 +277,7 @@ impl Pallet { } // Get current staker root claimed value. - let root_claimed: u128 = RootClaimed::::get((hotkey, coldkey, netuid)); + let root_claimed: u128 = RootClaimed::::get((netuid, hotkey, coldkey)); // Decrease root claimed based on the claimable rate. let new_root_claimed = root_claimed.saturating_sub( @@ -287,7 +287,7 @@ impl Pallet { ); // Set the new root_claimed value. - RootClaimed::::insert((hotkey, coldkey, netuid), new_root_claimed); + RootClaimed::::insert((netuid, hotkey, coldkey), new_root_claimed); } } @@ -359,10 +359,10 @@ impl Pallet { old_coldkey: &T::AccountId, new_coldkey: &T::AccountId, ) { - let old_root_claimed = RootClaimed::::get((old_hotkey, old_coldkey, netuid)); - RootClaimed::::remove((old_hotkey, old_coldkey, netuid)); + let old_root_claimed = RootClaimed::::get((netuid, old_hotkey, old_coldkey)); + RootClaimed::::remove((netuid, old_hotkey, old_coldkey)); - RootClaimed::::mutate((new_hotkey, new_coldkey, netuid), |new_root_claimed| { + RootClaimed::::mutate((netuid, new_hotkey, new_coldkey), |new_root_claimed| { *new_root_claimed = old_root_claimed.saturating_add(*new_root_claimed); }); } @@ -386,35 +386,14 @@ impl Pallet { /// Claim all root dividends for subnet and remove all associated data. pub fn finalize_all_subnet_root_dividends(netuid: NetUid) { - let mut hotkeys_to_clear = BTreeSet::new(); - for (hotkey, root_claimable) in RootClaimable::::iter() { - if root_claimable.contains_key(&netuid) { - let alpha_values: Vec<((T::AccountId, NetUid), U64F64)> = - Alpha::::iter_prefix((&hotkey,)).collect(); - - for ((coldkey, alpha_netuid), _) in alpha_values.into_iter() { - if alpha_netuid == NetUid::ROOT { - Self::root_claim_on_subnet( - &hotkey, - &coldkey, - netuid, - RootClaimTypeEnum::Swap, - true, - ); - - RootClaimed::::remove((hotkey.clone(), coldkey, netuid)); - if !hotkeys_to_clear.contains(&hotkey) { - hotkeys_to_clear.insert(hotkey.clone()); - } - } - } - } - } + let hotkeys = RootClaimable::::iter_keys().collect::>(); - for hotkey in hotkeys_to_clear.into_iter() { - RootClaimable::::mutate(&hotkey, |claimable| { + for hotkey in hotkeys.iter() { + RootClaimable::::mutate(hotkey, |claimable| { claimable.remove(&netuid); }); } + + let _ = RootClaimed::::clear_prefix((netuid,), u32::MAX, None); } } diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index e8a295ae0d..e73417a326 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -135,7 +135,7 @@ fn test_claim_root_with_drain_emissions() { // Check root claimed value saved - let claimed = RootClaimed::::get((&hotkey, &coldkey, netuid)); + let claimed = RootClaimed::::get((netuid, &hotkey, &coldkey)); assert_eq!(u128::from(new_stake), claimed); // Distribute pending root alpha (round 2) @@ -180,7 +180,7 @@ fn test_claim_root_with_drain_emissions() { // Check root claimed value saved (round 2) - let claimed = RootClaimed::::get((&hotkey, &coldkey, netuid)); + let claimed = RootClaimed::::get((netuid, &hotkey, &coldkey)); assert_eq!(u128::from(u64::from(new_stake2)), claimed); }); } @@ -1125,11 +1125,11 @@ fn test_claim_root_with_swap_coldkey() { assert_eq!( u128::from(new_stake), - RootClaimed::::get((&hotkey, &coldkey, netuid)) + RootClaimed::::get((netuid, &hotkey, &coldkey)) ); assert_eq!( 0u128, - RootClaimed::::get((&hotkey, &new_coldkey, netuid)) + RootClaimed::::get((netuid, &hotkey, &new_coldkey)) ); // Swap coldkey @@ -1143,10 +1143,10 @@ fn test_claim_root_with_swap_coldkey() { // Check swapped keys claimed values - assert_eq!(0u128, RootClaimed::::get((&hotkey, &coldkey, netuid))); + assert_eq!(0u128, RootClaimed::::get((netuid, &hotkey, &coldkey))); assert_eq!( u128::from(new_stake), - RootClaimed::::get((&hotkey, &new_coldkey, netuid)) + RootClaimed::::get((netuid, &hotkey, &new_coldkey,)) ); }); } @@ -1215,11 +1215,11 @@ fn test_claim_root_with_swap_hotkey() { assert_eq!( u128::from(new_stake), - RootClaimed::::get((&hotkey, &coldkey, netuid)) + RootClaimed::::get((netuid, &hotkey, &coldkey,)) ); assert_eq!( 0u128, - RootClaimed::::get((&new_hotkey, &coldkey, netuid)) + RootClaimed::::get((netuid, &new_hotkey, &coldkey,)) ); let _old_claimable = *RootClaimable::::get(hotkey) @@ -1239,10 +1239,13 @@ fn test_claim_root_with_swap_hotkey() { // Check swapped keys claimed values - assert_eq!(0u128, RootClaimed::::get((&hotkey, &coldkey, netuid))); + assert_eq!( + 0u128, + RootClaimed::::get((netuid, &hotkey, &coldkey,)) + ); assert_eq!( u128::from(new_stake), - RootClaimed::::get((&new_hotkey, &coldkey, netuid)) + RootClaimed::::get((netuid, &new_hotkey, &coldkey,)) ); assert!(!RootClaimable::::get(hotkey).contains_key(&netuid)); @@ -1281,7 +1284,6 @@ fn test_claim_root_on_network_deregistration() { NetUid::ROOT, root_stake.into(), ); - let root_stake_rate = 0.1f64; SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &other_coldkey, @@ -1307,33 +1309,23 @@ fn test_claim_root_on_network_deregistration() { AlphaCurrency::ZERO, ); - // Claim root via network deregistration - - assert_ok!(SubtensorModule::do_dissolve_network(netuid)); + assert_ok!(SubtensorModule::claim_root( + RuntimeOrigin::signed(coldkey), + BTreeSet::from([netuid]) + )); - // Check new stake - let validator_take_percent = 0.18f64; + assert!(RootClaimable::::get(hotkey).contains_key(&netuid)); - let new_stake: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey, - &coldkey, - NetUid::ROOT, - ) - .into(); + assert!(RootClaimed::::contains_key(( + netuid, &hotkey, &coldkey, + ))); - let estimated_stake_increment = (pending_root_alpha as f64) - * (1f64 - validator_take_percent) - * current_price - * root_stake_rate; + // Claim root via network deregistration - assert_abs_diff_eq!( - new_stake, - root_stake + estimated_stake_increment as u64, - epsilon = 10000u64, - ); + assert_ok!(SubtensorModule::do_dissolve_network(netuid)); assert!(!RootClaimed::::contains_key(( - &hotkey, &coldkey, netuid + netuid, &hotkey, &coldkey, ))); assert!(!RootClaimable::::get(hotkey).contains_key(&netuid)); }); @@ -1505,7 +1497,7 @@ fn test_claim_root_with_unrelated_subnets() { // Check root claimed value saved - let claimed = RootClaimed::::get((&hotkey, &coldkey, netuid)); + let claimed = RootClaimed::::get((netuid, &hotkey, &coldkey)); assert_eq!(u128::from(new_stake), claimed); }); } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index a4ee4a3b93..d8277164e2 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 333, + spec_version: 334, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From e4da0529bceb1d8951f7c971ef6afa857a8fc22b Mon Sep 17 00:00:00 2001 From: Shamil Gadelshin Date: Mon, 3 Nov 2025 18:57:26 +0700 Subject: [PATCH 021/210] Bump chain spec version. --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index a4ee4a3b93..d8277164e2 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 333, + spec_version: 334, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From c6cfaeb14ee407ae291d48832f10cc416fe35103 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 3 Nov 2025 15:53:08 +0000 Subject: [PATCH 022/210] auto-update benchmark weights --- pallets/subtensor/src/macros/dispatches.rs | 28 +++++++++++----------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index af497958a2..7885e92552 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -712,8 +712,8 @@ mod dispatches { /// #[pallet::call_index(2)] #[pallet::weight((Weight::from_parts(340_800_000, 0) - .saturating_add(T::DbWeight::get().reads(24_u64)) - .saturating_add(T::DbWeight::get().writes(15)), DispatchClass::Normal, Pays::Yes))] + .saturating_add(T::DbWeight::get().reads(25_u64)) + .saturating_add(T::DbWeight::get().writes(16_u64)), DispatchClass::Normal, Pays::Yes))] pub fn add_stake( origin: OriginFor, hotkey: T::AccountId, @@ -1587,8 +1587,8 @@ mod dispatches { /// - Thrown if key has hit transaction rate limit #[pallet::call_index(84)] #[pallet::weight((Weight::from_parts(358_500_000, 0) - .saturating_add(T::DbWeight::get().reads(39_u64)) - .saturating_add(T::DbWeight::get().writes(24_u64)), DispatchClass::Normal, Pays::Yes))] + .saturating_add(T::DbWeight::get().reads(41_u64)) + .saturating_add(T::DbWeight::get().writes(26_u64)), DispatchClass::Normal, Pays::Yes))] pub fn unstake_all_alpha(origin: OriginFor, hotkey: T::AccountId) -> DispatchResult { Self::do_unstake_all_alpha(origin, hotkey) } @@ -1701,8 +1701,8 @@ mod dispatches { #[pallet::call_index(87)] #[pallet::weight(( Weight::from_parts(351_300_000, 0) - .saturating_add(T::DbWeight::get().reads(35_u64)) - .saturating_add(T::DbWeight::get().writes(22_u64)), + .saturating_add(T::DbWeight::get().reads(37_u64)) + .saturating_add(T::DbWeight::get().writes(24_u64)), DispatchClass::Normal, Pays::Yes ))] @@ -1766,8 +1766,8 @@ mod dispatches { /// #[pallet::call_index(88)] #[pallet::weight((Weight::from_parts(402_900_000, 0) - .saturating_add(T::DbWeight::get().reads(24_u64)) - .saturating_add(T::DbWeight::get().writes(15)), DispatchClass::Normal, Pays::Yes))] + .saturating_add(T::DbWeight::get().reads(25_u64)) + .saturating_add(T::DbWeight::get().writes(16_u64)), DispatchClass::Normal, Pays::Yes))] pub fn add_stake_limit( origin: OriginFor, hotkey: T::AccountId, @@ -1830,8 +1830,8 @@ mod dispatches { /// #[pallet::call_index(89)] #[pallet::weight((Weight::from_parts(377_400_000, 0) - .saturating_add(T::DbWeight::get().reads(28_u64)) - .saturating_add(T::DbWeight::get().writes(14)), DispatchClass::Normal, Pays::Yes))] + .saturating_add(T::DbWeight::get().reads(29_u64)) + .saturating_add(T::DbWeight::get().writes(15_u64)), DispatchClass::Normal, Pays::Yes))] pub fn remove_stake_limit( origin: OriginFor, hotkey: T::AccountId, @@ -1874,8 +1874,8 @@ mod dispatches { #[pallet::call_index(90)] #[pallet::weight(( Weight::from_parts(411_500_000, 0) - .saturating_add(T::DbWeight::get().reads(35_u64)) - .saturating_add(T::DbWeight::get().writes(22_u64)), + .saturating_add(T::DbWeight::get().reads(37_u64)) + .saturating_add(T::DbWeight::get().writes(24_u64)), DispatchClass::Normal, Pays::Yes ))] @@ -2052,8 +2052,8 @@ mod dispatches { /// Without limit_price it remove all the stake similar to `remove_stake` extrinsic #[pallet::call_index(103)] #[pallet::weight((Weight::from_parts(395_300_000, 10142) - .saturating_add(T::DbWeight::get().reads(28_u64)) - .saturating_add(T::DbWeight::get().writes(14_u64)), DispatchClass::Normal, Pays::Yes))] + .saturating_add(T::DbWeight::get().reads(29_u64)) + .saturating_add(T::DbWeight::get().writes(15_u64)), DispatchClass::Normal, Pays::Yes))] pub fn remove_stake_full_limit( origin: T::RuntimeOrigin, hotkey: T::AccountId, From 0d66053d30a0d9d9845d596a62108a343a8b7d7e Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 3 Nov 2025 13:22:57 -0500 Subject: [PATCH 023/210] Init flows instead of transition, set temperature = 1.0 --- .../src/coinbase/subnet_emissions.rs | 91 ++-------------- pallets/subtensor/src/lib.rs | 8 +- pallets/subtensor/src/macros/hooks.rs | 2 - .../migrate_set_first_tao_flow_block.rs | 38 ------- pallets/subtensor/src/migrations/mod.rs | 1 - pallets/subtensor/src/tests/coinbase.rs | 25 ++--- .../subtensor/src/tests/subnet_emissions.rs | 100 ------------------ 7 files changed, 20 insertions(+), 245 deletions(-) delete mode 100644 pallets/subtensor/src/migrations/migrate_set_first_tao_flow_block.rs diff --git a/pallets/subtensor/src/coinbase/subnet_emissions.rs b/pallets/subtensor/src/coinbase/subnet_emissions.rs index 3677b96f97..58335ac62c 100644 --- a/pallets/subtensor/src/coinbase/subnet_emissions.rs +++ b/pallets/subtensor/src/coinbase/subnet_emissions.rs @@ -4,6 +4,7 @@ use alloc::collections::BTreeMap; use safe_math::FixedExt; use substrate_fixed::transcendental::{exp, ln}; use substrate_fixed::types::{I32F32, I64F64, U64F64, U96F32}; +use subtensor_swap_interface::SwapHandler; impl Pallet { pub fn get_subnet_block_emissions( @@ -73,8 +74,12 @@ impl Pallet { last_block_ema } } else { - // Initialize EMA flow, set S(current_block) = 0 - let ema_flow = I64F64::saturating_from_num(0); + // Initialize EMA flow, set S(current_block) = min(price, ema_price) * init_factor + let init_factor = I64F64::saturating_from_num(1.); + let moving_price = I64F64::saturating_from_num(Self::get_moving_alpha_price(netuid)); + let current_price = + I64F64::saturating_from_num(T::SwapInterface::current_alpha_price(netuid)); + let ema_flow = moving_price.min(current_price).saturating_mul(init_factor); SubnetEmaTaoFlow::::insert(netuid, (current_block, ema_flow)); ema_flow } @@ -206,88 +211,8 @@ impl Pallet { offset_flows } - // DEPRECATED: Implementation of shares that uses EMA prices will be gradually deprecated - fn get_shares_price_ema(subnets_to_emit_to: &[NetUid]) -> BTreeMap { - // Get sum of alpha moving prices - let total_moving_prices = subnets_to_emit_to - .iter() - .map(|netuid| U64F64::saturating_from_num(Self::get_moving_alpha_price(*netuid))) - .fold(U64F64::saturating_from_num(0.0), |acc, ema| { - acc.saturating_add(ema) - }); - log::debug!("total_moving_prices: {total_moving_prices:?}"); - - // Calculate shares. - subnets_to_emit_to - .iter() - .map(|netuid| { - let moving_price = - U64F64::saturating_from_num(Self::get_moving_alpha_price(*netuid)); - log::debug!("moving_price_i: {moving_price:?}"); - - let share = moving_price - .checked_div(total_moving_prices) - .unwrap_or(U64F64::saturating_from_num(0)); - - (*netuid, share) - }) - .collect::>() - } - // Combines ema price method and tao flow method linearly over FlowHalfLife blocks pub(crate) fn get_shares(subnets_to_emit_to: &[NetUid]) -> BTreeMap { - let current_block: u64 = Self::get_current_block_as_u64(); - - // Weight of tao flow method - let period = FlowHalfLife::::get(); - let one = U64F64::saturating_from_num(1); - let zero = U64F64::saturating_from_num(0); - let tao_flow_weight = if let Some(start_block) = FlowFirstBlock::::get() { - if (current_block > start_block) && (current_block < start_block.saturating_add(period)) - { - // Combination period in progress - let start_fixed = U64F64::saturating_from_num(start_block); - let current_fixed = U64F64::saturating_from_num(current_block); - let period_fixed = U64F64::saturating_from_num(period); - current_fixed - .saturating_sub(start_fixed) - .safe_div(period_fixed) - } else if current_block >= start_block.saturating_add(period) { - // Over combination period - one - } else { - // Not yet in combination period - zero - } - } else { - zero - }; - - // Get shares for each method as needed - let shares_flow = if tao_flow_weight > zero { - Self::get_shares_flow(subnets_to_emit_to) - } else { - BTreeMap::new() - }; - - let shares_prices = if tao_flow_weight < one { - Self::get_shares_price_ema(subnets_to_emit_to) - } else { - BTreeMap::new() - }; - - // Combine - let mut shares_combined = BTreeMap::new(); - for netuid in subnets_to_emit_to.iter() { - let share_flow = shares_flow.get(netuid).unwrap_or(&zero); - let share_prices = shares_prices.get(netuid).unwrap_or(&zero); - shares_combined.insert( - *netuid, - share_flow.saturating_mul(tao_flow_weight).saturating_add( - share_prices.saturating_mul(one.saturating_sub(tao_flow_weight)), - ), - ); - } - shares_combined + Self::get_shares_flow(subnets_to_emit_to) } } diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 93d082f4a6..bfd0e7235b 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -88,7 +88,6 @@ pub mod pallet { use frame_system::pallet_prelude::*; use pallet_drand::types::RoundNumber; use runtime_common::prod_or_fast; - use safe_math::FixedExt; use sp_core::{ConstU32, H160, H256}; use sp_runtime::traits::{Dispatchable, TrailingZeroInput}; use sp_std::collections::btree_map::BTreeMap; @@ -1292,7 +1291,7 @@ pub mod pallet { #[pallet::type_value] /// Default value for flow normalization exponent. pub fn DefaultFlowNormExponent() -> U64F64 { - U64F64::saturating_from_num(15).safe_div(U64F64::saturating_from_num(10)) + U64F64::saturating_from_num(1) } #[pallet::storage] /// --- ITEM --> Flow Normalization Exponent (p) @@ -1302,7 +1301,7 @@ pub mod pallet { /// Default value for flow EMA smoothing. pub fn DefaultFlowEmaSmoothingFactor() -> u64 { // Example values: - // half-life factor value i64 normalized + // half-life factor value i64 normalized (x 2^63) // 216000 (1 month) --> 0.000003209009576 ( 29_597_889_189_277) // 50400 (1 week) --> 0.000013752825678 (126_847_427_788_335) 29_597_889_189_277 @@ -1316,9 +1315,6 @@ pub mod pallet { /// --- ITEM --> Flow EMA smoothing factor (flow alpha), u64 normalized pub type FlowEmaSmoothingFactor = StorageValue<_, u64, ValueQuery, DefaultFlowEmaSmoothingFactor>; - #[pallet::storage] - /// --- ITEM --> Block when TAO flow calculation starts(ed) - pub type FlowFirstBlock = StorageValue<_, u64, OptionQuery>; /// ============================ /// ==== Global Parameters ===== diff --git a/pallets/subtensor/src/macros/hooks.rs b/pallets/subtensor/src/macros/hooks.rs index a4cdbfbe94..87a87e911c 100644 --- a/pallets/subtensor/src/macros/hooks.rs +++ b/pallets/subtensor/src/macros/hooks.rs @@ -158,8 +158,6 @@ mod hooks { .saturating_add(migrations::migrate_auto_stake_destination::migrate_auto_stake_destination::()) // Migrate Kappa to default (0.5) .saturating_add(migrations::migrate_kappa_map_to_default::migrate_kappa_map_to_default::()) - // Set the first block of tao flow - .saturating_add(migrations::migrate_set_first_tao_flow_block::migrate_set_first_tao_flow_block::()) // Remove obsolete map entries .saturating_add(migrations::migrate_remove_tao_dividends::migrate_remove_tao_dividends::()); weight diff --git a/pallets/subtensor/src/migrations/migrate_set_first_tao_flow_block.rs b/pallets/subtensor/src/migrations/migrate_set_first_tao_flow_block.rs deleted file mode 100644 index 99c10b99ba..0000000000 --- a/pallets/subtensor/src/migrations/migrate_set_first_tao_flow_block.rs +++ /dev/null @@ -1,38 +0,0 @@ -use super::*; -use crate::HasMigrationRun; -use frame_support::{traits::Get, weights::Weight}; -use scale_info::prelude::string::String; - -pub fn migrate_set_first_tao_flow_block() -> Weight { - let migration_name = b"migrate_set_first_tao_flow_block".to_vec(); - - let mut weight = T::DbWeight::get().reads(1); - if HasMigrationRun::::get(&migration_name) { - log::info!( - "Migration '{:?}' has already run. Skipping.", - String::from_utf8_lossy(&migration_name) - ); - return weight; - } - - log::info!( - "Running migration '{:?}'", - String::from_utf8_lossy(&migration_name) - ); - - // Actual migration - let current_block = Pallet::::get_current_block_as_u64(); - FlowFirstBlock::::set(Some(current_block)); - weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); - - // Mark Migration as Completed - HasMigrationRun::::insert(&migration_name, true); - weight = weight.saturating_add(T::DbWeight::get().reads(2)); - - log::info!( - "Migration '{:?}' completed successfully.", - String::from_utf8_lossy(&migration_name) - ); - - weight -} diff --git a/pallets/subtensor/src/migrations/mod.rs b/pallets/subtensor/src/migrations/mod.rs index 2313870244..d95e4c7bac 100644 --- a/pallets/subtensor/src/migrations/mod.rs +++ b/pallets/subtensor/src/migrations/mod.rs @@ -40,7 +40,6 @@ pub mod migrate_remove_zero_total_hotkey_alpha; pub mod migrate_reset_bonds_moving_average; pub mod migrate_reset_max_burn; pub mod migrate_set_first_emission_block_number; -pub mod migrate_set_first_tao_flow_block; pub mod migrate_set_min_burn; pub mod migrate_set_min_difficulty; pub mod migrate_set_nominator_min_stake; diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 75d4cc3ce9..2d0376c896 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -127,7 +127,6 @@ fn test_coinbase_tao_issuance_base_low_flow() { // 100% tao flow method let block_num = FlowHalfLife::::get(); SubnetEmaTaoFlow::::insert(netuid, (block_num, I64F64::from_num(1_000_000_000))); - FlowFirstBlock::::set(Some(0_u64)); System::set_block_number(block_num); let tao_in_before = SubnetTAO::::get(netuid); @@ -237,12 +236,12 @@ fn test_coinbase_tao_issuance_different_prices() { assert_abs_diff_eq!( SubnetTAO::::get(netuid1), TaoCurrency::from(initial_tao + emission / 3), - epsilon = 1.into(), + epsilon = 10.into(), ); assert_abs_diff_eq!( SubnetTAO::::get(netuid2), TaoCurrency::from(initial_tao + 2 * emission / 3), - epsilon = 1.into(), + epsilon = 10.into(), ); // Prices are low => we limit tao issued (buy alpha with it) @@ -298,11 +297,9 @@ fn test_coinbase_tao_issuance_different_flows() { SubnetMovingPrice::::insert(netuid2, I96F32::from_num(1)); // Set subnet tao flow ema. - // 100% tao flow method let block_num = FlowHalfLife::::get(); SubnetEmaTaoFlow::::insert(netuid1, (block_num, I64F64::from_num(1))); SubnetEmaTaoFlow::::insert(netuid2, (block_num, I64F64::from_num(2))); - FlowFirstBlock::::set(Some(0_u64)); System::set_block_number(block_num); // Set normalization exponent to 1 for simplicity @@ -490,9 +487,9 @@ fn test_coinbase_alpha_issuance_base() { }); } -// Test alpha issuance with different subnet prices. +// Test alpha issuance with different subnet flows. // This test verifies that: -// - Alpha issuance is proportional to subnet prices +// - Alpha issuance is proportional to subnet flows // - Higher priced subnets receive more TAO emission // - Alpha issuance is correctly calculated based on price ratios // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_coinbase_alpha_issuance_different --exact --show-output --nocapture @@ -507,18 +504,16 @@ fn test_coinbase_alpha_issuance_different() { // Make subnets dynamic. SubnetMechanism::::insert(netuid1, 1); SubnetMechanism::::insert(netuid2, 1); - // Setup prices 1 and 1 + // Setup prices 1 and 2 let initial: u64 = 1_000_000; SubnetTAO::::insert(netuid1, TaoCurrency::from(initial)); SubnetAlphaIn::::insert(netuid1, AlphaCurrency::from(initial)); - SubnetTAO::::insert(netuid2, TaoCurrency::from(initial)); + SubnetTAO::::insert(netuid2, TaoCurrency::from(2 * initial)); SubnetAlphaIn::::insert(netuid2, AlphaCurrency::from(initial)); - // Set subnet prices. + // Set subnet ema prices to 1 and 2 SubnetMovingPrice::::insert(netuid1, I96F32::from_num(1)); SubnetMovingPrice::::insert(netuid2, I96F32::from_num(2)); - // Set tao flow - SubnetEmaTaoFlow::::insert(netuid1, (1u64, I64F64::from_num(1))); - SubnetEmaTaoFlow::::insert(netuid2, (1u64, I64F64::from_num(2))); + // Do NOT Set tao flow, let it initialize // Run coinbase SubtensorModule::run_coinbase(U96F32::from_num(emission)); // tao_in = 333_333 @@ -528,10 +523,10 @@ fn test_coinbase_alpha_issuance_different() { (initial + emission / 3).into() ); // tao_in = 666_666 - // alpha_in = 666_666/price = 666_666 + initial + // alpha_in = 666_666/price = 333_333 + initial assert_eq!( SubnetAlphaIn::::get(netuid2), - (initial + emission / 3 + emission / 3).into() + (initial + (emission * 2 / 3) / 2).into() ); }); } diff --git a/pallets/subtensor/src/tests/subnet_emissions.rs b/pallets/subtensor/src/tests/subnet_emissions.rs index aeece09c2e..9f29b3fee2 100644 --- a/pallets/subtensor/src/tests/subnet_emissions.rs +++ b/pallets/subtensor/src/tests/subnet_emissions.rs @@ -163,9 +163,7 @@ fn get_shares_normal_flows_three_subnets() { let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); let n3 = add_dynamic_network(&owner_hotkey, &owner_coldkey); - // 100% tao flow method let block_num = FlowHalfLife::::get(); - FlowFirstBlock::::set(Some(0_u64)); System::set_block_number(block_num); // Set (block_number, flow) with reasonable positive flows @@ -212,9 +210,7 @@ fn get_shares_low_flows_sum_one_and_ordering() { let n1 = add_dynamic_network(&owner_hotkey, &owner_coldkey); let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); - // 100% tao flow method let block_num = FlowHalfLife::::get(); - FlowFirstBlock::::set(Some(0_u64)); System::set_block_number(block_num); // Tiny flows to exercise precision/scaling path @@ -256,9 +252,7 @@ fn get_shares_high_flows_sum_one_and_ordering() { let n1 = add_dynamic_network(&owner_hotkey, &owner_coldkey); let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); - // 100% tao flow method let block_num = FlowHalfLife::::get(); - FlowFirstBlock::::set(Some(0_u64)); System::set_block_number(block_num); // Large but safe flows for I64F64 @@ -298,95 +292,6 @@ fn seed_price_and_flow(n1: NetUid, n2: NetUid, price1: f64, price2: f64, flow1: SubnetEmaTaoFlow::::insert(n2, (now, i64f64(flow2))); } -#[test] -fn get_shares_price_flow_blend_1v3_price_and_3v1_flow() { - new_test_ext(1).execute_with(|| { - // two subnets - let owner_hotkey = U256::from(42); - let owner_coldkey = U256::from(43); - let n1 = add_dynamic_network(&owner_hotkey, &owner_coldkey); - let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); - - // define "window" length half_life blocks and set first block to 0 - let half_life: u64 = FlowHalfLife::::get(); - FlowFirstBlock::::set(Some(0_u64)); - FlowNormExponent::::set(u64f64(1.0)); - - // t = 0: expect (0.25, 0.75) - frame_system::Pallet::::set_block_number(0); - seed_price_and_flow(n1, n2, /*price*/ 1.0, 3.0, /*flow*/ 3.0, 1.0); - let shares = SubtensorModule::get_shares(&[n1, n2]); - let s1 = shares.get(&n1).unwrap().to_num::(); - let s2 = shares.get(&n2).unwrap().to_num::(); - assert_abs_diff_eq!(s1 + s2, 1.0_f64, epsilon = 1e-9); - assert_abs_diff_eq!(s1, 0.25_f64, epsilon = 1e-6); - assert_abs_diff_eq!(s2, 0.75_f64, epsilon = 1e-6); - - // t = half_life/2: expect (0.5, 0.5) - frame_system::Pallet::::set_block_number(half_life / 2); - seed_price_and_flow(n1, n2, 1.0, 3.0, 3.0, 1.0); - let shares = SubtensorModule::get_shares(&[n1, n2]); - let s1 = shares.get(&n1).unwrap().to_num::(); - let s2 = shares.get(&n2).unwrap().to_num::(); - assert_abs_diff_eq!(s1 + s2, 1.0_f64, epsilon = 1e-9); - assert_abs_diff_eq!(s1, 0.5_f64, epsilon = 1e-6); - assert_abs_diff_eq!(s2, 0.5_f64, epsilon = 1e-6); - - // t = half_life: expect (0.75, 0.25) - frame_system::Pallet::::set_block_number(half_life); - seed_price_and_flow(n1, n2, 1.0, 3.0, 3.0, 1.0); - let shares = SubtensorModule::get_shares(&[n1, n2]); - let s1 = shares.get(&n1).unwrap().to_num::(); - let s2 = shares.get(&n2).unwrap().to_num::(); - assert_abs_diff_eq!(s1 + s2, 1.0_f64, epsilon = 1e-9); - assert_abs_diff_eq!(s1, 0.75_f64, epsilon = 1e-6); - assert_abs_diff_eq!(s2, 0.25_f64, epsilon = 1e-6); - }); -} - -#[test] -fn get_shares_price_flow_blend_3v1_price_and_1v3_flow() { - new_test_ext(1).execute_with(|| { - let owner_hotkey = U256::from(50); - let owner_coldkey = U256::from(51); - let n1 = add_dynamic_network(&owner_hotkey, &owner_coldkey); - let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); - - // window half_life and anchor at 0 - let half_life: u64 = FlowHalfLife::::get(); - FlowFirstBlock::::set(Some(0_u64)); - FlowNormExponent::::set(u64f64(1.0)); - - // t = 0: prices dominate → (0.75, 0.25) - frame_system::Pallet::::set_block_number(0); - seed_price_and_flow(n1, n2, /*price*/ 3.0, 1.0, /*flow*/ 1.0, 3.0); - let shares = SubtensorModule::get_shares(&[n1, n2]); - let s1 = shares.get(&n1).unwrap().to_num::(); - let s2 = shares.get(&n2).unwrap().to_num::(); - assert_abs_diff_eq!(s1 + s2, 1.0_f64, epsilon = 1e-9); - assert_abs_diff_eq!(s1, 0.75_f64, epsilon = 1e-6); - assert_abs_diff_eq!(s2, 0.25_f64, epsilon = 1e-6); - - // t = half_life/2: equal → (0.5, 0.5) - frame_system::Pallet::::set_block_number(half_life / 2); - seed_price_and_flow(n1, n2, 3.0, 1.0, 1.0, 3.0); - let shares = SubtensorModule::get_shares(&[n1, n2]); - let s1 = shares.get(&n1).unwrap().to_num::(); - let s2 = shares.get(&n2).unwrap().to_num::(); - assert_abs_diff_eq!(s1, 0.5_f64, epsilon = 1e-6); - assert_abs_diff_eq!(s2, 0.5_f64, epsilon = 1e-6); - - // t = half_life: flows dominate → (0.25, 0.75) - frame_system::Pallet::::set_block_number(half_life); - seed_price_and_flow(n1, n2, 3.0, 1.0, 1.0, 3.0); - let shares = SubtensorModule::get_shares(&[n1, n2]); - let s1 = shares.get(&n1).unwrap().to_num::(); - let s2 = shares.get(&n2).unwrap().to_num::(); - assert_abs_diff_eq!(s1, 0.25_f64, epsilon = 1e-6); - assert_abs_diff_eq!(s2, 0.75_f64, epsilon = 1e-6); - }); -} - /// If one subnet has a negative EMA flow and the other positive, /// the negative one should contribute no weight (treated as zero), /// so the positive-flow subnet gets the full share. @@ -401,7 +306,6 @@ fn get_shares_negative_vs_positive_flow() { // Configure blending window and current block let half_life: u64 = FlowHalfLife::::get(); - FlowFirstBlock::::set(Some(0_u64)); FlowNormExponent::::set(u64f64(1.0)); frame_system::Pallet::::set_block_number(half_life); TaoFlowCutoff::::set(I64F64::from_num(0)); @@ -442,7 +346,6 @@ fn get_shares_both_negative_flows_zero_emission() { // Configure blending window and current block let half_life: u64 = FlowHalfLife::::get(); - FlowFirstBlock::::set(Some(0_u64)); FlowNormExponent::::set(u64f64(1.0)); frame_system::Pallet::::set_block_number(half_life); TaoFlowCutoff::::set(I64F64::from_num(0)); @@ -479,7 +382,6 @@ fn get_shares_both_below_cutoff_zero_emission() { // Configure blending window and current block let half_life: u64 = FlowHalfLife::::get(); - FlowFirstBlock::::set(Some(0_u64)); FlowNormExponent::::set(u64f64(1.0)); frame_system::Pallet::::set_block_number(half_life); TaoFlowCutoff::::set(I64F64::from_num(2_000)); @@ -519,7 +421,6 @@ fn get_shares_one_below_cutoff_other_full_emission() { // Configure blending window and current block let half_life: u64 = FlowHalfLife::::get(); - FlowFirstBlock::::set(Some(0_u64)); FlowNormExponent::::set(u64f64(1.0)); frame_system::Pallet::::set_block_number(half_life); TaoFlowCutoff::::set(I64F64::from_num(2_000)); @@ -561,7 +462,6 @@ fn get_shares_both_negative_above_cutoff() { // Configure blending window and current block let half_life: u64 = FlowHalfLife::::get(); - FlowFirstBlock::::set(Some(0_u64)); FlowNormExponent::::set(u64f64(1.0)); frame_system::Pallet::::set_block_number(half_life); TaoFlowCutoff::::set(I64F64::from_num(-1000.0)); From 895a29c2459365a1efe828999203367ea440c383 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Mon, 3 Nov 2025 14:26:32 -0500 Subject: [PATCH 024/210] impl --- Cargo.lock | 1 + pallets/proxy/src/lib.rs | 19 ++++++++++++++++++- pallets/subtensor/Cargo.toml | 1 + pallets/subtensor/src/tests/mock.rs | 2 ++ precompiles/src/proxy.rs | 21 ++++++++++++++++++++- 5 files changed, 42 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 18ee1cd90f..05e79f5962 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8132,6 +8132,7 @@ dependencies = [ "pallet-drand", "pallet-membership", "pallet-preimage", + "pallet-proxy 40.1.0", "pallet-scheduler", "pallet-subtensor-swap", "pallet-utility 40.0.0", diff --git a/pallets/proxy/src/lib.rs b/pallets/proxy/src/lib.rs index a4325bd099..8d9be72bb2 100644 --- a/pallets/proxy/src/lib.rs +++ b/pallets/proxy/src/lib.rs @@ -41,6 +41,7 @@ use frame::{ }; pub use pallet::*; use subtensor_macros::freeze_struct; +use frame_system::pallet_prelude::BlockNumberFor as SystemBlockNumberFor; pub use weights::WeightInfo; type CallHashOf = <::CallHasher as Hash>::Output; @@ -747,6 +748,14 @@ pub mod pallet { NoSelfProxy, } + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_finalize(_n: SystemBlockNumberFor) { + // clear this map on end of each block + let _ = LastCallResult::::clear(u32::MAX, None); + } + } + /// The set of account proxies. Maps the account which has delegated to the accounts /// which are being delegated to, together with the amount held on deposit. #[pallet::storage] @@ -777,6 +786,11 @@ pub mod pallet { ValueQuery, >; + /// The result of the last call made by the proxy (key). + #[pallet::storage] + pub type LastCallResult = + StorageMap<_, Twox64Concat, T::AccountId, DispatchResult, OptionQuery>; + #[pallet::view_functions_experimental] impl Pallet { /// Check if a `RuntimeCall` is allowed for a given `ProxyType`. @@ -1022,7 +1036,7 @@ impl Pallet { ) { use frame::traits::{InstanceFilter as _, OriginTrait as _}; // This is a freshly authenticated new account, the origin restrictions doesn't apply. - let mut origin: T::RuntimeOrigin = frame_system::RawOrigin::Signed(real).into(); + let mut origin: T::RuntimeOrigin = frame_system::RawOrigin::Signed(real.clone()).into(); origin.add_filter(move |c: &::RuntimeCall| { let c = ::RuntimeCall::from_ref(c); // We make sure the proxy call does access this pallet to change modify proxies. @@ -1046,6 +1060,9 @@ impl Pallet { } }); let e = call.dispatch(origin); + + LastCallResult::::insert(real, e.map(|_| ()).map_err(|e| e.error)); + Self::deposit_event(Event::ProxyExecuted { result: e.map(|_| ()).map_err(|e| e.error), }); diff --git a/pallets/subtensor/Cargo.toml b/pallets/subtensor/Cargo.toml index ed40d8d36f..97ade5a97c 100644 --- a/pallets/subtensor/Cargo.toml +++ b/pallets/subtensor/Cargo.toml @@ -45,6 +45,7 @@ pallet-drand.workspace = true pallet-commitments.workspace = true pallet-collective.workspace = true pallet-membership.workspace = true +pallet-proxy.workspace = true hex-literal.workspace = true num-traits = { workspace = true, features = ["libm"] } tle.workspace = true diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index 651c208ad4..df4f6b8afe 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -45,6 +45,7 @@ frame_support::construct_runtime!( Drand: pallet_drand::{Pallet, Call, Storage, Event} = 11, Swap: pallet_subtensor_swap::{Pallet, Call, Storage, Event} = 12, Crowdloan: pallet_crowdloan::{Pallet, Call, Storage, Event} = 13, + Proxy: pallet_proxy = 14, } ); @@ -710,6 +711,7 @@ pub fn test_ext_with_balances(balances: Vec<(U256, u128)>) -> sp_io::TestExterna pub(crate) fn step_block(n: u16) { for _ in 0..n { Scheduler::on_finalize(System::block_number()); + Proxy::on_finalize(System::block_number()); SubtensorModule::on_finalize(System::block_number()); System::on_finalize(System::block_number()); System::set_block_number(System::block_number() + 1); diff --git a/precompiles/src/proxy.rs b/precompiles/src/proxy.rs index b243c4c161..e7fcca447f 100644 --- a/precompiles/src/proxy.rs +++ b/precompiles/src/proxy.rs @@ -153,7 +153,26 @@ where call: Box::new(call), }; - handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id)) + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id))?; + + let last_call_result = pallet_proxy::pallet::Pallet::::LastCallResult::get(real); + match last_call_result { + Some(last_call_result) => match last_call_result { + Ok(()) => { + return Ok(()); + } + Err(e) => { + return Err(PrecompileFailure::Error { + exit_status: ExitError::Other(e.to_string()), + }); + } + }, + None => { + return Err(PrecompileFailure::Error { + exit_status: ExitError::Other("Proxy execution failed".into()), + }); + } + } } #[precompile::public("addProxy(bytes32,uint8,uint32)")] From 431b1ca81a729dc3a5f520b514d8c237510db6c6 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Mon, 3 Nov 2025 14:26:49 -0500 Subject: [PATCH 025/210] chore: fmt --- pallets/proxy/src/lib.rs | 16 ++++++++-------- pallets/subtensor/src/tests/mock.rs | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pallets/proxy/src/lib.rs b/pallets/proxy/src/lib.rs index 8d9be72bb2..9c904fda75 100644 --- a/pallets/proxy/src/lib.rs +++ b/pallets/proxy/src/lib.rs @@ -39,9 +39,9 @@ use frame::{ prelude::*, traits::{Currency, InstanceFilter, ReservableCurrency}, }; +use frame_system::pallet_prelude::BlockNumberFor as SystemBlockNumberFor; pub use pallet::*; use subtensor_macros::freeze_struct; -use frame_system::pallet_prelude::BlockNumberFor as SystemBlockNumberFor; pub use weights::WeightInfo; type CallHashOf = <::CallHasher as Hash>::Output; @@ -748,13 +748,13 @@ pub mod pallet { NoSelfProxy, } - #[pallet::hooks] - impl Hooks> for Pallet { - fn on_finalize(_n: SystemBlockNumberFor) { - // clear this map on end of each block - let _ = LastCallResult::::clear(u32::MAX, None); - } - } + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_finalize(_n: SystemBlockNumberFor) { + // clear this map on end of each block + let _ = LastCallResult::::clear(u32::MAX, None); + } + } /// The set of account proxies. Maps the account which has delegated to the accounts /// which are being delegated to, together with the amount held on deposit. diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index df4f6b8afe..a5ea598984 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -45,7 +45,7 @@ frame_support::construct_runtime!( Drand: pallet_drand::{Pallet, Call, Storage, Event} = 11, Swap: pallet_subtensor_swap::{Pallet, Call, Storage, Event} = 12, Crowdloan: pallet_crowdloan::{Pallet, Call, Storage, Event} = 13, - Proxy: pallet_proxy = 14, + Proxy: pallet_proxy = 14, } ); @@ -711,7 +711,7 @@ pub fn test_ext_with_balances(balances: Vec<(U256, u128)>) -> sp_io::TestExterna pub(crate) fn step_block(n: u16) { for _ in 0..n { Scheduler::on_finalize(System::block_number()); - Proxy::on_finalize(System::block_number()); + Proxy::on_finalize(System::block_number()); SubtensorModule::on_finalize(System::block_number()); System::on_finalize(System::block_number()); System::set_block_number(System::block_number() + 1); From d6eacc2ec4ff78042decd5ee54b7fcaff3074b0f Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Mon, 3 Nov 2025 14:26:56 -0500 Subject: [PATCH 026/210] evm test wip --- evm-tests/test/pure-proxy.precompile.test.ts | 120 +++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 evm-tests/test/pure-proxy.precompile.test.ts diff --git a/evm-tests/test/pure-proxy.precompile.test.ts b/evm-tests/test/pure-proxy.precompile.test.ts new file mode 100644 index 0000000000..c8e9715f2a --- /dev/null +++ b/evm-tests/test/pure-proxy.precompile.test.ts @@ -0,0 +1,120 @@ +import * as assert from "assert"; + +import { getAliceSigner, getDevnetApi } from "../src/substrate" +import { generateRandomEthersWallet, generateRandomEthWallet } from "../src/utils"; +import { devnet, MultiAddress } from "@polkadot-api/descriptors" +import { hexToU8a } from "@polkadot/util"; +import { PolkadotSigner, TypedApi } from "polkadot-api"; +import { convertPublicKeyToSs58 } from "../src/address-utils" +import { IProxyABI, IPROXY_ADDRESS } from "../src/contracts/proxy" +import { keccak256, ethers } from 'ethers'; +import { forceSetBalanceToEthAddress, forceSetBalanceToSs58Address } from "../src/subtensor"; +import { Signer } from "@polkadot/api/types"; + +async function getTransferCallCode(api: TypedApi, signer: PolkadotSigner) { + const transferAmount = BigInt(1000000000); + + const unsignedTx = api.tx.Balances.transfer_keep_alive({ + dest: MultiAddress.Id(convertPublicKeyToSs58(signer.publicKey)), + value: transferAmount, + }); + const encodedCallDataBytes = await unsignedTx.getEncodedData(); + + // encoded call should be 0x050300d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d02286bee + // const transferCall = encodedCallDataBytes + + const data = encodedCallDataBytes.asBytes() + + return [...data] +} + +describe("Test pure proxy precompile", () => { + const evmWallet = generateRandomEthersWallet(); + const evmWallet2 = generateRandomEthersWallet(); + + let api: TypedApi + + let alice: PolkadotSigner; + + before(async () => { + api = await getDevnetApi() + alice = await getAliceSigner(); + + await forceSetBalanceToEthAddress(api, evmWallet.address) + await forceSetBalanceToEthAddress(api, evmWallet2.address) + + }) + + it("Call createPureProxy, then use proxy to call transfer", async () => { + const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet) + console.log("evmWallet", evmWallet.address) + + const tx = await contract.createPureProxy() + const proxyAddress = await tx.wait() + assert.equal(proxyAddress.length, 1, "proxy should be set") + + const ss58Address = convertPublicKeyToSs58(proxyAddress[0]) + + await forceSetBalanceToSs58Address(api, ss58Address) + + const callCode = await getTransferCallCode(api, alice) + const tx2 = await contract.proxyCall(proxyAddress[0], callCode) + await tx2.wait() + }) + + it("Call createPureProxy, add multiple proxies", async () => { + const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet) + + let proxies = [] + for (let i = 0; i < 10; i++) { + const tx = await contract.createPureProxy() + const proxyAddressAfterCreate = await tx.wait() + assert.equal(proxyAddressAfterCreate.length, i + 1, "proxy should be set") + proxies.push(proxyAddressAfterCreate[0]) + } + + const tx = await contract.killPureProxy(proxies[proxies.length - 1]) + await tx.wait() + }) + + it("Call createPureProxy, edge cases", async () => { + const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet2) + + const callCode = await getTransferCallCode(api, alice) + + // call without proxy + try { + const tx = await contract.proxyCall(callCode) + await tx.wait() + } catch (error) { + assert.notEqual(error, undefined, "should fail if proxy not set") + } + + const tx = await contract.createPureProxy() + const proxyAddress = await tx.wait() + + // set the proxy again + try { + const tx = await contract.createPureProxy() + await tx.wait() + } catch (error) { + assert.notEqual(error, undefined, "should fail if set proxy again") + } + + // send extrinsic without token + try { + const tx = await contract.proxyCall(callCode) + await tx.wait() + } catch (error) { + assert.notEqual(error, undefined, "should fail if proxy without balance") + } + + // set balance for proxy account + const ss58Address = convertPublicKeyToSs58(proxyAddress[0]) + await forceSetBalanceToSs58Address(api, ss58Address) + + // try proxy call finally + const tx2 = await contract.proxyCall(proxyAddress[0], callCode) + await tx2.wait() + }) +}); From 9f84b02bde810173927f76339a4f1b8b5b63b72c Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 3 Nov 2025 15:56:49 -0500 Subject: [PATCH 027/210] Remove init_factor --- pallets/subtensor/src/coinbase/subnet_emissions.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pallets/subtensor/src/coinbase/subnet_emissions.rs b/pallets/subtensor/src/coinbase/subnet_emissions.rs index 58335ac62c..80ecf85a42 100644 --- a/pallets/subtensor/src/coinbase/subnet_emissions.rs +++ b/pallets/subtensor/src/coinbase/subnet_emissions.rs @@ -75,11 +75,10 @@ impl Pallet { } } else { // Initialize EMA flow, set S(current_block) = min(price, ema_price) * init_factor - let init_factor = I64F64::saturating_from_num(1.); let moving_price = I64F64::saturating_from_num(Self::get_moving_alpha_price(netuid)); let current_price = I64F64::saturating_from_num(T::SwapInterface::current_alpha_price(netuid)); - let ema_flow = moving_price.min(current_price).saturating_mul(init_factor); + let ema_flow = moving_price.min(current_price); SubnetEmaTaoFlow::::insert(netuid, (current_block, ema_flow)); ema_flow } From 3ef0ba914bf28fdab069f135dffe63b8d35717b9 Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 4 Nov 2025 08:54:26 +0800 Subject: [PATCH 028/210] typo --- common/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/lib.rs b/common/src/lib.rs index d66df115e7..28a33c2ae6 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -172,7 +172,7 @@ impl TryFrom for ProxyType { 2 => Ok(Self::NonCritical), 3 => Ok(Self::NonTransfer), 4 => Ok(Self::Senate), - 5 => Ok(Self::NonFungibile), + 5 => Ok(Self::NonFungible), 6 => Ok(Self::Triumvirate), 7 => Ok(Self::Governance), 8 => Ok(Self::Staking), From 3de77ee160d4c9622f561e3838d0779dbcb63bae Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 4 Nov 2025 09:34:57 +0800 Subject: [PATCH 029/210] fix linter --- precompiles/src/proxy.rs | 46 ++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/precompiles/src/proxy.rs b/precompiles/src/proxy.rs index e7fcca447f..c64d177b3d 100644 --- a/precompiles/src/proxy.rs +++ b/precompiles/src/proxy.rs @@ -2,6 +2,7 @@ use core::marker::PhantomData; use crate::{PrecompileExt, PrecompileHandleExt}; +use alloc::format; use fp_evm::{ExitError, PrecompileFailure}; use frame_support::dispatch::{GetDispatchInfo, PostDispatchInfo}; use frame_system::RawOrigin; @@ -23,11 +24,11 @@ where R: frame_system::Config + pallet_evm::Config + pallet_subtensor::Config - + pallet_proxy::Config, + + pallet_subtensor_proxy::Config, R::AccountId: From<[u8; 32]> + Into<[u8; 32]>, ::AddressMapping: AddressMapping, ::RuntimeCall: From> - + From> + + From> + GetDispatchInfo + Dispatchable, ::AddressMapping: AddressMapping, @@ -42,11 +43,11 @@ where R: frame_system::Config + pallet_evm::Config + pallet_subtensor::Config - + pallet_proxy::Config, + + pallet_subtensor_proxy::Config, R::AccountId: From<[u8; 32]> + Into<[u8; 32]>, ::AddressMapping: AddressMapping, ::RuntimeCall: From> - + From> + + From> + GetDispatchInfo + Dispatchable, <::Lookup as StaticLookup>::Source: From, @@ -64,7 +65,7 @@ where exit_status: ExitError::Other("Invalid proxy type".into()), })?; - let call = pallet_proxy::Call::::create_pure { + let call = pallet_subtensor_proxy::Call::::create_pure { proxy_type, delay: delay.into(), index, @@ -74,12 +75,19 @@ where // Success! // Try to get proxy address - let proxy_address: [u8; 32] = - pallet_proxy::pallet::Pallet::::pure_account(&account_id, &proxy_type, index, None) - .into(); + let proxy_address: [u8; 32] = pallet_subtensor_proxy::pallet::Pallet::::pure_account( + &account_id, + &proxy_type, + index, + None, + ) + .map_err(|_| PrecompileFailure::Error { + exit_status: ExitError::Other("Proxy not found".into()), + })? + .into(); // Check if in the proxies map - let proxy_entry = pallet_proxy::pallet::Pallet::::proxies(account_id.clone()); + let proxy_entry = pallet_subtensor_proxy::pallet::Pallet::::proxies(account_id.clone()); if proxy_entry .0 .iter() @@ -107,7 +115,7 @@ where exit_status: ExitError::Other("Invalid proxy type".into()), })?; - let call = pallet_proxy::Call::::kill_pure { + let call = pallet_subtensor_proxy::Call::::kill_pure { spawner: <::Lookup as StaticLookup>::Source::from( spawner.0.into(), ), @@ -129,7 +137,7 @@ where ) -> EvmResult<()> { let account_id = handle.caller_account_id::(); - let call = ::RuntimeCall::decode_with_depth_limit( + let call = ::RuntimeCall::decode_with_depth_limit( MAX_DECODE_DEPTH, &mut &call[..], ) @@ -145,7 +153,7 @@ where proxy_type = Some(proxy_type_); }; - let call = pallet_proxy::Call::::proxy { + let call = pallet_subtensor_proxy::Call::::proxy { real: <::Lookup as StaticLookup>::Source::from( real.0.into(), ), @@ -155,7 +163,9 @@ where handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id))?; - let last_call_result = pallet_proxy::pallet::Pallet::::LastCallResult::get(real); + let real_account_id = R::AccountId::from(real.0.into()); + + let last_call_result = pallet_subtensor_proxy::LastCallResult::::get(real_account_id); match last_call_result { Some(last_call_result) => match last_call_result { Ok(()) => { @@ -163,7 +173,7 @@ where } Err(e) => { return Err(PrecompileFailure::Error { - exit_status: ExitError::Other(e.to_string()), + exit_status: ExitError::Other(format!("{:?}", e).into()), }); } }, @@ -187,7 +197,7 @@ where exit_status: ExitError::Other("Invalid proxy type".into()), })?; - let call = pallet_proxy::Call::::add_proxy { + let call = pallet_subtensor_proxy::Call::::add_proxy { delegate: <::Lookup as StaticLookup>::Source::from( delegate.0.into(), ), @@ -210,7 +220,7 @@ where exit_status: ExitError::Other("Invalid proxy type".into()), })?; - let call = pallet_proxy::Call::::remove_proxy { + let call = pallet_subtensor_proxy::Call::::remove_proxy { delegate: <::Lookup as StaticLookup>::Source::from( delegate.0.into(), ), @@ -225,7 +235,7 @@ where pub fn remove_proxies(handle: &mut impl PrecompileHandle) -> EvmResult<()> { let account_id = handle.caller_account_id::(); - let call = pallet_proxy::Call::::remove_proxies {}; + let call = pallet_subtensor_proxy::Call::::remove_proxies {}; handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id)) } @@ -234,7 +244,7 @@ where pub fn poke_deposit(handle: &mut impl PrecompileHandle) -> EvmResult<()> { let account_id = handle.caller_account_id::(); - let call = pallet_proxy::Call::::poke_deposit {}; + let call = pallet_subtensor_proxy::Call::::poke_deposit {}; handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id)) } From c0f1720c48eeb49b15e218681882a2f8bcb7b7b1 Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 4 Nov 2025 09:46:52 +0800 Subject: [PATCH 030/210] commit Cargo.lock --- pallets/subtensor/Cargo.toml | 1 + pallets/subtensor/src/tests/mock.rs | 8 +++++++- precompiles/src/proxy.rs | 12 ++++++------ 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/pallets/subtensor/Cargo.toml b/pallets/subtensor/Cargo.toml index fdd5e5f9ab..e05779e2c2 100644 --- a/pallets/subtensor/Cargo.toml +++ b/pallets/subtensor/Cargo.toml @@ -59,6 +59,7 @@ pallet-subtensor-proxy.workspace = true [dev-dependencies] pallet-balances = { workspace = true, features = ["std"] } pallet-scheduler.workspace = true +pallet-subtensor-proxy.workspace = true pallet-subtensor-swap.workspace = true sp-version.workspace = true # Substrate diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index f381e4cc61..9c41de0079 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -46,7 +46,7 @@ frame_support::construct_runtime!( Drand: pallet_drand::{Pallet, Call, Storage, Event} = 11, Swap: pallet_subtensor_swap::{Pallet, Call, Storage, Event} = 12, Crowdloan: pallet_crowdloan::{Pallet, Call, Storage, Event} = 13, - Proxy: pallet_proxy = 14, + Proxy: pallet_subtensor_proxy = 14, } ); @@ -426,6 +426,12 @@ impl pallet_crowdloan::Config for Test { type MaxContributors = MaxContributors; } +impl pallet_subtensor_proxy::Config for Test { + type RuntimeCall = RuntimeCall; + type PalletsOrigin = OriginCaller; + type WeightInfo = pallet_subtensor_proxy::weights::SubstrateWeight; +} + mod test_crypto { use super::KEY_TYPE; use sp_core::{ diff --git a/precompiles/src/proxy.rs b/precompiles/src/proxy.rs index c64d177b3d..8738a2ab1f 100644 --- a/precompiles/src/proxy.rs +++ b/precompiles/src/proxy.rs @@ -169,18 +169,18 @@ where match last_call_result { Some(last_call_result) => match last_call_result { Ok(()) => { - return Ok(()); + Ok(()) } Err(e) => { - return Err(PrecompileFailure::Error { - exit_status: ExitError::Other(format!("{:?}", e).into()), - }); + Err(PrecompileFailure::Error { + exit_status: ExitError::Other(format!("{e:?}").into()), + }) } }, None => { - return Err(PrecompileFailure::Error { + Err(PrecompileFailure::Error { exit_status: ExitError::Other("Proxy execution failed".into()), - }); + }) } } } From 506c1eaa3f75cb11e15c2ee202fa76fc7f6fc7c1 Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 4 Nov 2025 09:58:53 +0800 Subject: [PATCH 031/210] commit Cargo.lock --- pallets/subtensor/Cargo.toml | 1 + pallets/subtensor/src/tests/mock.rs | 31 ++++++++++++-- precompiles/src/proxy.rs | 65 +++++++++++++---------------- 3 files changed, 56 insertions(+), 41 deletions(-) diff --git a/pallets/subtensor/Cargo.toml b/pallets/subtensor/Cargo.toml index e05779e2c2..2e35a89d19 100644 --- a/pallets/subtensor/Cargo.toml +++ b/pallets/subtensor/Cargo.toml @@ -60,6 +60,7 @@ pallet-subtensor-proxy.workspace = true pallet-balances = { workspace = true, features = ["std"] } pallet-scheduler.workspace = true pallet-subtensor-proxy.workspace = true +subtensor-runtime-common.workspace = true pallet-subtensor-swap.workspace = true sp-version.workspace = true # Substrate diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index 9c41de0079..690fcee00b 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -18,6 +18,7 @@ use frame_support::{ }; use frame_system as system; use frame_system::{EnsureRoot, RawOrigin, limits, offchain::CreateTransactionBase}; +use pallet_subtensor_proxy as pallet_proxy; use pallet_subtensor_utility as pallet_utility; use sp_core::{ConstU64, Get, H256, U256, offchain::KeyTypeId}; use sp_runtime::Perbill; @@ -30,7 +31,6 @@ use sp_tracing::tracing_subscriber; use subtensor_runtime_common::{NetUid, TaoCurrency}; use subtensor_swap_interface::{Order, SwapHandler}; use tracing_subscriber::{EnvFilter, layer::SubscriberExt, util::SubscriberInitExt}; - type Block = frame_system::mocking::MockBlock; // Configure a mock runtime to test the pallet. @@ -426,10 +426,33 @@ impl pallet_crowdloan::Config for Test { type MaxContributors = MaxContributors; } -impl pallet_subtensor_proxy::Config for Test { +// Proxy Pallet config +parameter_types! { + // One storage item; key size sizeof(AccountId) = 32, value sizeof(Balance) = 8; 40 total + pub const ProxyDepositBase: Balance = 1; + // Adding 32 bytes + sizeof(ProxyType) = 32 + 1 + pub const ProxyDepositFactor: Balance = 1; + pub const MaxProxies: u32 = 20; // max num proxies per acct + pub const MaxPending: u32 = 15 * 5; // max blocks pending ~15min + // 16 bytes + pub const AnnouncementDepositBase: Balance = 1; + // 68 bytes per announcement + pub const AnnouncementDepositFactor: Balance = 1; +} + +impl pallet_proxy::Config for Test { type RuntimeCall = RuntimeCall; - type PalletsOrigin = OriginCaller; - type WeightInfo = pallet_subtensor_proxy::weights::SubstrateWeight; + type Currency = Balances; + type ProxyType = subtensor_runtime_common::ProxyType; + type ProxyDepositBase = ProxyDepositBase; + type ProxyDepositFactor = ProxyDepositFactor; + type MaxProxies = MaxProxies; + type WeightInfo = pallet_proxy::weights::SubstrateWeight; + type MaxPending = MaxPending; + type CallHasher = BlakeTwo256; + type AnnouncementDepositBase = AnnouncementDepositBase; + type AnnouncementDepositFactor = AnnouncementDepositFactor; + type BlockNumberProvider = System; } mod test_crypto { diff --git a/precompiles/src/proxy.rs b/precompiles/src/proxy.rs index 8738a2ab1f..c2a1c41a38 100644 --- a/precompiles/src/proxy.rs +++ b/precompiles/src/proxy.rs @@ -7,6 +7,7 @@ use fp_evm::{ExitError, PrecompileFailure}; use frame_support::dispatch::{GetDispatchInfo, PostDispatchInfo}; use frame_system::RawOrigin; use pallet_evm::{AddressMapping, PrecompileHandle}; +use pallet_subtensor_proxy as pallet_proxy; use precompile_utils::EvmResult; use sp_core::H256; use sp_runtime::{ @@ -24,11 +25,11 @@ where R: frame_system::Config + pallet_evm::Config + pallet_subtensor::Config - + pallet_subtensor_proxy::Config, + + pallet_proxy::Config, R::AccountId: From<[u8; 32]> + Into<[u8; 32]>, ::AddressMapping: AddressMapping, ::RuntimeCall: From> - + From> + + From> + GetDispatchInfo + Dispatchable, ::AddressMapping: AddressMapping, @@ -43,11 +44,11 @@ where R: frame_system::Config + pallet_evm::Config + pallet_subtensor::Config - + pallet_subtensor_proxy::Config, + + pallet_proxy::Config, R::AccountId: From<[u8; 32]> + Into<[u8; 32]>, ::AddressMapping: AddressMapping, ::RuntimeCall: From> - + From> + + From> + GetDispatchInfo + Dispatchable, <::Lookup as StaticLookup>::Source: From, @@ -65,7 +66,7 @@ where exit_status: ExitError::Other("Invalid proxy type".into()), })?; - let call = pallet_subtensor_proxy::Call::::create_pure { + let call = pallet_proxy::Call::::create_pure { proxy_type, delay: delay.into(), index, @@ -75,19 +76,15 @@ where // Success! // Try to get proxy address - let proxy_address: [u8; 32] = pallet_subtensor_proxy::pallet::Pallet::::pure_account( - &account_id, - &proxy_type, - index, - None, - ) - .map_err(|_| PrecompileFailure::Error { - exit_status: ExitError::Other("Proxy not found".into()), - })? - .into(); + let proxy_address: [u8; 32] = + pallet_proxy::pallet::Pallet::::pure_account(&account_id, &proxy_type, index, None) + .map_err(|_| PrecompileFailure::Error { + exit_status: ExitError::Other("Proxy not found".into()), + })? + .into(); // Check if in the proxies map - let proxy_entry = pallet_subtensor_proxy::pallet::Pallet::::proxies(account_id.clone()); + let proxy_entry = pallet_proxy::pallet::Pallet::::proxies(account_id.clone()); if proxy_entry .0 .iter() @@ -115,7 +112,7 @@ where exit_status: ExitError::Other("Invalid proxy type".into()), })?; - let call = pallet_subtensor_proxy::Call::::kill_pure { + let call = pallet_proxy::Call::::kill_pure { spawner: <::Lookup as StaticLookup>::Source::from( spawner.0.into(), ), @@ -137,7 +134,7 @@ where ) -> EvmResult<()> { let account_id = handle.caller_account_id::(); - let call = ::RuntimeCall::decode_with_depth_limit( + let call = ::RuntimeCall::decode_with_depth_limit( MAX_DECODE_DEPTH, &mut &call[..], ) @@ -153,7 +150,7 @@ where proxy_type = Some(proxy_type_); }; - let call = pallet_subtensor_proxy::Call::::proxy { + let call = pallet_proxy::Call::::proxy { real: <::Lookup as StaticLookup>::Source::from( real.0.into(), ), @@ -165,23 +162,17 @@ where let real_account_id = R::AccountId::from(real.0.into()); - let last_call_result = pallet_subtensor_proxy::LastCallResult::::get(real_account_id); + let last_call_result = pallet_proxy::LastCallResult::::get(real_account_id); match last_call_result { Some(last_call_result) => match last_call_result { - Ok(()) => { - Ok(()) - } - Err(e) => { - Err(PrecompileFailure::Error { - exit_status: ExitError::Other(format!("{e:?}").into()), - }) - } + Ok(()) => Ok(()), + Err(e) => Err(PrecompileFailure::Error { + exit_status: ExitError::Other(format!("{e:?}").into()), + }), }, - None => { - Err(PrecompileFailure::Error { - exit_status: ExitError::Other("Proxy execution failed".into()), - }) - } + None => Err(PrecompileFailure::Error { + exit_status: ExitError::Other("Proxy execution failed".into()), + }), } } @@ -197,7 +188,7 @@ where exit_status: ExitError::Other("Invalid proxy type".into()), })?; - let call = pallet_subtensor_proxy::Call::::add_proxy { + let call = pallet_proxy::Call::::add_proxy { delegate: <::Lookup as StaticLookup>::Source::from( delegate.0.into(), ), @@ -220,7 +211,7 @@ where exit_status: ExitError::Other("Invalid proxy type".into()), })?; - let call = pallet_subtensor_proxy::Call::::remove_proxy { + let call = pallet_proxy::Call::::remove_proxy { delegate: <::Lookup as StaticLookup>::Source::from( delegate.0.into(), ), @@ -235,7 +226,7 @@ where pub fn remove_proxies(handle: &mut impl PrecompileHandle) -> EvmResult<()> { let account_id = handle.caller_account_id::(); - let call = pallet_subtensor_proxy::Call::::remove_proxies {}; + let call = pallet_proxy::Call::::remove_proxies {}; handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id)) } @@ -244,7 +235,7 @@ where pub fn poke_deposit(handle: &mut impl PrecompileHandle) -> EvmResult<()> { let account_id = handle.caller_account_id::(); - let call = pallet_subtensor_proxy::Call::::poke_deposit {}; + let call = pallet_proxy::Call::::poke_deposit {}; handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id)) } From c754affe8810086f816a5d7fe24d50fd0f062d83 Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 4 Nov 2025 10:00:41 +0800 Subject: [PATCH 032/210] commit Cargo.lock --- pallets/subtensor/src/tests/mock.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index 690fcee00b..549d062058 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -8,7 +8,7 @@ use core::num::NonZeroU64; use crate::utils::rate_limiting::TransactionType; use crate::*; -use frame_support::traits::{Contains, Everything, InherentBuilder, InsideBoth}; +use frame_support::traits::{Contains, Everything, InherentBuilder, InsideBoth, InstanceFilter}; use frame_support::weights::Weight; use frame_support::weights::constants::RocksDbWeight; use frame_support::{PalletId, derive_impl}; @@ -447,7 +447,7 @@ impl pallet_proxy::Config for Test { type ProxyDepositBase = ProxyDepositBase; type ProxyDepositFactor = ProxyDepositFactor; type MaxProxies = MaxProxies; - type WeightInfo = pallet_proxy::weights::SubstrateWeight; + type WeightInfo = pallet_proxy::weights::SubstrateWeight; type MaxPending = MaxPending; type CallHasher = BlakeTwo256; type AnnouncementDepositBase = AnnouncementDepositBase; @@ -455,6 +455,20 @@ impl pallet_proxy::Config for Test { type BlockNumberProvider = System; } +impl InstanceFilter for subtensor_runtime_common::ProxyType { + fn filter(&self, _c: &RuntimeCall) -> bool { + // In tests, allow all proxy types to pass through + true + } + fn is_superset(&self, o: &Self) -> bool { + match (self, o) { + (x, y) if x == y => true, + (subtensor_runtime_common::ProxyType::Any, _) => true, + _ => false, + } + } +} + mod test_crypto { use super::KEY_TYPE; use sp_core::{ From ed5971b136814bd16bd09057e1aa15c4560c7cc5 Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 4 Nov 2025 18:21:09 +0800 Subject: [PATCH 033/210] update test case --- evm-tests/src/address-utils.ts | 2 +- evm-tests/src/contracts/proxy.ts | 288 +++++++++---------- evm-tests/test/pure-proxy.precompile.test.ts | 137 +++++---- 3 files changed, 232 insertions(+), 195 deletions(-) diff --git a/evm-tests/src/address-utils.ts b/evm-tests/src/address-utils.ts index ed3abc5008..753eed2530 100644 --- a/evm-tests/src/address-utils.ts +++ b/evm-tests/src/address-utils.ts @@ -1,6 +1,6 @@ import { Address } from "viem" import { encodeAddress } from "@polkadot/util-crypto"; -import { ss58Address } from "@polkadot-labs/hdkd-helpers"; +import { ss58Address, ss58Decode } from "@polkadot-labs/hdkd-helpers"; import { hexToU8a } from "@polkadot/util"; import { blake2AsU8a, decodeAddress } from "@polkadot/util-crypto"; import { Binary } from "polkadot-api"; diff --git a/evm-tests/src/contracts/proxy.ts b/evm-tests/src/contracts/proxy.ts index 4059409276..48ffd0a50f 100644 --- a/evm-tests/src/contracts/proxy.ts +++ b/evm-tests/src/contracts/proxy.ts @@ -1,148 +1,148 @@ export const IPROXY_ADDRESS = "0x000000000000000000000000000000000000080b"; export const IProxyABI = [ - { - "inputs": [ - { - "internalType": "uint8", - "name": "proxy_type", - "type": "uint8" - }, - { - "internalType": "uint32", - "name": "delay", - "type": "uint32" - }, - { - "internalType": "uint16", - "name": "index", - "type": "uint16" - } - ], - "name": "createPureProxy", - "outputs": [ - { - "internalType": "bytes32", - "name": "proxy", - "type": "bytes32" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "spawner", - "type": "bytes32" - }, - { - "internalType": "uint8", - "name": "proxy_type", - "type": "uint8" - }, - { - "internalType": "uint16", - "name": "index", - "type": "uint16" - }, - { - "internalType": "uint32", - "name": "height", - "type": "uint32" - }, - { - "internalType": "uint32", - "name": "ext_index", - "type": "uint32" - } - ], - "name": "killPureProxy", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "real", - "type": "bytes32" - }, - { - "internalType": "uint8[]", - "name": "force_proxy_type", // optional - "type": "uint8[]" - }, - { - "internalType": "uint8[]", - "name": "call", - "type": "uint8[]" - } - ], - "name": "proxyCall", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "removeProxies", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - },{ - "inputs": [], - "name": "pokeDeposit", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "delegate", - "type": "bytes32" - }, - { - "internalType": "uint8", - "name": "proxy_type", - "type": "uint8" - }, - { - "internalType": "uint32", - "name": "delay", - "type": "uint32" - } - ], - "name": "removeProxy", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "delegate", - "type": "bytes32" - }, - { - "internalType": "uint8", - "name": "proxy_type", - "type": "uint8" - }, - { - "internalType": "uint32", - "name": "delay", - "type": "uint32" - } - ], - "name": "addProxy", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } + { + "inputs": [ + { + "internalType": "uint8", + "name": "proxy_type", + "type": "uint8" + }, + { + "internalType": "uint32", + "name": "delay", + "type": "uint32" + }, + { + "internalType": "uint16", + "name": "index", + "type": "uint16" + } + ], + "name": "createPureProxy", + "outputs": [ + { + "internalType": "bytes32", + "name": "proxy", + "type": "bytes32" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "spawner", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "proxy_type", + "type": "uint8" + }, + { + "internalType": "uint16", + "name": "index", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "height", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "ext_index", + "type": "uint32" + } + ], + "name": "killPureProxy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "real", + "type": "bytes32" + }, + { + "internalType": "uint8[]", + "name": "force_proxy_type", // optional + "type": "uint8[]" + }, + { + "internalType": "uint8[]", + "name": "call", + "type": "uint8[]" + } + ], + "name": "proxyCall", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "removeProxies", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { + "inputs": [], + "name": "pokeDeposit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "delegate", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "proxy_type", + "type": "uint8" + }, + { + "internalType": "uint32", + "name": "delay", + "type": "uint32" + } + ], + "name": "removeProxy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "delegate", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "proxy_type", + "type": "uint8" + }, + { + "internalType": "uint32", + "name": "delay", + "type": "uint32" + } + ], + "name": "addProxy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } ]; diff --git a/evm-tests/test/pure-proxy.precompile.test.ts b/evm-tests/test/pure-proxy.precompile.test.ts index c8e9715f2a..7ef3ace5c1 100644 --- a/evm-tests/test/pure-proxy.precompile.test.ts +++ b/evm-tests/test/pure-proxy.precompile.test.ts @@ -1,22 +1,24 @@ import * as assert from "assert"; -import { getAliceSigner, getDevnetApi } from "../src/substrate" +import { getAliceSigner, getDevnetApi, getRandomSubstrateKeypair } from "../src/substrate" import { generateRandomEthersWallet, generateRandomEthWallet } from "../src/utils"; import { devnet, MultiAddress } from "@polkadot-api/descriptors" import { hexToU8a } from "@polkadot/util"; import { PolkadotSigner, TypedApi } from "polkadot-api"; -import { convertPublicKeyToSs58 } from "../src/address-utils" +import { convertH160ToSS58, convertPublicKeyToSs58 } from "../src/address-utils" import { IProxyABI, IPROXY_ADDRESS } from "../src/contracts/proxy" import { keccak256, ethers } from 'ethers'; import { forceSetBalanceToEthAddress, forceSetBalanceToSs58Address } from "../src/subtensor"; import { Signer } from "@polkadot/api/types"; +import { KeyPair } from "@polkadot-labs/hdkd-helpers"; -async function getTransferCallCode(api: TypedApi, signer: PolkadotSigner) { - const transferAmount = BigInt(1000000000); +import { decodeAddress } from "@polkadot/util-crypto"; + +async function getTransferCallCode(api: TypedApi, receiver: KeyPair, transferAmount: number) { const unsignedTx = api.tx.Balances.transfer_keep_alive({ - dest: MultiAddress.Id(convertPublicKeyToSs58(signer.publicKey)), - value: transferAmount, + dest: MultiAddress.Id(convertPublicKeyToSs58(receiver.publicKey)), + value: BigInt(1000000000), }); const encodedCallDataBytes = await unsignedTx.getEncodedData(); @@ -28,9 +30,26 @@ async function getTransferCallCode(api: TypedApi, signer: Polkado return [...data] } +async function getProxies(api: TypedApi, address: string) { + const entries = await api.query.Proxy.Proxies.getEntries() + const result = [] + for (const entry of entries) { + const proxyAddress = entry.keyArgs[0] + const values = entry.value + const proxies = values[0] + for (const proxy of proxies) { + if (proxy.delegate === address) { + result.push(proxyAddress) + } + } + } + return result +} + describe("Test pure proxy precompile", () => { const evmWallet = generateRandomEthersWallet(); const evmWallet2 = generateRandomEthersWallet(); + const receiver = getRandomSubstrateKeypair(); let api: TypedApi @@ -46,75 +65,93 @@ describe("Test pure proxy precompile", () => { }) it("Call createPureProxy, then use proxy to call transfer", async () => { + const proxies = await getProxies(api, convertH160ToSS58(evmWallet.address)) const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet) console.log("evmWallet", evmWallet.address) - const tx = await contract.createPureProxy() - const proxyAddress = await tx.wait() - assert.equal(proxyAddress.length, 1, "proxy should be set") + const type = 0; + const delay = 0; + const index = 0; + const tx = await contract.createPureProxy(type, delay, index) + const response = await tx.wait() + console.log("response", response.blockNumber) - const ss58Address = convertPublicKeyToSs58(proxyAddress[0]) + const proxiesAfterAdd = await getProxies(api, convertH160ToSS58(evmWallet.address)) - await forceSetBalanceToSs58Address(api, ss58Address) + const length = proxiesAfterAdd.length + assert.equal(length, proxies.length + 1, "proxy should be set") + const proxy = proxiesAfterAdd[proxiesAfterAdd.length - 1] - const callCode = await getTransferCallCode(api, alice) - const tx2 = await contract.proxyCall(proxyAddress[0], callCode) - await tx2.wait() - }) + await forceSetBalanceToSs58Address(api, proxy) + const balance = (await api.query.System.Account.getValue(convertPublicKeyToSs58(receiver.publicKey))).data.free - it("Call createPureProxy, add multiple proxies", async () => { - const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet) + const amount = 1000000000; - let proxies = [] - for (let i = 0; i < 10; i++) { - const tx = await contract.createPureProxy() - const proxyAddressAfterCreate = await tx.wait() - assert.equal(proxyAddressAfterCreate.length, i + 1, "proxy should be set") - proxies.push(proxyAddressAfterCreate[0]) - } + const callCode = await getTransferCallCode(api, receiver, amount) + const tx2 = await contract.proxyCall(decodeAddress(proxy), [type], callCode) + await tx2.wait() - const tx = await contract.killPureProxy(proxies[proxies.length - 1]) - await tx.wait() + const balanceAfter = (await api.query.System.Account.getValue(convertPublicKeyToSs58(receiver.publicKey))).data.free + assert.equal(balanceAfter, balance + BigInt(amount), "balance should be increased") }) - it("Call createPureProxy, edge cases", async () => { - const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet2) + it("Call createPureProxy, add kill one", async () => { + const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet) + const type = 0; + const delay = 0; + const index = 0; + const extIndex = 1; + + const proxies = await getProxies(api, convertH160ToSS58(evmWallet.address)) + const length = proxies.length + const addTx = await contract.createPureProxy(type, delay, index) + const response = await addTx.wait() + const createBlockNumber = response.blockNumber - const callCode = await getTransferCallCode(api, alice) + const currentLength = (await getProxies(api, convertH160ToSS58(evmWallet.address))).length + assert.equal(currentLength, length + 1, "proxy should be set") - // call without proxy try { - const tx = await contract.proxyCall(callCode) + const tx = await contract.killPureProxy(decodeAddress(proxies[proxies.length - 1]), type, index, + createBlockNumber, extIndex) await tx.wait() } catch (error) { - assert.notEqual(error, undefined, "should fail if proxy not set") + console.log("error", error) } - const tx = await contract.createPureProxy() - const proxyAddress = await tx.wait() + const proxiesAfterRemove = await getProxies(api, convertH160ToSS58(evmWallet.address)) + assert.equal(proxiesAfterRemove.length, 0, "proxies should be removed") + }) - // set the proxy again - try { - const tx = await contract.createPureProxy() + it("Call createPureProxy, add multiple proxies", async () => { + const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet) + const type = 0; + const delay = 0; + const index = 0; + const proxies = await getProxies(api, convertH160ToSS58(evmWallet.address)) + const length = proxies.length + for (let i = 0; i < 5; i++) { + const tx = await contract.createPureProxy(type, delay, index) await tx.wait() - } catch (error) { - assert.notEqual(error, undefined, "should fail if set proxy again") + + await new Promise(resolve => setTimeout(resolve, 500)); + const currentProxies = await getProxies(api, convertH160ToSS58(evmWallet.address)) + assert.equal(currentProxies.length, length + i + 1, "proxy should be set") } + }) - // send extrinsic without token + it("Call createPureProxy, edge cases, call via wrong proxy", async () => { + const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet2) + const amount = 1000000000; + const callCode = await getTransferCallCode(api, receiver, amount) + const type = 0; + + // call with wrong proxy try { - const tx = await contract.proxyCall(callCode) + const tx = await contract.proxyCall(receiver, [type], callCode) await tx.wait() } catch (error) { - assert.notEqual(error, undefined, "should fail if proxy without balance") + assert.notEqual(error, undefined, "should fail if proxy not set") } - - // set balance for proxy account - const ss58Address = convertPublicKeyToSs58(proxyAddress[0]) - await forceSetBalanceToSs58Address(api, ss58Address) - - // try proxy call finally - const tx2 = await contract.proxyCall(proxyAddress[0], callCode) - await tx2.wait() }) }); From 596633575d99270a82cd9a183bb3fc3bfc5fe7ff Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 4 Nov 2025 21:44:24 +0800 Subject: [PATCH 034/210] add more e2e test --- evm-tests/test/pure-proxy.precompile.test.ts | 48 +++++++++++++++++--- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/evm-tests/test/pure-proxy.precompile.test.ts b/evm-tests/test/pure-proxy.precompile.test.ts index 7ef3ace5c1..4ab4eabf3e 100644 --- a/evm-tests/test/pure-proxy.precompile.test.ts +++ b/evm-tests/test/pure-proxy.precompile.test.ts @@ -1,15 +1,13 @@ import * as assert from "assert"; import { getAliceSigner, getDevnetApi, getRandomSubstrateKeypair } from "../src/substrate" -import { generateRandomEthersWallet, generateRandomEthWallet } from "../src/utils"; +import { generateRandomEthersWallet } from "../src/utils"; import { devnet, MultiAddress } from "@polkadot-api/descriptors" -import { hexToU8a } from "@polkadot/util"; import { PolkadotSigner, TypedApi } from "polkadot-api"; -import { convertH160ToSS58, convertPublicKeyToSs58 } from "../src/address-utils" +import { convertH160ToPublicKey, convertH160ToSS58, convertPublicKeyToSs58 } from "../src/address-utils" import { IProxyABI, IPROXY_ADDRESS } from "../src/contracts/proxy" -import { keccak256, ethers } from 'ethers'; +import { ethers } from 'ethers'; import { forceSetBalanceToEthAddress, forceSetBalanceToSs58Address } from "../src/subtensor"; -import { Signer } from "@polkadot/api/types"; import { KeyPair } from "@polkadot-labs/hdkd-helpers"; import { decodeAddress } from "@polkadot/util-crypto"; @@ -49,6 +47,8 @@ async function getProxies(api: TypedApi, address: string) { describe("Test pure proxy precompile", () => { const evmWallet = generateRandomEthersWallet(); const evmWallet2 = generateRandomEthersWallet(); + const evmWallet3 = generateRandomEthersWallet(); + const delegate = getRandomSubstrateKeypair(); const receiver = getRandomSubstrateKeypair(); let api: TypedApi @@ -61,7 +61,8 @@ describe("Test pure proxy precompile", () => { await forceSetBalanceToEthAddress(api, evmWallet.address) await forceSetBalanceToEthAddress(api, evmWallet2.address) - + await forceSetBalanceToEthAddress(api, evmWallet3.address) + await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(delegate.publicKey)) }) it("Call createPureProxy, then use proxy to call transfer", async () => { @@ -154,4 +155,39 @@ describe("Test pure proxy precompile", () => { assert.notEqual(error, undefined, "should fail if proxy not set") } }) + + it("Call createProxy, then use proxy to call transfer", async () => { + const proxies = await api.query.Proxy.Proxies.getValue(convertH160ToSS58(evmWallet2.address)) + const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet2) + + const type = 0; + const delay = 0; + + const tx = await contract.addProxy(convertH160ToPublicKey(evmWallet3.address), type, delay) + await tx.wait() + + + const proxiesAfterAdd = await await api.query.Proxy.Proxies.getValue(convertH160ToSS58(evmWallet2.address)) + + const length = proxiesAfterAdd[0].length + assert.equal(length, proxies[0].length + 1, "proxy should be set") + const proxy = proxiesAfterAdd[0][proxiesAfterAdd[0].length - 1] + + assert.equal(proxy.delegate, convertH160ToSS58(evmWallet3.address), "proxy should be set") + + + const balance = (await api.query.System.Account.getValue(convertPublicKeyToSs58(receiver.publicKey))).data.free + + const amount = 1000000000; + + const contract2 = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet3) + + + const callCode = await getTransferCallCode(api, receiver, amount) + const tx2 = await contract2.proxyCall(convertH160ToPublicKey(evmWallet2.address), [type], callCode) + await tx2.wait() + + const balanceAfter = (await api.query.System.Account.getValue(convertPublicKeyToSs58(receiver.publicKey))).data.free + assert.equal(balanceAfter, balance + BigInt(amount), "balance should be increased") + }) }); From 59711241c1a2bbf89ecfe3361b5b9edc0a879910 Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 4 Nov 2025 21:44:51 +0800 Subject: [PATCH 035/210] bump version --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index d8277164e2..548c192ece 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 334, + spec_version: 335, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 8f585ec480639def5002e9faa938895bc0284801 Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 4 Nov 2025 21:45:41 +0800 Subject: [PATCH 036/210] clean up code --- evm-tests/test/pure-proxy.precompile.test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/evm-tests/test/pure-proxy.precompile.test.ts b/evm-tests/test/pure-proxy.precompile.test.ts index 4ab4eabf3e..e9b9397918 100644 --- a/evm-tests/test/pure-proxy.precompile.test.ts +++ b/evm-tests/test/pure-proxy.precompile.test.ts @@ -48,7 +48,6 @@ describe("Test pure proxy precompile", () => { const evmWallet = generateRandomEthersWallet(); const evmWallet2 = generateRandomEthersWallet(); const evmWallet3 = generateRandomEthersWallet(); - const delegate = getRandomSubstrateKeypair(); const receiver = getRandomSubstrateKeypair(); let api: TypedApi @@ -62,7 +61,6 @@ describe("Test pure proxy precompile", () => { await forceSetBalanceToEthAddress(api, evmWallet.address) await forceSetBalanceToEthAddress(api, evmWallet2.address) await forceSetBalanceToEthAddress(api, evmWallet3.address) - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(delegate.publicKey)) }) it("Call createPureProxy, then use proxy to call transfer", async () => { From 05c8c693d7f2b423abe86270981b5851b35ad1ea Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Tue, 4 Nov 2025 18:36:25 -0500 Subject: [PATCH 037/210] Re-init tao flows --- pallets/subtensor/src/macros/hooks.rs | 4 +- .../src/migrations/migrate_init_tao_flow.rs | 57 +++++++++++++++++++ pallets/subtensor/src/migrations/mod.rs | 1 + 3 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 pallets/subtensor/src/migrations/migrate_init_tao_flow.rs diff --git a/pallets/subtensor/src/macros/hooks.rs b/pallets/subtensor/src/macros/hooks.rs index 87a87e911c..2369b25275 100644 --- a/pallets/subtensor/src/macros/hooks.rs +++ b/pallets/subtensor/src/macros/hooks.rs @@ -159,7 +159,9 @@ mod hooks { // Migrate Kappa to default (0.5) .saturating_add(migrations::migrate_kappa_map_to_default::migrate_kappa_map_to_default::()) // Remove obsolete map entries - .saturating_add(migrations::migrate_remove_tao_dividends::migrate_remove_tao_dividends::()); + .saturating_add(migrations::migrate_remove_tao_dividends::migrate_remove_tao_dividends::()) + // Re-init tao flows + .saturating_add(migrations::migrate_init_tao_flows::migrate_init_tao_flows::()); weight } diff --git a/pallets/subtensor/src/migrations/migrate_init_tao_flow.rs b/pallets/subtensor/src/migrations/migrate_init_tao_flow.rs new file mode 100644 index 0000000000..c0cdb87336 --- /dev/null +++ b/pallets/subtensor/src/migrations/migrate_init_tao_flow.rs @@ -0,0 +1,57 @@ +use alloc::string::String; + +use frame_support::IterableStorageMap; +use frame_support::{traits::Get, weights::Weight}; +use subtensor_runtime_common::NetUid; + +use super::*; + +pub fn migrate_init_tao_flows() -> Weight { + let migration_name = b"migrate_init_tao_flow".to_vec(); + + // Initialize the weight with one read operation. + let mut weight = T::DbWeight::get().reads(1); + + // Check if the migration has already run + if HasMigrationRun::::get(&migration_name) { + log::info!( + "Migration '{:?}' has already run. Skipping.", + String::from_utf8_lossy(&migration_name) + ); + return weight; + } + log::info!( + "Running migration '{}'", + String::from_utf8_lossy(&migration_name) + ); + + let netuids: Vec = as IterableStorageMap>::iter() + .map(|(netuid, _)| netuid) + .collect(); + weight = weight.saturating_add(T::DbWeight::get().reads(netuids.len() as u64)); + + for netuid in netuids.iter() { + if netuid.is_root() { + continue; + } + // Set SubnetEmaTaoFlow + SubnetEmaTaoFlow::::insert(*netuid, I64F64::saturating_from_num(0)); + + + + + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + } + + // Mark the migration as completed + HasMigrationRun::::insert(&migration_name, true); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + + log::info!( + "Migration '{:?}' completed.", + String::from_utf8_lossy(&migration_name) + ); + + // Return the migration weight. + weight +} diff --git a/pallets/subtensor/src/migrations/mod.rs b/pallets/subtensor/src/migrations/mod.rs index d95e4c7bac..dc14de151f 100644 --- a/pallets/subtensor/src/migrations/mod.rs +++ b/pallets/subtensor/src/migrations/mod.rs @@ -20,6 +20,7 @@ pub mod migrate_fix_is_network_member; pub mod migrate_fix_root_subnet_tao; pub mod migrate_fix_root_tao_and_alpha_in; pub mod migrate_identities_v2; +pub mod migrate_init_tao_flows; pub mod migrate_init_total_issuance; pub mod migrate_kappa_map_to_default; pub mod migrate_network_immunity_period; From 60c4a5c5803a8bb9ac9d298ac2a08df239c7ddc0 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Tue, 4 Nov 2025 18:54:07 -0500 Subject: [PATCH 038/210] Revert back to ema-prices --- .../src/coinbase/subnet_emissions.rs | 34 +- pallets/subtensor/src/tests/coinbase.rs | 200 +++--- .../subtensor/src/tests/subnet_emissions.rs | 654 +++++++++--------- runtime/src/lib.rs | 2 +- 4 files changed, 461 insertions(+), 429 deletions(-) diff --git a/pallets/subtensor/src/coinbase/subnet_emissions.rs b/pallets/subtensor/src/coinbase/subnet_emissions.rs index 80ecf85a42..4f3911e3ae 100644 --- a/pallets/subtensor/src/coinbase/subnet_emissions.rs +++ b/pallets/subtensor/src/coinbase/subnet_emissions.rs @@ -51,6 +51,7 @@ impl Pallet { // Update SubnetEmaTaoFlow if needed and return its value for // the current block + #[allow(dead_code)] fn get_ema_flow(netuid: NetUid) -> I64F64 { let current_block: u64 = Self::get_current_block_as_u64(); @@ -87,6 +88,7 @@ impl Pallet { // Either the minimal EMA flow L = min{Si}, or an artificial // cut off at some higher value A (TaoFlowCutoff) // L = max {A, min{min{S[i], 0}}} + #[allow(dead_code)] fn get_lower_limit(ema_flows: &BTreeMap) -> I64F64 { let zero = I64F64::saturating_from_num(0); let min_flow = ema_flows @@ -178,6 +180,7 @@ impl Pallet { } // Implementation of shares that uses TAO flow + #[allow(dead_code)] fn get_shares_flow(subnets_to_emit_to: &[NetUid]) -> BTreeMap { // Get raw flows let ema_flows = subnets_to_emit_to @@ -210,8 +213,37 @@ impl Pallet { offset_flows } + // DEPRECATED: Implementation of shares that uses EMA prices will be gradually deprecated + fn get_shares_price_ema(subnets_to_emit_to: &[NetUid]) -> BTreeMap { + // Get sum of alpha moving prices + let total_moving_prices = subnets_to_emit_to + .iter() + .map(|netuid| U64F64::saturating_from_num(Self::get_moving_alpha_price(*netuid))) + .fold(U64F64::saturating_from_num(0.0), |acc, ema| { + acc.saturating_add(ema) + }); + log::debug!("total_moving_prices: {total_moving_prices:?}"); + + // Calculate shares. + subnets_to_emit_to + .iter() + .map(|netuid| { + let moving_price = + U64F64::saturating_from_num(Self::get_moving_alpha_price(*netuid)); + log::debug!("moving_price_i: {moving_price:?}"); + + let share = moving_price + .checked_div(total_moving_prices) + .unwrap_or(U64F64::saturating_from_num(0)); + + (*netuid, share) + }) + .collect::>() + } + // Combines ema price method and tao flow method linearly over FlowHalfLife blocks pub(crate) fn get_shares(subnets_to_emit_to: &[NetUid]) -> BTreeMap { - Self::get_shares_flow(subnets_to_emit_to) + // Self::get_shares_flow(subnets_to_emit_to) + Self::get_shares_price_ema(subnets_to_emit_to) } } diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 2d0376c896..60644b2a28 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -115,28 +115,28 @@ fn test_coinbase_tao_issuance_base_low() { } // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_coinbase_tao_issuance_base_low_flow --exact --show-output --nocapture -#[test] -fn test_coinbase_tao_issuance_base_low_flow() { - new_test_ext(1).execute_with(|| { - let emission = TaoCurrency::from(1_234_567); - let subnet_owner_ck = U256::from(1001); - let subnet_owner_hk = U256::from(1002); - let netuid = add_dynamic_network(&subnet_owner_hk, &subnet_owner_ck); - let emission = TaoCurrency::from(1); - - // 100% tao flow method - let block_num = FlowHalfLife::::get(); - SubnetEmaTaoFlow::::insert(netuid, (block_num, I64F64::from_num(1_000_000_000))); - System::set_block_number(block_num); - - let tao_in_before = SubnetTAO::::get(netuid); - let total_stake_before = TotalStake::::get(); - SubtensorModule::run_coinbase(U96F32::from_num(emission)); - assert_eq!(SubnetTAO::::get(netuid), tao_in_before + emission); - assert_eq!(TotalIssuance::::get(), emission); - assert_eq!(TotalStake::::get(), total_stake_before + emission); - }); -} +// #[test] +// fn test_coinbase_tao_issuance_base_low_flow() { +// new_test_ext(1).execute_with(|| { +// let emission = TaoCurrency::from(1_234_567); +// let subnet_owner_ck = U256::from(1001); +// let subnet_owner_hk = U256::from(1002); +// let netuid = add_dynamic_network(&subnet_owner_hk, &subnet_owner_ck); +// let emission = TaoCurrency::from(1); + +// // 100% tao flow method +// let block_num = FlowHalfLife::::get(); +// SubnetEmaTaoFlow::::insert(netuid, (block_num, I64F64::from_num(1_000_000_000))); +// System::set_block_number(block_num); + +// let tao_in_before = SubnetTAO::::get(netuid); +// let total_stake_before = TotalStake::::get(); +// SubtensorModule::run_coinbase(U96F32::from_num(emission)); +// assert_eq!(SubnetTAO::::get(netuid), tao_in_before + emission); +// assert_eq!(TotalIssuance::::get(), emission); +// assert_eq!(TotalStake::::get(), total_stake_before + emission); +// }); +// } // Test emission distribution across multiple subnets. // This test verifies that: @@ -260,85 +260,85 @@ fn test_coinbase_tao_issuance_different_prices() { } // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_coinbase_tao_issuance_different_flows --exact --show-output --nocapture -#[test] -fn test_coinbase_tao_issuance_different_flows() { - new_test_ext(1).execute_with(|| { - let subnet_owner_ck = U256::from(1001); - let subnet_owner_hk = U256::from(1002); - let netuid1 = add_dynamic_network(&subnet_owner_hk, &subnet_owner_ck); - let netuid2 = add_dynamic_network(&subnet_owner_hk, &subnet_owner_ck); - let emission = 100_000_000; - - // Setup prices 0.1 and 0.2 - let initial_tao: u64 = 100_000_u64; - let initial_alpha1: u64 = initial_tao * 10; - let initial_alpha2: u64 = initial_tao * 5; - mock::setup_reserves(netuid1, initial_tao.into(), initial_alpha1.into()); - mock::setup_reserves(netuid2, initial_tao.into(), initial_alpha2.into()); - - // Force the swap to initialize - SubtensorModule::swap_tao_for_alpha( - netuid1, - TaoCurrency::ZERO, - 1_000_000_000_000.into(), - false, - ) - .unwrap(); - SubtensorModule::swap_tao_for_alpha( - netuid2, - TaoCurrency::ZERO, - 1_000_000_000_000.into(), - false, - ) - .unwrap(); - - // Set subnet prices to reversed proportion to ensure they don't affect emissions. - SubnetMovingPrice::::insert(netuid1, I96F32::from_num(2)); - SubnetMovingPrice::::insert(netuid2, I96F32::from_num(1)); - - // Set subnet tao flow ema. - let block_num = FlowHalfLife::::get(); - SubnetEmaTaoFlow::::insert(netuid1, (block_num, I64F64::from_num(1))); - SubnetEmaTaoFlow::::insert(netuid2, (block_num, I64F64::from_num(2))); - System::set_block_number(block_num); - - // Set normalization exponent to 1 for simplicity - FlowNormExponent::::set(U64F64::from(1_u64)); - - // Assert initial TAO reserves. - assert_eq!(SubnetTAO::::get(netuid1), initial_tao.into()); - assert_eq!(SubnetTAO::::get(netuid2), initial_tao.into()); - let total_stake_before = TotalStake::::get(); - - // Run the coinbase with the emission amount. - SubtensorModule::run_coinbase(U96F32::from_num(emission)); - - // Assert tao emission is split evenly. - assert_abs_diff_eq!( - SubnetTAO::::get(netuid1), - TaoCurrency::from(initial_tao + emission / 3), - epsilon = 10.into(), - ); - assert_abs_diff_eq!( - SubnetTAO::::get(netuid2), - TaoCurrency::from(initial_tao + 2 * emission / 3), - epsilon = 10.into(), - ); +// #[test] +// fn test_coinbase_tao_issuance_different_flows() { +// new_test_ext(1).execute_with(|| { +// let subnet_owner_ck = U256::from(1001); +// let subnet_owner_hk = U256::from(1002); +// let netuid1 = add_dynamic_network(&subnet_owner_hk, &subnet_owner_ck); +// let netuid2 = add_dynamic_network(&subnet_owner_hk, &subnet_owner_ck); +// let emission = 100_000_000; + +// // Setup prices 0.1 and 0.2 +// let initial_tao: u64 = 100_000_u64; +// let initial_alpha1: u64 = initial_tao * 10; +// let initial_alpha2: u64 = initial_tao * 5; +// mock::setup_reserves(netuid1, initial_tao.into(), initial_alpha1.into()); +// mock::setup_reserves(netuid2, initial_tao.into(), initial_alpha2.into()); + +// // Force the swap to initialize +// SubtensorModule::swap_tao_for_alpha( +// netuid1, +// TaoCurrency::ZERO, +// 1_000_000_000_000.into(), +// false, +// ) +// .unwrap(); +// SubtensorModule::swap_tao_for_alpha( +// netuid2, +// TaoCurrency::ZERO, +// 1_000_000_000_000.into(), +// false, +// ) +// .unwrap(); + +// // Set subnet prices to reversed proportion to ensure they don't affect emissions. +// SubnetMovingPrice::::insert(netuid1, I96F32::from_num(2)); +// SubnetMovingPrice::::insert(netuid2, I96F32::from_num(1)); + +// // Set subnet tao flow ema. +// let block_num = FlowHalfLife::::get(); +// SubnetEmaTaoFlow::::insert(netuid1, (block_num, I64F64::from_num(1))); +// SubnetEmaTaoFlow::::insert(netuid2, (block_num, I64F64::from_num(2))); +// System::set_block_number(block_num); + +// // Set normalization exponent to 1 for simplicity +// FlowNormExponent::::set(U64F64::from(1_u64)); + +// // Assert initial TAO reserves. +// assert_eq!(SubnetTAO::::get(netuid1), initial_tao.into()); +// assert_eq!(SubnetTAO::::get(netuid2), initial_tao.into()); +// let total_stake_before = TotalStake::::get(); + +// // Run the coinbase with the emission amount. +// SubtensorModule::run_coinbase(U96F32::from_num(emission)); + +// // Assert tao emission is split evenly. +// assert_abs_diff_eq!( +// SubnetTAO::::get(netuid1), +// TaoCurrency::from(initial_tao + emission / 3), +// epsilon = 10.into(), +// ); +// assert_abs_diff_eq!( +// SubnetTAO::::get(netuid2), +// TaoCurrency::from(initial_tao + 2 * emission / 3), +// epsilon = 10.into(), +// ); - // Prices are low => we limit tao issued (buy alpha with it) - let tao_issued = TaoCurrency::from(((0.1 + 0.2) * emission as f64) as u64); - assert_abs_diff_eq!( - TotalIssuance::::get(), - tao_issued, - epsilon = 10.into() - ); - assert_abs_diff_eq!( - TotalStake::::get(), - total_stake_before + emission.into(), - epsilon = 10.into() - ); - }); -} +// // Prices are low => we limit tao issued (buy alpha with it) +// let tao_issued = TaoCurrency::from(((0.1 + 0.2) * emission as f64) as u64); +// assert_abs_diff_eq!( +// TotalIssuance::::get(), +// tao_issued, +// epsilon = 10.into() +// ); +// assert_abs_diff_eq!( +// TotalStake::::get(), +// total_stake_before + emission.into(), +// epsilon = 10.into() +// ); +// }); +// } // Test moving price updates with different alpha values. // This test verifies that: diff --git a/pallets/subtensor/src/tests/subnet_emissions.rs b/pallets/subtensor/src/tests/subnet_emissions.rs index 9f29b3fee2..311a930647 100644 --- a/pallets/subtensor/src/tests/subnet_emissions.rs +++ b/pallets/subtensor/src/tests/subnet_emissions.rs @@ -151,137 +151,137 @@ fn inplace_pow_normalize_fractional_exponent() { }) } -/// Normal (moderate, non-zero) EMA flows across 3 subnets. -/// Expect: shares sum to ~1 and are monotonic with flows. -#[test] -fn get_shares_normal_flows_three_subnets() { - new_test_ext(1).execute_with(|| { - let owner_hotkey = U256::from(10); - let owner_coldkey = U256::from(20); - - let n1 = add_dynamic_network(&owner_hotkey, &owner_coldkey); - let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); - let n3 = add_dynamic_network(&owner_hotkey, &owner_coldkey); - - let block_num = FlowHalfLife::::get(); - System::set_block_number(block_num); - - // Set (block_number, flow) with reasonable positive flows - SubnetEmaTaoFlow::::insert(n1, (block_num, i64f64(1_000.0))); - SubnetEmaTaoFlow::::insert(n2, (block_num, i64f64(3_000.0))); - SubnetEmaTaoFlow::::insert(n3, (block_num, i64f64(6_000.0))); - - let subnets = vec![n1, n2, n3]; - let shares = SubtensorModule::get_shares(&subnets); - - // Sum ≈ 1 - let sum: f64 = shares.values().map(|v| v.to_num::()).sum(); - assert_abs_diff_eq!(sum, 1.0_f64, epsilon = 1e-9); - - // Each share in [0,1] and finite - for (k, v) in &shares { - let f = v.to_num::(); - assert!(f.is_finite(), "share for {k:?} not finite"); - assert!( - (0.0..=1.0).contains(&f), - "share for {k:?} out of [0,1]: {f}" - ); - } - - // Monotonicity with the flows: share(n3) > share(n2) > share(n1) - let s1 = shares.get(&n1).unwrap().to_num::(); - let s2 = shares.get(&n2).unwrap().to_num::(); - let s3 = shares.get(&n3).unwrap().to_num::(); - assert!( - s3 > s2 && s2 > s1, - "expected s3 > s2 > s1; got {s1}, {s2}, {s3}" - ); - }); -} - -/// Very low (but non-zero) EMA flows across 2 subnets. -/// Expect: shares sum to ~1 and higher-flow subnet gets higher share. -#[test] -fn get_shares_low_flows_sum_one_and_ordering() { - new_test_ext(1).execute_with(|| { - let owner_hotkey = U256::from(11); - let owner_coldkey = U256::from(21); - - let n1 = add_dynamic_network(&owner_hotkey, &owner_coldkey); - let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); - - let block_num = FlowHalfLife::::get(); - System::set_block_number(block_num); - - // Tiny flows to exercise precision/scaling path - SubnetEmaTaoFlow::::insert(n1, (block_num, i64f64(1e-9))); - SubnetEmaTaoFlow::::insert(n2, (block_num, i64f64(2e-9))); - - let subnets = vec![n1, n2]; - let shares = SubtensorModule::get_shares(&subnets); - - let sum: f64 = shares.values().map(|v| v.to_num::()).sum(); - assert_abs_diff_eq!(sum, 1.0_f64, epsilon = 1e-8); - - for (k, v) in &shares { - let f = v.to_num::(); - assert!(f.is_finite(), "share for {k:?} not finite"); - assert!( - (0.0..=1.0).contains(&f), - "share for {k:?} out of [0,1]: {f}" - ); - } - - let s1 = shares.get(&n1).unwrap().to_num::(); - let s2 = shares.get(&n2).unwrap().to_num::(); - assert!( - s2 > s1, - "expected s2 > s1 with higher flow; got s1={s1}, s2={s2}" - ); - }); -} - -/// High EMA flows across 2 subnets. -/// Expect: no overflow, shares sum to ~1, and ordering follows flows. -#[test] -fn get_shares_high_flows_sum_one_and_ordering() { - new_test_ext(1).execute_with(|| { - let owner_hotkey = U256::from(12); - let owner_coldkey = U256::from(22); - - let n1 = add_dynamic_network(&owner_hotkey, &owner_coldkey); - let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); - - let block_num = FlowHalfLife::::get(); - System::set_block_number(block_num); - - // Large but safe flows for I64F64 - SubnetEmaTaoFlow::::insert(n1, (block_num, i64f64(9.0e11))); - SubnetEmaTaoFlow::::insert(n2, (block_num, i64f64(1.8e12))); - - let subnets = vec![n1, n2]; - let shares = SubtensorModule::get_shares(&subnets); - - let sum: f64 = shares.values().map(|v| v.to_num::()).sum(); - assert_abs_diff_eq!(sum, 1.0_f64, epsilon = 1e-9); - - for (k, v) in &shares { - let f = v.to_num::(); - assert!(f.is_finite(), "share for {k:?} not finite"); - assert!( - (0.0..=1.0).contains(&f), - "share for {k:?} out of [0,1]: {f}" - ); - } - - let s1 = shares.get(&n1).unwrap().to_num::(); - let s2 = shares.get(&n2).unwrap().to_num::(); - assert!( - s2 > s1, - "expected s2 > s1 with higher flow; got s1={s1}, s2={s2}" - ); - }); -} +// /// Normal (moderate, non-zero) EMA flows across 3 subnets. +// /// Expect: shares sum to ~1 and are monotonic with flows. +// #[test] +// fn get_shares_normal_flows_three_subnets() { +// new_test_ext(1).execute_with(|| { +// let owner_hotkey = U256::from(10); +// let owner_coldkey = U256::from(20); + +// let n1 = add_dynamic_network(&owner_hotkey, &owner_coldkey); +// let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); +// let n3 = add_dynamic_network(&owner_hotkey, &owner_coldkey); + +// let block_num = FlowHalfLife::::get(); +// System::set_block_number(block_num); + +// // Set (block_number, flow) with reasonable positive flows +// SubnetEmaTaoFlow::::insert(n1, (block_num, i64f64(1_000.0))); +// SubnetEmaTaoFlow::::insert(n2, (block_num, i64f64(3_000.0))); +// SubnetEmaTaoFlow::::insert(n3, (block_num, i64f64(6_000.0))); + +// let subnets = vec![n1, n2, n3]; +// let shares = SubtensorModule::get_shares(&subnets); + +// // Sum ≈ 1 +// let sum: f64 = shares.values().map(|v| v.to_num::()).sum(); +// assert_abs_diff_eq!(sum, 1.0_f64, epsilon = 1e-9); + +// // Each share in [0,1] and finite +// for (k, v) in &shares { +// let f = v.to_num::(); +// assert!(f.is_finite(), "share for {k:?} not finite"); +// assert!( +// (0.0..=1.0).contains(&f), +// "share for {k:?} out of [0,1]: {f}" +// ); +// } + +// // Monotonicity with the flows: share(n3) > share(n2) > share(n1) +// let s1 = shares.get(&n1).unwrap().to_num::(); +// let s2 = shares.get(&n2).unwrap().to_num::(); +// let s3 = shares.get(&n3).unwrap().to_num::(); +// assert!( +// s3 > s2 && s2 > s1, +// "expected s3 > s2 > s1; got {s1}, {s2}, {s3}" +// ); +// }); +// } + +// /// Very low (but non-zero) EMA flows across 2 subnets. +// /// Expect: shares sum to ~1 and higher-flow subnet gets higher share. +// #[test] +// fn get_shares_low_flows_sum_one_and_ordering() { +// new_test_ext(1).execute_with(|| { +// let owner_hotkey = U256::from(11); +// let owner_coldkey = U256::from(21); + +// let n1 = add_dynamic_network(&owner_hotkey, &owner_coldkey); +// let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); + +// let block_num = FlowHalfLife::::get(); +// System::set_block_number(block_num); + +// // Tiny flows to exercise precision/scaling path +// SubnetEmaTaoFlow::::insert(n1, (block_num, i64f64(1e-9))); +// SubnetEmaTaoFlow::::insert(n2, (block_num, i64f64(2e-9))); + +// let subnets = vec![n1, n2]; +// let shares = SubtensorModule::get_shares(&subnets); + +// let sum: f64 = shares.values().map(|v| v.to_num::()).sum(); +// assert_abs_diff_eq!(sum, 1.0_f64, epsilon = 1e-8); + +// for (k, v) in &shares { +// let f = v.to_num::(); +// assert!(f.is_finite(), "share for {k:?} not finite"); +// assert!( +// (0.0..=1.0).contains(&f), +// "share for {k:?} out of [0,1]: {f}" +// ); +// } + +// let s1 = shares.get(&n1).unwrap().to_num::(); +// let s2 = shares.get(&n2).unwrap().to_num::(); +// assert!( +// s2 > s1, +// "expected s2 > s1 with higher flow; got s1={s1}, s2={s2}" +// ); +// }); +// } + +// /// High EMA flows across 2 subnets. +// /// Expect: no overflow, shares sum to ~1, and ordering follows flows. +// #[test] +// fn get_shares_high_flows_sum_one_and_ordering() { +// new_test_ext(1).execute_with(|| { +// let owner_hotkey = U256::from(12); +// let owner_coldkey = U256::from(22); + +// let n1 = add_dynamic_network(&owner_hotkey, &owner_coldkey); +// let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); + +// let block_num = FlowHalfLife::::get(); +// System::set_block_number(block_num); + +// // Large but safe flows for I64F64 +// SubnetEmaTaoFlow::::insert(n1, (block_num, i64f64(9.0e11))); +// SubnetEmaTaoFlow::::insert(n2, (block_num, i64f64(1.8e12))); + +// let subnets = vec![n1, n2]; +// let shares = SubtensorModule::get_shares(&subnets); + +// let sum: f64 = shares.values().map(|v| v.to_num::()).sum(); +// assert_abs_diff_eq!(sum, 1.0_f64, epsilon = 1e-9); + +// for (k, v) in &shares { +// let f = v.to_num::(); +// assert!(f.is_finite(), "share for {k:?} not finite"); +// assert!( +// (0.0..=1.0).contains(&f), +// "share for {k:?} out of [0,1]: {f}" +// ); +// } + +// let s1 = shares.get(&n1).unwrap().to_num::(); +// let s2 = shares.get(&n2).unwrap().to_num::(); +// assert!( +// s2 > s1, +// "expected s2 > s1 with higher flow; got s1={s1}, s2={s2}" +// ); +// }); +// } /// Helper to (re)seed EMA price & flow at the *current* block. fn seed_price_and_flow(n1: NetUid, n2: NetUid, price1: f64, price2: f64, flow1: f64, flow2: f64) { @@ -292,199 +292,199 @@ fn seed_price_and_flow(n1: NetUid, n2: NetUid, price1: f64, price2: f64, flow1: SubnetEmaTaoFlow::::insert(n2, (now, i64f64(flow2))); } -/// If one subnet has a negative EMA flow and the other positive, -/// the negative one should contribute no weight (treated as zero), -/// so the positive-flow subnet gets the full share. -#[test] -fn get_shares_negative_vs_positive_flow() { - new_test_ext(1).execute_with(|| { - // 2 subnets - let owner_hotkey = U256::from(60); - let owner_coldkey = U256::from(61); - let n1 = add_dynamic_network(&owner_hotkey, &owner_coldkey); - let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); - - // Configure blending window and current block - let half_life: u64 = FlowHalfLife::::get(); - FlowNormExponent::::set(u64f64(1.0)); - frame_system::Pallet::::set_block_number(half_life); - TaoFlowCutoff::::set(I64F64::from_num(0)); - - // Equal EMA prices so price side doesn't bias - SubnetMovingPrice::::insert(n1, i96f32(1.0)); - SubnetMovingPrice::::insert(n2, i96f32(1.0)); - - // Set flows: n1 negative, n2 positive - let now = frame_system::Pallet::::block_number(); - SubnetEmaTaoFlow::::insert(n1, (now, i64f64(-100.0))); - SubnetEmaTaoFlow::::insert(n2, (now, i64f64(500.0))); - - let shares = SubtensorModule::get_shares(&[n1, n2]); - let s1 = shares.get(&n1).unwrap().to_num::(); - let s2 = shares.get(&n2).unwrap().to_num::(); - - // Sum ~ 1 - assert_abs_diff_eq!(s1 + s2, 1.0_f64, epsilon = 1e-9); - // Negative flow subnet should not get weight from flow; with equal prices mid-window, - // positive-flow subnet should dominate and get all the allocation. - assert!( - s2 > 0.999_999 && s1 < 1e-6, - "expected s2≈1, s1≈0; got s1={s1}, s2={s2}" - ); - }); -} - -/// If both subnets have negative EMA flows, flows should contribute zero weight -#[test] -fn get_shares_both_negative_flows_zero_emission() { - new_test_ext(1).execute_with(|| { - // 2 subnets - let owner_hotkey = U256::from(60); - let owner_coldkey = U256::from(61); - let n1 = add_dynamic_network(&owner_hotkey, &owner_coldkey); - let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); - - // Configure blending window and current block - let half_life: u64 = FlowHalfLife::::get(); - FlowNormExponent::::set(u64f64(1.0)); - frame_system::Pallet::::set_block_number(half_life); - TaoFlowCutoff::::set(I64F64::from_num(0)); - - // Equal EMA prices so price side doesn't bias - SubnetMovingPrice::::insert(n1, i96f32(1.0)); - SubnetMovingPrice::::insert(n2, i96f32(1.0)); - - // Set flows - let now = frame_system::Pallet::::block_number(); - SubnetEmaTaoFlow::::insert(n1, (now, i64f64(-100.0))); - SubnetEmaTaoFlow::::insert(n2, (now, i64f64(-200.0))); - - let shares = SubtensorModule::get_shares(&[n1, n2]); - let s1 = shares.get(&n1).unwrap().to_num::(); - let s2 = shares.get(&n2).unwrap().to_num::(); - - assert!( - s1 < 1e-20 && s2 < 1e-20, - "expected s2≈0, s1≈0; got s1={s1}, s2={s2}" - ); - }); -} - -/// If both subnets have positive EMA flows lower than or equal to cutoff, flows should contribute zero weight -#[test] -fn get_shares_both_below_cutoff_zero_emission() { - new_test_ext(1).execute_with(|| { - // 2 subnets - let owner_hotkey = U256::from(60); - let owner_coldkey = U256::from(61); - let n1 = add_dynamic_network(&owner_hotkey, &owner_coldkey); - let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); - - // Configure blending window and current block - let half_life: u64 = FlowHalfLife::::get(); - FlowNormExponent::::set(u64f64(1.0)); - frame_system::Pallet::::set_block_number(half_life); - TaoFlowCutoff::::set(I64F64::from_num(2_000)); - - // Equal EMA prices so price side doesn't bias - SubnetMovingPrice::::insert(n1, i96f32(1.0)); - SubnetMovingPrice::::insert(n2, i96f32(1.0)); - - // Set flows - let now = frame_system::Pallet::::block_number(); - SubnetEmaTaoFlow::::insert(n1, (now, i64f64(1000.0))); - SubnetEmaTaoFlow::::insert(n2, (now, i64f64(2000.0))); - - let shares = SubtensorModule::get_shares(&[n1, n2]); - let s1 = shares.get(&n1).unwrap().to_num::(); - let s2 = shares.get(&n2).unwrap().to_num::(); - - assert!( - s1 < 1e-20 && s2 < 1e-20, - "expected s2≈0, s1≈0; got s1={s1}, s2={s2}" - ); - }); -} - -/// If one subnet has positive EMA flow lower than cutoff, the other gets full emission -#[test] -fn get_shares_one_below_cutoff_other_full_emission() { - new_test_ext(1).execute_with(|| { - [(1000.0, 2000.00001), (1000.0, 2000.001), (1000.0, 5000.0)] - .into_iter() - .for_each(|(flow1, flow2)| { - // 2 subnets - let owner_hotkey = U256::from(60); - let owner_coldkey = U256::from(61); - let n1 = add_dynamic_network(&owner_hotkey, &owner_coldkey); - let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); - - // Configure blending window and current block - let half_life: u64 = FlowHalfLife::::get(); - FlowNormExponent::::set(u64f64(1.0)); - frame_system::Pallet::::set_block_number(half_life); - TaoFlowCutoff::::set(I64F64::from_num(2_000)); - - // Equal EMA prices (price side doesn't bias) - SubnetMovingPrice::::insert(n1, i96f32(1.0)); - SubnetMovingPrice::::insert(n2, i96f32(1.0)); - - // Set flows - let now = frame_system::Pallet::::block_number(); - SubnetEmaTaoFlow::::insert(n1, (now, i64f64(flow1))); - SubnetEmaTaoFlow::::insert(n2, (now, i64f64(flow2))); - - let shares = SubtensorModule::get_shares(&[n1, n2]); - let s1 = shares.get(&n1).unwrap().to_num::(); - let s2 = shares.get(&n2).unwrap().to_num::(); - - // Sum ~ 1 - assert_abs_diff_eq!(s1 + s2, 1.0_f64, epsilon = 1e-9); - assert!( - s2 > 0.999_999 && s1 < 1e-6, - "expected s2≈1, s1≈0; got s1={s1}, s2={s2}" - ); - }); - }); -} - -/// If subnets have negative EMA flows, but they are above the cut-off, emissions are proportional -/// for all except the bottom one, which gets nothing -#[test] -fn get_shares_both_negative_above_cutoff() { - new_test_ext(1).execute_with(|| { - // 2 subnets - let owner_hotkey = U256::from(60); - let owner_coldkey = U256::from(61); - let n1 = add_dynamic_network(&owner_hotkey, &owner_coldkey); - let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); - let n3 = add_dynamic_network(&owner_hotkey, &owner_coldkey); - - // Configure blending window and current block - let half_life: u64 = FlowHalfLife::::get(); - FlowNormExponent::::set(u64f64(1.0)); - frame_system::Pallet::::set_block_number(half_life); - TaoFlowCutoff::::set(I64F64::from_num(-1000.0)); - - // Equal EMA prices so price side doesn't bias - SubnetMovingPrice::::insert(n1, i96f32(1.0)); - SubnetMovingPrice::::insert(n2, i96f32(1.0)); - SubnetMovingPrice::::insert(n3, i96f32(1.0)); - - // Set flows - let now = frame_system::Pallet::::block_number(); - SubnetEmaTaoFlow::::insert(n1, (now, i64f64(-100.0))); - SubnetEmaTaoFlow::::insert(n2, (now, i64f64(-300.0))); - SubnetEmaTaoFlow::::insert(n3, (now, i64f64(-400.0))); - - let shares = SubtensorModule::get_shares(&[n1, n2, n3]); - let s1 = shares.get(&n1).unwrap().to_num::(); - let s2 = shares.get(&n2).unwrap().to_num::(); - let s3 = shares.get(&n3).unwrap().to_num::(); - - assert_abs_diff_eq!(s1, 0.75, epsilon = s1 / 100.0); - assert_abs_diff_eq!(s2, 0.25, epsilon = s2 / 100.0); - assert_abs_diff_eq!(s3, 0.0, epsilon = 1e-9); - assert_abs_diff_eq!(s1 + s2 + s3, 1.0, epsilon = 1e-9); - }); -} +// /// If one subnet has a negative EMA flow and the other positive, +// /// the negative one should contribute no weight (treated as zero), +// /// so the positive-flow subnet gets the full share. +// #[test] +// fn get_shares_negative_vs_positive_flow() { +// new_test_ext(1).execute_with(|| { +// // 2 subnets +// let owner_hotkey = U256::from(60); +// let owner_coldkey = U256::from(61); +// let n1 = add_dynamic_network(&owner_hotkey, &owner_coldkey); +// let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); + +// // Configure blending window and current block +// let half_life: u64 = FlowHalfLife::::get(); +// FlowNormExponent::::set(u64f64(1.0)); +// frame_system::Pallet::::set_block_number(half_life); +// TaoFlowCutoff::::set(I64F64::from_num(0)); + +// // Equal EMA prices so price side doesn't bias +// SubnetMovingPrice::::insert(n1, i96f32(1.0)); +// SubnetMovingPrice::::insert(n2, i96f32(1.0)); + +// // Set flows: n1 negative, n2 positive +// let now = frame_system::Pallet::::block_number(); +// SubnetEmaTaoFlow::::insert(n1, (now, i64f64(-100.0))); +// SubnetEmaTaoFlow::::insert(n2, (now, i64f64(500.0))); + +// let shares = SubtensorModule::get_shares(&[n1, n2]); +// let s1 = shares.get(&n1).unwrap().to_num::(); +// let s2 = shares.get(&n2).unwrap().to_num::(); + +// // Sum ~ 1 +// assert_abs_diff_eq!(s1 + s2, 1.0_f64, epsilon = 1e-9); +// // Negative flow subnet should not get weight from flow; with equal prices mid-window, +// // positive-flow subnet should dominate and get all the allocation. +// assert!( +// s2 > 0.999_999 && s1 < 1e-6, +// "expected s2≈1, s1≈0; got s1={s1}, s2={s2}" +// ); +// }); +// } + +// /// If both subnets have negative EMA flows, flows should contribute zero weight +// #[test] +// fn get_shares_both_negative_flows_zero_emission() { +// new_test_ext(1).execute_with(|| { +// // 2 subnets +// let owner_hotkey = U256::from(60); +// let owner_coldkey = U256::from(61); +// let n1 = add_dynamic_network(&owner_hotkey, &owner_coldkey); +// let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); + +// // Configure blending window and current block +// let half_life: u64 = FlowHalfLife::::get(); +// FlowNormExponent::::set(u64f64(1.0)); +// frame_system::Pallet::::set_block_number(half_life); +// TaoFlowCutoff::::set(I64F64::from_num(0)); + +// // Equal EMA prices so price side doesn't bias +// SubnetMovingPrice::::insert(n1, i96f32(1.0)); +// SubnetMovingPrice::::insert(n2, i96f32(1.0)); + +// // Set flows +// let now = frame_system::Pallet::::block_number(); +// SubnetEmaTaoFlow::::insert(n1, (now, i64f64(-100.0))); +// SubnetEmaTaoFlow::::insert(n2, (now, i64f64(-200.0))); + +// let shares = SubtensorModule::get_shares(&[n1, n2]); +// let s1 = shares.get(&n1).unwrap().to_num::(); +// let s2 = shares.get(&n2).unwrap().to_num::(); + +// assert!( +// s1 < 1e-20 && s2 < 1e-20, +// "expected s2≈0, s1≈0; got s1={s1}, s2={s2}" +// ); +// }); +// } + +// /// If both subnets have positive EMA flows lower than or equal to cutoff, flows should contribute zero weight +// #[test] +// fn get_shares_both_below_cutoff_zero_emission() { +// new_test_ext(1).execute_with(|| { +// // 2 subnets +// let owner_hotkey = U256::from(60); +// let owner_coldkey = U256::from(61); +// let n1 = add_dynamic_network(&owner_hotkey, &owner_coldkey); +// let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); + +// // Configure blending window and current block +// let half_life: u64 = FlowHalfLife::::get(); +// FlowNormExponent::::set(u64f64(1.0)); +// frame_system::Pallet::::set_block_number(half_life); +// TaoFlowCutoff::::set(I64F64::from_num(2_000)); + +// // Equal EMA prices so price side doesn't bias +// SubnetMovingPrice::::insert(n1, i96f32(1.0)); +// SubnetMovingPrice::::insert(n2, i96f32(1.0)); + +// // Set flows +// let now = frame_system::Pallet::::block_number(); +// SubnetEmaTaoFlow::::insert(n1, (now, i64f64(1000.0))); +// SubnetEmaTaoFlow::::insert(n2, (now, i64f64(2000.0))); + +// let shares = SubtensorModule::get_shares(&[n1, n2]); +// let s1 = shares.get(&n1).unwrap().to_num::(); +// let s2 = shares.get(&n2).unwrap().to_num::(); + +// assert!( +// s1 < 1e-20 && s2 < 1e-20, +// "expected s2≈0, s1≈0; got s1={s1}, s2={s2}" +// ); +// }); +// } + +// /// If one subnet has positive EMA flow lower than cutoff, the other gets full emission +// #[test] +// fn get_shares_one_below_cutoff_other_full_emission() { +// new_test_ext(1).execute_with(|| { +// [(1000.0, 2000.00001), (1000.0, 2000.001), (1000.0, 5000.0)] +// .into_iter() +// .for_each(|(flow1, flow2)| { +// // 2 subnets +// let owner_hotkey = U256::from(60); +// let owner_coldkey = U256::from(61); +// let n1 = add_dynamic_network(&owner_hotkey, &owner_coldkey); +// let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); + +// // Configure blending window and current block +// let half_life: u64 = FlowHalfLife::::get(); +// FlowNormExponent::::set(u64f64(1.0)); +// frame_system::Pallet::::set_block_number(half_life); +// TaoFlowCutoff::::set(I64F64::from_num(2_000)); + +// // Equal EMA prices (price side doesn't bias) +// SubnetMovingPrice::::insert(n1, i96f32(1.0)); +// SubnetMovingPrice::::insert(n2, i96f32(1.0)); + +// // Set flows +// let now = frame_system::Pallet::::block_number(); +// SubnetEmaTaoFlow::::insert(n1, (now, i64f64(flow1))); +// SubnetEmaTaoFlow::::insert(n2, (now, i64f64(flow2))); + +// let shares = SubtensorModule::get_shares(&[n1, n2]); +// let s1 = shares.get(&n1).unwrap().to_num::(); +// let s2 = shares.get(&n2).unwrap().to_num::(); + +// // Sum ~ 1 +// assert_abs_diff_eq!(s1 + s2, 1.0_f64, epsilon = 1e-9); +// assert!( +// s2 > 0.999_999 && s1 < 1e-6, +// "expected s2≈1, s1≈0; got s1={s1}, s2={s2}" +// ); +// }); +// }); +// } + +// /// If subnets have negative EMA flows, but they are above the cut-off, emissions are proportional +// /// for all except the bottom one, which gets nothing +// #[test] +// fn get_shares_both_negative_above_cutoff() { +// new_test_ext(1).execute_with(|| { +// // 2 subnets +// let owner_hotkey = U256::from(60); +// let owner_coldkey = U256::from(61); +// let n1 = add_dynamic_network(&owner_hotkey, &owner_coldkey); +// let n2 = add_dynamic_network(&owner_hotkey, &owner_coldkey); +// let n3 = add_dynamic_network(&owner_hotkey, &owner_coldkey); + +// // Configure blending window and current block +// let half_life: u64 = FlowHalfLife::::get(); +// FlowNormExponent::::set(u64f64(1.0)); +// frame_system::Pallet::::set_block_number(half_life); +// TaoFlowCutoff::::set(I64F64::from_num(-1000.0)); + +// // Equal EMA prices so price side doesn't bias +// SubnetMovingPrice::::insert(n1, i96f32(1.0)); +// SubnetMovingPrice::::insert(n2, i96f32(1.0)); +// SubnetMovingPrice::::insert(n3, i96f32(1.0)); + +// // Set flows +// let now = frame_system::Pallet::::block_number(); +// SubnetEmaTaoFlow::::insert(n1, (now, i64f64(-100.0))); +// SubnetEmaTaoFlow::::insert(n2, (now, i64f64(-300.0))); +// SubnetEmaTaoFlow::::insert(n3, (now, i64f64(-400.0))); + +// let shares = SubtensorModule::get_shares(&[n1, n2, n3]); +// let s1 = shares.get(&n1).unwrap().to_num::(); +// let s2 = shares.get(&n2).unwrap().to_num::(); +// let s3 = shares.get(&n3).unwrap().to_num::(); + +// assert_abs_diff_eq!(s1, 0.75, epsilon = s1 / 100.0); +// assert_abs_diff_eq!(s2, 0.25, epsilon = s2 / 100.0); +// assert_abs_diff_eq!(s3, 0.0, epsilon = 1e-9); +// assert_abs_diff_eq!(s1 + s2 + s3, 1.0, epsilon = 1e-9); +// }); +// } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index d8277164e2..548c192ece 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 334, + spec_version: 335, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 3ec6631689de3acfe39e61f50cafc7e32ddc30ed Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Tue, 4 Nov 2025 20:31:27 -0500 Subject: [PATCH 039/210] Migration to remove SubnetEmaTaoFlow and cause it's re-initialization --- .../src/coinbase/subnet_emissions.rs | 3 ++- pallets/subtensor/src/macros/hooks.rs | 2 +- .../src/migrations/migrate_init_tao_flow.rs | 23 +++---------------- pallets/subtensor/src/migrations/mod.rs | 2 +- runtime/src/lib.rs | 2 +- 5 files changed, 8 insertions(+), 24 deletions(-) diff --git a/pallets/subtensor/src/coinbase/subnet_emissions.rs b/pallets/subtensor/src/coinbase/subnet_emissions.rs index 80ecf85a42..2d9c3b1473 100644 --- a/pallets/subtensor/src/coinbase/subnet_emissions.rs +++ b/pallets/subtensor/src/coinbase/subnet_emissions.rs @@ -75,10 +75,11 @@ impl Pallet { } } else { // Initialize EMA flow, set S(current_block) = min(price, ema_price) * init_factor + let init_factor = I64F64::saturating_from_num(1_000_000_000); let moving_price = I64F64::saturating_from_num(Self::get_moving_alpha_price(netuid)); let current_price = I64F64::saturating_from_num(T::SwapInterface::current_alpha_price(netuid)); - let ema_flow = moving_price.min(current_price); + let ema_flow = init_factor.saturating_mul(moving_price.min(current_price)); SubnetEmaTaoFlow::::insert(netuid, (current_block, ema_flow)); ema_flow } diff --git a/pallets/subtensor/src/macros/hooks.rs b/pallets/subtensor/src/macros/hooks.rs index 2369b25275..7095ce8ecb 100644 --- a/pallets/subtensor/src/macros/hooks.rs +++ b/pallets/subtensor/src/macros/hooks.rs @@ -161,7 +161,7 @@ mod hooks { // Remove obsolete map entries .saturating_add(migrations::migrate_remove_tao_dividends::migrate_remove_tao_dividends::()) // Re-init tao flows - .saturating_add(migrations::migrate_init_tao_flows::migrate_init_tao_flows::()); + .saturating_add(migrations::migrate_init_tao_flow::migrate_init_tao_flow::()); weight } diff --git a/pallets/subtensor/src/migrations/migrate_init_tao_flow.rs b/pallets/subtensor/src/migrations/migrate_init_tao_flow.rs index c0cdb87336..477410600f 100644 --- a/pallets/subtensor/src/migrations/migrate_init_tao_flow.rs +++ b/pallets/subtensor/src/migrations/migrate_init_tao_flow.rs @@ -1,12 +1,10 @@ use alloc::string::String; -use frame_support::IterableStorageMap; use frame_support::{traits::Get, weights::Weight}; -use subtensor_runtime_common::NetUid; use super::*; -pub fn migrate_init_tao_flows() -> Weight { +pub fn migrate_init_tao_flow() -> Weight { let migration_name = b"migrate_init_tao_flow".to_vec(); // Initialize the weight with one read operation. @@ -25,23 +23,8 @@ pub fn migrate_init_tao_flows() -> Weight { String::from_utf8_lossy(&migration_name) ); - let netuids: Vec = as IterableStorageMap>::iter() - .map(|(netuid, _)| netuid) - .collect(); - weight = weight.saturating_add(T::DbWeight::get().reads(netuids.len() as u64)); - - for netuid in netuids.iter() { - if netuid.is_root() { - continue; - } - // Set SubnetEmaTaoFlow - SubnetEmaTaoFlow::::insert(*netuid, I64F64::saturating_from_num(0)); - - - - - weight = weight.saturating_add(T::DbWeight::get().writes(1)); - } + let _ = SubnetEmaTaoFlow::::clear(u32::MAX, None); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); // Mark the migration as completed HasMigrationRun::::insert(&migration_name, true); diff --git a/pallets/subtensor/src/migrations/mod.rs b/pallets/subtensor/src/migrations/mod.rs index dc14de151f..7fe37617fd 100644 --- a/pallets/subtensor/src/migrations/mod.rs +++ b/pallets/subtensor/src/migrations/mod.rs @@ -20,7 +20,7 @@ pub mod migrate_fix_is_network_member; pub mod migrate_fix_root_subnet_tao; pub mod migrate_fix_root_tao_and_alpha_in; pub mod migrate_identities_v2; -pub mod migrate_init_tao_flows; +pub mod migrate_init_tao_flow; pub mod migrate_init_total_issuance; pub mod migrate_kappa_map_to_default; pub mod migrate_network_immunity_period; diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index d8277164e2..548c192ece 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 334, + spec_version: 335, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 04337b0d51b8e3cca36c097ca978400815c0c249 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Tue, 4 Nov 2025 20:38:55 -0500 Subject: [PATCH 040/210] Spec bump --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 548c192ece..7bd2f459b4 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 335, + spec_version: 336, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 54b5090fece276f4ec99bdfbdbf67b99f4b3bd02 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Tue, 4 Nov 2025 21:12:49 -0500 Subject: [PATCH 041/210] Spec bump --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 548c192ece..ae7e3d80fc 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 335, + spec_version: 337, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From a95bc8e08546c17acb711362575fb348d81a2ca1 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Tue, 4 Nov 2025 21:22:01 -0500 Subject: [PATCH 042/210] Reorder functions --- pallets/subtensor/src/coinbase/subnet_emissions.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pallets/subtensor/src/coinbase/subnet_emissions.rs b/pallets/subtensor/src/coinbase/subnet_emissions.rs index 4f3911e3ae..d1cd4ffdd4 100644 --- a/pallets/subtensor/src/coinbase/subnet_emissions.rs +++ b/pallets/subtensor/src/coinbase/subnet_emissions.rs @@ -213,6 +213,12 @@ impl Pallet { offset_flows } + // Combines ema price method and tao flow method linearly over FlowHalfLife blocks + pub(crate) fn get_shares(subnets_to_emit_to: &[NetUid]) -> BTreeMap { + // Self::get_shares_flow(subnets_to_emit_to) + Self::get_shares_price_ema(subnets_to_emit_to) + } + // DEPRECATED: Implementation of shares that uses EMA prices will be gradually deprecated fn get_shares_price_ema(subnets_to_emit_to: &[NetUid]) -> BTreeMap { // Get sum of alpha moving prices @@ -240,10 +246,4 @@ impl Pallet { }) .collect::>() } - - // Combines ema price method and tao flow method linearly over FlowHalfLife blocks - pub(crate) fn get_shares(subnets_to_emit_to: &[NetUid]) -> BTreeMap { - // Self::get_shares_flow(subnets_to_emit_to) - Self::get_shares_price_ema(subnets_to_emit_to) - } } From e36f94cd7db87c429f2d2a9f6d190969af4d147c Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 5 Nov 2025 10:37:47 +0800 Subject: [PATCH 043/210] fix bug --- precompiles/src/proxy.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/precompiles/src/proxy.rs b/precompiles/src/proxy.rs index c2a1c41a38..1399177766 100644 --- a/precompiles/src/proxy.rs +++ b/precompiles/src/proxy.rs @@ -84,7 +84,7 @@ where .into(); // Check if in the proxies map - let proxy_entry = pallet_proxy::pallet::Pallet::::proxies(account_id.clone()); + let proxy_entry = pallet_proxy::pallet::Pallet::::proxies(proxy_address.into()); if proxy_entry .0 .iter() From fe78a6094626a9bc5144f8e558f0c63425636561 Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 5 Nov 2025 10:39:46 +0800 Subject: [PATCH 044/210] cargo fix --- evm-tests/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm-tests/package.json b/evm-tests/package.json index ae756ae55f..653ea3fbdf 100644 --- a/evm-tests/package.json +++ b/evm-tests/package.json @@ -1,6 +1,6 @@ { "scripts": { - "test": "mocha --timeout 999999 --retries 3 --file src/setup.ts --require ts-node/register test/*test.ts" + "test": "mocha --timeout 999999 --retries 3 --file src/setup.ts --require ts-node/register test/pure*test.ts" }, "keywords": [], "author": "", From bf0ee07602655da65c242b13f0d3c45dc8bb7582 Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 5 Nov 2025 10:47:17 +0800 Subject: [PATCH 045/210] remove kill case --- evm-tests/package.json | 2 +- evm-tests/test/pure-proxy.precompile.test.ts | 34 ++------------------ 2 files changed, 4 insertions(+), 32 deletions(-) diff --git a/evm-tests/package.json b/evm-tests/package.json index 653ea3fbdf..ae756ae55f 100644 --- a/evm-tests/package.json +++ b/evm-tests/package.json @@ -1,6 +1,6 @@ { "scripts": { - "test": "mocha --timeout 999999 --retries 3 --file src/setup.ts --require ts-node/register test/pure*test.ts" + "test": "mocha --timeout 999999 --retries 3 --file src/setup.ts --require ts-node/register test/*test.ts" }, "keywords": [], "author": "", diff --git a/evm-tests/test/pure-proxy.precompile.test.ts b/evm-tests/test/pure-proxy.precompile.test.ts index e9b9397918..b308438263 100644 --- a/evm-tests/test/pure-proxy.precompile.test.ts +++ b/evm-tests/test/pure-proxy.precompile.test.ts @@ -94,47 +94,19 @@ describe("Test pure proxy precompile", () => { assert.equal(balanceAfter, balance + BigInt(amount), "balance should be increased") }) - it("Call createPureProxy, add kill one", async () => { - const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet) - const type = 0; - const delay = 0; - const index = 0; - const extIndex = 1; - - const proxies = await getProxies(api, convertH160ToSS58(evmWallet.address)) - const length = proxies.length - const addTx = await contract.createPureProxy(type, delay, index) - const response = await addTx.wait() - const createBlockNumber = response.blockNumber - - const currentLength = (await getProxies(api, convertH160ToSS58(evmWallet.address))).length - assert.equal(currentLength, length + 1, "proxy should be set") - - try { - const tx = await contract.killPureProxy(decodeAddress(proxies[proxies.length - 1]), type, index, - createBlockNumber, extIndex) - await tx.wait() - } catch (error) { - console.log("error", error) - } - - const proxiesAfterRemove = await getProxies(api, convertH160ToSS58(evmWallet.address)) - assert.equal(proxiesAfterRemove.length, 0, "proxies should be removed") - }) - it("Call createPureProxy, add multiple proxies", async () => { - const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet) + const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet1) const type = 0; const delay = 0; const index = 0; - const proxies = await getProxies(api, convertH160ToSS58(evmWallet.address)) + const proxies = await getProxies(api, convertH160ToSS58(evmWallet1.address)) const length = proxies.length for (let i = 0; i < 5; i++) { const tx = await contract.createPureProxy(type, delay, index) await tx.wait() await new Promise(resolve => setTimeout(resolve, 500)); - const currentProxies = await getProxies(api, convertH160ToSS58(evmWallet.address)) + const currentProxies = await getProxies(api, convertH160ToSS58(evmWallet1.address)) assert.equal(currentProxies.length, length + i + 1, "proxy should be set") } }) From 120be4f85a95fe3964e01ba69e1ee6fe1acb1c8e Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Tue, 4 Nov 2025 22:42:27 -0500 Subject: [PATCH 046/210] Re-enable tao flow --- pallets/subtensor/src/coinbase/subnet_emissions.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/coinbase/subnet_emissions.rs b/pallets/subtensor/src/coinbase/subnet_emissions.rs index cb9fe95ce0..184f27a4ba 100644 --- a/pallets/subtensor/src/coinbase/subnet_emissions.rs +++ b/pallets/subtensor/src/coinbase/subnet_emissions.rs @@ -216,11 +216,12 @@ impl Pallet { // Combines ema price method and tao flow method linearly over FlowHalfLife blocks pub(crate) fn get_shares(subnets_to_emit_to: &[NetUid]) -> BTreeMap { - // Self::get_shares_flow(subnets_to_emit_to) - Self::get_shares_price_ema(subnets_to_emit_to) + Self::get_shares_flow(subnets_to_emit_to) + // Self::get_shares_price_ema(subnets_to_emit_to) } // DEPRECATED: Implementation of shares that uses EMA prices will be gradually deprecated + #[allow(dead_code)] fn get_shares_price_ema(subnets_to_emit_to: &[NetUid]) -> BTreeMap { // Get sum of alpha moving prices let total_moving_prices = subnets_to_emit_to From e48ef843214a525aa65796e65d6abb3d015d1a2d Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Tue, 4 Nov 2025 22:44:23 -0500 Subject: [PATCH 047/210] Spec bump --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index ae7e3d80fc..266a755708 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 337, + spec_version: 338, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 757b846e284fb17477b5c56573309ffa2d641782 Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 5 Nov 2025 14:02:22 +0800 Subject: [PATCH 048/210] fix wrong variable --- evm-tests/test/pure-proxy.precompile.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/evm-tests/test/pure-proxy.precompile.test.ts b/evm-tests/test/pure-proxy.precompile.test.ts index b308438263..1a34b02cf7 100644 --- a/evm-tests/test/pure-proxy.precompile.test.ts +++ b/evm-tests/test/pure-proxy.precompile.test.ts @@ -95,18 +95,18 @@ describe("Test pure proxy precompile", () => { }) it("Call createPureProxy, add multiple proxies", async () => { - const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet1) + const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet) const type = 0; const delay = 0; const index = 0; - const proxies = await getProxies(api, convertH160ToSS58(evmWallet1.address)) + const proxies = await getProxies(api, convertH160ToSS58(evmWallet.address)) const length = proxies.length for (let i = 0; i < 5; i++) { const tx = await contract.createPureProxy(type, delay, index) await tx.wait() await new Promise(resolve => setTimeout(resolve, 500)); - const currentProxies = await getProxies(api, convertH160ToSS58(evmWallet1.address)) + const currentProxies = await getProxies(api, convertH160ToSS58(evmWallet.address)) assert.equal(currentProxies.length, length + i + 1, "proxy should be set") } }) From b2385bd6fc304d116e3e1c9de78ef0070c51203b Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 5 Nov 2025 21:45:38 +0800 Subject: [PATCH 049/210] add comments for pallet config in test --- pallets/subtensor/src/tests/mock.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index 549d062058..cf6f4b9a75 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -428,15 +428,17 @@ impl pallet_crowdloan::Config for Test { // Proxy Pallet config parameter_types! { - // One storage item; key size sizeof(AccountId) = 32, value sizeof(Balance) = 8; 40 total + // Set as 1 for testing purposes pub const ProxyDepositBase: Balance = 1; - // Adding 32 bytes + sizeof(ProxyType) = 32 + 1 + // Set as 1 for testing purposes pub const ProxyDepositFactor: Balance = 1; + // Set as 20 for testing purposes pub const MaxProxies: u32 = 20; // max num proxies per acct - pub const MaxPending: u32 = 15 * 5; // max blocks pending ~15min - // 16 bytes + // Set as 15 for testing purposes + pub const MaxPending: u32 = 15; // max blocks pending ~15min + // Set as 1 for testing purposes pub const AnnouncementDepositBase: Balance = 1; - // 68 bytes per announcement + // Set as 1 for testing purposes pub const AnnouncementDepositFactor: Balance = 1; } From 842c2aee503aa2e183f65f74deff10dc49be41ee Mon Sep 17 00:00:00 2001 From: Aliaksandr Tsurko Date: Wed, 5 Nov 2025 18:44:32 +0300 Subject: [PATCH 050/210] Separate admin freeze window check from rate limit check --- pallets/admin-utils/src/lib.rs | 27 ++++++++++++++++++++++ pallets/subtensor/src/tests/ensure.rs | 15 ++++++++---- pallets/subtensor/src/utils/misc.rs | 33 ++++----------------------- 3 files changed, 41 insertions(+), 34 deletions(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 01e9e7b33e..f8f9a56838 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -236,6 +236,7 @@ pub mod pallet { netuid, &[Hyperparameter::ServingRateLimit.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; pallet_subtensor::Pallet::::set_serving_rate_limit(netuid, serving_rate_limit); log::debug!("ServingRateLimitSet( serving_rate_limit: {serving_rate_limit:?} ) "); pallet_subtensor::Pallet::::record_owner_rl( @@ -288,6 +289,7 @@ pub mod pallet { netuid, &[Hyperparameter::MaxDifficulty.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), @@ -322,6 +324,7 @@ pub mod pallet { netuid, &[TransactionType::SetWeightsVersionKey], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), @@ -413,6 +416,7 @@ pub mod pallet { netuid, &[Hyperparameter::AdjustmentAlpha.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), @@ -445,6 +449,7 @@ pub mod pallet { netuid, &[Hyperparameter::ImmunityPeriod.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist @@ -479,6 +484,7 @@ pub mod pallet { netuid, &[Hyperparameter::MinAllowedWeights.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), @@ -513,6 +519,7 @@ pub mod pallet { netuid, &[Hyperparameter::MaxAllowedUids.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist @@ -572,6 +579,7 @@ pub mod pallet { netuid, &[Hyperparameter::Rho.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), @@ -604,6 +612,7 @@ pub mod pallet { netuid, &[Hyperparameter::ActivityCutoff.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), @@ -672,6 +681,7 @@ pub mod pallet { netuid, &[Hyperparameter::PowRegistrationAllowed.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; pallet_subtensor::Pallet::::set_network_pow_registration_allowed( netuid, @@ -733,6 +743,7 @@ pub mod pallet { netuid, &[Hyperparameter::MinBurn.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist @@ -773,6 +784,7 @@ pub mod pallet { netuid, &[Hyperparameter::MaxBurn.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist @@ -868,6 +880,7 @@ pub mod pallet { netuid, &[Hyperparameter::BondsMovingAverage.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; if maybe_owner.is_some() { ensure!( bonds_moving_average <= 975000, @@ -908,6 +921,7 @@ pub mod pallet { netuid, &[Hyperparameter::BondsPenalty.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), @@ -1245,6 +1259,7 @@ pub mod pallet { netuid, &[Hyperparameter::CommitRevealEnabled.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), @@ -1288,6 +1303,7 @@ pub mod pallet { netuid, &[Hyperparameter::LiquidAlphaEnabled.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; pallet_subtensor::Pallet::::set_liquid_alpha_enabled(netuid, enabled); log::debug!("LiquidAlphaEnableToggled( netuid: {netuid:?}, Enabled: {enabled:?} ) "); pallet_subtensor::Pallet::::record_owner_rl( @@ -1318,6 +1334,7 @@ pub mod pallet { netuid, &[Hyperparameter::AlphaValues.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; let res = pallet_subtensor::Pallet::::do_set_alpha_values( origin, netuid, alpha_low, alpha_high, ); @@ -1437,6 +1454,7 @@ pub mod pallet { netuid, &[Hyperparameter::WeightCommitInterval.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), @@ -1535,6 +1553,7 @@ pub mod pallet { netuid, &[Hyperparameter::TransferEnabled.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; let res = pallet_subtensor::Pallet::::toggle_transfer(netuid, toggle); if res.is_ok() { pallet_subtensor::Pallet::::record_owner_rl( @@ -1567,6 +1586,7 @@ pub mod pallet { netuid, &[Hyperparameter::RecycleOrBurn.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; pallet_subtensor::Pallet::::set_recycle_or_burn(netuid, recycle_or_burn); pallet_subtensor::Pallet::::record_owner_rl( @@ -1734,6 +1754,7 @@ pub mod pallet { netuid, &[Hyperparameter::AlphaSigmoidSteepness.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), @@ -1784,6 +1805,7 @@ pub mod pallet { netuid, &[Hyperparameter::Yuma3Enabled.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; pallet_subtensor::Pallet::::set_yuma3_enabled(netuid, enabled); Self::deposit_event(Event::Yuma3EnableToggled { netuid, enabled }); @@ -1823,6 +1845,7 @@ pub mod pallet { netuid, &[Hyperparameter::BondsResetEnabled.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; pallet_subtensor::Pallet::::set_bonds_reset(netuid, enabled); Self::deposit_event(Event::BondsResetToggled { netuid, enabled }); @@ -1948,6 +1971,7 @@ pub mod pallet { netuid, &[Hyperparameter::ImmuneNeuronLimit.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; pallet_subtensor::Pallet::::set_owner_immune_neuron_limit(netuid, immune_neurons)?; pallet_subtensor::Pallet::::record_owner_rl( maybe_owner, @@ -2021,6 +2045,7 @@ pub mod pallet { netuid, &[TransactionType::MechanismCountUpdate], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; pallet_subtensor::Pallet::::do_set_mechanism_count(netuid, mechanism_count)?; @@ -2047,6 +2072,7 @@ pub mod pallet { netuid, &[TransactionType::MechanismEmission], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; pallet_subtensor::Pallet::::do_set_emission_split(netuid, maybe_split)?; @@ -2077,6 +2103,7 @@ pub mod pallet { netuid, &[TransactionType::MaxUidsTrimming], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; pallet_subtensor::Pallet::::trim_to_max_allowed_uids(netuid, max_n)?; diff --git a/pallets/subtensor/src/tests/ensure.rs b/pallets/subtensor/src/tests/ensure.rs index a59bfd7484..3b4db87f4e 100644 --- a/pallets/subtensor/src/tests/ensure.rs +++ b/pallets/subtensor/src/tests/ensure.rs @@ -111,6 +111,7 @@ fn ensure_owner_or_root_with_limits_checks_rl_and_freeze() { ) .expect("should pass"); assert_eq!(res, Some(owner)); + assert_ok!(crate::Pallet::::ensure_admin_window_open(netuid)); // Simulate previous update at current block -> next call should fail due to rate limit let now = crate::Pallet::::get_current_block_as_u64(); @@ -127,11 +128,14 @@ fn ensure_owner_or_root_with_limits_checks_rl_and_freeze() { // Advance beyond RL and ensure passes again run_to_block(now + 3); + TransactionType::from(Hyperparameter::Kappa) + .set_last_block_on_subnet::(&owner, netuid, 0); assert_ok!(crate::Pallet::::ensure_sn_owner_or_root_with_limits( <::RuntimeOrigin>::signed(owner), netuid, &[Hyperparameter::Kappa.into()] )); + assert_ok!(crate::Pallet::::ensure_admin_window_open(netuid)); // Now advance into the freeze window; ensure blocks // (using loop for clarity, because epoch calculation function uses netuid) @@ -148,12 +152,13 @@ fn ensure_owner_or_root_with_limits_checks_rl_and_freeze() { } run_to_block(cur + 1); } + assert_ok!(crate::Pallet::::ensure_sn_owner_or_root_with_limits( + <::RuntimeOrigin>::signed(owner), + netuid, + &[Hyperparameter::Kappa.into()] + )); assert_noop!( - crate::Pallet::::ensure_sn_owner_or_root_with_limits( - <::RuntimeOrigin>::signed(owner), - netuid, - &[Hyperparameter::Kappa.into()], - ), + crate::Pallet::::ensure_admin_window_open(netuid), crate::Error::::AdminActionProhibitedDuringWeightsWindow ); }); diff --git a/pallets/subtensor/src/utils/misc.rs b/pallets/subtensor/src/utils/misc.rs index 0ba3df1103..7a14fafd6d 100644 --- a/pallets/subtensor/src/utils/misc.rs +++ b/pallets/subtensor/src/utils/misc.rs @@ -42,23 +42,16 @@ impl Pallet { netuid: NetUid, ) -> Result<(), DispatchError> { ensure_root(o)?; - let now = Self::get_current_block_as_u64(); - Self::ensure_not_in_admin_freeze_window(netuid, now)?; - Ok(()) + Self::ensure_admin_window_open(netuid) } /// Ensure owner-or-root with a set of TransactionType rate checks (owner only). - /// - Root: only freeze window is enforced; no TransactionType checks. - /// - Owner (Signed): freeze window plus all rate checks in `limits` using signer extracted from - /// origin. pub fn ensure_sn_owner_or_root_with_limits( o: T::RuntimeOrigin, netuid: NetUid, limits: &[crate::utils::rate_limiting::TransactionType], ) -> Result, DispatchError> { let maybe_who = Self::ensure_subnet_owner_or_root(o, netuid)?; - let now = Self::get_current_block_as_u64(); - Self::ensure_not_in_admin_freeze_window(netuid, now)?; if let Some(who) = maybe_who.as_ref() { for tx in limits.iter() { ensure!( @@ -70,26 +63,6 @@ impl Pallet { Ok(maybe_who) } - /// Ensure the caller is the subnet owner and passes all provided rate limits. - /// This does NOT allow root; it is strictly owner-only. - /// Returns the signer (owner) on success so callers may record last-blocks. - pub fn ensure_sn_owner_with_limits( - o: T::RuntimeOrigin, - netuid: NetUid, - limits: &[crate::utils::rate_limiting::TransactionType], - ) -> Result { - let who = Self::ensure_subnet_owner(o, netuid)?; - let now = Self::get_current_block_as_u64(); - Self::ensure_not_in_admin_freeze_window(netuid, now)?; - for tx in limits.iter() { - ensure!( - tx.passes_rate_limit_on_subnet::(&who, netuid), - Error::::TxRateLimitExceeded - ); - } - Ok(who) - } - /// Returns true if the current block is within the terminal freeze window of the tempo for the /// given subnet. During this window, admin ops are prohibited to avoid interference with /// validator weight submissions. @@ -103,7 +76,9 @@ impl Pallet { remaining < window } - fn ensure_not_in_admin_freeze_window(netuid: NetUid, now: u64) -> Result<(), DispatchError> { + /// Ensures the admin freeze window is not currently active for the given subnet. + pub fn ensure_admin_window_open(netuid: NetUid) -> Result<(), DispatchError> { + let now = Self::get_current_block_as_u64(); ensure!( !Self::is_in_admin_freeze_window(netuid, now), Error::::AdminActionProhibitedDuringWeightsWindow From 34dc6e73738eb2446abe5dc4b5542ed8f3bc3a2c Mon Sep 17 00:00:00 2001 From: Aliaksandr Tsurko Date: Wed, 5 Nov 2025 18:57:46 +0300 Subject: [PATCH 051/210] Update spec version --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 266a755708..d95eb45ee9 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 338, + spec_version: 339, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From b6cb0884891a6848003eec2d86e42590481de46d Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Wed, 5 Nov 2025 15:13:49 -0500 Subject: [PATCH 052/210] impl tests benchmarks --- pallets/subtensor/src/benchmarks.rs | 1 + .../subtensor/src/coinbase/run_coinbase.rs | 14 ++++++++++++-- pallets/subtensor/src/tests/claim_root.rs | 14 ++++++++++++++ pallets/subtensor/src/tests/coinbase.rs | 19 +++++++++++++++++++ 4 files changed, 46 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index 2a13a18aa0..0e53facabc 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -1619,6 +1619,7 @@ mod pallet_benchmarks { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 53ac3c6ac5..9ced81fc5b 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -229,6 +229,9 @@ impl Pallet { if Self::should_run_epoch(netuid, current_block) && Self::is_epoch_input_state_consistent(netuid) { + let alpha_out_i: AlphaCurrency = + tou64!(*alpha_out.get(&netuid).unwrap_or(&asfloat!(0.0))).into(); + // Restart counters. BlocksSinceLastStep::::insert(netuid, 0); LastMechansimStepBlock::::insert(netuid, current_block); @@ -246,7 +249,13 @@ impl Pallet { PendingOwnerCut::::insert(netuid, AlphaCurrency::ZERO); // Drain pending root alpha divs, alpha emission, and owner cut. - Self::drain_pending_emission(netuid, pending_alpha, pending_root_alpha, owner_cut); + Self::drain_pending_emission( + netuid, + pending_alpha, + pending_root_alpha, + alpha_out_i, + owner_cut, + ); } else { // Increment BlocksSinceLastStep::::mutate(netuid, |total| *total = total.saturating_add(1)); @@ -604,6 +613,7 @@ impl Pallet { netuid: NetUid, pending_alpha: AlphaCurrency, pending_root_alpha: AlphaCurrency, + alpha_out: AlphaCurrency, // total alpha out for the subnet owner_cut: AlphaCurrency, ) { log::debug!( @@ -614,7 +624,7 @@ impl Pallet { // Run the epoch. let hotkey_emission: Vec<(T::AccountId, AlphaCurrency, AlphaCurrency)> = - Self::epoch_with_mechanisms(netuid, pending_alpha.saturating_add(pending_root_alpha)); + Self::epoch_with_mechanisms(netuid, alpha_out); log::debug!("hotkey_emission: {hotkey_emission:?}"); // Compute the pending validator alpha. diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index e73417a326..c75b4f5784 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -76,6 +76,7 @@ fn test_claim_root_with_drain_emissions() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -144,6 +145,7 @@ fn test_claim_root_with_drain_emissions() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -245,6 +247,7 @@ fn test_claim_root_adding_stake_proportionally_for_two_stakers() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -346,6 +349,7 @@ fn test_claim_root_adding_stake_disproportionally_for_two_stakers() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -437,6 +441,7 @@ fn test_claim_root_with_changed_stake() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -489,6 +494,7 @@ fn test_claim_root_with_changed_stake() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -542,6 +548,7 @@ fn test_claim_root_with_changed_stake() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -634,6 +641,7 @@ fn test_claim_root_with_drain_emissions_and_swap_claim_type() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -678,6 +686,7 @@ fn test_claim_root_with_drain_emissions_and_swap_claim_type() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -714,6 +723,7 @@ fn test_claim_root_with_drain_emissions_and_swap_claim_type() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -1100,6 +1110,7 @@ fn test_claim_root_with_swap_coldkey() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -1190,6 +1201,7 @@ fn test_claim_root_with_swap_hotkey() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -1306,6 +1318,7 @@ fn test_claim_root_on_network_deregistration() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -1446,6 +1459,7 @@ fn test_claim_root_with_unrelated_subnets() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 60644b2a28..bbb7061e27 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -689,6 +689,7 @@ fn test_drain_base() { 0.into(), AlphaCurrency::ZERO, AlphaCurrency::ZERO, + AlphaCurrency::ZERO, // alpha out AlphaCurrency::ZERO, ) }); @@ -704,6 +705,7 @@ fn test_drain_base_with_subnet() { netuid, AlphaCurrency::ZERO, AlphaCurrency::ZERO, + AlphaCurrency::ZERO, // alpha out AlphaCurrency::ZERO, ) }); @@ -729,6 +731,7 @@ fn test_drain_base_with_subnet_with_single_staker_not_registered() { netuid, pending_alpha.into(), AlphaCurrency::ZERO, + pending_alpha.into(), // alpha out AlphaCurrency::ZERO, ); let stake_after = @@ -758,6 +761,7 @@ fn test_drain_base_with_subnet_with_single_staker_registered() { netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, // alpha out AlphaCurrency::ZERO, ); let stake_after = @@ -802,6 +806,7 @@ fn test_drain_base_with_subnet_with_single_staker_registered_root_weight() { netuid, pending_alpha, pending_root_alpha, + pending_alpha.saturating_add(pending_root_alpha), // alpha out AlphaCurrency::ZERO, ); let stake_after = @@ -849,6 +854,7 @@ fn test_drain_base_with_subnet_with_two_stakers_registered() { netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); let stake_after1 = @@ -914,6 +920,7 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root() { netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); let stake_after1 = @@ -989,6 +996,7 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_am netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, 0.into(), ); let stake_after1 = @@ -1069,6 +1077,7 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_am netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); let stake_after1 = @@ -1130,6 +1139,7 @@ fn test_drain_alpha_childkey_parentkey() { netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); let parent_stake_after = SubtensorModule::get_stake_for_hotkey_on_subnet(&parent, netuid); @@ -1355,6 +1365,7 @@ fn test_get_root_children_drain() { alpha, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); @@ -1379,6 +1390,7 @@ fn test_get_root_children_drain() { pending_alpha, // pending_root1, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); @@ -1402,6 +1414,7 @@ fn test_get_root_children_drain() { alpha, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); @@ -1490,6 +1503,7 @@ fn test_get_root_children_drain_half_proportion() { alpha, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); @@ -1576,6 +1590,7 @@ fn test_get_root_children_drain_with_take() { alpha, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); @@ -1663,6 +1678,7 @@ fn test_get_root_children_drain_with_half_take() { alpha, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); @@ -2378,6 +2394,7 @@ fn test_drain_pending_emission_no_miners_all_drained() { netuid, emission, AlphaCurrency::ZERO, + emission, AlphaCurrency::ZERO, ); @@ -2451,6 +2468,7 @@ fn test_drain_pending_emission_zero_emission() { 0.into(), AlphaCurrency::ZERO, AlphaCurrency::ZERO, + AlphaCurrency::ZERO, ); // Get the new stake of the hotkey. @@ -2743,6 +2761,7 @@ fn test_drain_alpha_childkey_parentkey_with_burn() { netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); let parent_stake_after = SubtensorModule::get_stake_for_hotkey_on_subnet(&parent, netuid); From 3118bb7c53b78b94cd0b49f8277403f348134ea0 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Wed, 5 Nov 2025 15:13:59 -0500 Subject: [PATCH 053/210] bump spec --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 266a755708..240c490736 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 338, + spec_version: 340, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 843bc884453a9aac28912f72a2acaf5f838a7bc8 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Wed, 5 Nov 2025 15:22:39 -0500 Subject: [PATCH 054/210] remove duplicate remove --- pallets/subtensor/src/coinbase/root.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/pallets/subtensor/src/coinbase/root.rs b/pallets/subtensor/src/coinbase/root.rs index 642a7f18ac..80965f0bbb 100644 --- a/pallets/subtensor/src/coinbase/root.rs +++ b/pallets/subtensor/src/coinbase/root.rs @@ -348,7 +348,6 @@ impl Pallet { RAORecycledForRegistration::::remove(netuid); MaxRegistrationsPerBlock::::remove(netuid); WeightsVersionKey::::remove(netuid); - PendingRootAlphaDivs::::remove(netuid); // --- 17. Subtoken / feature flags. LiquidAlphaOn::::remove(netuid); From fb053c863b84c062551a1feccf0d14a27f64d9f3 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Wed, 5 Nov 2025 15:41:11 -0500 Subject: [PATCH 055/210] use alpha out here --- pallets/subtensor/src/coinbase/run_coinbase.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 9ced81fc5b..8ab4361353 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -640,8 +640,7 @@ impl Pallet { log::debug!("incentive_sum: {incentive_sum:?}"); let pending_validator_alpha = if !incentive_sum.is_zero() { - pending_alpha - .saturating_add(pending_root_alpha) + alpha_out .saturating_div(2.into()) .saturating_sub(pending_root_alpha) } else { From cb2085e031253123e21908a341c6422e5f89cce6 Mon Sep 17 00:00:00 2001 From: 0xcacti <97139981+0xcacti@users.noreply.github.com> Date: Wed, 5 Nov 2025 15:53:34 -0500 Subject: [PATCH 056/210] Hotfix/vune/epoch sub fix (#2186) * fix: injection logic * fix: fmt' --- .../subtensor/src/coinbase/run_coinbase.rs | 81 +++++++++++-------- 1 file changed, 48 insertions(+), 33 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 8ab4361353..6e759b9243 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -34,13 +34,19 @@ impl Pallet { // 2. Get subnets to emit to and emissions let subnet_emissions = Self::get_subnet_block_emissions(&subnets, block_emission); let subnets_to_emit_to: Vec = subnet_emissions.keys().copied().collect(); + let total_ema_price: U96F32 = subnets_to_emit_to + .iter() + .map(|netuid| Self::get_moving_alpha_price(*netuid)) + .sum(); + let subsidy_mode = total_ema_price <= U96F32::saturating_from_num(1); // --- 3. Get subnet terms (tao_in, alpha_in, and alpha_out) // Computation is described in detail in the dtao whitepaper. let mut tao_in: BTreeMap = BTreeMap::new(); let mut alpha_in: BTreeMap = BTreeMap::new(); let mut alpha_out: BTreeMap = BTreeMap::new(); - let mut is_subsidized: BTreeMap = BTreeMap::new(); + let mut subsidy_amount: BTreeMap = BTreeMap::new(); + // Only calculate for subnets that we are emitting to. for netuid_i in subnets_to_emit_to.iter() { // Get subnet price. @@ -62,32 +68,16 @@ impl Pallet { // Get initial alpha_in let mut alpha_in_i: U96F32; let mut tao_in_i: U96F32; - let tao_in_ratio: U96F32 = default_tao_in_i.safe_div_or( - U96F32::saturating_from_num(block_emission), - U96F32::saturating_from_num(0.0), - ); - if price_i < tao_in_ratio { - tao_in_i = price_i.saturating_mul(U96F32::saturating_from_num(block_emission)); - alpha_in_i = block_emission; + + if default_alpha_in_i > alpha_emission_i { + alpha_in_i = alpha_emission_i; + tao_in_i = alpha_in_i.saturating_mul(price_i); let difference_tao: U96F32 = default_tao_in_i.saturating_sub(tao_in_i); - // Difference becomes buy. - let buy_swap_result = Self::swap_tao_for_alpha( - *netuid_i, - tou64!(difference_tao).into(), - T::SwapInterface::max_price(), - true, - ); - if let Ok(buy_swap_result_ok) = buy_swap_result { - let bought_alpha = AlphaCurrency::from(buy_swap_result_ok.amount_paid_out); - SubnetAlphaOut::::mutate(*netuid_i, |total| { - *total = total.saturating_sub(bought_alpha); - }); - } - is_subsidized.insert(*netuid_i, true); + subsidy_amount.insert(*netuid_i, difference_tao); } else { tao_in_i = default_tao_in_i; - alpha_in_i = tao_in_i.safe_div_or(price_i, alpha_emission_i); - is_subsidized.insert(*netuid_i, false); + alpha_in_i = alpha_in_default_i; + subsidy_amount.insert(*netuid_i, U96F32::from_num(0.0)); } log::debug!("alpha_in_i: {alpha_in_i:?}"); @@ -110,9 +100,33 @@ impl Pallet { log::debug!("alpha_in: {alpha_in:?}"); log::debug!("alpha_out: {alpha_out:?}"); - // --- 4. Injection. - // Actually perform the injection of alpha_in, alpha_out and tao_in into the subnet pool. - // This operation changes the pool liquidity each block. + // --- 4. Inject and subsidize + for netuid_i in subnets_to_emit_to.iter() { + let tao_in_i: TaoCurrency = + tou64!(*tao_in.get(netuid_i).unwrap_or(&asfloat!(0))).into(); + let alpha_in_i: AlphaCurrency = + AlphaCurrency::from(tou64!(*alpha_in.get(netuid_i).unwrap_or(&asfloat!(0)))); + let difference_tao: U96F32 = *subsidy_amount.get(netuid_i).unwrap_or(&asfloat!(0)); + + T::SwapInterface::adjust_protocol_liquidity(*netuid_i, tao_in_i, alpha_in_i); + + if difference_tao > asfloat!(0) { + let buy_swap_result = Self::swap_tao_for_alpha( + *netuid_i, + tou64!(difference_tao).into(), + T::SwapInterface::max_price(), + true, + ); + if let Ok(buy_swap_result_ok) = buy_swap_result { + let bought_alpha = AlphaCurrency::from(buy_swap_result_ok.amount_paid_out); + SubnetAlphaOut::::mutate(*netuid_i, |total| { + *total = total.saturating_sub(bought_alpha); + }); + } + } + } + + // --- 5. Update counters for netuid_i in subnets_to_emit_to.iter() { // Inject Alpha in. let alpha_in_i = @@ -138,14 +152,15 @@ impl Pallet { TotalStake::::mutate(|total| { *total = total.saturating_add(tao_in_i.into()); }); + + let difference_tao: U96F32 = *subsidy_amount.get(netuid_i).unwrap_or(&asfloat!(0)); TotalIssuance::::mutate(|total| { *total = total.saturating_add(tao_in_i.into()); + *total = total.saturating_add(tou64!(difference_tao).into()); }); - // Adjust protocol liquidity based on new reserves - T::SwapInterface::adjust_protocol_liquidity(*netuid_i, tao_in_i, alpha_in_i); } - // --- 5. Compute owner cuts and remove them from alpha_out remaining. + // --- 6. Compute owner cuts and remove them from alpha_out remaining. // Remove owner cuts here so that we can properly seperate root dividends in the next step. // Owner cuts are accumulated and then fed to the drain at the end of this func. let cut_percent: U96F32 = Self::get_float_subnet_owner_cut(); @@ -174,7 +189,7 @@ impl Pallet { let tao_weight: U96F32 = root_tao.saturating_mul(Self::get_tao_weight()); log::debug!("tao_weight: {tao_weight:?}"); - // --- 6. Seperate out root dividends in alpha and keep them. + // --- 7. Seperate out root dividends in alpha and keep them. // Then accumulate those dividends for later. for netuid_i in subnets_to_emit_to.iter() { // Get remaining alpha out. @@ -211,14 +226,14 @@ impl Pallet { }); } - // --- 7. Update moving prices after using them in the emission calculation. + // --- 8. Update moving prices after using them in the emission calculation. // Only update price EMA for subnets that we emit to. for netuid_i in subnets_to_emit_to.iter() { // Update moving prices after using them above. Self::update_moving_price(*netuid_i); } - // --- 8. Drain pending emission through the subnet based on tempo. + // --- 9. Drain pending emission through the subnet based on tempo. // Run the epoch for *all* subnets, even if we don't emit anything. for &netuid in subnets.iter() { // Reveal matured weights. From b068d0247311858dc22a198162ae9767df22c7a9 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Wed, 5 Nov 2025 15:55:14 -0500 Subject: [PATCH 057/210] emit full alpha if subsidized --- .../subtensor/src/coinbase/run_coinbase.rs | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 6e759b9243..e1635afb4e 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -204,21 +204,23 @@ impl Pallet { .unwrap_or(asfloat!(0.0)); log::debug!("root_proportion: {root_proportion:?}"); // Get root proportion of alpha_out dividends. - let root_alpha: U96F32 = root_proportion - .saturating_mul(alpha_out_i) // Total alpha emission per block remaining. - .saturating_mul(asfloat!(0.5)); // 50% to validators. - // Remove root alpha from alpha_out. - log::debug!("root_alpha: {root_alpha:?}"); - // Get pending alpha as original alpha_out - root_alpha. - let pending_alpha: U96F32 = alpha_out_i.saturating_sub(root_alpha); - log::debug!("pending_alpha: {pending_alpha:?}"); - + let mut root_alpha: U96F32 = asfloat!(0.0); let subsidized: bool = *is_subsidized.get(netuid_i).unwrap_or(&false); if !subsidized { + // Only give root alpha if not being subsidized. + root_alpha = root_proportion + .saturating_mul(alpha_out_i) // Total alpha emission per block remaining. + .saturating_mul(asfloat!(0.5)); // 50% to validators. PendingRootAlphaDivs::::mutate(*netuid_i, |total| { *total = total.saturating_add(tou64!(root_alpha).into()); }); } + // Remove root alpha from alpha_out. + log::debug!("root_alpha: {root_alpha:?}"); + + // Get pending alpha as original alpha_out - root_alpha. + let pending_alpha: U96F32 = alpha_out_i.saturating_sub(root_alpha); + log::debug!("pending_alpha: {pending_alpha:?}"); // Accumulate alpha emission in pending. PendingEmission::::mutate(*netuid_i, |total| { From 7d77cf0bea7da09a12ae72761a5e7330310b1784 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Wed, 5 Nov 2025 15:59:43 -0500 Subject: [PATCH 058/210] fix coinbase test --- pallets/subtensor/src/tests/coinbase.rs | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index bbb7061e27..29fbfacf48 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -245,7 +245,7 @@ fn test_coinbase_tao_issuance_different_prices() { ); // Prices are low => we limit tao issued (buy alpha with it) - let tao_issued = TaoCurrency::from(((0.1 + 0.2) * emission as f64) as u64); + let tao_issued = TaoCurrency::from(((1.0) * emission as f64) as u64); assert_abs_diff_eq!( TotalIssuance::::get(), tao_issued, @@ -689,7 +689,6 @@ fn test_drain_base() { 0.into(), AlphaCurrency::ZERO, AlphaCurrency::ZERO, - AlphaCurrency::ZERO, // alpha out AlphaCurrency::ZERO, ) }); @@ -705,7 +704,6 @@ fn test_drain_base_with_subnet() { netuid, AlphaCurrency::ZERO, AlphaCurrency::ZERO, - AlphaCurrency::ZERO, // alpha out AlphaCurrency::ZERO, ) }); @@ -731,7 +729,6 @@ fn test_drain_base_with_subnet_with_single_staker_not_registered() { netuid, pending_alpha.into(), AlphaCurrency::ZERO, - pending_alpha.into(), // alpha out AlphaCurrency::ZERO, ); let stake_after = @@ -761,7 +758,6 @@ fn test_drain_base_with_subnet_with_single_staker_registered() { netuid, pending_alpha, AlphaCurrency::ZERO, - pending_alpha, // alpha out AlphaCurrency::ZERO, ); let stake_after = @@ -806,7 +802,6 @@ fn test_drain_base_with_subnet_with_single_staker_registered_root_weight() { netuid, pending_alpha, pending_root_alpha, - pending_alpha.saturating_add(pending_root_alpha), // alpha out AlphaCurrency::ZERO, ); let stake_after = @@ -854,7 +849,6 @@ fn test_drain_base_with_subnet_with_two_stakers_registered() { netuid, pending_alpha, AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); let stake_after1 = @@ -920,7 +914,6 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root() { netuid, pending_alpha, AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); let stake_after1 = @@ -996,7 +989,6 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_am netuid, pending_alpha, AlphaCurrency::ZERO, - pending_alpha, 0.into(), ); let stake_after1 = @@ -1077,7 +1069,6 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_am netuid, pending_alpha, AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); let stake_after1 = @@ -1139,7 +1130,6 @@ fn test_drain_alpha_childkey_parentkey() { netuid, pending_alpha, AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); let parent_stake_after = SubtensorModule::get_stake_for_hotkey_on_subnet(&parent, netuid); @@ -1365,7 +1355,6 @@ fn test_get_root_children_drain() { alpha, pending_alpha, AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); @@ -1390,7 +1379,6 @@ fn test_get_root_children_drain() { pending_alpha, // pending_root1, AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); @@ -1414,7 +1402,6 @@ fn test_get_root_children_drain() { alpha, pending_alpha, AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); @@ -1503,7 +1490,6 @@ fn test_get_root_children_drain_half_proportion() { alpha, pending_alpha, AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); @@ -1590,7 +1576,6 @@ fn test_get_root_children_drain_with_take() { alpha, pending_alpha, AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); @@ -1678,7 +1663,6 @@ fn test_get_root_children_drain_with_half_take() { alpha, pending_alpha, AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); @@ -2394,7 +2378,6 @@ fn test_drain_pending_emission_no_miners_all_drained() { netuid, emission, AlphaCurrency::ZERO, - emission, AlphaCurrency::ZERO, ); @@ -2468,7 +2451,6 @@ fn test_drain_pending_emission_zero_emission() { 0.into(), AlphaCurrency::ZERO, AlphaCurrency::ZERO, - AlphaCurrency::ZERO, ); // Get the new stake of the hotkey. @@ -2761,7 +2743,6 @@ fn test_drain_alpha_childkey_parentkey_with_burn() { netuid, pending_alpha, AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); let parent_stake_after = SubtensorModule::get_stake_for_hotkey_on_subnet(&parent, netuid); From e792dd19d9d355d03a4bc0bc4de50326b494db82 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Wed, 5 Nov 2025 16:08:07 -0500 Subject: [PATCH 059/210] fix tests --- pallets/subtensor/src/tests/coinbase.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 29fbfacf48..0fc1ea8c98 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -690,6 +690,7 @@ fn test_drain_base() { AlphaCurrency::ZERO, AlphaCurrency::ZERO, AlphaCurrency::ZERO, + AlphaCurrency::ZERO, ) }); } @@ -705,6 +706,7 @@ fn test_drain_base_with_subnet() { AlphaCurrency::ZERO, AlphaCurrency::ZERO, AlphaCurrency::ZERO, + AlphaCurrency::ZERO, ) }); } @@ -730,6 +732,7 @@ fn test_drain_base_with_subnet_with_single_staker_not_registered() { pending_alpha.into(), AlphaCurrency::ZERO, AlphaCurrency::ZERO, + AlphaCurrency::ZERO, ); let stake_after = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid); @@ -758,6 +761,7 @@ fn test_drain_base_with_subnet_with_single_staker_registered() { netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); let stake_after = @@ -802,6 +806,7 @@ fn test_drain_base_with_subnet_with_single_staker_registered_root_weight() { netuid, pending_alpha, pending_root_alpha, + pending_alpha.saturating_add(pending_root_alpha), AlphaCurrency::ZERO, ); let stake_after = @@ -849,6 +854,7 @@ fn test_drain_base_with_subnet_with_two_stakers_registered() { netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); let stake_after1 = @@ -914,6 +920,7 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root() { netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); let stake_after1 = @@ -989,6 +996,7 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_am netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, 0.into(), ); let stake_after1 = @@ -1069,6 +1077,7 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_am netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); let stake_after1 = @@ -1130,6 +1139,7 @@ fn test_drain_alpha_childkey_parentkey() { netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); let parent_stake_after = SubtensorModule::get_stake_for_hotkey_on_subnet(&parent, netuid); @@ -1355,6 +1365,7 @@ fn test_get_root_children_drain() { alpha, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); @@ -1379,6 +1390,7 @@ fn test_get_root_children_drain() { pending_alpha, // pending_root1, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); @@ -1402,6 +1414,7 @@ fn test_get_root_children_drain() { alpha, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); @@ -1490,6 +1503,7 @@ fn test_get_root_children_drain_half_proportion() { alpha, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); @@ -1576,6 +1590,7 @@ fn test_get_root_children_drain_with_take() { alpha, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); @@ -1663,6 +1678,7 @@ fn test_get_root_children_drain_with_half_take() { alpha, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); @@ -2378,6 +2394,7 @@ fn test_drain_pending_emission_no_miners_all_drained() { netuid, emission, AlphaCurrency::ZERO, + emission, AlphaCurrency::ZERO, ); @@ -2451,6 +2468,7 @@ fn test_drain_pending_emission_zero_emission() { 0.into(), AlphaCurrency::ZERO, AlphaCurrency::ZERO, + AlphaCurrency::ZERO, ); // Get the new stake of the hotkey. @@ -2743,6 +2761,7 @@ fn test_drain_alpha_childkey_parentkey_with_burn() { netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); let parent_stake_after = SubtensorModule::get_stake_for_hotkey_on_subnet(&parent, netuid); From d7a28ba22961771d4f44222799354abd9c29b70d Mon Sep 17 00:00:00 2001 From: camfairchild Date: Wed, 5 Nov 2025 17:54:01 -0500 Subject: [PATCH 060/210] Revert "Hotfix/vune/epoch sub fix (#2186)" This reverts commit ccc6bba7bdfa7501a461d0adfef80dd6488f9dfb. --- .../subtensor/src/coinbase/run_coinbase.rs | 81 ++++++++----------- 1 file changed, 33 insertions(+), 48 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index e1635afb4e..2bc985a7b1 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -34,19 +34,13 @@ impl Pallet { // 2. Get subnets to emit to and emissions let subnet_emissions = Self::get_subnet_block_emissions(&subnets, block_emission); let subnets_to_emit_to: Vec = subnet_emissions.keys().copied().collect(); - let total_ema_price: U96F32 = subnets_to_emit_to - .iter() - .map(|netuid| Self::get_moving_alpha_price(*netuid)) - .sum(); - let subsidy_mode = total_ema_price <= U96F32::saturating_from_num(1); // --- 3. Get subnet terms (tao_in, alpha_in, and alpha_out) // Computation is described in detail in the dtao whitepaper. let mut tao_in: BTreeMap = BTreeMap::new(); let mut alpha_in: BTreeMap = BTreeMap::new(); let mut alpha_out: BTreeMap = BTreeMap::new(); - let mut subsidy_amount: BTreeMap = BTreeMap::new(); - + let mut is_subsidized: BTreeMap = BTreeMap::new(); // Only calculate for subnets that we are emitting to. for netuid_i in subnets_to_emit_to.iter() { // Get subnet price. @@ -68,16 +62,32 @@ impl Pallet { // Get initial alpha_in let mut alpha_in_i: U96F32; let mut tao_in_i: U96F32; - - if default_alpha_in_i > alpha_emission_i { - alpha_in_i = alpha_emission_i; - tao_in_i = alpha_in_i.saturating_mul(price_i); + let tao_in_ratio: U96F32 = default_tao_in_i.safe_div_or( + U96F32::saturating_from_num(block_emission), + U96F32::saturating_from_num(0.0), + ); + if price_i < tao_in_ratio { + tao_in_i = price_i.saturating_mul(U96F32::saturating_from_num(block_emission)); + alpha_in_i = block_emission; let difference_tao: U96F32 = default_tao_in_i.saturating_sub(tao_in_i); - subsidy_amount.insert(*netuid_i, difference_tao); + // Difference becomes buy. + let buy_swap_result = Self::swap_tao_for_alpha( + *netuid_i, + tou64!(difference_tao).into(), + T::SwapInterface::max_price(), + true, + ); + if let Ok(buy_swap_result_ok) = buy_swap_result { + let bought_alpha = AlphaCurrency::from(buy_swap_result_ok.amount_paid_out); + SubnetAlphaOut::::mutate(*netuid_i, |total| { + *total = total.saturating_sub(bought_alpha); + }); + } + is_subsidized.insert(*netuid_i, true); } else { tao_in_i = default_tao_in_i; - alpha_in_i = alpha_in_default_i; - subsidy_amount.insert(*netuid_i, U96F32::from_num(0.0)); + alpha_in_i = tao_in_i.safe_div_or(price_i, alpha_emission_i); + is_subsidized.insert(*netuid_i, false); } log::debug!("alpha_in_i: {alpha_in_i:?}"); @@ -100,33 +110,9 @@ impl Pallet { log::debug!("alpha_in: {alpha_in:?}"); log::debug!("alpha_out: {alpha_out:?}"); - // --- 4. Inject and subsidize - for netuid_i in subnets_to_emit_to.iter() { - let tao_in_i: TaoCurrency = - tou64!(*tao_in.get(netuid_i).unwrap_or(&asfloat!(0))).into(); - let alpha_in_i: AlphaCurrency = - AlphaCurrency::from(tou64!(*alpha_in.get(netuid_i).unwrap_or(&asfloat!(0)))); - let difference_tao: U96F32 = *subsidy_amount.get(netuid_i).unwrap_or(&asfloat!(0)); - - T::SwapInterface::adjust_protocol_liquidity(*netuid_i, tao_in_i, alpha_in_i); - - if difference_tao > asfloat!(0) { - let buy_swap_result = Self::swap_tao_for_alpha( - *netuid_i, - tou64!(difference_tao).into(), - T::SwapInterface::max_price(), - true, - ); - if let Ok(buy_swap_result_ok) = buy_swap_result { - let bought_alpha = AlphaCurrency::from(buy_swap_result_ok.amount_paid_out); - SubnetAlphaOut::::mutate(*netuid_i, |total| { - *total = total.saturating_sub(bought_alpha); - }); - } - } - } - - // --- 5. Update counters + // --- 4. Injection. + // Actually perform the injection of alpha_in, alpha_out and tao_in into the subnet pool. + // This operation changes the pool liquidity each block. for netuid_i in subnets_to_emit_to.iter() { // Inject Alpha in. let alpha_in_i = @@ -152,15 +138,14 @@ impl Pallet { TotalStake::::mutate(|total| { *total = total.saturating_add(tao_in_i.into()); }); - - let difference_tao: U96F32 = *subsidy_amount.get(netuid_i).unwrap_or(&asfloat!(0)); TotalIssuance::::mutate(|total| { *total = total.saturating_add(tao_in_i.into()); - *total = total.saturating_add(tou64!(difference_tao).into()); }); + // Adjust protocol liquidity based on new reserves + T::SwapInterface::adjust_protocol_liquidity(*netuid_i, tao_in_i, alpha_in_i); } - // --- 6. Compute owner cuts and remove them from alpha_out remaining. + // --- 5. Compute owner cuts and remove them from alpha_out remaining. // Remove owner cuts here so that we can properly seperate root dividends in the next step. // Owner cuts are accumulated and then fed to the drain at the end of this func. let cut_percent: U96F32 = Self::get_float_subnet_owner_cut(); @@ -189,7 +174,7 @@ impl Pallet { let tao_weight: U96F32 = root_tao.saturating_mul(Self::get_tao_weight()); log::debug!("tao_weight: {tao_weight:?}"); - // --- 7. Seperate out root dividends in alpha and keep them. + // --- 6. Seperate out root dividends in alpha and keep them. // Then accumulate those dividends for later. for netuid_i in subnets_to_emit_to.iter() { // Get remaining alpha out. @@ -228,14 +213,14 @@ impl Pallet { }); } - // --- 8. Update moving prices after using them in the emission calculation. + // --- 7. Update moving prices after using them in the emission calculation. // Only update price EMA for subnets that we emit to. for netuid_i in subnets_to_emit_to.iter() { // Update moving prices after using them above. Self::update_moving_price(*netuid_i); } - // --- 9. Drain pending emission through the subnet based on tempo. + // --- 8. Drain pending emission through the subnet based on tempo. // Run the epoch for *all* subnets, even if we don't emit anything. for &netuid in subnets.iter() { // Reveal matured weights. From a837632fdefc9e0528adcce86fe0c3a6e941e4c7 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Wed, 5 Nov 2025 17:59:28 -0500 Subject: [PATCH 061/210] add total issuance bump --- pallets/subtensor/src/coinbase/run_coinbase.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 2bc985a7b1..c1f7052d84 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -69,11 +69,15 @@ impl Pallet { if price_i < tao_in_ratio { tao_in_i = price_i.saturating_mul(U96F32::saturating_from_num(block_emission)); alpha_in_i = block_emission; - let difference_tao: U96F32 = default_tao_in_i.saturating_sub(tao_in_i); + let difference_tao: TaoCurrency = + tou64!(default_tao_in_i.saturating_sub(tao_in_i)).into(); + TotalIssuance::::mutate(|total| { + *total = total.saturating_add(difference_tao); + }); // Difference becomes buy. let buy_swap_result = Self::swap_tao_for_alpha( *netuid_i, - tou64!(difference_tao).into(), + difference_tao, T::SwapInterface::max_price(), true, ); From 6bddb56301974770eeb58f92844e2f4f359821de Mon Sep 17 00:00:00 2001 From: camfairchild Date: Wed, 5 Nov 2025 21:34:14 -0500 Subject: [PATCH 062/210] no alpha out thing --- .../subtensor/src/coinbase/run_coinbase.rs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index c1f7052d84..9b1ad984af 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -83,9 +83,7 @@ impl Pallet { ); if let Ok(buy_swap_result_ok) = buy_swap_result { let bought_alpha = AlphaCurrency::from(buy_swap_result_ok.amount_paid_out); - SubnetAlphaOut::::mutate(*netuid_i, |total| { - *total = total.saturating_sub(bought_alpha); - }); + Self::recycle_subnet_alpha(*netuid_i, bought_alpha); } is_subsidized.insert(*netuid_i, true); } else { @@ -110,6 +108,7 @@ impl Pallet { alpha_in.insert(*netuid_i, alpha_in_i); alpha_out.insert(*netuid_i, alpha_out_i); } + log::debug!("is_subsidized: {is_subsidized:?}"); log::debug!("tao_in: {tao_in:?}"); log::debug!("alpha_in: {alpha_in:?}"); log::debug!("alpha_out: {alpha_out:?}"); @@ -235,9 +234,6 @@ impl Pallet { if Self::should_run_epoch(netuid, current_block) && Self::is_epoch_input_state_consistent(netuid) { - let alpha_out_i: AlphaCurrency = - tou64!(*alpha_out.get(&netuid).unwrap_or(&asfloat!(0.0))).into(); - // Restart counters. BlocksSinceLastStep::::insert(netuid, 0); LastMechansimStepBlock::::insert(netuid, current_block); @@ -259,7 +255,7 @@ impl Pallet { netuid, pending_alpha, pending_root_alpha, - alpha_out_i, + pending_alpha.saturating_add(pending_root_alpha), owner_cut, ); } else { @@ -497,6 +493,7 @@ impl Pallet { let destination = maybe_dest.clone().unwrap_or(hotkey.clone()); if let Some(dest) = maybe_dest { + log::debug!("incentives: auto staking {incentive:?} to {dest:?}"); Self::deposit_event(Event::::AutoStakeAdded { netuid, destination: dest, @@ -505,6 +502,9 @@ impl Pallet { incentive, }); } + log::debug!( + "incentives: increasing stake for {hotkey:?} to {incentive:?} with owner {owner:?}" + ); Self::increase_stake_for_hotkey_and_coldkey_on_subnet( &destination, &owner, @@ -619,7 +619,7 @@ impl Pallet { netuid: NetUid, pending_alpha: AlphaCurrency, pending_root_alpha: AlphaCurrency, - alpha_out: AlphaCurrency, // total alpha out for the subnet + total_alpha: AlphaCurrency, owner_cut: AlphaCurrency, ) { log::debug!( @@ -630,7 +630,7 @@ impl Pallet { // Run the epoch. let hotkey_emission: Vec<(T::AccountId, AlphaCurrency, AlphaCurrency)> = - Self::epoch_with_mechanisms(netuid, alpha_out); + Self::epoch_with_mechanisms(netuid, total_alpha); log::debug!("hotkey_emission: {hotkey_emission:?}"); // Compute the pending validator alpha. @@ -646,7 +646,7 @@ impl Pallet { log::debug!("incentive_sum: {incentive_sum:?}"); let pending_validator_alpha = if !incentive_sum.is_zero() { - alpha_out + total_alpha .saturating_div(2.into()) .saturating_sub(pending_root_alpha) } else { From b857d4d4bc8cc2fb0702af4c6d6aba64ef304a4c Mon Sep 17 00:00:00 2001 From: camfairchild Date: Wed, 5 Nov 2025 21:34:29 -0500 Subject: [PATCH 063/210] add tests for emissions w/ subs --- pallets/subtensor/src/tests/coinbase.rs | 350 ++++++++++++++++++++++++ 1 file changed, 350 insertions(+) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 0fc1ea8c98..56f7d2171f 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -2928,3 +2928,353 @@ fn test_zero_shares_zero_emission() { assert_eq!(SubnetAlphaIn::::get(netuid2), initial.into()); }); } + +#[test] +fn test_mining_emission_distribution_with_subsidy() { + new_test_ext(1).execute_with(|| { + let validator_coldkey = U256::from(1); + let validator_hotkey = U256::from(2); + let validator_miner_coldkey = U256::from(3); + let validator_miner_hotkey = U256::from(4); + let miner_coldkey = U256::from(5); + let miner_hotkey = U256::from(6); + let netuid = NetUid::from(1); + let subnet_tempo = 10; + let stake: u64 = 100_000_000_000; + let root_stake: u64 = 200_000_000_000; // 200 TAO + + // Create root network + SubtensorModule::set_tao_weight(0); // Start tao weight at 0 + SubtokenEnabled::::insert(NetUid::ROOT, true); + NetworksAdded::::insert(NetUid::ROOT, true); + + // Add network, register hotkeys, and setup network parameters + add_network(netuid, subnet_tempo, 0); + SubnetMechanism::::insert(netuid, 1); // Set mechanism to 1 + + // Setup large LPs to prevent slippage + SubnetTAO::::insert(netuid, TaoCurrency::from(1_000_000_000_000_000)); + SubnetAlphaIn::::insert(netuid, AlphaCurrency::from(1_000_000_000_000_000)); + + register_ok_neuron(netuid, validator_hotkey, validator_coldkey, 0); + register_ok_neuron(netuid, validator_miner_hotkey, validator_miner_coldkey, 1); + register_ok_neuron(netuid, miner_hotkey, miner_coldkey, 2); + SubtensorModule::add_balance_to_coldkey_account( + &validator_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::add_balance_to_coldkey_account( + &validator_miner_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::add_balance_to_coldkey_account( + &miner_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::set_weights_set_rate_limit(netuid, 0); + step_block(subnet_tempo); + SubnetOwnerCut::::set(u16::MAX / 10); + // There are two validators and three neurons + MaxAllowedUids::::set(netuid, 3); + SubtensorModule::set_max_allowed_validators(netuid, 2); + + // Setup stakes: + // Stake from validator + // Stake from valiminer + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_coldkey), + validator_hotkey, + netuid, + stake.into() + )); + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_miner_coldkey), + validator_miner_hotkey, + netuid, + stake.into() + )); + + // Setup YUMA so that it creates emissions + Weights::::insert(NetUidStorageIndex::from(netuid), 0, vec![(1, 0xFFFF)]); + Weights::::insert(NetUidStorageIndex::from(netuid), 1, vec![(2, 0xFFFF)]); + BlockAtRegistration::::set(netuid, 0, 1); + BlockAtRegistration::::set(netuid, 1, 1); + BlockAtRegistration::::set(netuid, 2, 1); + LastUpdate::::set(NetUidStorageIndex::from(netuid), vec![2, 2, 2]); + Kappa::::set(netuid, u16::MAX / 5); + ActivityCutoff::::set(netuid, u16::MAX); // makes all stake active + ValidatorPermit::::insert(netuid, vec![true, true, false]); + + // Run run_coinbase until emissions are drained + step_block(subnet_tempo); + + // Add stake to validator so it has root stake + SubtensorModule::add_balance_to_coldkey_account(&validator_coldkey, root_stake.into()); + // init root + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_coldkey), + validator_hotkey, + NetUid::ROOT, + root_stake.into() + )); + // Set tao weight non zero + SubtensorModule::set_tao_weight(u64::MAX / 10); + + // Make subsidy happen + // set price very low, e.g. a lot of alpha in + //SubnetAlphaIn::::insert(netuid, AlphaCurrency::from(1_000_000_000_000_000)); + pallet_subtensor_swap::AlphaSqrtPrice::::insert( + netuid, + U64F64::saturating_from_num(0.01), + ); + + // Run run_coinbase until emissions are drained + step_block(subnet_tempo); + + log::info!("is_sub: Running epoch with subsidy"); + + let old_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); + let per_block_emission = SubtensorModule::get_block_emission_for_issuance( + SubtensorModule::get_alpha_issuance(netuid).into(), + ) + .unwrap_or(0); + + // step by one block + step_block(1); + // Verify that root alpha divs + let new_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); + // Check that we are indeed being subsidized, i.e. that root alpha divs are NOT increasing + assert_eq!( + new_root_alpha_divs, old_root_alpha_divs, + "Root alpha divs should not increase" + ); + // Check root divs are zero + assert_eq!( + new_root_alpha_divs, + AlphaCurrency::ZERO, + "Root alpha divs should be zero" + ); + let miner_stake_before_epoch = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &miner_hotkey, + &miner_coldkey, + netuid, + ); + // Run again but with some root stake + step_block(subnet_tempo - 2); + assert_abs_diff_eq!( + PendingEmission::::get(netuid).to_u64(), + U96F32::saturating_from_num(per_block_emission) + .saturating_mul(U96F32::saturating_from_num(subnet_tempo as u64)) + .saturating_mul(U96F32::saturating_from_num(0.90)) + .saturating_to_num::(), + epsilon = 100_000_u64.into() + ); + step_block(1); + assert!( + BlocksSinceLastStep::::get(netuid) == 0, + "Blocks since last step should be 0" + ); + + let miner_uid = Uids::::get(netuid, miner_hotkey).unwrap_or(0); + log::info!("Miner uid: {miner_uid:?}"); + let miner_incentive: AlphaCurrency = + (*Incentive::::get(NetUidStorageIndex::from(netuid)) + .get(miner_uid as usize) + .expect("Miner uid should be present") as u64) + .into(); + log::info!("Miner incentive: {miner_incentive:?}"); + + // Miner emissions + let miner_emission_1: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &miner_hotkey, + &miner_coldkey, + netuid, + ) + .to_u64() + - miner_stake_before_epoch.to_u64(); + + assert_abs_diff_eq!( + Incentive::::get(NetUidStorageIndex::from(netuid)) + .iter() + .sum::(), + u16::MAX, + epsilon = 10 + ); + + assert_abs_diff_eq!( + miner_emission_1, + U96F32::saturating_from_num(miner_incentive) + .saturating_div(u16::MAX.into()) + .saturating_mul(U96F32::saturating_from_num(per_block_emission)) + .saturating_mul(U96F32::saturating_from_num(subnet_tempo + 1)) + .saturating_mul(U96F32::saturating_from_num(0.45)) // miner cut + .saturating_to_num::(), + epsilon = 1_000_000_u64 + ); + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_mining_emission_distribution_with_no_subsidy --exact --show-output --nocapture +#[test] +fn test_mining_emission_distribution_with_no_subsidy() { + new_test_ext(1).execute_with(|| { + let validator_coldkey = U256::from(1); + let validator_hotkey = U256::from(2); + let validator_miner_coldkey = U256::from(3); + let validator_miner_hotkey = U256::from(4); + let miner_coldkey = U256::from(5); + let miner_hotkey = U256::from(6); + let netuid = NetUid::from(1); + let subnet_tempo = 10; + let stake: u64 = 100_000_000_000; + let root_stake: u64 = 200_000_000_000; // 200 TAO + + // Create root network + SubtensorModule::set_tao_weight(0); // Start tao weight at 0 + SubtokenEnabled::::insert(NetUid::ROOT, true); + NetworksAdded::::insert(NetUid::ROOT, true); + + // Add network, register hotkeys, and setup network parameters + add_network(netuid, subnet_tempo, 0); + SubnetMechanism::::insert(netuid, 1); // Set mechanism to 1 + + // Setup large LPs to prevent slippage + SubnetTAO::::insert(netuid, TaoCurrency::from(1_000_000_000_000_000)); + SubnetAlphaIn::::insert(netuid, AlphaCurrency::from(1_000_000_000_000_000)); + + register_ok_neuron(netuid, validator_hotkey, validator_coldkey, 0); + register_ok_neuron(netuid, validator_miner_hotkey, validator_miner_coldkey, 1); + register_ok_neuron(netuid, miner_hotkey, miner_coldkey, 2); + SubtensorModule::add_balance_to_coldkey_account( + &validator_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::add_balance_to_coldkey_account( + &validator_miner_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::add_balance_to_coldkey_account( + &miner_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::set_weights_set_rate_limit(netuid, 0); + step_block(subnet_tempo); + SubnetOwnerCut::::set(u16::MAX / 10); + // There are two validators and three neurons + MaxAllowedUids::::set(netuid, 3); + SubtensorModule::set_max_allowed_validators(netuid, 2); + + // Setup stakes: + // Stake from validator + // Stake from valiminer + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_coldkey), + validator_hotkey, + netuid, + stake.into() + )); + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_miner_coldkey), + validator_miner_hotkey, + netuid, + stake.into() + )); + + // Setup YUMA so that it creates emissions + Weights::::insert(NetUidStorageIndex::from(netuid), 0, vec![(1, 0xFFFF)]); + Weights::::insert(NetUidStorageIndex::from(netuid), 1, vec![(2, 0xFFFF)]); + BlockAtRegistration::::set(netuid, 0, 1); + BlockAtRegistration::::set(netuid, 1, 1); + BlockAtRegistration::::set(netuid, 2, 1); + LastUpdate::::set(NetUidStorageIndex::from(netuid), vec![2, 2, 2]); + Kappa::::set(netuid, u16::MAX / 5); + ActivityCutoff::::set(netuid, u16::MAX); // makes all stake active + ValidatorPermit::::insert(netuid, vec![true, true, false]); + + // Run run_coinbase until emissions are drained + step_block(subnet_tempo); + + // Add stake to validator so it has root stake + SubtensorModule::add_balance_to_coldkey_account(&validator_coldkey, root_stake.into()); + // init root + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_coldkey), + validator_hotkey, + NetUid::ROOT, + root_stake.into() + )); + // Set tao weight non zero + SubtensorModule::set_tao_weight(u64::MAX / 10); + + // Make subsidy not happen + // set price very high + // e.g. very little alpha in pool + //SubnetAlphaIn::::insert(netuid, AlphaCurrency::from(5)); + pallet_subtensor_swap::AlphaSqrtPrice::::insert( + netuid, + U64F64::saturating_from_num(10.0), + ); + + // Run run_coinbase until emissions are drained + step_block(subnet_tempo); + + log::info!("is_sub: Running epoch with no subsidy"); + + let old_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); + let miner_stake_before_epoch = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &miner_hotkey, + &miner_coldkey, + netuid, + ); + + // step by one block + step_block(1); + // Verify that root alpha divs + let new_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); + // Check that we are NOT being subsidized, i.e. that root alpha divs are are changing + assert_ne!( + new_root_alpha_divs, old_root_alpha_divs, + "Root alpha divs should be changing" + ); + assert!( + new_root_alpha_divs > AlphaCurrency::ZERO, + "Root alpha divs should be greater than 0" + ); + + // Run again but with some root stake + step_block(subnet_tempo - 1); + + let miner_uid = Uids::::get(netuid, miner_hotkey).unwrap_or(0); + let miner_incentive: AlphaCurrency = + (*Incentive::::get(NetUidStorageIndex::from(netuid)) + .get(miner_uid as usize) + .expect("Miner uid should be present") as u64) + .into(); + log::info!("Miner incentive: {miner_incentive:?}"); + + let per_block_emission = SubtensorModule::get_block_emission_for_issuance( + SubtensorModule::get_alpha_issuance(netuid).into(), + ) + .unwrap_or(0); + + // Miner emissions + let miner_emission_1: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &miner_hotkey, + &miner_coldkey, + netuid, + ) + .to_u64() + - miner_stake_before_epoch.to_u64(); + + assert_abs_diff_eq!( + miner_emission_1, + U96F32::saturating_from_num(miner_incentive) + .saturating_div(u16::MAX.into()) + .saturating_mul(U96F32::saturating_from_num(per_block_emission)) + .saturating_mul(U96F32::saturating_from_num(subnet_tempo + 1)) + .saturating_mul(U96F32::saturating_from_num(0.45)) // miner cut + .saturating_to_num::(), + epsilon = 1_000_000_u64 + ); + }); +} From e1feb05c3936b010a15a1c64844245b144c8e659 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Wed, 5 Nov 2025 21:41:14 -0500 Subject: [PATCH 064/210] bump spec --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 240c490736..42f35cbc80 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 340, + spec_version: 341, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From ad602c101f076ff7a2ed7794e249244f1e4d1e9c Mon Sep 17 00:00:00 2001 From: Shamil Gadelshin Date: Thu, 6 Nov 2025 18:48:38 +0700 Subject: [PATCH 065/210] Fix clippy warnings --- pallets/subtensor/src/tests/coinbase.rs | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 56f7d2171f..525de22d1d 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -3077,11 +3077,15 @@ fn test_mining_emission_distribution_with_subsidy() { let miner_uid = Uids::::get(netuid, miner_hotkey).unwrap_or(0); log::info!("Miner uid: {miner_uid:?}"); - let miner_incentive: AlphaCurrency = - (*Incentive::::get(NetUidStorageIndex::from(netuid)) + let miner_incentive: AlphaCurrency = { + let miner_incentive = Incentive::::get(NetUidStorageIndex::from(netuid)) .get(miner_uid as usize) - .expect("Miner uid should be present") as u64) - .into(); + .copied(); + + assert!(miner_incentive.is_some()); + + (miner_incentive.unwrap_or_default() as u64).into() + }; log::info!("Miner incentive: {miner_incentive:?}"); // Miner emissions @@ -3245,11 +3249,15 @@ fn test_mining_emission_distribution_with_no_subsidy() { step_block(subnet_tempo - 1); let miner_uid = Uids::::get(netuid, miner_hotkey).unwrap_or(0); - let miner_incentive: AlphaCurrency = - (*Incentive::::get(NetUidStorageIndex::from(netuid)) + let miner_incentive: AlphaCurrency = { + let miner_incentive = Incentive::::get(NetUidStorageIndex::from(netuid)) .get(miner_uid as usize) - .expect("Miner uid should be present") as u64) - .into(); + .copied(); + + assert!(miner_incentive.is_some()); + + (miner_incentive.unwrap_or_default() as u64).into() + }; log::info!("Miner incentive: {miner_incentive:?}"); let per_block_emission = SubtensorModule::get_block_emission_for_issuance( From cac43c52eb076dd7086d93b18838250de603e70e Mon Sep 17 00:00:00 2001 From: 0xcacti <97139981+0xcacti@users.noreply.github.com> Date: Wed, 5 Nov 2025 15:53:34 -0500 Subject: [PATCH 066/210] Hotfix/vune/epoch sub fix (#2186) * fix: injection logic * fix: fmt' --- .../subtensor/src/coinbase/run_coinbase.rs | 85 +++++++++++-------- 1 file changed, 49 insertions(+), 36 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 9b1ad984af..1a39bea498 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -34,13 +34,19 @@ impl Pallet { // 2. Get subnets to emit to and emissions let subnet_emissions = Self::get_subnet_block_emissions(&subnets, block_emission); let subnets_to_emit_to: Vec = subnet_emissions.keys().copied().collect(); + let total_ema_price: U96F32 = subnets_to_emit_to + .iter() + .map(|netuid| Self::get_moving_alpha_price(*netuid)) + .sum(); + let subsidy_mode = total_ema_price <= U96F32::saturating_from_num(1); // --- 3. Get subnet terms (tao_in, alpha_in, and alpha_out) // Computation is described in detail in the dtao whitepaper. let mut tao_in: BTreeMap = BTreeMap::new(); let mut alpha_in: BTreeMap = BTreeMap::new(); let mut alpha_out: BTreeMap = BTreeMap::new(); - let mut is_subsidized: BTreeMap = BTreeMap::new(); + let mut subsidy_amount: BTreeMap = BTreeMap::new(); + // Only calculate for subnets that we are emitting to. for netuid_i in subnets_to_emit_to.iter() { // Get subnet price. @@ -62,34 +68,16 @@ impl Pallet { // Get initial alpha_in let mut alpha_in_i: U96F32; let mut tao_in_i: U96F32; - let tao_in_ratio: U96F32 = default_tao_in_i.safe_div_or( - U96F32::saturating_from_num(block_emission), - U96F32::saturating_from_num(0.0), - ); - if price_i < tao_in_ratio { - tao_in_i = price_i.saturating_mul(U96F32::saturating_from_num(block_emission)); - alpha_in_i = block_emission; - let difference_tao: TaoCurrency = - tou64!(default_tao_in_i.saturating_sub(tao_in_i)).into(); - TotalIssuance::::mutate(|total| { - *total = total.saturating_add(difference_tao); - }); - // Difference becomes buy. - let buy_swap_result = Self::swap_tao_for_alpha( - *netuid_i, - difference_tao, - T::SwapInterface::max_price(), - true, - ); - if let Ok(buy_swap_result_ok) = buy_swap_result { - let bought_alpha = AlphaCurrency::from(buy_swap_result_ok.amount_paid_out); - Self::recycle_subnet_alpha(*netuid_i, bought_alpha); - } - is_subsidized.insert(*netuid_i, true); + + if default_alpha_in_i > alpha_emission_i { + alpha_in_i = alpha_emission_i; + tao_in_i = alpha_in_i.saturating_mul(price_i); + let difference_tao: U96F32 = default_tao_in_i.saturating_sub(tao_in_i); + subsidy_amount.insert(*netuid_i, difference_tao); } else { tao_in_i = default_tao_in_i; - alpha_in_i = tao_in_i.safe_div_or(price_i, alpha_emission_i); - is_subsidized.insert(*netuid_i, false); + alpha_in_i = alpha_in_default_i; + subsidy_amount.insert(*netuid_i, U96F32::from_num(0.0)); } log::debug!("alpha_in_i: {alpha_in_i:?}"); @@ -113,9 +101,33 @@ impl Pallet { log::debug!("alpha_in: {alpha_in:?}"); log::debug!("alpha_out: {alpha_out:?}"); - // --- 4. Injection. - // Actually perform the injection of alpha_in, alpha_out and tao_in into the subnet pool. - // This operation changes the pool liquidity each block. + // --- 4. Inject and subsidize + for netuid_i in subnets_to_emit_to.iter() { + let tao_in_i: TaoCurrency = + tou64!(*tao_in.get(netuid_i).unwrap_or(&asfloat!(0))).into(); + let alpha_in_i: AlphaCurrency = + AlphaCurrency::from(tou64!(*alpha_in.get(netuid_i).unwrap_or(&asfloat!(0)))); + let difference_tao: U96F32 = *subsidy_amount.get(netuid_i).unwrap_or(&asfloat!(0)); + + T::SwapInterface::adjust_protocol_liquidity(*netuid_i, tao_in_i, alpha_in_i); + + if difference_tao > asfloat!(0) { + let buy_swap_result = Self::swap_tao_for_alpha( + *netuid_i, + tou64!(difference_tao).into(), + T::SwapInterface::max_price(), + true, + ); + if let Ok(buy_swap_result_ok) = buy_swap_result { + let bought_alpha = AlphaCurrency::from(buy_swap_result_ok.amount_paid_out); + SubnetAlphaOut::::mutate(*netuid_i, |total| { + *total = total.saturating_sub(bought_alpha); + }); + } + } + } + + // --- 5. Update counters for netuid_i in subnets_to_emit_to.iter() { // Inject Alpha in. let alpha_in_i = @@ -141,14 +153,15 @@ impl Pallet { TotalStake::::mutate(|total| { *total = total.saturating_add(tao_in_i.into()); }); + + let difference_tao: U96F32 = *subsidy_amount.get(netuid_i).unwrap_or(&asfloat!(0)); TotalIssuance::::mutate(|total| { *total = total.saturating_add(tao_in_i.into()); + *total = total.saturating_add(tou64!(difference_tao).into()); }); - // Adjust protocol liquidity based on new reserves - T::SwapInterface::adjust_protocol_liquidity(*netuid_i, tao_in_i, alpha_in_i); } - // --- 5. Compute owner cuts and remove them from alpha_out remaining. + // --- 6. Compute owner cuts and remove them from alpha_out remaining. // Remove owner cuts here so that we can properly seperate root dividends in the next step. // Owner cuts are accumulated and then fed to the drain at the end of this func. let cut_percent: U96F32 = Self::get_float_subnet_owner_cut(); @@ -177,7 +190,7 @@ impl Pallet { let tao_weight: U96F32 = root_tao.saturating_mul(Self::get_tao_weight()); log::debug!("tao_weight: {tao_weight:?}"); - // --- 6. Seperate out root dividends in alpha and keep them. + // --- 7. Seperate out root dividends in alpha and keep them. // Then accumulate those dividends for later. for netuid_i in subnets_to_emit_to.iter() { // Get remaining alpha out. @@ -216,14 +229,14 @@ impl Pallet { }); } - // --- 7. Update moving prices after using them in the emission calculation. + // --- 8. Update moving prices after using them in the emission calculation. // Only update price EMA for subnets that we emit to. for netuid_i in subnets_to_emit_to.iter() { // Update moving prices after using them above. Self::update_moving_price(*netuid_i); } - // --- 8. Drain pending emission through the subnet based on tempo. + // --- 9. Drain pending emission through the subnet based on tempo. // Run the epoch for *all* subnets, even if we don't emit anything. for &netuid in subnets.iter() { // Reveal matured weights. From 85fe2d4bf95339fd3d1204230f4b420475ed559f Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 00:24:10 -0500 Subject: [PATCH 067/210] rename drain_pending to distr em --- pallets/subtensor/src/benchmarks.rs | 2 +- .../subtensor/src/coinbase/run_coinbase.rs | 4 +- .../src/coinbase/subnet_emissions.rs | 19 ++++---- pallets/subtensor/src/tests/children.rs | 2 +- pallets/subtensor/src/tests/claim_root.rs | 28 +++++------ pallets/subtensor/src/tests/coinbase.rs | 46 +++++++++---------- 6 files changed, 51 insertions(+), 50 deletions(-) diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index 0e53facabc..5d9567fec1 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -1615,7 +1615,7 @@ mod pallet_benchmarks { ); let pending_root_alpha = 10_000_000u64; - Subtensor::::drain_pending_emission( + Subtensor::::distribute_emission( netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 1a39bea498..62e5492cb8 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -264,7 +264,7 @@ impl Pallet { PendingOwnerCut::::insert(netuid, AlphaCurrency::ZERO); // Drain pending root alpha divs, alpha emission, and owner cut. - Self::drain_pending_emission( + Self::distribute_emission( netuid, pending_alpha, pending_root_alpha, @@ -628,7 +628,7 @@ impl Pallet { (incentives, (alpha_dividends, root_alpha_dividends)) } - pub fn drain_pending_emission( + pub fn distribute_emission( netuid: NetUid, pending_alpha: AlphaCurrency, pending_root_alpha: AlphaCurrency, diff --git a/pallets/subtensor/src/coinbase/subnet_emissions.rs b/pallets/subtensor/src/coinbase/subnet_emissions.rs index 184f27a4ba..12396b6ea0 100644 --- a/pallets/subtensor/src/coinbase/subnet_emissions.rs +++ b/pallets/subtensor/src/coinbase/subnet_emissions.rs @@ -7,21 +7,22 @@ use substrate_fixed::types::{I32F32, I64F64, U64F64, U96F32}; use subtensor_swap_interface::SwapHandler; impl Pallet { - pub fn get_subnet_block_emissions( - subnets: &[NetUid], - block_emission: U96F32, - ) -> BTreeMap { + + pub fn get_subnets_to_emit_to(subnets: &[NetUid]) -> Vec { // Filter out subnets with no first emission block number. - let subnets_to_emit_to: Vec = subnets + subnets .to_owned() - .clone() .into_iter() .filter(|netuid| FirstEmissionBlockNumber::::get(*netuid).is_some()) - .collect(); - log::debug!("Subnets to emit to: {subnets_to_emit_to:?}"); + .collect() + } + pub fn get_subnet_block_emissions( + subnets_to_emit_to: &[NetUid], + block_emission: U96F32, + ) -> BTreeMap { // Get subnet TAO emissions. - let shares = Self::get_shares(&subnets_to_emit_to); + let shares = Self::get_shares(subnets_to_emit_to); log::debug!("Subnet emission shares = {shares:?}"); shares diff --git a/pallets/subtensor/src/tests/children.rs b/pallets/subtensor/src/tests/children.rs index e9b8c2aa6b..14b7a0b29d 100644 --- a/pallets/subtensor/src/tests/children.rs +++ b/pallets/subtensor/src/tests/children.rs @@ -2827,7 +2827,7 @@ fn test_set_weights_no_parent() { }); } -/// Test that drain_pending_emission sends childkey take fully to the nominators if childkey +/// Test that distribute_emission sends childkey take fully to the nominators if childkey /// doesn't have its own stake, independently of parent hotkey take. /// cargo test --package pallet-subtensor --lib -- tests::children::test_childkey_take_drain --exact --show-output #[allow(clippy::assertions_on_constants)] diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index c75b4f5784..77e7b2a317 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -72,7 +72,7 @@ fn test_claim_root_with_drain_emissions() { // Distribute pending root alpha let pending_root_alpha = 1_000_000u64; - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), @@ -141,7 +141,7 @@ fn test_claim_root_with_drain_emissions() { // Distribute pending root alpha (round 2) - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), @@ -243,7 +243,7 @@ fn test_claim_root_adding_stake_proportionally_for_two_stakers() { // Distribute pending root alpha let pending_root_alpha = 10_000_000u64; - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), @@ -345,7 +345,7 @@ fn test_claim_root_adding_stake_disproportionally_for_two_stakers() { // Distribute pending root alpha let pending_root_alpha = 10_000_000u64; - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), @@ -437,7 +437,7 @@ fn test_claim_root_with_changed_stake() { // Distribute pending root alpha let pending_root_alpha = 10_000_000u64; - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), @@ -490,7 +490,7 @@ fn test_claim_root_with_changed_stake() { // Distribute pending root alpha let pending_root_alpha = 10_000_000u64; - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), @@ -544,7 +544,7 @@ fn test_claim_root_with_changed_stake() { // Distribute pending root alpha let pending_root_alpha = 10_000_000u64; - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), @@ -637,7 +637,7 @@ fn test_claim_root_with_drain_emissions_and_swap_claim_type() { // Distribute pending root alpha let pending_root_alpha = 10_000_000u64; - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), @@ -682,7 +682,7 @@ fn test_claim_root_with_drain_emissions_and_swap_claim_type() { // Distribute and claim pending root alpha (round 2) - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), @@ -719,7 +719,7 @@ fn test_claim_root_with_drain_emissions_and_swap_claim_type() { ); // Distribute and claim pending root alpha (round 3) - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), @@ -1106,7 +1106,7 @@ fn test_claim_root_with_swap_coldkey() { // Distribute pending root alpha let pending_root_alpha = 1_000_000u64; - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), @@ -1197,7 +1197,7 @@ fn test_claim_root_with_swap_hotkey() { // Distribute pending root alpha let pending_root_alpha = 1_000_000u64; - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), @@ -1314,7 +1314,7 @@ fn test_claim_root_on_network_deregistration() { // Distribute pending root alpha let pending_root_alpha = 10_000_000u64; - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), @@ -1455,7 +1455,7 @@ fn test_claim_root_with_unrelated_subnets() { // Distribute pending root alpha let pending_root_alpha = 1_000_000u64; - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 525de22d1d..919cff7b7d 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -685,7 +685,7 @@ fn test_pending_emission() { #[test] fn test_drain_base() { new_test_ext(1).execute_with(|| { - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( 0.into(), AlphaCurrency::ZERO, AlphaCurrency::ZERO, @@ -701,7 +701,7 @@ fn test_drain_base_with_subnet() { new_test_ext(1).execute_with(|| { let netuid = NetUid::from(1); add_network(netuid, 1, 0); - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, AlphaCurrency::ZERO, @@ -727,7 +727,7 @@ fn test_drain_base_with_subnet_with_single_staker_not_registered() { stake_before, ); let pending_alpha = AlphaCurrency::from(1_000_000_000); - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, pending_alpha.into(), AlphaCurrency::ZERO, @@ -757,7 +757,7 @@ fn test_drain_base_with_subnet_with_single_staker_registered() { stake_before, ); let pending_alpha = AlphaCurrency::from(1_000_000_000); - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, pending_alpha, AlphaCurrency::ZERO, @@ -802,7 +802,7 @@ fn test_drain_base_with_subnet_with_single_staker_registered_root_weight() { let pending_alpha = AlphaCurrency::from(1_000_000_000); let pending_root_alpha = AlphaCurrency::from(1_000_000_000); assert_eq!(SubnetTAO::::get(NetUid::ROOT), TaoCurrency::ZERO); - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, pending_alpha, pending_root_alpha, @@ -850,7 +850,7 @@ fn test_drain_base_with_subnet_with_two_stakers_registered() { stake_before, ); let pending_alpha = AlphaCurrency::from(1_000_000_000); - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, pending_alpha, AlphaCurrency::ZERO, @@ -916,7 +916,7 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root() { let pending_tao = TaoCurrency::from(1_000_000_000); let pending_alpha = AlphaCurrency::from(1_000_000_000); assert_eq!(SubnetTAO::::get(NetUid::ROOT), TaoCurrency::ZERO); - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, pending_alpha, AlphaCurrency::ZERO, @@ -992,7 +992,7 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_am let pending_tao = TaoCurrency::from(1_000_000_000); let pending_alpha = AlphaCurrency::from(1_000_000_000); assert_eq!(SubnetTAO::::get(NetUid::ROOT), TaoCurrency::ZERO); - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, pending_alpha, AlphaCurrency::ZERO, @@ -1073,7 +1073,7 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_am let pending_tao = TaoCurrency::from(1_000_000_000); let pending_alpha = AlphaCurrency::from(1_000_000_000); assert_eq!(SubnetTAO::::get(NetUid::ROOT), TaoCurrency::ZERO); - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, pending_alpha, AlphaCurrency::ZERO, @@ -1135,7 +1135,7 @@ fn test_drain_alpha_childkey_parentkey() { ChildkeyTake::::insert(child, netuid, u16::MAX / 10); let pending_alpha = AlphaCurrency::from(1_000_000_000); - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, pending_alpha, AlphaCurrency::ZERO, @@ -1361,7 +1361,7 @@ fn test_get_root_children_drain() { // Lets drain let pending_alpha = AlphaCurrency::from(1_000_000_000); - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( alpha, pending_alpha, AlphaCurrency::ZERO, @@ -1385,7 +1385,7 @@ fn test_get_root_children_drain() { // Lets drain let pending_alpha = AlphaCurrency::from(1_000_000_000); let pending_root1 = TaoCurrency::from(1_000_000_000); - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( alpha, pending_alpha, // pending_root1, @@ -1410,7 +1410,7 @@ fn test_get_root_children_drain() { // Lets drain let pending_alpha = AlphaCurrency::from(1_000_000_000); let pending_root2 = TaoCurrency::from(1_000_000_000); - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( alpha, pending_alpha, AlphaCurrency::ZERO, @@ -1499,7 +1499,7 @@ fn test_get_root_children_drain_half_proportion() { // Lets drain! let pending_alpha = AlphaCurrency::from(1_000_000_000); - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( alpha, pending_alpha, AlphaCurrency::ZERO, @@ -1586,7 +1586,7 @@ fn test_get_root_children_drain_with_take() { // Lets drain! let pending_alpha = AlphaCurrency::from(1_000_000_000); - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( alpha, pending_alpha, AlphaCurrency::ZERO, @@ -1674,7 +1674,7 @@ fn test_get_root_children_drain_with_half_take() { // Lets drain! let pending_alpha = AlphaCurrency::from(1_000_000_000); - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( alpha, pending_alpha, AlphaCurrency::ZERO, @@ -1777,7 +1777,7 @@ fn test_get_root_children_drain_with_half_take() { // // Lets drain! // let pending_alpha = AlphaCurrency::from(1_000_000_000); -// SubtensorModule::drain_pending_emission(alpha, pending_alpha, 0, 0.into(), 0.into()); +// SubtensorModule::distribute_emission(alpha, pending_alpha, 0, 0.into(), 0.into()); // // Alice and Bob make the same amount. // close( @@ -2365,7 +2365,7 @@ fn test_calculate_dividends_and_incentives_only_miners() { } #[test] -fn test_drain_pending_emission_no_miners_all_drained() { +fn test_distribute_emission_no_miners_all_drained() { new_test_ext(1).execute_with(|| { let netuid = add_dynamic_network(&U256::from(1), &U256::from(2)); let hotkey = U256::from(3); @@ -2390,7 +2390,7 @@ fn test_drain_pending_emission_no_miners_all_drained() { // Set the emission to be 1 million. let emission = AlphaCurrency::from(1_000_000); // Run drain pending without any miners. - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, emission, AlphaCurrency::ZERO, @@ -2410,9 +2410,9 @@ fn test_drain_pending_emission_no_miners_all_drained() { }); } -// cargo test --package pallet-subtensor --lib -- tests::coinbase::test_drain_pending_emission_zero_emission --exact --show-output +// cargo test --package pallet-subtensor --lib -- tests::coinbase::test_distribute_emission_zero_emission --exact --show-output #[test] -fn test_drain_pending_emission_zero_emission() { +fn test_distribute_emission_zero_emission() { new_test_ext(1).execute_with(|| { let netuid = add_dynamic_network_disable_commit_reveal(&U256::from(1), &U256::from(2)); let hotkey = U256::from(3); @@ -2463,7 +2463,7 @@ fn test_drain_pending_emission_zero_emission() { Dividends::::remove(netuid); // Set the emission to be ZERO. - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, 0.into(), AlphaCurrency::ZERO, @@ -2757,7 +2757,7 @@ fn test_drain_alpha_childkey_parentkey_with_burn() { let child_stake_before = SubtensorModule::get_stake_for_hotkey_on_subnet(&child, netuid); let pending_alpha = AlphaCurrency::from(1_000_000_000); - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, pending_alpha, AlphaCurrency::ZERO, From 90d4be48a02d24fe0cc9ddaad4e939358156ef1e Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 00:35:58 -0500 Subject: [PATCH 068/210] remove arg and total internally --- pallets/subtensor/src/benchmarks.rs | 1 - .../subtensor/src/coinbase/run_coinbase.rs | 5 ++--- pallets/subtensor/src/tests/claim_root.rs | 14 -------------- pallets/subtensor/src/tests/coinbase.rs | 19 ------------------- 4 files changed, 2 insertions(+), 37 deletions(-) diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index 5d9567fec1..1e7d6991c1 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -1619,7 +1619,6 @@ mod pallet_benchmarks { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), - pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 62e5492cb8..f58814460c 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -263,12 +263,11 @@ impl Pallet { let owner_cut = PendingOwnerCut::::get(netuid); PendingOwnerCut::::insert(netuid, AlphaCurrency::ZERO); - // Drain pending root alpha divs, alpha emission, and owner cut. + // Distribute the emission. Self::distribute_emission( netuid, pending_alpha, pending_root_alpha, - pending_alpha.saturating_add(pending_root_alpha), owner_cut, ); } else { @@ -632,7 +631,6 @@ impl Pallet { netuid: NetUid, pending_alpha: AlphaCurrency, pending_root_alpha: AlphaCurrency, - total_alpha: AlphaCurrency, owner_cut: AlphaCurrency, ) { log::debug!( @@ -640,6 +638,7 @@ impl Pallet { ); let tao_weight = Self::get_tao_weight(); + let total_alpha = pending_alpha.saturating_add(pending_root_alpha); // Run the epoch. let hotkey_emission: Vec<(T::AccountId, AlphaCurrency, AlphaCurrency)> = diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index 77e7b2a317..8d46841d5b 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -76,7 +76,6 @@ fn test_claim_root_with_drain_emissions() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), - pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -145,7 +144,6 @@ fn test_claim_root_with_drain_emissions() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), - pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -247,7 +245,6 @@ fn test_claim_root_adding_stake_proportionally_for_two_stakers() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), - pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -349,7 +346,6 @@ fn test_claim_root_adding_stake_disproportionally_for_two_stakers() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), - pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -441,7 +437,6 @@ fn test_claim_root_with_changed_stake() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), - pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -494,7 +489,6 @@ fn test_claim_root_with_changed_stake() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), - pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -548,7 +542,6 @@ fn test_claim_root_with_changed_stake() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), - pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -641,7 +634,6 @@ fn test_claim_root_with_drain_emissions_and_swap_claim_type() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), - pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -686,7 +678,6 @@ fn test_claim_root_with_drain_emissions_and_swap_claim_type() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), - pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -723,7 +714,6 @@ fn test_claim_root_with_drain_emissions_and_swap_claim_type() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), - pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -1110,7 +1100,6 @@ fn test_claim_root_with_swap_coldkey() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), - pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -1201,7 +1190,6 @@ fn test_claim_root_with_swap_hotkey() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), - pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -1318,7 +1306,6 @@ fn test_claim_root_on_network_deregistration() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), - pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -1459,7 +1446,6 @@ fn test_claim_root_with_unrelated_subnets() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), - pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 919cff7b7d..1ff974ac57 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -690,7 +690,6 @@ fn test_drain_base() { AlphaCurrency::ZERO, AlphaCurrency::ZERO, AlphaCurrency::ZERO, - AlphaCurrency::ZERO, ) }); } @@ -706,7 +705,6 @@ fn test_drain_base_with_subnet() { AlphaCurrency::ZERO, AlphaCurrency::ZERO, AlphaCurrency::ZERO, - AlphaCurrency::ZERO, ) }); } @@ -732,7 +730,6 @@ fn test_drain_base_with_subnet_with_single_staker_not_registered() { pending_alpha.into(), AlphaCurrency::ZERO, AlphaCurrency::ZERO, - AlphaCurrency::ZERO, ); let stake_after = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid); @@ -761,7 +758,6 @@ fn test_drain_base_with_subnet_with_single_staker_registered() { netuid, pending_alpha, AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); let stake_after = @@ -806,7 +802,6 @@ fn test_drain_base_with_subnet_with_single_staker_registered_root_weight() { netuid, pending_alpha, pending_root_alpha, - pending_alpha.saturating_add(pending_root_alpha), AlphaCurrency::ZERO, ); let stake_after = @@ -854,7 +849,6 @@ fn test_drain_base_with_subnet_with_two_stakers_registered() { netuid, pending_alpha, AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); let stake_after1 = @@ -920,7 +914,6 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root() { netuid, pending_alpha, AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); let stake_after1 = @@ -996,7 +989,6 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_am netuid, pending_alpha, AlphaCurrency::ZERO, - pending_alpha, 0.into(), ); let stake_after1 = @@ -1077,7 +1069,6 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_am netuid, pending_alpha, AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); let stake_after1 = @@ -1139,7 +1130,6 @@ fn test_drain_alpha_childkey_parentkey() { netuid, pending_alpha, AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); let parent_stake_after = SubtensorModule::get_stake_for_hotkey_on_subnet(&parent, netuid); @@ -1365,7 +1355,6 @@ fn test_get_root_children_drain() { alpha, pending_alpha, AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); @@ -1390,7 +1379,6 @@ fn test_get_root_children_drain() { pending_alpha, // pending_root1, AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); @@ -1414,7 +1402,6 @@ fn test_get_root_children_drain() { alpha, pending_alpha, AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); @@ -1503,7 +1490,6 @@ fn test_get_root_children_drain_half_proportion() { alpha, pending_alpha, AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); @@ -1590,7 +1576,6 @@ fn test_get_root_children_drain_with_take() { alpha, pending_alpha, AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); @@ -1678,7 +1663,6 @@ fn test_get_root_children_drain_with_half_take() { alpha, pending_alpha, AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); @@ -2394,7 +2378,6 @@ fn test_distribute_emission_no_miners_all_drained() { netuid, emission, AlphaCurrency::ZERO, - emission, AlphaCurrency::ZERO, ); @@ -2468,7 +2451,6 @@ fn test_distribute_emission_zero_emission() { 0.into(), AlphaCurrency::ZERO, AlphaCurrency::ZERO, - AlphaCurrency::ZERO, ); // Get the new stake of the hotkey. @@ -2761,7 +2743,6 @@ fn test_drain_alpha_childkey_parentkey_with_burn() { netuid, pending_alpha, AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); let parent_stake_after = SubtensorModule::get_stake_for_hotkey_on_subnet(&parent, netuid); From f81986b73b30838c77747310a1ef4ddaa9765b7c Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 01:23:26 -0500 Subject: [PATCH 069/210] test pending em --- pallets/subtensor/src/tests/coinbase.rs | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 1ff974ac57..c81148e520 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -661,15 +661,27 @@ fn test_owner_cut_base() { #[test] fn test_pending_emission() { new_test_ext(1).execute_with(|| { - let netuid = NetUid::from(1); let emission: u64 = 1_000_000; - add_network(netuid, 1, 0); + let hotkey = U256::from(1); + let coldkey = U256::from(2); + let netuid = add_dynamic_network(&hotkey, &coldkey); + Tempo::::insert(netuid, 1); + FirstEmissionBlockNumber::::insert(netuid, 0); + mock::setup_reserves(netuid, 1_000_000.into(), 1.into()); SubtensorModule::run_coinbase(U96F32::from_num(0)); SubnetTAO::::insert(NetUid::ROOT, TaoCurrency::from(1_000_000_000)); // Add root weight. SubtensorModule::run_coinbase(U96F32::from_num(0)); SubtensorModule::set_tempo(netuid, 10000); // Large number (dont drain) SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 + + // Set moving price > 1.0 + SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); + + // Make sure we are not subsidizing, so we have root alpha divs. + let subsidy_mode = SubtensorModule::get_network_subsidy_mode(&[netuid]); + assert!(!subsidy_mode, "Subsidy mode should be false"); + SubtensorModule::run_coinbase(U96F32::from_num(0)); // 1 TAO / ( 1 + 3 ) = 0.25 * 1 / 2 = 125000000 @@ -678,6 +690,12 @@ fn test_pending_emission() { 1_000_000_000 - 125000000, epsilon = 1 ); // 1 - swapped. + + assert_abs_diff_eq!( + u64::from(PendingRootAlphaDivs::::get(netuid)), + 125000000, + epsilon = 1 + ); // 1 / 2 = 125000000 }); } From be16df688594211a2664c5db24dda6a9873fbfea Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 01:31:20 -0500 Subject: [PATCH 070/210] make sure we check subsidize during those tests --- pallets/subtensor/src/tests/coinbase.rs | 29 +++++++++++++++---------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index c81148e520..7477a70676 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -3027,11 +3027,13 @@ fn test_mining_emission_distribution_with_subsidy() { U64F64::saturating_from_num(0.01), ); + // Make sure we ARE subsidizing, so we have root alpha divs. + let subsidy_mode = SubtensorModule::get_network_subsidy_mode(&[netuid]); + assert!(subsidy_mode, "Subsidy mode should be true"); + // Run run_coinbase until emissions are drained step_block(subnet_tempo); - log::info!("is_sub: Running epoch with subsidy"); - let old_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); let per_block_emission = SubtensorModule::get_block_emission_for_issuance( SubtensorModule::get_alpha_issuance(netuid).into(), @@ -3127,7 +3129,6 @@ fn test_mining_emission_distribution_with_no_subsidy() { let validator_miner_hotkey = U256::from(4); let miner_coldkey = U256::from(5); let miner_hotkey = U256::from(6); - let netuid = NetUid::from(1); let subnet_tempo = 10; let stake: u64 = 100_000_000_000; let root_stake: u64 = 200_000_000_000; // 200 TAO @@ -3138,8 +3139,11 @@ fn test_mining_emission_distribution_with_no_subsidy() { NetworksAdded::::insert(NetUid::ROOT, true); // Add network, register hotkeys, and setup network parameters - add_network(netuid, subnet_tempo, 0); - SubnetMechanism::::insert(netuid, 1); // Set mechanism to 1 + let owner_hotkey = U256::from(10); + let owner_coldkey = U256::from(11); + let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); + Tempo::::insert(netuid, 1); + FirstEmissionBlockNumber::::insert(netuid, 0); // Setup large LPs to prevent slippage SubnetTAO::::insert(netuid, TaoCurrency::from(1_000_000_000_000_000)); @@ -3210,19 +3214,22 @@ fn test_mining_emission_distribution_with_no_subsidy() { SubtensorModule::set_tao_weight(u64::MAX / 10); // Make subsidy not happen - // set price very high - // e.g. very little alpha in pool - //SubnetAlphaIn::::insert(netuid, AlphaCurrency::from(5)); + // Set moving price > 1.0 + // Set price > 1.0 pallet_subtensor_swap::AlphaSqrtPrice::::insert( netuid, U64F64::saturating_from_num(10.0), ); + SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); + + // Make sure we are not subsidizing, so we have root alpha divs. + let subsidy_mode = SubtensorModule::get_network_subsidy_mode(&[netuid]); + assert!(!subsidy_mode, "Subsidy mode should be false"); + // Run run_coinbase until emissions are drained step_block(subnet_tempo); - log::info!("is_sub: Running epoch with no subsidy"); - let old_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); let miner_stake_before_epoch = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &miner_hotkey, @@ -3232,7 +3239,7 @@ fn test_mining_emission_distribution_with_no_subsidy() { // step by one block step_block(1); - // Verify that root alpha divs + // Verify root alpha divs let new_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); // Check that we are NOT being subsidized, i.e. that root alpha divs are are changing assert_ne!( From 69249f43e6e9aaa7ec05ade04b6d81cb8ce89322 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 01:51:05 -0500 Subject: [PATCH 071/210] fix root claim tests --- pallets/subtensor/src/tests/claim_root.rs | 49 ++++++++++++++++++++--- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index 8d46841d5b..71b8947c43 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -6,8 +6,8 @@ use crate::tests::mock::{ use crate::{ DefaultMinRootClaimAmount, Error, MAX_NUM_ROOT_CLAIMS, MAX_ROOT_CLAIM_THRESHOLD, NetworksAdded, NumRootClaim, NumStakingColdkeys, PendingRootAlphaDivs, RootClaimable, RootClaimableThreshold, - StakingColdkeys, StakingColdkeysByIndex, SubnetAlphaIn, SubnetMechanism, SubnetTAO, - SubtokenEnabled, Tempo, pallet, + StakingColdkeys, StakingColdkeysByIndex, SubnetAlphaIn, SubnetMechanism, SubnetMovingPrice, + SubnetTAO, SubtokenEnabled, Tempo, pallet, }; use crate::{RootClaimType, RootClaimTypeEnum, RootClaimed}; use approx::assert_abs_diff_eq; @@ -18,7 +18,7 @@ use frame_support::{assert_err, assert_noop, assert_ok}; use sp_core::{H256, U256}; use sp_runtime::DispatchError; use std::collections::BTreeSet; -use substrate_fixed::types::{I96F32, U96F32}; +use substrate_fixed::types::{I96F32, U64F64, U96F32}; use subtensor_runtime_common::{AlphaCurrency, Currency, NetUid, TaoCurrency}; use subtensor_swap_interface::SwapHandler; @@ -779,6 +779,18 @@ fn test_claim_root_with_run_coinbase() { initial_total_hotkey_alpha.into(), ); + // Set moving price > 1.0 and price > 1.0 + // So we turn off subsidy + SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); + pallet_subtensor_swap::AlphaSqrtPrice::::insert( + netuid, + U64F64::saturating_from_num(10.0), + ); + + // Make sure we are not subsidizing, so we have root alpha divs. + let subsidy_mode = SubtensorModule::get_network_subsidy_mode(&[netuid]); + assert!(!subsidy_mode, "Subsidy mode should be false"); + // Distribute pending root alpha let initial_stake: u64 = @@ -878,6 +890,18 @@ fn test_claim_root_with_block_emissions() { ); SubtensorModule::maybe_add_coldkey_index(&coldkey); + // Set moving price > 1.0 and price > 1.0 + // So we turn off subsidy + SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); + pallet_subtensor_swap::AlphaSqrtPrice::::insert( + netuid, + U64F64::saturating_from_num(10.0), + ); + + // Make sure we are not subsidizing, so we have root alpha divs. + let subsidy_mode = SubtensorModule::get_network_subsidy_mode(&[netuid]); + assert!(!subsidy_mode, "Subsidy mode should be false"); + let initial_total_hotkey_alpha = 10_000_000u64; SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, @@ -991,12 +1015,27 @@ fn test_claim_root_coinbase_distribution() { let initial_alpha_issuance = SubtensorModule::get_alpha_issuance(netuid); let alpha_emissions: AlphaCurrency = 1_000_000_000u64.into(); - // Check total issuance (saved to pending alpha divs) + // Set moving price > 1.0 and price > 1.0 + // So we turn off subsidy + SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); + pallet_subtensor_swap::AlphaSqrtPrice::::insert( + netuid, + U64F64::saturating_from_num(10.0), + ); + + // Make sure we are not subsidizing, so we have root alpha divs. + let subsidy_mode = SubtensorModule::get_network_subsidy_mode(&[netuid]); + assert!(!subsidy_mode, "Subsidy mode should be false"); + // Check total issuance (saved to pending alpha divs) run_to_block(2); let alpha_issuance = SubtensorModule::get_alpha_issuance(netuid); - assert_eq!(initial_alpha_issuance + alpha_emissions, alpha_issuance); + // We went two blocks so we should have 2x the alpha emissions + assert_eq!( + initial_alpha_issuance + alpha_emissions.saturating_mul(2.into()), + alpha_issuance + ); let root_prop = initial_tao as f64 / (u64::from(alpha_issuance) + initial_tao) as f64; let root_validators_share = 0.5f64; From 9762f6ad423d48d9d3fb0ea8c074623c395862f0 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 11:01:33 -0500 Subject: [PATCH 072/210] helper and clippy --- .../subtensor/src/coinbase/run_coinbase.rs | 33 ++++++++++--------- .../src/coinbase/subnet_emissions.rs | 6 ++-- pallets/subtensor/src/tests/coinbase.rs | 7 +--- 3 files changed, 21 insertions(+), 25 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index f58814460c..7071f6ff6b 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -34,11 +34,7 @@ impl Pallet { // 2. Get subnets to emit to and emissions let subnet_emissions = Self::get_subnet_block_emissions(&subnets, block_emission); let subnets_to_emit_to: Vec = subnet_emissions.keys().copied().collect(); - let total_ema_price: U96F32 = subnets_to_emit_to - .iter() - .map(|netuid| Self::get_moving_alpha_price(*netuid)) - .sum(); - let subsidy_mode = total_ema_price <= U96F32::saturating_from_num(1); + let subsidy_mode = Self::get_network_subsidy_mode(&subnets_to_emit_to); // --- 3. Get subnet terms (tao_in, alpha_in, and alpha_out) // Computation is described in detail in the dtao whitepaper. @@ -58,6 +54,9 @@ impl Pallet { .copied() .unwrap_or(asfloat!(0)); log::debug!("default_tao_in_i: {default_tao_in_i:?}"); + let default_alpha_in_i: U96F32 = + default_tao_in_i.safe_div_or(price_i, U96F32::saturating_from_num(0.0)); + log::debug!("default_alpha_in_i: {default_alpha_in_i:?}"); // Get alpha_emission total let alpha_emission_i: U96F32 = asfloat!( Self::get_block_emission_for_issuance(Self::get_alpha_issuance(*netuid_i).into()) @@ -76,7 +75,7 @@ impl Pallet { subsidy_amount.insert(*netuid_i, difference_tao); } else { tao_in_i = default_tao_in_i; - alpha_in_i = alpha_in_default_i; + alpha_in_i = default_alpha_in_i; subsidy_amount.insert(*netuid_i, U96F32::from_num(0.0)); } log::debug!("alpha_in_i: {alpha_in_i:?}"); @@ -96,10 +95,10 @@ impl Pallet { alpha_in.insert(*netuid_i, alpha_in_i); alpha_out.insert(*netuid_i, alpha_out_i); } - log::debug!("is_subsidized: {is_subsidized:?}"); log::debug!("tao_in: {tao_in:?}"); log::debug!("alpha_in: {alpha_in:?}"); log::debug!("alpha_out: {alpha_out:?}"); + log::debug!("subsidy_mode: {subsidy_mode:?}"); // --- 4. Inject and subsidize for netuid_i in subnets_to_emit_to.iter() { @@ -206,8 +205,7 @@ impl Pallet { log::debug!("root_proportion: {root_proportion:?}"); // Get root proportion of alpha_out dividends. let mut root_alpha: U96F32 = asfloat!(0.0); - let subsidized: bool = *is_subsidized.get(netuid_i).unwrap_or(&false); - if !subsidized { + if !subsidy_mode { // Only give root alpha if not being subsidized. root_alpha = root_proportion .saturating_mul(alpha_out_i) // Total alpha emission per block remaining. @@ -264,12 +262,7 @@ impl Pallet { PendingOwnerCut::::insert(netuid, AlphaCurrency::ZERO); // Distribute the emission. - Self::distribute_emission( - netuid, - pending_alpha, - pending_root_alpha, - owner_cut, - ); + Self::distribute_emission(netuid, pending_alpha, pending_root_alpha, owner_cut); } else { // Increment BlocksSinceLastStep::::mutate(netuid, |total| *total = total.saturating_add(1)); @@ -277,6 +270,16 @@ impl Pallet { } } + pub fn get_network_subsidy_mode(subnets_to_emit_to: &[NetUid]) -> bool { + let total_ema_price: U96F32 = subnets_to_emit_to + .iter() + .map(|netuid| Self::get_moving_alpha_price(*netuid)) + .sum(); + + // If the total EMA price is less than or equal to 1, then we subsidize the network. + total_ema_price <= U96F32::saturating_from_num(1) + } + pub fn calculate_dividends_and_incentives( netuid: NetUid, hotkey_emission: Vec<(T::AccountId, AlphaCurrency, AlphaCurrency)>, diff --git a/pallets/subtensor/src/coinbase/subnet_emissions.rs b/pallets/subtensor/src/coinbase/subnet_emissions.rs index 12396b6ea0..895a908ba8 100644 --- a/pallets/subtensor/src/coinbase/subnet_emissions.rs +++ b/pallets/subtensor/src/coinbase/subnet_emissions.rs @@ -1,5 +1,4 @@ use super::*; -use crate::alloc::borrow::ToOwned; use alloc::collections::BTreeMap; use safe_math::FixedExt; use substrate_fixed::transcendental::{exp, ln}; @@ -7,13 +6,12 @@ use substrate_fixed::types::{I32F32, I64F64, U64F64, U96F32}; use subtensor_swap_interface::SwapHandler; impl Pallet { - pub fn get_subnets_to_emit_to(subnets: &[NetUid]) -> Vec { // Filter out subnets with no first emission block number. subnets - .to_owned() - .into_iter() + .iter() .filter(|netuid| FirstEmissionBlockNumber::::get(*netuid).is_some()) + .copied() .collect() } diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 7477a70676..87a8f560e3 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -1003,12 +1003,7 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_am let pending_tao = TaoCurrency::from(1_000_000_000); let pending_alpha = AlphaCurrency::from(1_000_000_000); assert_eq!(SubnetTAO::::get(NetUid::ROOT), TaoCurrency::ZERO); - SubtensorModule::distribute_emission( - netuid, - pending_alpha, - AlphaCurrency::ZERO, - 0.into(), - ); + SubtensorModule::distribute_emission(netuid, pending_alpha, AlphaCurrency::ZERO, 0.into()); let stake_after1 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey1, &coldkey, netuid); let root_after1 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( From 94d31d74ecbaf132e649ccf8c9127ed5ef71878a Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 11:03:02 -0500 Subject: [PATCH 073/210] bump spec --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 42f35cbc80..fe9bda8b6c 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 341, + spec_version: 342, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 186c7178c176d0ab287d2f0d4c65c513dd597128 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 11:11:53 -0500 Subject: [PATCH 074/210] rename subsidy to root flag --- .../subtensor/src/coinbase/run_coinbase.rs | 25 ++++++------------- pallets/subtensor/src/tests/claim_root.rs | 18 ++++++------- pallets/subtensor/src/tests/coinbase.rs | 20 +++++++-------- 3 files changed, 27 insertions(+), 36 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 7071f6ff6b..bd539af0da 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -34,7 +34,7 @@ impl Pallet { // 2. Get subnets to emit to and emissions let subnet_emissions = Self::get_subnet_block_emissions(&subnets, block_emission); let subnets_to_emit_to: Vec = subnet_emissions.keys().copied().collect(); - let subsidy_mode = Self::get_network_subsidy_mode(&subnets_to_emit_to); + let root_sell_flag = Self::get_network_root_sell_flag(&subnets_to_emit_to); // --- 3. Get subnet terms (tao_in, alpha_in, and alpha_out) // Computation is described in detail in the dtao whitepaper. @@ -98,7 +98,8 @@ impl Pallet { log::debug!("tao_in: {tao_in:?}"); log::debug!("alpha_in: {alpha_in:?}"); log::debug!("alpha_out: {alpha_out:?}"); - log::debug!("subsidy_mode: {subsidy_mode:?}"); + log::debug!("subsidy_amount: {subsidy_amount:?}"); + log::debug!("root_sell_flag: {root_sell_flag:?}"); // --- 4. Inject and subsidize for netuid_i in subnets_to_emit_to.iter() { @@ -195,18 +196,7 @@ impl Pallet { // Get remaining alpha out. let alpha_out_i: U96F32 = *alpha_out.get(netuid_i).unwrap_or(&asfloat!(0.0)); log::debug!("alpha_out_i: {alpha_out_i:?}"); - // Get total ALPHA on subnet. - let alpha_issuance: U96F32 = asfloat!(Self::get_alpha_issuance(*netuid_i)); - log::debug!("alpha_issuance: {alpha_issuance:?}"); - // Get root proportional dividends. - let root_proportion: U96F32 = tao_weight - .checked_div(tao_weight.saturating_add(alpha_issuance)) - .unwrap_or(asfloat!(0.0)); - log::debug!("root_proportion: {root_proportion:?}"); - // Get root proportion of alpha_out dividends. - let mut root_alpha: U96F32 = asfloat!(0.0); - if !subsidy_mode { - // Only give root alpha if not being subsidized. + if root_sell_flag { root_alpha = root_proportion .saturating_mul(alpha_out_i) // Total alpha emission per block remaining. .saturating_mul(asfloat!(0.5)); // 50% to validators. @@ -270,14 +260,15 @@ impl Pallet { } } - pub fn get_network_subsidy_mode(subnets_to_emit_to: &[NetUid]) -> bool { + pub fn get_network_root_sell_flag(subnets_to_emit_to: &[NetUid]) -> bool { let total_ema_price: U96F32 = subnets_to_emit_to .iter() .map(|netuid| Self::get_moving_alpha_price(*netuid)) .sum(); - // If the total EMA price is less than or equal to 1, then we subsidize the network. - total_ema_price <= U96F32::saturating_from_num(1) + // If the total EMA price is less than or equal to 1 + // then we WILL NOT root sell. + total_ema_price > U96F32::saturating_from_num(1) } pub fn calculate_dividends_and_incentives( diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index 71b8947c43..e143f19ef4 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -787,9 +787,9 @@ fn test_claim_root_with_run_coinbase() { U64F64::saturating_from_num(10.0), ); - // Make sure we are not subsidizing, so we have root alpha divs. - let subsidy_mode = SubtensorModule::get_network_subsidy_mode(&[netuid]); - assert!(!subsidy_mode, "Subsidy mode should be false"); + // Make sure we are root selling, so we have root alpha divs. + let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); + assert!(root_sell_flag, "Root sell flag should be true"); // Distribute pending root alpha @@ -898,9 +898,9 @@ fn test_claim_root_with_block_emissions() { U64F64::saturating_from_num(10.0), ); - // Make sure we are not subsidizing, so we have root alpha divs. - let subsidy_mode = SubtensorModule::get_network_subsidy_mode(&[netuid]); - assert!(!subsidy_mode, "Subsidy mode should be false"); + // Make sure we are root selling, so we have root alpha divs. + let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); + assert!(root_sell_flag, "Root sell flag should be true"); let initial_total_hotkey_alpha = 10_000_000u64; SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( @@ -1023,9 +1023,9 @@ fn test_claim_root_coinbase_distribution() { U64F64::saturating_from_num(10.0), ); - // Make sure we are not subsidizing, so we have root alpha divs. - let subsidy_mode = SubtensorModule::get_network_subsidy_mode(&[netuid]); - assert!(!subsidy_mode, "Subsidy mode should be false"); + // Make sure we are root selling, so we have root alpha divs. + let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); + assert!(root_sell_flag, "Root sell flag should be true"); // Check total issuance (saved to pending alpha divs) run_to_block(2); diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 87a8f560e3..dae954e6df 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -678,9 +678,9 @@ fn test_pending_emission() { // Set moving price > 1.0 SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); - // Make sure we are not subsidizing, so we have root alpha divs. - let subsidy_mode = SubtensorModule::get_network_subsidy_mode(&[netuid]); - assert!(!subsidy_mode, "Subsidy mode should be false"); + // Make sure we are root selling, so we have root alpha divs. + let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); + assert!(root_sell_flag, "Root sell flag should be true"); SubtensorModule::run_coinbase(U96F32::from_num(0)); // 1 TAO / ( 1 + 3 ) = 0.25 * 1 / 2 = 125000000 @@ -3022,9 +3022,9 @@ fn test_mining_emission_distribution_with_subsidy() { U64F64::saturating_from_num(0.01), ); - // Make sure we ARE subsidizing, so we have root alpha divs. - let subsidy_mode = SubtensorModule::get_network_subsidy_mode(&[netuid]); - assert!(subsidy_mode, "Subsidy mode should be true"); + // Make sure we ARE NOT root selling, so we do not have root alpha divs. + let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); + assert!(!root_sell_flag, "Root sell flag should be false"); // Run run_coinbase until emissions are drained step_block(subnet_tempo); @@ -3039,7 +3039,7 @@ fn test_mining_emission_distribution_with_subsidy() { step_block(1); // Verify that root alpha divs let new_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); - // Check that we are indeed being subsidized, i.e. that root alpha divs are NOT increasing + // Check that we are indeed NOT root selling, i.e. that root alpha divs are NOT increasing assert_eq!( new_root_alpha_divs, old_root_alpha_divs, "Root alpha divs should not increase" @@ -3218,9 +3218,9 @@ fn test_mining_emission_distribution_with_no_subsidy() { SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); - // Make sure we are not subsidizing, so we have root alpha divs. - let subsidy_mode = SubtensorModule::get_network_subsidy_mode(&[netuid]); - assert!(!subsidy_mode, "Subsidy mode should be false"); + // Make sure we are root selling, so we have root alpha divs. + let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); + assert!(root_sell_flag, "Root sell flag should be true"); // Run run_coinbase until emissions are drained step_block(subnet_tempo); From 25290f19dd9c677691de3fadbbd4856f3b543c50 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 11:12:53 -0500 Subject: [PATCH 075/210] only pull these values if root selling --- pallets/subtensor/src/coinbase/run_coinbase.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index bd539af0da..de31101ece 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -196,7 +196,21 @@ impl Pallet { // Get remaining alpha out. let alpha_out_i: U96F32 = *alpha_out.get(netuid_i).unwrap_or(&asfloat!(0.0)); log::debug!("alpha_out_i: {alpha_out_i:?}"); + + // Get root proportion of alpha_out dividends. + let mut root_alpha: U96F32 = asfloat!(0.0); if root_sell_flag { + // Get ALPHA issuance. + let alpha_issuance: U96F32 = asfloat!(Self::get_alpha_issuance(*netuid_i)); + log::debug!("alpha_issuance: {alpha_issuance:?}"); + + // Get root proportional dividends. + let root_proportion: U96F32 = tao_weight + .checked_div(tao_weight.saturating_add(alpha_issuance)) + .unwrap_or(asfloat!(0.0)); + log::debug!("root_proportion: {root_proportion:?}"); + + // Get root alpha from root prop. root_alpha = root_proportion .saturating_mul(alpha_out_i) // Total alpha emission per block remaining. .saturating_mul(asfloat!(0.5)); // 50% to validators. From d3698bf2fc4f0b1a804467b6b053e98d92e628bc Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 11:17:25 -0500 Subject: [PATCH 076/210] rename subsidy checks to the root_sell flag --- pallets/subtensor/src/tests/claim_root.rs | 6 +++--- pallets/subtensor/src/tests/coinbase.rs | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index e143f19ef4..951dfa0ae1 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -780,7 +780,7 @@ fn test_claim_root_with_run_coinbase() { ); // Set moving price > 1.0 and price > 1.0 - // So we turn off subsidy + // So we turn ON root sell SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); pallet_subtensor_swap::AlphaSqrtPrice::::insert( netuid, @@ -891,7 +891,7 @@ fn test_claim_root_with_block_emissions() { SubtensorModule::maybe_add_coldkey_index(&coldkey); // Set moving price > 1.0 and price > 1.0 - // So we turn off subsidy + // So we turn ON root sell SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); pallet_subtensor_swap::AlphaSqrtPrice::::insert( netuid, @@ -1016,7 +1016,7 @@ fn test_claim_root_coinbase_distribution() { let alpha_emissions: AlphaCurrency = 1_000_000_000u64.into(); // Set moving price > 1.0 and price > 1.0 - // So we turn off subsidy + // So we turn ON root sell SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); pallet_subtensor_swap::AlphaSqrtPrice::::insert( netuid, diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index dae954e6df..5d03afa973 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -2924,7 +2924,7 @@ fn test_zero_shares_zero_emission() { } #[test] -fn test_mining_emission_distribution_with_subsidy() { +fn test_mining_emission_distribution_with_no_root_sell() { new_test_ext(1).execute_with(|| { let validator_coldkey = U256::from(1); let validator_hotkey = U256::from(2); @@ -3014,7 +3014,7 @@ fn test_mining_emission_distribution_with_subsidy() { // Set tao weight non zero SubtensorModule::set_tao_weight(u64::MAX / 10); - // Make subsidy happen + // Make root sell NOT happen // set price very low, e.g. a lot of alpha in //SubnetAlphaIn::::insert(netuid, AlphaCurrency::from(1_000_000_000_000_000)); pallet_subtensor_swap::AlphaSqrtPrice::::insert( @@ -3114,9 +3114,9 @@ fn test_mining_emission_distribution_with_subsidy() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_mining_emission_distribution_with_no_subsidy --exact --show-output --nocapture +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_mining_emission_distribution_with_root_sell --exact --show-output --nocapture #[test] -fn test_mining_emission_distribution_with_no_subsidy() { +fn test_mining_emission_distribution_with_root_sell() { new_test_ext(1).execute_with(|| { let validator_coldkey = U256::from(1); let validator_hotkey = U256::from(2); @@ -3208,7 +3208,7 @@ fn test_mining_emission_distribution_with_no_subsidy() { // Set tao weight non zero SubtensorModule::set_tao_weight(u64::MAX / 10); - // Make subsidy not happen + // Make root sell happen // Set moving price > 1.0 // Set price > 1.0 pallet_subtensor_swap::AlphaSqrtPrice::::insert( @@ -3236,7 +3236,7 @@ fn test_mining_emission_distribution_with_no_subsidy() { step_block(1); // Verify root alpha divs let new_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); - // Check that we are NOT being subsidized, i.e. that root alpha divs are are changing + // Check that we ARE root selling, i.e. that root alpha divs are changing assert_ne!( new_root_alpha_divs, old_root_alpha_divs, "Root alpha divs should be changing" From 62ecfe14bc264d60bf875781f254d3dfaa609578 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 11:18:37 -0500 Subject: [PATCH 077/210] rename subsidy to excess_tao --- pallets/subtensor/src/coinbase/run_coinbase.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index de31101ece..1fa8641ff0 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -41,7 +41,7 @@ impl Pallet { let mut tao_in: BTreeMap = BTreeMap::new(); let mut alpha_in: BTreeMap = BTreeMap::new(); let mut alpha_out: BTreeMap = BTreeMap::new(); - let mut subsidy_amount: BTreeMap = BTreeMap::new(); + let mut excess_tao: BTreeMap = BTreeMap::new(); // Only calculate for subnets that we are emitting to. for netuid_i in subnets_to_emit_to.iter() { @@ -72,11 +72,11 @@ impl Pallet { alpha_in_i = alpha_emission_i; tao_in_i = alpha_in_i.saturating_mul(price_i); let difference_tao: U96F32 = default_tao_in_i.saturating_sub(tao_in_i); - subsidy_amount.insert(*netuid_i, difference_tao); + excess_tao.insert(*netuid_i, difference_tao); } else { tao_in_i = default_tao_in_i; alpha_in_i = default_alpha_in_i; - subsidy_amount.insert(*netuid_i, U96F32::from_num(0.0)); + excess_tao.insert(*netuid_i, U96F32::from_num(0.0)); } log::debug!("alpha_in_i: {alpha_in_i:?}"); @@ -98,16 +98,16 @@ impl Pallet { log::debug!("tao_in: {tao_in:?}"); log::debug!("alpha_in: {alpha_in:?}"); log::debug!("alpha_out: {alpha_out:?}"); - log::debug!("subsidy_amount: {subsidy_amount:?}"); + log::debug!("excess_tao: {excess_tao:?}"); log::debug!("root_sell_flag: {root_sell_flag:?}"); - // --- 4. Inject and subsidize + // --- 4. Inject and buy Alpha with any excess TAO. for netuid_i in subnets_to_emit_to.iter() { let tao_in_i: TaoCurrency = tou64!(*tao_in.get(netuid_i).unwrap_or(&asfloat!(0))).into(); let alpha_in_i: AlphaCurrency = AlphaCurrency::from(tou64!(*alpha_in.get(netuid_i).unwrap_or(&asfloat!(0)))); - let difference_tao: U96F32 = *subsidy_amount.get(netuid_i).unwrap_or(&asfloat!(0)); + let difference_tao: U96F32 = *excess_tao.get(netuid_i).unwrap_or(&asfloat!(0)); T::SwapInterface::adjust_protocol_liquidity(*netuid_i, tao_in_i, alpha_in_i); @@ -154,7 +154,7 @@ impl Pallet { *total = total.saturating_add(tao_in_i.into()); }); - let difference_tao: U96F32 = *subsidy_amount.get(netuid_i).unwrap_or(&asfloat!(0)); + let difference_tao: U96F32 = *excess_tao.get(netuid_i).unwrap_or(&asfloat!(0)); TotalIssuance::::mutate(|total| { *total = total.saturating_add(tao_in_i.into()); *total = total.saturating_add(tou64!(difference_tao).into()); From c5a7f514aa5b63ccb606eab4892d67bd9412f37c Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 11:20:02 -0500 Subject: [PATCH 078/210] use recycle alpha helper --- pallets/subtensor/src/coinbase/run_coinbase.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 1fa8641ff0..5b1c4b5704 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -120,9 +120,7 @@ impl Pallet { ); if let Ok(buy_swap_result_ok) = buy_swap_result { let bought_alpha = AlphaCurrency::from(buy_swap_result_ok.amount_paid_out); - SubnetAlphaOut::::mutate(*netuid_i, |total| { - *total = total.saturating_sub(bought_alpha); - }); + Self::recycle_subnet_alpha(*netuid_i, bought_alpha); } } } From 54e7033c712ae668a2054f7d7c6364d5f446c448 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 00:26:41 -0500 Subject: [PATCH 079/210] change name of reveal fn and move to block step --- pallets/subtensor/src/coinbase/block_step.rs | 14 +++++++++++++- pallets/subtensor/src/coinbase/reveal_commits.rs | 2 +- pallets/subtensor/src/coinbase/run_coinbase.rs | 7 ++----- pallets/subtensor/src/tests/weights.rs | 6 +++--- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/pallets/subtensor/src/coinbase/block_step.rs b/pallets/subtensor/src/coinbase/block_step.rs index 818235a955..3547230a54 100644 --- a/pallets/subtensor/src/coinbase/block_step.rs +++ b/pallets/subtensor/src/coinbase/block_step.rs @@ -18,7 +18,9 @@ impl Pallet { .to_u64(), ); log::debug!("Block emission: {block_emission:?}"); - // --- 3. Run emission through network. + + // --- 3. Reveal matured weights. + Self::reveal_crv3_commits(); Self::run_coinbase(block_emission); // --- 4. Set pending children on the epoch; but only after the coinbase has been run. Self::try_set_pending_children(block_number); @@ -261,4 +263,14 @@ impl Pallet { return next_value.saturating_to_num::().into(); } } + + pub fn reveal_crv3_commits() { + let netuids: Vec = Self::get_all_subnet_netuids(); + for netuid in netuids.into_iter().filter(|netuid| *netuid != NetUid::ROOT) { + // Reveal matured weights. + if let Err(e) = Self::reveal_crv3_commits_for_subnet(netuid) { + log::warn!("Failed to reveal commits for subnet {netuid} due to error: {e:?}"); + }; + } + } } diff --git a/pallets/subtensor/src/coinbase/reveal_commits.rs b/pallets/subtensor/src/coinbase/reveal_commits.rs index 889c41d96a..6fdafa76da 100644 --- a/pallets/subtensor/src/coinbase/reveal_commits.rs +++ b/pallets/subtensor/src/coinbase/reveal_commits.rs @@ -36,7 +36,7 @@ pub struct LegacyWeightsTlockPayload { impl Pallet { /// The `reveal_crv3_commits` function is run at the very beginning of epoch `n`, - pub fn reveal_crv3_commits(netuid: NetUid) -> dispatch::DispatchResult { + pub fn reveal_crv3_commits_for_subnet(netuid: NetUid) -> dispatch::DispatchResult { let reveal_period = Self::get_reveal_period(netuid); let cur_block = Self::get_current_block_as_u64(); let cur_epoch = Self::get_epoch_index(netuid, cur_block); diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 5b1c4b5704..3824ceb8bd 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -239,11 +239,8 @@ impl Pallet { // --- 9. Drain pending emission through the subnet based on tempo. // Run the epoch for *all* subnets, even if we don't emit anything. for &netuid in subnets.iter() { - // Reveal matured weights. - if let Err(e) = Self::reveal_crv3_commits(netuid) { - log::warn!("Failed to reveal commits for subnet {netuid} due to error: {e:?}"); - }; - // Pass on subnets that have not reached their tempo. + + // Run the epoch if applicable. if Self::should_run_epoch(netuid, current_block) && Self::is_epoch_input_state_consistent(netuid) { diff --git a/pallets/subtensor/src/tests/weights.rs b/pallets/subtensor/src/tests/weights.rs index a71a225dc7..21d37984e4 100644 --- a/pallets/subtensor/src/tests/weights.rs +++ b/pallets/subtensor/src/tests/weights.rs @@ -5016,7 +5016,7 @@ fn test_reveal_crv3_commits_cannot_reveal_after_reveal_epoch() { step_epochs(1, netuid); // Attempt to reveal commits after the reveal epoch has passed - assert_ok!(SubtensorModule::reveal_crv3_commits(netuid)); + assert_ok!(SubtensorModule::reveal_crv3_commits_for_subnet(netuid)); // Verify that the weights for the neuron have not been set let weights_sparse = SubtensorModule::get_weights_sparse(netuid.into()); @@ -5353,7 +5353,7 @@ fn test_reveal_crv3_commits_decryption_failure() { }, ); - assert_ok!(SubtensorModule::reveal_crv3_commits(netuid)); + assert_ok!(SubtensorModule::reveal_crv3_commits_for_subnet(netuid)); let neuron_uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey) .expect("Failed to get neuron UID for hotkey") as usize; @@ -5973,7 +5973,7 @@ fn test_reveal_crv3_commits_removes_past_epoch_commits() { // --------------------------------------------------------------------- // Run the reveal pass WITHOUT a pulse – only expiry housekeeping runs. // --------------------------------------------------------------------- - assert_ok!(SubtensorModule::reveal_crv3_commits(netuid)); + assert_ok!(SubtensorModule::reveal_crv3_commits_for_subnet(netuid)); // past_epoch (< reveal_epoch) must be gone assert!( From 8ad48b37cb8f05bb6999fa8708e3c3d266a2fb19 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 00:28:22 -0500 Subject: [PATCH 080/210] update moving prices outside of coinbase --- pallets/subtensor/src/coinbase/block_step.rs | 19 ++++++++++++++++--- .../subtensor/src/coinbase/run_coinbase.rs | 11 ++--------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/pallets/subtensor/src/coinbase/block_step.rs b/pallets/subtensor/src/coinbase/block_step.rs index 3547230a54..21f7866459 100644 --- a/pallets/subtensor/src/coinbase/block_step.rs +++ b/pallets/subtensor/src/coinbase/block_step.rs @@ -21,12 +21,15 @@ impl Pallet { // --- 3. Reveal matured weights. Self::reveal_crv3_commits(); + // --- 4. Run emission through network. Self::run_coinbase(block_emission); - // --- 4. Set pending children on the epoch; but only after the coinbase has been run. + // --- 5. Update moving prices AFTER using them for emissions. + Self::update_moving_prices(); + // --- 6. Set pending children on the epoch; but only after the coinbase has been run. Self::try_set_pending_children(block_number); - // --- 5. Run auto-claim root divs. + // --- 7. Run auto-claim root divs. Self::run_auto_claim_root_divs(last_block_hash); - // --- 6. Populate root coldkey maps. + // --- 8. Populate root coldkey maps. Self::populate_root_coldkey_staking_maps(); // Return ok. @@ -264,6 +267,16 @@ impl Pallet { } } + pub fn update_moving_prices() { + let subnets_to_emit_to: Vec = + Self::get_subnets_to_emit_to(&Self::get_all_subnet_netuids()); + // Only update price EMA for subnets that we emit to. + for netuid_i in subnets_to_emit_to.iter() { + // Update moving prices after using them above. + Self::update_moving_price(*netuid_i); + } + } + pub fn reveal_crv3_commits() { let netuids: Vec = Self::get_all_subnet_netuids(); for netuid in netuids.into_iter().filter(|netuid| *netuid != NetUid::ROOT) { diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 3824ceb8bd..07f33e67a6 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -228,15 +228,8 @@ impl Pallet { *total = total.saturating_add(tou64!(pending_alpha).into()); }); } - - // --- 8. Update moving prices after using them in the emission calculation. - // Only update price EMA for subnets that we emit to. - for netuid_i in subnets_to_emit_to.iter() { - // Update moving prices after using them above. - Self::update_moving_price(*netuid_i); - } - - // --- 9. Drain pending emission through the subnet based on tempo. + + // --- Drain pending emissions for all subnets hat are at their tempo. // Run the epoch for *all* subnets, even if we don't emit anything. for &netuid in subnets.iter() { From f00a7ac9a0d12de60b990ca548762f31a4e00367 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 00:28:37 -0500 Subject: [PATCH 081/210] filter root out just in case --- pallets/subtensor/src/coinbase/subnet_emissions.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pallets/subtensor/src/coinbase/subnet_emissions.rs b/pallets/subtensor/src/coinbase/subnet_emissions.rs index 895a908ba8..c2a12b3c61 100644 --- a/pallets/subtensor/src/coinbase/subnet_emissions.rs +++ b/pallets/subtensor/src/coinbase/subnet_emissions.rs @@ -7,9 +7,11 @@ use subtensor_swap_interface::SwapHandler; impl Pallet { pub fn get_subnets_to_emit_to(subnets: &[NetUid]) -> Vec { + // Filter out root subnet. // Filter out subnets with no first emission block number. subnets .iter() + .filter(|netuid| *netuid != NetUid::ROOT) .filter(|netuid| FirstEmissionBlockNumber::::get(*netuid).is_some()) .copied() .collect() From feeee61979ffcddfeac5567b728750e8a73124f0 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 00:37:13 -0500 Subject: [PATCH 082/210] refactor coinbase --- .../subtensor/src/coinbase/run_coinbase.rs | 332 +++++++++++------- .../src/coinbase/subnet_emissions.rs | 2 +- 2 files changed, 199 insertions(+), 135 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 07f33e67a6..12c11a0118 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -22,111 +22,67 @@ impl Pallet { pub fn run_coinbase(block_emission: U96F32) { // --- 0. Get current block. let current_block: u64 = Self::get_current_block_as_u64(); - log::debug!("Current block: {current_block:?}"); - - // --- 1. Get all netuids (filter out root) + log::debug!( + "Running coinbase for block {current_block:?} with block emission: {block_emission:?}" + ); + // --- 1. Get all subnets (excluding root). let subnets: Vec = Self::get_all_subnet_netuids() .into_iter() .filter(|netuid| *netuid != NetUid::ROOT) .collect(); - log::debug!("All subnet netuids: {subnets:?}"); + log::debug!("All subnets: {subnets:?}"); - // 2. Get subnets to emit to and emissions - let subnet_emissions = Self::get_subnet_block_emissions(&subnets, block_emission); - let subnets_to_emit_to: Vec = subnet_emissions.keys().copied().collect(); - let root_sell_flag = Self::get_network_root_sell_flag(&subnets_to_emit_to); + // --- 2. Get subnets to emit to + let subnets_to_emit_to: Vec = Self::get_subnets_to_emit_to(&subnets); + log::debug!("Subnets to emit to: {subnets_to_emit_to:?}"); - // --- 3. Get subnet terms (tao_in, alpha_in, and alpha_out) - // Computation is described in detail in the dtao whitepaper. - let mut tao_in: BTreeMap = BTreeMap::new(); - let mut alpha_in: BTreeMap = BTreeMap::new(); - let mut alpha_out: BTreeMap = BTreeMap::new(); - let mut excess_tao: BTreeMap = BTreeMap::new(); - - // Only calculate for subnets that we are emitting to. - for netuid_i in subnets_to_emit_to.iter() { - // Get subnet price. - let price_i = T::SwapInterface::current_alpha_price((*netuid_i).into()); - log::debug!("price_i: {price_i:?}"); - // Emission is price over total. - let default_tao_in_i: U96F32 = subnet_emissions - .get(netuid_i) - .copied() - .unwrap_or(asfloat!(0)); - log::debug!("default_tao_in_i: {default_tao_in_i:?}"); - let default_alpha_in_i: U96F32 = - default_tao_in_i.safe_div_or(price_i, U96F32::saturating_from_num(0.0)); - log::debug!("default_alpha_in_i: {default_alpha_in_i:?}"); - // Get alpha_emission total - let alpha_emission_i: U96F32 = asfloat!( - Self::get_block_emission_for_issuance(Self::get_alpha_issuance(*netuid_i).into()) - .unwrap_or(0) - ); - log::debug!("alpha_emission_i: {alpha_emission_i:?}"); + // --- 3. Get emissions for subnets to emit to + let subnet_emissions = + Self::get_subnet_block_emissions(&subnets_to_emit_to, block_emission); + log::debug!("Subnet emissions: {subnet_emissions:?}"); + let root_sell_flag = Self::get_network_root_sell_flag(&subnets_to_emit_to); + log::debug!("Root sell flag: {root_sell_flag:?}"); - // Get initial alpha_in - let mut alpha_in_i: U96F32; - let mut tao_in_i: U96F32; + // --- 4. Emit to subnets for this block. + Self::emit_to_subnets(&subnets_to_emit_to, &subnet_emissions, root_sell_flag); - if default_alpha_in_i > alpha_emission_i { - alpha_in_i = alpha_emission_i; - tao_in_i = alpha_in_i.saturating_mul(price_i); - let difference_tao: U96F32 = default_tao_in_i.saturating_sub(tao_in_i); - excess_tao.insert(*netuid_i, difference_tao); - } else { - tao_in_i = default_tao_in_i; - alpha_in_i = default_alpha_in_i; - excess_tao.insert(*netuid_i, U96F32::from_num(0.0)); - } - log::debug!("alpha_in_i: {alpha_in_i:?}"); + // --- 5. Drain pending emissions. + let emissions_to_distribute = Self::drain_pending(&subnets, current_block); - // Get alpha_out. - let mut alpha_out_i = alpha_emission_i; - // Only emit TAO if the subnetwork allows registration. - if !Self::get_network_registration_allowed(*netuid_i) - && !Self::get_network_pow_registration_allowed(*netuid_i) - { - tao_in_i = asfloat!(0.0); - alpha_in_i = asfloat!(0.0); - alpha_out_i = asfloat!(0.0); - } - // Insert values into maps - tao_in.insert(*netuid_i, tao_in_i); - alpha_in.insert(*netuid_i, alpha_in_i); - alpha_out.insert(*netuid_i, alpha_out_i); - } - log::debug!("tao_in: {tao_in:?}"); - log::debug!("alpha_in: {alpha_in:?}"); - log::debug!("alpha_out: {alpha_out:?}"); - log::debug!("excess_tao: {excess_tao:?}"); - log::debug!("root_sell_flag: {root_sell_flag:?}"); + // --- 6. Distribute the emissions to the subnets. + Self::distribute_emissions_to_subnets(&emissions_to_distribute); + } - // --- 4. Inject and buy Alpha with any excess TAO. + pub fn inject_and_subsidize( + subnets_to_emit_to: &[NetUid], + tao_in: &BTreeMap, + alpha_in: &BTreeMap, + excess_tao: &BTreeMap, + ) { + // --- 2. Inject and subsidize for netuid_i in subnets_to_emit_to.iter() { let tao_in_i: TaoCurrency = tou64!(*tao_in.get(netuid_i).unwrap_or(&asfloat!(0))).into(); let alpha_in_i: AlphaCurrency = - AlphaCurrency::from(tou64!(*alpha_in.get(netuid_i).unwrap_or(&asfloat!(0)))); - let difference_tao: U96F32 = *excess_tao.get(netuid_i).unwrap_or(&asfloat!(0)); + tou64!(*alpha_in.get(netuid_i).unwrap_or(&asfloat!(0))).into(); + let tao_to_swap_with: TaoCurrency = + tou64!(excess_tao.get(netuid_i).unwrap_or(&asfloat!(0))).into(); T::SwapInterface::adjust_protocol_liquidity(*netuid_i, tao_in_i, alpha_in_i); - if difference_tao > asfloat!(0) { + if tao_to_swap_with > TaoCurrency::ZERO { let buy_swap_result = Self::swap_tao_for_alpha( *netuid_i, - tou64!(difference_tao).into(), + tao_to_swap_with, T::SwapInterface::max_price(), true, ); if let Ok(buy_swap_result_ok) = buy_swap_result { - let bought_alpha = AlphaCurrency::from(buy_swap_result_ok.amount_paid_out); + let bought_alpha: AlphaCurrency = buy_swap_result_ok.amount_paid_out.into(); Self::recycle_subnet_alpha(*netuid_i, bought_alpha); } } - } - // --- 5. Update counters - for netuid_i in subnets_to_emit_to.iter() { // Inject Alpha in. let alpha_in_i = AlphaCurrency::from(tou64!(*alpha_in.get(netuid_i).unwrap_or(&asfloat!(0)))); @@ -134,52 +90,116 @@ impl Pallet { SubnetAlphaIn::::mutate(*netuid_i, |total| { *total = total.saturating_add(alpha_in_i); }); - // Injection Alpha out. - let alpha_out_i = - AlphaCurrency::from(tou64!(*alpha_out.get(netuid_i).unwrap_or(&asfloat!(0)))); - SubnetAlphaOutEmission::::insert(*netuid_i, alpha_out_i); - SubnetAlphaOut::::mutate(*netuid_i, |total| { - *total = total.saturating_add(alpha_out_i); - }); + // Inject TAO in. - let tao_in_i: TaoCurrency = + let injected_tao: TaoCurrency = tou64!(*tao_in.get(netuid_i).unwrap_or(&asfloat!(0))).into(); - SubnetTaoInEmission::::insert(*netuid_i, TaoCurrency::from(tao_in_i)); + SubnetTaoInEmission::::insert(*netuid_i, injected_tao); SubnetTAO::::mutate(*netuid_i, |total| { - *total = total.saturating_add(tao_in_i.into()); + *total = total.saturating_add(injected_tao); }); TotalStake::::mutate(|total| { - *total = total.saturating_add(tao_in_i.into()); + *total = total.saturating_add(injected_tao); }); - let difference_tao: U96F32 = *excess_tao.get(netuid_i).unwrap_or(&asfloat!(0)); + // Update total TAO issuance. + let difference_tao = tou64!(*excess_tao.get(netuid_i).unwrap_or(&asfloat!(0))); TotalIssuance::::mutate(|total| { - *total = total.saturating_add(tao_in_i.into()); - *total = total.saturating_add(tou64!(difference_tao).into()); + *total = total + .saturating_add(injected_tao.into()) + .saturating_add(difference_tao.into()); }); } + } - // --- 6. Compute owner cuts and remove them from alpha_out remaining. - // Remove owner cuts here so that we can properly seperate root dividends in the next step. - // Owner cuts are accumulated and then fed to the drain at the end of this func. - let cut_percent: U96F32 = Self::get_float_subnet_owner_cut(); - let mut owner_cuts: BTreeMap = BTreeMap::new(); - for netuid_i in subnets_to_emit_to.iter() { - // Get alpha out. - let alpha_out_i: U96F32 = *alpha_out.get(netuid_i).unwrap_or(&asfloat!(0)); - log::debug!("alpha_out_i: {alpha_out_i:?}"); - // Calculate the owner cut. - let owner_cut_i: U96F32 = alpha_out_i.saturating_mul(cut_percent); - log::debug!("owner_cut_i: {owner_cut_i:?}"); - // Save owner cut. - *owner_cuts.entry(*netuid_i).or_insert(asfloat!(0)) = owner_cut_i; - // Save new alpha_out. - alpha_out.insert(*netuid_i, alpha_out_i.saturating_sub(owner_cut_i)); - // Accumulate the owner cut in pending. - PendingOwnerCut::::mutate(*netuid_i, |total| { - *total = total.saturating_add(tou64!(owner_cut_i).into()); - }); + pub fn get_subnet_terms( + subnet_emissions: &BTreeMap, + ) -> ( + BTreeMap, + BTreeMap, + BTreeMap, + BTreeMap, + ) { + // Computation is described in detail in the dtao whitepaper. + let mut tao_in: BTreeMap = BTreeMap::new(); + let mut alpha_in: BTreeMap = BTreeMap::new(); + let mut alpha_out: BTreeMap = BTreeMap::new(); + let mut subsidy_amount: BTreeMap = BTreeMap::new(); + + // Only calculate for subnets that we are emitting to. + for (&netuid_i, &tao_emission_i) in subnet_emissions.iter() { + let tao_in_i: U96F32; + let alpha_in_i: U96F32; + let alpha_out_i: U96F32; + // Only emit if the subnetwork allows registration. + if !Self::get_network_registration_allowed(netuid_i) + && !Self::get_network_pow_registration_allowed(netuid_i) + { + tao_in_i = asfloat!(0.0); + alpha_in_i = asfloat!(0.0); + alpha_out_i = asfloat!(0.0); + } else { + // Get alpha_emission total + let alpha_emission_i: U96F32 = asfloat!( + Self::get_block_emission_for_issuance( + Self::get_alpha_issuance(netuid_i).into() + ) + .unwrap_or(0) + ); + log::debug!("alpha_emission_i: {alpha_emission_i:?}"); + + // Get alpha_out. + alpha_out_i = alpha_emission_i; + + // Get subnet price. + let price_i = T::SwapInterface::current_alpha_price(netuid_i.into()); + log::debug!("price_i: {price_i:?}"); + + log::debug!("default_tao_in_i: {tao_emission_i:?}"); + let default_alpha_in_i: U96F32 = + tao_emission_i.safe_div_or(price_i, U96F32::saturating_from_num(alpha_emission_i)); + + // Get initial alpha_in + if default_alpha_in_i > alpha_emission_i { + alpha_in_i = alpha_emission_i; + tao_in_i = alpha_in_i.saturating_mul(price_i); + } else { + tao_in_i = tao_emission_i; + alpha_in_i = tao_in_i.safe_div_or(price_i, alpha_emission_i); + } + + let subsidy_tao: U96F32 = tao_emission_i.saturating_sub(tao_in_i); + subsidy_amount.insert(netuid_i, subsidy_tao); + } + + // Insert values into maps + tao_in.insert(netuid_i, tao_in_i); + alpha_in.insert(netuid_i, alpha_in_i); + alpha_out.insert(netuid_i, alpha_out_i); } + (tao_in, alpha_in, alpha_out, subsidy_amount) + } + + pub fn emit_to_subnets( + subnets_to_emit_to: &[NetUid], + subnet_emissions: &BTreeMap, + root_sell_flag: bool, + ) { + // --- 1. Get subnet terms (tao_in, alpha_in, and alpha_out) + // and subsidy amount. + let (tao_in, alpha_in, alpha_out, subsidy_amount) = + Self::get_subnet_terms(subnet_emissions); + + log::debug!("tao_in: {tao_in:?}"); + log::debug!("alpha_in: {alpha_in:?}"); + log::debug!("alpha_out: {alpha_out:?}"); + log::debug!("subsidy_amount: {subsidy_amount:?}"); + + // --- 2. Inject TAO and ALPHA to pool and subsidize. + Self::inject_and_subsidize(subnets_to_emit_to, &tao_in, &alpha_in, &subsidy_amount); + + // --- 3. Inject ALPHA for participants. + let cut_percent: U96F32 = Self::get_float_subnet_owner_cut(); // Get total TAO on root. let root_tao: U96F32 = asfloat!(SubnetTAO::::get(NetUid::ROOT)); @@ -188,20 +208,32 @@ impl Pallet { let tao_weight: U96F32 = root_tao.saturating_mul(Self::get_tao_weight()); log::debug!("tao_weight: {tao_weight:?}"); - // --- 7. Seperate out root dividends in alpha and keep them. - // Then accumulate those dividends for later. for netuid_i in subnets_to_emit_to.iter() { - // Get remaining alpha out. - let alpha_out_i: U96F32 = *alpha_out.get(netuid_i).unwrap_or(&asfloat!(0.0)); - log::debug!("alpha_out_i: {alpha_out_i:?}"); + // Get alpha_out for this block. + let mut alpha_out_i: U96F32 = *alpha_out.get(netuid_i).unwrap_or(&asfloat!(0)); + + let alpha_created: AlphaCurrency = AlphaCurrency::from(tou64!(alpha_out_i)); + SubnetAlphaOutEmission::::insert(*netuid_i, alpha_created); + SubnetAlphaOut::::mutate(*netuid_i, |total| { + *total = total.saturating_add(alpha_created); + }); + + // Calculate the owner cut. + let owner_cut_i: U96F32 = alpha_out_i.saturating_mul(cut_percent); + log::debug!("owner_cut_i: {owner_cut_i:?}"); + // Deduct owner cut from alpha_out. + alpha_out_i = alpha_out_i.saturating_sub(owner_cut_i); + // Accumulate the owner cut in pending. + PendingOwnerCut::::mutate(*netuid_i, |total| { + *total = total.saturating_add(tou64!(owner_cut_i).into()); + }); // Get root proportion of alpha_out dividends. let mut root_alpha: U96F32 = asfloat!(0.0); if root_sell_flag { - // Get ALPHA issuance. + // Get total ALPHA on subnet. let alpha_issuance: U96F32 = asfloat!(Self::get_alpha_issuance(*netuid_i)); log::debug!("alpha_issuance: {alpha_issuance:?}"); - // Get root proportional dividends. let root_proportion: U96F32 = tao_weight .checked_div(tao_weight.saturating_add(alpha_issuance)) @@ -216,23 +248,45 @@ impl Pallet { *total = total.saturating_add(tou64!(root_alpha).into()); }); } - // Remove root alpha from alpha_out. log::debug!("root_alpha: {root_alpha:?}"); - // Get pending alpha as original alpha_out - root_alpha. - let pending_alpha: U96F32 = alpha_out_i.saturating_sub(root_alpha); - log::debug!("pending_alpha: {pending_alpha:?}"); + // Deduct root alpha from alpha_out. + alpha_out_i = alpha_out_i.saturating_sub(root_alpha); // Accumulate alpha emission in pending. + let pending_alpha: AlphaCurrency = tou64!(alpha_out_i).into(); + log::debug!("pending_alpha: {pending_alpha:?}"); PendingEmission::::mutate(*netuid_i, |total| { - *total = total.saturating_add(tou64!(pending_alpha).into()); + *total = total.saturating_add(pending_alpha); }); } - + } + + pub fn get_network_subsidy_mode(subnets_to_emit_to: &[NetUid]) -> bool { + let total_ema_price: U96F32 = subnets_to_emit_to + .iter() + .map(|netuid| Self::get_moving_alpha_price(*netuid)) + .sum(); + + // If the total EMA price is less than or equal to 1, then we subsidize the network. + total_ema_price <= U96F32::saturating_from_num(1) + } + + pub fn drain_pending( + subnets: &[NetUid], + current_block: u64, + ) -> BTreeMap { + // Map of netuid to (pending_alpha, pending_root_alpha, pending_owner_cut). + let mut emissions_to_distribute: BTreeMap< + NetUid, + (AlphaCurrency, AlphaCurrency, AlphaCurrency), + > = BTreeMap::new(); // --- Drain pending emissions for all subnets hat are at their tempo. // Run the epoch for *all* subnets, even if we don't emit anything. for &netuid in subnets.iter() { - + // Increment blocks since last step. + BlocksSinceLastStep::::mutate(netuid, |total| *total = total.saturating_add(1)); + // Run the epoch if applicable. if Self::should_run_epoch(netuid, current_block) && Self::is_epoch_input_state_consistent(netuid) @@ -241,25 +295,35 @@ impl Pallet { BlocksSinceLastStep::::insert(netuid, 0); LastMechansimStepBlock::::insert(netuid, current_block); - // Get and drain the subnet pending emission. + // Get and drain the pending subnet emission. let pending_alpha = PendingEmission::::get(netuid); PendingEmission::::insert(netuid, AlphaCurrency::ZERO); - // Get and drain the subnet pending root alpha divs. + // Get and drain the pending Alpha for root divs. let pending_root_alpha = PendingRootAlphaDivs::::get(netuid); PendingRootAlphaDivs::::insert(netuid, AlphaCurrency::ZERO); - // Get owner cut and drain. + // Get and drain the pending owner cut. let owner_cut = PendingOwnerCut::::get(netuid); PendingOwnerCut::::insert(netuid, AlphaCurrency::ZERO); - // Distribute the emission. - Self::distribute_emission(netuid, pending_alpha, pending_root_alpha, owner_cut); - } else { - // Increment - BlocksSinceLastStep::::mutate(netuid, |total| *total = total.saturating_add(1)); + // Save the emissions to distribute. + emissions_to_distribute + .insert(netuid, (pending_alpha, pending_root_alpha, owner_cut)); } } + emissions_to_distribute + } + + pub fn distribute_emissions_to_subnets( + emissions_to_distribute: &BTreeMap, + ) { + for (&netuid, &(pending_alpha, pending_root_alpha, pending_owner_cut)) in + emissions_to_distribute.iter() + { + // Distribute the emission to the subnet. + Self::distribute_emission(netuid, pending_alpha, pending_root_alpha, pending_owner_cut); + } } pub fn get_network_root_sell_flag(subnets_to_emit_to: &[NetUid]) -> bool { diff --git a/pallets/subtensor/src/coinbase/subnet_emissions.rs b/pallets/subtensor/src/coinbase/subnet_emissions.rs index c2a12b3c61..374619b22f 100644 --- a/pallets/subtensor/src/coinbase/subnet_emissions.rs +++ b/pallets/subtensor/src/coinbase/subnet_emissions.rs @@ -11,7 +11,7 @@ impl Pallet { // Filter out subnets with no first emission block number. subnets .iter() - .filter(|netuid| *netuid != NetUid::ROOT) + .filter(|&netuid| *netuid != NetUid::ROOT) .filter(|netuid| FirstEmissionBlockNumber::::get(*netuid).is_some()) .copied() .collect() From 4abd92df8d626a35cd41cb8a41cb20d86a6be691 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 00:37:26 -0500 Subject: [PATCH 083/210] chore: fmt --- pallets/subtensor/src/coinbase/run_coinbase.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 12c11a0118..d678b8b1a8 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -156,8 +156,8 @@ impl Pallet { log::debug!("price_i: {price_i:?}"); log::debug!("default_tao_in_i: {tao_emission_i:?}"); - let default_alpha_in_i: U96F32 = - tao_emission_i.safe_div_or(price_i, U96F32::saturating_from_num(alpha_emission_i)); + let default_alpha_in_i: U96F32 = tao_emission_i + .safe_div_or(price_i, U96F32::saturating_from_num(alpha_emission_i)); // Get initial alpha_in if default_alpha_in_i > alpha_emission_i { From 1f965d47ddfcf03694b49dc473b3e52bf3cb24f6 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 00:49:35 -0500 Subject: [PATCH 084/210] chore: clippy --- pallets/subtensor/src/coinbase/subnet_emissions.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/coinbase/subnet_emissions.rs b/pallets/subtensor/src/coinbase/subnet_emissions.rs index 374619b22f..3f06092084 100644 --- a/pallets/subtensor/src/coinbase/subnet_emissions.rs +++ b/pallets/subtensor/src/coinbase/subnet_emissions.rs @@ -12,7 +12,7 @@ impl Pallet { subnets .iter() .filter(|&netuid| *netuid != NetUid::ROOT) - .filter(|netuid| FirstEmissionBlockNumber::::get(*netuid).is_some()) + .filter(|&netuid| FirstEmissionBlockNumber::::get(*netuid).is_some()) .copied() .collect() } From 1ea4ddf1cc25d0469bb4c71832d4907e28e8a482 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 02:04:57 -0500 Subject: [PATCH 085/210] add test for reg disabled --- pallets/subtensor/src/tests/coinbase.rs | 46 +++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 5d03afa973..6eeca6a436 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -3287,3 +3287,49 @@ fn test_mining_emission_distribution_with_root_sell() { ); }); } + +#[test] +fn test_coinbase_subnets_with_no_reg_get_no_emission() { + new_test_ext(1).execute_with(|| { + let zero = U96F32::saturating_from_num(0); + let netuid0 = add_dynamic_network(&U256::from(1), &U256::from(2)); + let netuid1 = add_dynamic_network(&U256::from(3), &U256::from(4)); + + let subnet_emissions = BTreeMap::from([ + (netuid0, U96F32::saturating_from_num(1)), + (netuid1, U96F32::saturating_from_num(1)), + ]); + + let (tao_in, alpha_in, alpha_out, subsidy_amount) = + SubtensorModule::get_subnet_terms(&subnet_emissions); + assert_eq!(tao_in.len(), 2); + assert_eq!(alpha_in.len(), 2); + assert_eq!(alpha_out.len(), 2); + + assert!(tao_in[&netuid0] > zero); + assert!(alpha_in[&netuid0] > zero); + assert!(alpha_out[&netuid0] > zero); + + assert!(tao_in[&netuid1] > zero); + assert!(alpha_in[&netuid1] > zero); + assert!(alpha_out[&netuid1] > zero); + + // Disabled registration of both methods + NetworkRegistrationAllowed::::insert(netuid0, false); + NetworkPowRegistrationAllowed::::insert(netuid0, false); + + let (tao_in_2, alpha_in_2, alpha_out_2, subsidy_amount_2) = + SubtensorModule::get_subnet_terms(&subnet_emissions); + assert_eq!(tao_in_2.len(), 2); + assert_eq!(alpha_in_2.len(), 2); + assert_eq!(alpha_out_2.len(), 2); + + assert!(tao_in_2[&netuid0] == zero); + assert!(alpha_in_2[&netuid0] == zero); + assert!(alpha_out_2[&netuid0] == zero); + + assert!(tao_in_2[&netuid1] > zero); + assert!(alpha_in_2[&netuid1] > zero); + assert!(alpha_out_2[&netuid1] > zero); + }); +} From 7a9b662e6f25e4d8cfb32db46c13174c5665e394 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 02:14:24 -0500 Subject: [PATCH 086/210] refactor get sn terms --- pallets/subtensor/src/coinbase/run_coinbase.rs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index d678b8b1a8..91e67a5f68 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -128,8 +128,8 @@ impl Pallet { // Only calculate for subnets that we are emitting to. for (&netuid_i, &tao_emission_i) in subnet_emissions.iter() { - let tao_in_i: U96F32; - let alpha_in_i: U96F32; + let mut tao_in_i: U96F32; + let mut alpha_in_i: U96F32; let alpha_out_i: U96F32; // Only emit if the subnetwork allows registration. if !Self::get_network_registration_allowed(netuid_i) @@ -155,17 +155,12 @@ impl Pallet { let price_i = T::SwapInterface::current_alpha_price(netuid_i.into()); log::debug!("price_i: {price_i:?}"); - log::debug!("default_tao_in_i: {tao_emission_i:?}"); - let default_alpha_in_i: U96F32 = tao_emission_i - .safe_div_or(price_i, U96F32::saturating_from_num(alpha_emission_i)); + tao_in_i = tao_emission_i; + alpha_in_i = tao_emission_i.safe_div_or(price_i, U96F32::saturating_from_num(0.0)); - // Get initial alpha_in - if default_alpha_in_i > alpha_emission_i { + if alpha_in_i > alpha_emission_i { alpha_in_i = alpha_emission_i; tao_in_i = alpha_in_i.saturating_mul(price_i); - } else { - tao_in_i = tao_emission_i; - alpha_in_i = tao_in_i.safe_div_or(price_i, alpha_emission_i); } let subsidy_tao: U96F32 = tao_emission_i.saturating_sub(tao_in_i); From 0b5df3c8ed890706ef9c468ebd9aeaa7cbf195d9 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 10:36:17 -0500 Subject: [PATCH 087/210] test wip -- set price --- pallets/subtensor/src/tests/coinbase.rs | 79 ++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 6eeca6a436..1696441a74 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -8,7 +8,10 @@ use approx::assert_abs_diff_eq; use frame_support::assert_ok; use pallet_subtensor_swap::position::PositionId; use sp_core::U256; -use substrate_fixed::types::{I64F64, I96F32, U64F64, U96F32}; +use substrate_fixed::{ + transcendental::sqrt, + types::{I64F64, I96F32, U64F64, U96F32}, +}; use subtensor_runtime_common::{AlphaCurrency, NetUidStorageIndex}; use subtensor_swap_interface::{SwapEngine, SwapHandler}; @@ -3333,3 +3336,77 @@ fn test_coinbase_subnets_with_no_reg_get_no_emission() { assert!(alpha_out_2[&netuid1] > zero); }); } + +#[test] +fn test_coinbase_alpha_in_more_than_alpha_emission() { + new_test_ext(1).execute_with(|| { + let zero = U96F32::saturating_from_num(0); + let netuid0 = add_dynamic_network(&U256::from(1), &U256::from(2)); + mock::setup_reserves( + netuid0, + TaoCurrency::from(1_000_000_000_000_000), + AlphaCurrency::from(1_000_000_000_000_000), + ); + + // Set netuid0 to have price tao_emission / price > alpha_emission + let alpha_emission = U96F32::saturating_from_num( + SubtensorModule::get_block_emission_for_issuance( + SubtensorModule::get_alpha_issuance(netuid0).into(), + ) + .unwrap_or(0), + ); + let price_to_set: U64F64 = U64F64::saturating_from_num(0.2); + let price_to_set_fixed: U96F32 = U96F32::saturating_from_num(price_to_set); + let sqrt_price_to_set: U64F64 = sqrt(price_to_set).unwrap(); + + let tao_emission: U96F32 = U96F32::saturating_from_num(alpha_emission) + .saturating_mul(price_to_set_fixed) + .saturating_add(U96F32::saturating_from_num(0.01)); + + // Set the price + pallet_subtensor_swap::AlphaSqrtPrice::::insert(netuid0, sqrt_price_to_set); + // Check the price is set + assert_abs_diff_eq!( + pallet_subtensor_swap::Pallet::::current_alpha_price(netuid0).to_num::(), + price_to_set.to_num::(), + epsilon = 1.0 + ); + + let subnet_emissions = BTreeMap::from([(netuid0, tao_emission)]); + + let (tao_in, alpha_in, alpha_out, subsidy_amount) = + SubtensorModule::get_subnet_terms(&subnet_emissions); + + // Check our condition is met + assert!(tao_emission / price_to_set_fixed > alpha_emission); + + // alpha_out should be the alpha_emission, always + assert_abs_diff_eq!( + alpha_out[&netuid0].to_num::(), + alpha_emission.to_num::(), + epsilon = 1.0 + ); + + // alpha_in should equal the alpha_emission + assert_abs_diff_eq!( + alpha_in[&netuid0].to_num::(), + alpha_emission.to_num::(), + epsilon = 1.0 + ); + // tao_in should be the alpha_in at the ratio of the price + assert_abs_diff_eq!( + tao_in[&netuid0].to_num::(), + alpha_in[&netuid0] + .saturating_mul(price_to_set_fixed) + .to_num::(), + epsilon = 1.0 + ); + + // subsidy_amount should be the difference between the tao_emission and the tao_in + assert_abs_diff_eq!( + subsidy_amount[&netuid0].to_num::(), + tao_emission.to_num::() - tao_in[&netuid0].to_num::(), + epsilon = 1.0 + ); + }); +} From 75440be62905687a4575161ccd6f67fdacfe2587 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 6 Nov 2025 14:39:39 -0500 Subject: [PATCH 088/210] Hotfix/vune/subnet-dereg-burn-use-issuance (#2190) * use alpha issuance for owner estimate * fix tests * use price for estimate only * fix tests for using just price * docs: comment fix --- pallets/subtensor/src/staking/remove_stake.rs | 34 ++--- pallets/subtensor/src/tests/networks.rs | 132 ++++++++++-------- 2 files changed, 81 insertions(+), 85 deletions(-) diff --git a/pallets/subtensor/src/staking/remove_stake.rs b/pallets/subtensor/src/staking/remove_stake.rs index 5771029f74..7fdd335556 100644 --- a/pallets/subtensor/src/staking/remove_stake.rs +++ b/pallets/subtensor/src/staking/remove_stake.rs @@ -447,19 +447,13 @@ impl Pallet { let should_refund_owner: bool = reg_at < start_block; // 3) Compute owner's received emission in TAO at current price (ONLY if we may refund). - // Emission:: is Vec. We: - // - sum emitted α, + // We: + // - get the current alpha issuance, // - apply owner fraction to get owner α, // - price that α using a *simulated* AMM swap. let mut owner_emission_tao = TaoCurrency::ZERO; if should_refund_owner && !lock_cost.is_zero() { - let total_emitted_alpha_u128: u128 = - Emission::::get(netuid) - .into_iter() - .fold(0u128, |acc, e_alpha| { - let e_u64: u64 = Into::::into(e_alpha); - acc.saturating_add(e_u64 as u128) - }); + let total_emitted_alpha_u128: u128 = Self::get_alpha_issuance(netuid).to_u64() as u128; if total_emitted_alpha_u128 > 0 { let owner_fraction: U96F32 = Self::get_float_subnet_owner_cut(); @@ -469,22 +463,12 @@ impl Pallet { .saturating_to_num::(); owner_emission_tao = if owner_alpha_u64 > 0 { - let order = GetTaoForAlpha::with_amount(owner_alpha_u64); - match T::SwapInterface::sim_swap(netuid.into(), order) { - Ok(sim) => TaoCurrency::from(sim.amount_paid_out), - Err(e) => { - log::debug!( - "destroy_alpha_in_out_stakes: sim_swap owner α→τ failed (netuid={netuid:?}, alpha={owner_alpha_u64}, err={e:?}); falling back to price multiply.", - ); - let cur_price: U96F32 = - T::SwapInterface::current_alpha_price(netuid.into()); - let val_u64 = U96F32::from_num(owner_alpha_u64) - .saturating_mul(cur_price) - .floor() - .saturating_to_num::(); - TaoCurrency::from(val_u64) - } - } + let cur_price: U96F32 = T::SwapInterface::current_alpha_price(netuid.into()); + let val_u64 = U96F32::from_num(owner_alpha_u64) + .saturating_mul(cur_price) + .floor() + .saturating_to_num::(); + TaoCurrency::from(val_u64) } else { TaoCurrency::ZERO }; diff --git a/pallets/subtensor/src/tests/networks.rs b/pallets/subtensor/src/tests/networks.rs index 0449c67f86..3706878bb6 100644 --- a/pallets/subtensor/src/tests/networks.rs +++ b/pallets/subtensor/src/tests/networks.rs @@ -216,43 +216,49 @@ fn dissolve_owner_cut_refund_logic() { // One staker and a TAO pot (not relevant to refund amount). let sh = U256::from(77); let sc = U256::from(88); - Alpha::::insert((sh, sc, net), U64F64::from_num(100u128)); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &sh, + &sc, + net, + AlphaCurrency::from(800u64), + ); SubnetTAO::::insert(net, TaoCurrency::from(1_000)); // Lock & emissions: total emitted α = 800. let lock: TaoCurrency = TaoCurrency::from(2_000); SubtensorModule::set_subnet_locked_balance(net, lock); - Emission::::insert( - net, - vec![AlphaCurrency::from(200), AlphaCurrency::from(600)], - ); + // ensure there was some Alpha issued + assert!(SubtensorModule::get_alpha_issuance(net).to_u64() > 0); // Owner cut = 11796 / 65535 (about 18%). SubnetOwnerCut::::put(11_796u16); // Compute expected refund with the SAME math as the pallet. let frac: U96F32 = SubtensorModule::get_float_subnet_owner_cut(); - let total_emitted_alpha: u64 = 800; + let total_emitted_alpha: u64 = SubtensorModule::get_alpha_issuance(net).to_u64(); let owner_alpha_u64: u64 = U96F32::from_num(total_emitted_alpha) .saturating_mul(frac) .floor() .saturating_to_num::(); - // Current α→τ price for this subnet. - let price: U96F32 = - ::SwapInterface::current_alpha_price(net.into()); - let owner_emission_tao_u64: u64 = U96F32::from_num(owner_alpha_u64) - .saturating_mul(price) - .floor() - .saturating_to_num::(); + // Use the current alpha price to estimate the TAO equivalent. + let owner_emission_tao = { + let price: U96F32 = + ::SwapInterface::current_alpha_price(net.into()); + U96F32::from_num(owner_alpha_u64) + .saturating_mul(price) + .floor() + .saturating_to_num::() + .into() + }; - let expected_refund: TaoCurrency = - lock.saturating_sub(TaoCurrency::from(owner_emission_tao_u64)); + let expected_refund: TaoCurrency = lock.saturating_sub(owner_emission_tao); let before = SubtensorModule::get_coldkey_balance(&oc); assert_ok!(SubtensorModule::do_dissolve_network(net)); let after = SubtensorModule::get_coldkey_balance(&oc); + assert!(after > before); // some refund is expected assert_eq!( TaoCurrency::from(after), TaoCurrency::from(before) + expected_refund @@ -841,15 +847,10 @@ fn destroy_alpha_out_many_stakers_complex_distribution() { SubnetTAO::::insert(netuid, TaoCurrency::from(tao_pot)); SubtensorModule::set_subnet_locked_balance(netuid, TaoCurrency::from(lock)); + // ensure there was some Alpha issued + assert!(SubtensorModule::get_alpha_issuance(netuid).to_u64() > 0); + // Owner already earned some emission; owner-cut = 50 % - Emission::::insert( - netuid, - vec![ - AlphaCurrency::from(1_000), - AlphaCurrency::from(2_000), - AlphaCurrency::from(1_500), - ], - ); SubnetOwnerCut::::put(32_768u16); // ~ 0.5 in fixed-point // ── 4) balances before ────────────────────────────────────────────── @@ -879,28 +880,23 @@ fn destroy_alpha_out_many_stakers_complex_distribution() { // ── 5b) expected owner refund with price-aware emission deduction ─── let frac: U96F32 = SubtensorModule::get_float_subnet_owner_cut(); - let total_emitted_alpha: u64 = 1_000 + 2_000 + 1_500; // 4500 α + let total_emitted_alpha: u64 = SubtensorModule::get_alpha_issuance(netuid).to_u64(); let owner_alpha_u64: u64 = U96F32::from_num(total_emitted_alpha) .saturating_mul(frac) .floor() .saturating_to_num::(); - let order = GetTaoForAlpha::::with_amount(owner_alpha_u64); - let owner_emission_tao = - ::SwapInterface::sim_swap(netuid.into(), order) - .map(|res| res.amount_paid_out) - .unwrap_or_else(|_| { - // Fallback matches the pallet's fallback - let price: U96F32 = - ::SwapInterface::current_alpha_price(netuid.into()); - U96F32::from_num(owner_alpha_u64) - .saturating_mul(price) - .floor() - .saturating_to_num::() - .into() - }); - - let expected_refund = lock.saturating_sub(owner_emission_tao.to_u64()); + let owner_emission_tao: u64 = { + // Fallback matches the pallet's fallback + let price: U96F32 = + ::SwapInterface::current_alpha_price(netuid.into()); + U96F32::from_num(owner_alpha_u64) + .saturating_mul(price) + .floor() + .saturating_to_num::() + }; + + let expected_refund = lock.saturating_sub(owner_emission_tao); // ── 6) run distribution (credits τ to coldkeys, wipes α state) ───── assert_ok!(SubtensorModule::destroy_alpha_in_out_stakes(netuid)); @@ -947,34 +943,38 @@ fn destroy_alpha_out_refund_gating_by_registration_block() { // Lock and (nonzero) emissions let lock_u64: u64 = 50_000; SubtensorModule::set_subnet_locked_balance(netuid, TaoCurrency::from(lock_u64)); - Emission::::insert( - netuid, - vec![AlphaCurrency::from(1_500u64), AlphaCurrency::from(3_000u64)], // total 4_500 α - ); // Owner cut ≈ 50% SubnetOwnerCut::::put(32_768u16); + // give some stake to other key + let other_cold = U256::from(1_234); + let other_hot = U256::from(2_345); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &other_hot, + &other_cold, + netuid, + AlphaCurrency::from(30u64), // not nearly enough to cover the lock + ); + + // ensure there was some Alpha issued + assert!(SubtensorModule::get_alpha_issuance(netuid).to_u64() > 0); + // Compute expected refund using the same math as the pallet let frac: U96F32 = SubtensorModule::get_float_subnet_owner_cut(); - let total_emitted_alpha: u64 = 1_500 + 3_000; // 4_500 α + let total_emitted_alpha: u64 = SubtensorModule::get_alpha_issuance(netuid).to_u64(); let owner_alpha_u64: u64 = U96F32::from_num(total_emitted_alpha) .saturating_mul(frac) .floor() .saturating_to_num::(); - // Prefer sim_swap; fall back to current price if unavailable. - let order = GetTaoForAlpha::::with_amount(owner_alpha_u64); - let owner_emission_tao_u64 = - ::SwapInterface::sim_swap(netuid.into(), order) - .map(|res| res.amount_paid_out.to_u64()) - .unwrap_or_else(|_| { - let price: U96F32 = - ::SwapInterface::current_alpha_price(netuid.into()); - U96F32::from_num(owner_alpha_u64) - .saturating_mul(price) - .floor() - .saturating_to_num::() - }); + let owner_emission_tao_u64 = { + let price: U96F32 = + ::SwapInterface::current_alpha_price(netuid.into()); + U96F32::from_num(owner_alpha_u64) + .saturating_mul(price) + .floor() + .saturating_to_num::() + }; let expected_refund: u64 = lock_u64.saturating_sub(owner_emission_tao_u64); @@ -1011,7 +1011,17 @@ fn destroy_alpha_out_refund_gating_by_registration_block() { // Lock and emissions present (should be ignored for refund) let lock_u64: u64 = 42_000; SubtensorModule::set_subnet_locked_balance(netuid, TaoCurrency::from(lock_u64)); - Emission::::insert(netuid, vec![AlphaCurrency::from(5_000u64)]); + // give some stake to other key + let other_cold = U256::from(1_234); + let other_hot = U256::from(2_345); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &other_hot, + &other_cold, + netuid, + AlphaCurrency::from(300u64), // not nearly enough to cover the lock + ); + // ensure there was some Alpha issued + assert!(SubtensorModule::get_alpha_issuance(netuid).to_u64() > 0); SubnetOwnerCut::::put(32_768u16); // ~50% // Balances before @@ -1046,7 +1056,9 @@ fn destroy_alpha_out_refund_gating_by_registration_block() { // lock = 0; emissions present (must not matter) SubtensorModule::set_subnet_locked_balance(netuid, TaoCurrency::from(0u64)); - Emission::::insert(netuid, vec![AlphaCurrency::from(10_000u64)]); + SubnetAlphaOut::::insert(netuid, AlphaCurrency::from(10_000)); + // ensure there was some Alpha issued + assert!(SubtensorModule::get_alpha_issuance(netuid).to_u64() > 0); SubnetOwnerCut::::put(32_768u16); // ~50% let owner_before = SubtensorModule::get_coldkey_balance(&owner_cold); From 40b4ffca057db9056a2211b6051353c56c040099 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 15:00:59 -0500 Subject: [PATCH 089/210] remove log out --- pallets/subtensor/src/coinbase/run_coinbase.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 5b1c4b5704..d998ad1f7d 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -520,9 +520,7 @@ impl Pallet { incentive, }); } - log::debug!( - "incentives: increasing stake for {hotkey:?} to {incentive:?} with owner {owner:?}" - ); + Self::increase_stake_for_hotkey_and_coldkey_on_subnet( &destination, &owner, From fb0cd73328e6b0e044674cb2de6f34694e6b0a75 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 18:28:48 -0500 Subject: [PATCH 090/210] make maybe init v3 pub --- pallets/swap/src/pallet/impls.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/swap/src/pallet/impls.rs b/pallets/swap/src/pallet/impls.rs index 34b5e624e6..de08022060 100644 --- a/pallets/swap/src/pallet/impls.rs +++ b/pallets/swap/src/pallet/impls.rs @@ -68,7 +68,7 @@ impl Pallet { } // initializes V3 swap for a subnet if needed - pub(super) fn maybe_initialize_v3(netuid: NetUid) -> Result<(), Error> { + pub fn maybe_initialize_v3(netuid: NetUid) -> Result<(), Error> { if SwapV3Initialized::::get(netuid) { return Ok(()); } From 6e2003594ac01ad672849495ab512a67db447b8e Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 18:29:05 -0500 Subject: [PATCH 091/210] add type --- pallets/subtensor/src/coinbase/run_coinbase.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 91e67a5f68..ef15d8a425 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -152,7 +152,7 @@ impl Pallet { alpha_out_i = alpha_emission_i; // Get subnet price. - let price_i = T::SwapInterface::current_alpha_price(netuid_i.into()); + let price_i: U96F32 = T::SwapInterface::current_alpha_price(netuid_i.into()); log::debug!("price_i: {price_i:?}"); tao_in_i = tao_emission_i; From 660b1c647b3da7025931ea795c517d9f3d7a9ecf Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 18:29:18 -0500 Subject: [PATCH 092/210] fix coinbase test --- pallets/subtensor/src/tests/coinbase.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 1696441a74..80d1e9b4f7 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -3347,6 +3347,8 @@ fn test_coinbase_alpha_in_more_than_alpha_emission() { TaoCurrency::from(1_000_000_000_000_000), AlphaCurrency::from(1_000_000_000_000_000), ); + // Initialize swap v3 + Swap::maybe_initialize_v3(netuid0); // Set netuid0 to have price tao_emission / price > alpha_emission let alpha_emission = U96F32::saturating_from_num( @@ -3355,7 +3357,7 @@ fn test_coinbase_alpha_in_more_than_alpha_emission() { ) .unwrap_or(0), ); - let price_to_set: U64F64 = U64F64::saturating_from_num(0.2); + let price_to_set: U64F64 = U64F64::saturating_from_num(0.01); let price_to_set_fixed: U96F32 = U96F32::saturating_from_num(price_to_set); let sqrt_price_to_set: U64F64 = sqrt(price_to_set).unwrap(); @@ -3369,7 +3371,7 @@ fn test_coinbase_alpha_in_more_than_alpha_emission() { assert_abs_diff_eq!( pallet_subtensor_swap::Pallet::::current_alpha_price(netuid0).to_num::(), price_to_set.to_num::(), - epsilon = 1.0 + epsilon = 0.001 ); let subnet_emissions = BTreeMap::from([(netuid0, tao_emission)]); From a0c2369aea6ec72b7fc7e23550e83c38f3514d50 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 18:31:08 -0500 Subject: [PATCH 093/210] rename test vars and add comment --- pallets/subtensor/src/tests/coinbase.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 80d1e9b4f7..36e8a2b783 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -3337,6 +3337,7 @@ fn test_coinbase_subnets_with_no_reg_get_no_emission() { }); } +// Tests for the excess TAO condition #[test] fn test_coinbase_alpha_in_more_than_alpha_emission() { new_test_ext(1).execute_with(|| { @@ -3376,7 +3377,7 @@ fn test_coinbase_alpha_in_more_than_alpha_emission() { let subnet_emissions = BTreeMap::from([(netuid0, tao_emission)]); - let (tao_in, alpha_in, alpha_out, subsidy_amount) = + let (tao_in, alpha_in, alpha_out, excess_tao) = SubtensorModule::get_subnet_terms(&subnet_emissions); // Check our condition is met @@ -3404,9 +3405,9 @@ fn test_coinbase_alpha_in_more_than_alpha_emission() { epsilon = 1.0 ); - // subsidy_amount should be the difference between the tao_emission and the tao_in + // excess_tao should be the difference between the tao_emission and the tao_in assert_abs_diff_eq!( - subsidy_amount[&netuid0].to_num::(), + excess_tao[&netuid0].to_num::(), tao_emission.to_num::() - tao_in[&netuid0].to_num::(), epsilon = 1.0 ); From 8652a5fd2b2ac9dfa4a578c2c9d3c46a6d125a0b Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 18:34:49 -0500 Subject: [PATCH 094/210] rename fn --- pallets/subtensor/src/coinbase/run_coinbase.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index ef15d8a425..d0f6a1ac09 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -53,13 +53,12 @@ impl Pallet { Self::distribute_emissions_to_subnets(&emissions_to_distribute); } - pub fn inject_and_subsidize( + pub fn inject_and_maybe_swap( subnets_to_emit_to: &[NetUid], tao_in: &BTreeMap, alpha_in: &BTreeMap, excess_tao: &BTreeMap, ) { - // --- 2. Inject and subsidize for netuid_i in subnets_to_emit_to.iter() { let tao_in_i: TaoCurrency = tou64!(*tao_in.get(netuid_i).unwrap_or(&asfloat!(0))).into(); @@ -190,8 +189,8 @@ impl Pallet { log::debug!("alpha_out: {alpha_out:?}"); log::debug!("subsidy_amount: {subsidy_amount:?}"); - // --- 2. Inject TAO and ALPHA to pool and subsidize. - Self::inject_and_subsidize(subnets_to_emit_to, &tao_in, &alpha_in, &subsidy_amount); + // --- 2. Inject TAO and ALPHA to pool and swap with excess TAO. + Self::inject_and_maybe_swap(subnets_to_emit_to, &tao_in, &alpha_in, &excess_amount); // --- 3. Inject ALPHA for participants. let cut_percent: U96F32 = Self::get_float_subnet_owner_cut(); From ba0fae0332fd63a92c02a8e4a9f675a7df70ce44 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 18:35:09 -0500 Subject: [PATCH 095/210] rename sub -> excess --- pallets/subtensor/src/coinbase/run_coinbase.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index d0f6a1ac09..fda0210549 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -123,7 +123,7 @@ impl Pallet { let mut tao_in: BTreeMap = BTreeMap::new(); let mut alpha_in: BTreeMap = BTreeMap::new(); let mut alpha_out: BTreeMap = BTreeMap::new(); - let mut subsidy_amount: BTreeMap = BTreeMap::new(); + let mut excess_amount: BTreeMap = BTreeMap::new(); // Only calculate for subnets that we are emitting to. for (&netuid_i, &tao_emission_i) in subnet_emissions.iter() { @@ -162,8 +162,8 @@ impl Pallet { tao_in_i = alpha_in_i.saturating_mul(price_i); } - let subsidy_tao: U96F32 = tao_emission_i.saturating_sub(tao_in_i); - subsidy_amount.insert(netuid_i, subsidy_tao); + let excess_tao: U96F32 = tao_emission_i.saturating_sub(tao_in_i); + excess_amount.insert(netuid_i, excess_tao); } // Insert values into maps @@ -171,7 +171,7 @@ impl Pallet { alpha_in.insert(netuid_i, alpha_in_i); alpha_out.insert(netuid_i, alpha_out_i); } - (tao_in, alpha_in, alpha_out, subsidy_amount) + (tao_in, alpha_in, alpha_out, excess_amount) } pub fn emit_to_subnets( @@ -180,14 +180,13 @@ impl Pallet { root_sell_flag: bool, ) { // --- 1. Get subnet terms (tao_in, alpha_in, and alpha_out) - // and subsidy amount. - let (tao_in, alpha_in, alpha_out, subsidy_amount) = - Self::get_subnet_terms(subnet_emissions); + // and excess_tao amounts. + let (tao_in, alpha_in, alpha_out, excess_amount) = Self::get_subnet_terms(subnet_emissions); log::debug!("tao_in: {tao_in:?}"); log::debug!("alpha_in: {alpha_in:?}"); log::debug!("alpha_out: {alpha_out:?}"); - log::debug!("subsidy_amount: {subsidy_amount:?}"); + log::debug!("excess_amount: {excess_amount:?}"); // --- 2. Inject TAO and ALPHA to pool and swap with excess TAO. Self::inject_and_maybe_swap(subnets_to_emit_to, &tao_in, &alpha_in, &excess_amount); From 8e6286c20dc49c7419ed4c5b8d641abe709f764f Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 18:49:11 -0500 Subject: [PATCH 096/210] remove ref to subs --- pallets/subtensor/src/tests/coinbase.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 36e8a2b783..b1546d219d 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -3303,8 +3303,7 @@ fn test_coinbase_subnets_with_no_reg_get_no_emission() { (netuid1, U96F32::saturating_from_num(1)), ]); - let (tao_in, alpha_in, alpha_out, subsidy_amount) = - SubtensorModule::get_subnet_terms(&subnet_emissions); + let (tao_in, alpha_in, alpha_out, _) = SubtensorModule::get_subnet_terms(&subnet_emissions); assert_eq!(tao_in.len(), 2); assert_eq!(alpha_in.len(), 2); assert_eq!(alpha_out.len(), 2); @@ -3321,7 +3320,7 @@ fn test_coinbase_subnets_with_no_reg_get_no_emission() { NetworkRegistrationAllowed::::insert(netuid0, false); NetworkPowRegistrationAllowed::::insert(netuid0, false); - let (tao_in_2, alpha_in_2, alpha_out_2, subsidy_amount_2) = + let (tao_in_2, alpha_in_2, alpha_out_2, _) = SubtensorModule::get_subnet_terms(&subnet_emissions); assert_eq!(tao_in_2.len(), 2); assert_eq!(alpha_in_2.len(), 2); From c38aa09dcf67330afd116d752f1c30092bf3ead9 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 18:49:41 -0500 Subject: [PATCH 097/210] remove dup fn --- pallets/subtensor/src/coinbase/run_coinbase.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index a03992347f..b82a0b0bf0 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -255,16 +255,6 @@ impl Pallet { } } - pub fn get_network_subsidy_mode(subnets_to_emit_to: &[NetUid]) -> bool { - let total_ema_price: U96F32 = subnets_to_emit_to - .iter() - .map(|netuid| Self::get_moving_alpha_price(*netuid)) - .sum(); - - // If the total EMA price is less than or equal to 1, then we subsidize the network. - total_ema_price <= U96F32::saturating_from_num(1) - } - pub fn drain_pending( subnets: &[NetUid], current_block: u64, From 61227c8a21e9e17f08f9a4cc050533d4b17e257a Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 18:49:51 -0500 Subject: [PATCH 098/210] rename vars --- pallets/subtensor/src/coinbase/run_coinbase.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index b82a0b0bf0..4c0ba98974 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -123,7 +123,7 @@ impl Pallet { let mut tao_in: BTreeMap = BTreeMap::new(); let mut alpha_in: BTreeMap = BTreeMap::new(); let mut alpha_out: BTreeMap = BTreeMap::new(); - let mut excess_amount: BTreeMap = BTreeMap::new(); + let mut excess_tao: BTreeMap = BTreeMap::new(); // Only calculate for subnets that we are emitting to. for (&netuid_i, &tao_emission_i) in subnet_emissions.iter() { @@ -162,8 +162,8 @@ impl Pallet { tao_in_i = alpha_in_i.saturating_mul(price_i); } - let excess_tao: U96F32 = tao_emission_i.saturating_sub(tao_in_i); - excess_amount.insert(netuid_i, excess_tao); + let excess_amount: U96F32 = tao_emission_i.saturating_sub(tao_in_i); + excess_tao.insert(netuid_i, excess_amount); } // Insert values into maps @@ -171,7 +171,7 @@ impl Pallet { alpha_in.insert(netuid_i, alpha_in_i); alpha_out.insert(netuid_i, alpha_out_i); } - (tao_in, alpha_in, alpha_out, excess_amount) + (tao_in, alpha_in, alpha_out, excess_tao) } pub fn emit_to_subnets( From c2598a9296b921e11b3a705168656ed60fd0228c Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 20:10:26 -0500 Subject: [PATCH 099/210] add test for injection order --- pallets/subtensor/src/tests/coinbase.rs | 39 +++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index b1546d219d..6b4f83746c 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -3412,3 +3412,42 @@ fn test_coinbase_alpha_in_more_than_alpha_emission() { ); }); } + +// Tests for the excess TAO condition +#[test] +fn test_coinbase_inject_and_maybe_swap_does_not_skew_reserves() { + new_test_ext(1).execute_with(|| { + let zero = U96F32::saturating_from_num(0); + let netuid0 = add_dynamic_network(&U256::from(1), &U256::from(2)); + mock::setup_reserves( + netuid0, + TaoCurrency::from(1_000_000_000_000_000), + AlphaCurrency::from(1_000_000_000_000_000), + ); + // Initialize swap v3 + Swap::maybe_initialize_v3(netuid0); + + let tao_in = BTreeMap::from([(netuid0, U96F32::saturating_from_num(123))]); + let alpha_in = BTreeMap::from([(netuid0, U96F32::saturating_from_num(456))]); + let excess_tao = BTreeMap::from([(netuid0, U96F32::saturating_from_num(789100))]); + + // Run the inject and maybe swap + SubtensorModule::inject_and_maybe_swap(&[netuid0], &tao_in, &alpha_in, &excess_tao); + + let tao_in_after = SubnetTAO::::get(netuid0); + let alpha_in_after = SubnetAlphaIn::::get(netuid0); + + // Make sure that when we inject and swap, we do it in the right order. + // Thereby not skewing the ratio away from the price. + let ratio_after: U96F32 = U96F32::saturating_from_num(alpha_in_after.to_u64()) + .saturating_div(U96F32::saturating_from_num(tao_in_after.to_u64())); + let price_after: U96F32 = U96F32::saturating_from_num( + pallet_subtensor_swap::Pallet::::current_alpha_price(netuid0).to_num::(), + ); + assert_abs_diff_eq!( + ratio_after.to_num::(), + price_after.to_num::(), + epsilon = 1.0 + ); + }); +} From 09a2cac9d15b55dfccd8d25f2e73b7f7c2ce80ff Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 20:12:28 -0500 Subject: [PATCH 100/210] add comment to test --- pallets/subtensor/src/tests/coinbase.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 6b4f83746c..c50ecb1276 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -3429,6 +3429,7 @@ fn test_coinbase_inject_and_maybe_swap_does_not_skew_reserves() { let tao_in = BTreeMap::from([(netuid0, U96F32::saturating_from_num(123))]); let alpha_in = BTreeMap::from([(netuid0, U96F32::saturating_from_num(456))]); + // We have excess TAO, so we will be swapping with it. let excess_tao = BTreeMap::from([(netuid0, U96F32::saturating_from_num(789100))]); // Run the inject and maybe swap From 3b9c11e20b846fd197c1cec3af619d3cecb9f148 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 20:46:37 -0500 Subject: [PATCH 101/210] add tests for drain pending --- pallets/subtensor/src/tests/coinbase.rs | 89 ++++++++++++++++++++++++- 1 file changed, 88 insertions(+), 1 deletion(-) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index c50ecb1276..52a2b5f482 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -3413,7 +3413,7 @@ fn test_coinbase_alpha_in_more_than_alpha_emission() { }); } -// Tests for the excess TAO condition +// Tests for the inject and swap are in the right order. #[test] fn test_coinbase_inject_and_maybe_swap_does_not_skew_reserves() { new_test_ext(1).execute_with(|| { @@ -3452,3 +3452,90 @@ fn test_coinbase_inject_and_maybe_swap_does_not_skew_reserves() { ); }); } + +#[test] +fn test_coinbase_drain_pending_increments_blockssincelaststep() { + new_test_ext(1).execute_with(|| { + let zero = U96F32::saturating_from_num(0); + let netuid0 = add_dynamic_network(&U256::from(1), &U256::from(2)); + + let blocks_since_last_step_before = BlocksSinceLastStep::::get(netuid0); + + // Check that blockssincelaststep is incremented + SubtensorModule::drain_pending(&[netuid0], 1); + + let blocks_since_last_step_after = BlocksSinceLastStep::::get(netuid0); + assert!(blocks_since_last_step_after > blocks_since_last_step_before); + assert_eq!( + blocks_since_last_step_after, + blocks_since_last_step_before + 1 + ); + }); +} + +#[test] +fn test_coinbase_drain_pending_resets_blockssincelaststep() { + new_test_ext(1).execute_with(|| { + let zero = U96F32::saturating_from_num(0); + let netuid0 = add_dynamic_network(&U256::from(1), &U256::from(2)); + Tempo::::insert(netuid0, 100); + // Ensure the block number we use is the tempo block + let block_number = 98; + assert!(SubtensorModule::should_run_epoch(netuid0, block_number)); + + let blocks_since_last_step_before = 12345678; + BlocksSinceLastStep::::insert(netuid0, blocks_since_last_step_before); + LastMechansimStepBlock::::insert(netuid0, 12345); // garbage value + + // Check that blockssincelaststep is reset to 0 on tempo + SubtensorModule::drain_pending(&[netuid0], block_number); + + let blocks_since_last_step_after = BlocksSinceLastStep::::get(netuid0); + assert_eq!(blocks_since_last_step_after, 0); + // Also check LastMechansimStepBlock is set to the block number we ran on + assert_eq!(LastMechansimStepBlock::::get(netuid0), block_number); + }); +} + +#[test] +fn test_coinbase_drain_pending_gets_counters_and_resets_them() { + new_test_ext(1).execute_with(|| { + let zero = U96F32::saturating_from_num(0); + let netuid0 = add_dynamic_network(&U256::from(1), &U256::from(2)); + Tempo::::insert(netuid0, 100); + // Ensure the block number we use is the tempo block + let block_number = 98; + assert!(SubtensorModule::should_run_epoch(netuid0, block_number)); + + mock::setup_reserves( + netuid0, + TaoCurrency::from(1_000_000_000_000_000), + AlphaCurrency::from(1_000_000_000_000_000), + ); + // Initialize swap v3 + Swap::maybe_initialize_v3(netuid0); + + let pending_em = AlphaCurrency::from(123434534); + let pending_root = AlphaCurrency::from(12222222); + let pending_owner_cut = AlphaCurrency::from(12345678); + + PendingEmission::::insert(netuid0, pending_em); + PendingRootAlphaDivs::::insert(netuid0, pending_root); + PendingOwnerCut::::insert(netuid0, pending_owner_cut); + + let emissions_to_distribute = SubtensorModule::drain_pending(&[netuid0], block_number); + assert_eq!(emissions_to_distribute.len(), 1); + assert_eq!( + emissions_to_distribute[&netuid0], + (pending_em, pending_root, pending_owner_cut) + ); + + // Check that the pending emissions are reset + assert_eq!(PendingEmission::::get(netuid0), AlphaCurrency::ZERO); + assert_eq!( + PendingRootAlphaDivs::::get(netuid0), + AlphaCurrency::ZERO + ); + assert_eq!(PendingOwnerCut::::get(netuid0), AlphaCurrency::ZERO); + }); +} From 1b540d9727808737b25a4b0e50796da09c201ab0 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 20:54:31 -0500 Subject: [PATCH 102/210] rename test --- pallets/subtensor/src/tests/coinbase.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 52a2b5f482..07ffdf09cf 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -3338,7 +3338,7 @@ fn test_coinbase_subnets_with_no_reg_get_no_emission() { // Tests for the excess TAO condition #[test] -fn test_coinbase_alpha_in_more_than_alpha_emission() { +fn test_coinbase_subnet_terms_with_alpha_in_more_than_alpha_emission() { new_test_ext(1).execute_with(|| { let zero = U96F32::saturating_from_num(0); let netuid0 = add_dynamic_network(&U256::from(1), &U256::from(2)); From d3d5819a4e413f3b2dace869ac9522f914545224 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 20:59:50 -0500 Subject: [PATCH 103/210] rename tests --- pallets/subtensor/src/tests/coinbase.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 07ffdf09cf..d90b7fee5f 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -3292,7 +3292,7 @@ fn test_mining_emission_distribution_with_root_sell() { } #[test] -fn test_coinbase_subnets_with_no_reg_get_no_emission() { +fn test_coinbase_subnet_terms_with_no_reg_get_no_emission() { new_test_ext(1).execute_with(|| { let zero = U96F32::saturating_from_num(0); let netuid0 = add_dynamic_network(&U256::from(1), &U256::from(2)); @@ -3338,7 +3338,7 @@ fn test_coinbase_subnets_with_no_reg_get_no_emission() { // Tests for the excess TAO condition #[test] -fn test_coinbase_subnet_terms_with_alpha_in_more_than_alpha_emission() { +fn test_coinbase_subnet_terms_with_alpha_in_gt_alpha_emission() { new_test_ext(1).execute_with(|| { let zero = U96F32::saturating_from_num(0); let netuid0 = add_dynamic_network(&U256::from(1), &U256::from(2)); From f1c04761291dbd8f3c0e0d7ebbe93caf97a61292 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 21:01:46 -0500 Subject: [PATCH 104/210] lower eps --- pallets/subtensor/src/tests/coinbase.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index d90b7fee5f..39173c8aee 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -3386,14 +3386,14 @@ fn test_coinbase_subnet_terms_with_alpha_in_gt_alpha_emission() { assert_abs_diff_eq!( alpha_out[&netuid0].to_num::(), alpha_emission.to_num::(), - epsilon = 1.0 + epsilon = 0.01 ); // alpha_in should equal the alpha_emission assert_abs_diff_eq!( alpha_in[&netuid0].to_num::(), alpha_emission.to_num::(), - epsilon = 1.0 + epsilon = 0.01 ); // tao_in should be the alpha_in at the ratio of the price assert_abs_diff_eq!( @@ -3401,14 +3401,14 @@ fn test_coinbase_subnet_terms_with_alpha_in_gt_alpha_emission() { alpha_in[&netuid0] .saturating_mul(price_to_set_fixed) .to_num::(), - epsilon = 1.0 + epsilon = 0.01 ); // excess_tao should be the difference between the tao_emission and the tao_in assert_abs_diff_eq!( excess_tao[&netuid0].to_num::(), tao_emission.to_num::() - tao_in[&netuid0].to_num::(), - epsilon = 1.0 + epsilon = 0.01 ); }); } From 9de47edd62018e4621beedf237ce1800956ecc4a Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 21:02:00 -0500 Subject: [PATCH 105/210] add another subnetterms test --- pallets/subtensor/src/tests/coinbase.rs | 62 +++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 39173c8aee..50769262da 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -3413,6 +3413,68 @@ fn test_coinbase_subnet_terms_with_alpha_in_gt_alpha_emission() { }); } +#[test] +fn test_coinbase_subnet_terms_with_alpha_in_lte_alpha_emission() { + new_test_ext(1).execute_with(|| { + let zero = U96F32::saturating_from_num(0); + let netuid0 = add_dynamic_network(&U256::from(1), &U256::from(2)); + mock::setup_reserves( + netuid0, + TaoCurrency::from(1_000_000_000_000_000), + AlphaCurrency::from(1_000_000_000_000_000), + ); + // Initialize swap v3 + Swap::maybe_initialize_v3(netuid0); + + let alpha_emission = U96F32::saturating_from_num( + SubtensorModule::get_block_emission_for_issuance( + SubtensorModule::get_alpha_issuance(netuid0).into(), + ) + .unwrap_or(0), + ); + let tao_emission = U96F32::saturating_from_num(34566756_u64); + + let price: U96F32 = Swap::current_alpha_price(netuid0); + + let subnet_emissions = BTreeMap::from([(netuid0, tao_emission)]); + + let (tao_in, alpha_in, alpha_out, excess_tao) = + SubtensorModule::get_subnet_terms(&subnet_emissions); + + // Check our condition is met + assert!(tao_emission / price <= alpha_emission); + + // alpha_out should be the alpha_emission, always + assert_abs_diff_eq!( + alpha_out[&netuid0].to_num::(), + alpha_emission.to_num::(), + epsilon = 0.1 + ); + + // assuming alpha_in < alpha_emission + // Then alpha_in should be tao_emission / price + assert_abs_diff_eq!( + alpha_in[&netuid0].to_num::(), + tao_emission.to_num::() / price.to_num::(), + epsilon = 0.01 + ); + + // tao_in should be the tao_emission + assert_abs_diff_eq!( + tao_in[&netuid0].to_num::(), + tao_emission.to_num::(), + epsilon = 0.01 + ); + + // excess_tao should be 0 + assert_abs_diff_eq!( + excess_tao[&netuid0].to_num::(), + tao_emission.to_num::() - tao_in[&netuid0].to_num::(), + epsilon = 0.01 + ); + }); +} + // Tests for the inject and swap are in the right order. #[test] fn test_coinbase_inject_and_maybe_swap_does_not_skew_reserves() { From 01f8f6135e273839f240572b56bf3cc211aa8d3b Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 21:04:30 -0500 Subject: [PATCH 106/210] remove unneeded test setup --- pallets/subtensor/src/tests/coinbase.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 50769262da..726c7c608e 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -3569,14 +3569,6 @@ fn test_coinbase_drain_pending_gets_counters_and_resets_them() { let block_number = 98; assert!(SubtensorModule::should_run_epoch(netuid0, block_number)); - mock::setup_reserves( - netuid0, - TaoCurrency::from(1_000_000_000_000_000), - AlphaCurrency::from(1_000_000_000_000_000), - ); - // Initialize swap v3 - Swap::maybe_initialize_v3(netuid0); - let pending_em = AlphaCurrency::from(123434534); let pending_root = AlphaCurrency::from(12222222); let pending_owner_cut = AlphaCurrency::from(12345678); From 6a76ecc0d576c13f32189393360f2d95fb4a78f2 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 6 Nov 2025 21:55:56 -0500 Subject: [PATCH 107/210] hotfix: epoch w/subsidy fix (#2187) * impl tests benchmarks * bump spec * remove duplicate remove * use alpha out here * Hotfix/vune/epoch sub fix (#2186) * fix: injection logic * fix: fmt' * emit full alpha if subsidized * fix coinbase test * fix tests * Revert "Hotfix/vune/epoch sub fix (#2186)" This reverts commit ccc6bba7bdfa7501a461d0adfef80dd6488f9dfb. * add total issuance bump * no alpha out thing * add tests for emissions w/ subs * bump spec * Fix clippy warnings * Hotfix/vune/epoch sub fix (#2186) * fix: injection logic * fix: fmt' * rename drain_pending to distr em * remove arg and total internally * test pending em * make sure we check subsidize during those tests * fix root claim tests * helper and clippy * bump spec * rename subsidy to root flag * only pull these values if root selling * rename subsidy checks to the root_sell flag * rename subsidy to excess_tao * use recycle alpha helper * Hotfix/vune/subnet-dereg-burn-use-issuance (#2190) * use alpha issuance for owner estimate * fix tests * use price for estimate only * fix tests for using just price * docs: comment fix * remove log out --------- Co-authored-by: 0xcacti <97139981+0xcacti@users.noreply.github.com> Co-authored-by: Shamil Gadelshin --- pallets/subtensor/src/benchmarks.rs | 2 +- pallets/subtensor/src/coinbase/root.rs | 1 - .../subtensor/src/coinbase/run_coinbase.rs | 145 +++--- .../src/coinbase/subnet_emissions.rs | 23 +- pallets/subtensor/src/staking/remove_stake.rs | 34 +- pallets/subtensor/src/tests/children.rs | 2 +- pallets/subtensor/src/tests/claim_root.rs | 77 ++- pallets/subtensor/src/tests/coinbase.rs | 440 ++++++++++++++++-- pallets/subtensor/src/tests/networks.rs | 132 +++--- runtime/src/lib.rs | 2 +- 10 files changed, 650 insertions(+), 208 deletions(-) diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index 2a13a18aa0..1e7d6991c1 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -1615,7 +1615,7 @@ mod pallet_benchmarks { ); let pending_root_alpha = 10_000_000u64; - Subtensor::::drain_pending_emission( + Subtensor::::distribute_emission( netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), diff --git a/pallets/subtensor/src/coinbase/root.rs b/pallets/subtensor/src/coinbase/root.rs index 642a7f18ac..80965f0bbb 100644 --- a/pallets/subtensor/src/coinbase/root.rs +++ b/pallets/subtensor/src/coinbase/root.rs @@ -348,7 +348,6 @@ impl Pallet { RAORecycledForRegistration::::remove(netuid); MaxRegistrationsPerBlock::::remove(netuid); WeightsVersionKey::::remove(netuid); - PendingRootAlphaDivs::::remove(netuid); // --- 17. Subtoken / feature flags. LiquidAlphaOn::::remove(netuid); diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 53ac3c6ac5..d998ad1f7d 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -34,13 +34,15 @@ impl Pallet { // 2. Get subnets to emit to and emissions let subnet_emissions = Self::get_subnet_block_emissions(&subnets, block_emission); let subnets_to_emit_to: Vec = subnet_emissions.keys().copied().collect(); + let root_sell_flag = Self::get_network_root_sell_flag(&subnets_to_emit_to); // --- 3. Get subnet terms (tao_in, alpha_in, and alpha_out) // Computation is described in detail in the dtao whitepaper. let mut tao_in: BTreeMap = BTreeMap::new(); let mut alpha_in: BTreeMap = BTreeMap::new(); let mut alpha_out: BTreeMap = BTreeMap::new(); - let mut is_subsidized: BTreeMap = BTreeMap::new(); + let mut excess_tao: BTreeMap = BTreeMap::new(); + // Only calculate for subnets that we are emitting to. for netuid_i in subnets_to_emit_to.iter() { // Get subnet price. @@ -52,6 +54,9 @@ impl Pallet { .copied() .unwrap_or(asfloat!(0)); log::debug!("default_tao_in_i: {default_tao_in_i:?}"); + let default_alpha_in_i: U96F32 = + default_tao_in_i.safe_div_or(price_i, U96F32::saturating_from_num(0.0)); + log::debug!("default_alpha_in_i: {default_alpha_in_i:?}"); // Get alpha_emission total let alpha_emission_i: U96F32 = asfloat!( Self::get_block_emission_for_issuance(Self::get_alpha_issuance(*netuid_i).into()) @@ -62,32 +67,16 @@ impl Pallet { // Get initial alpha_in let mut alpha_in_i: U96F32; let mut tao_in_i: U96F32; - let tao_in_ratio: U96F32 = default_tao_in_i.safe_div_or( - U96F32::saturating_from_num(block_emission), - U96F32::saturating_from_num(0.0), - ); - if price_i < tao_in_ratio { - tao_in_i = price_i.saturating_mul(U96F32::saturating_from_num(block_emission)); - alpha_in_i = block_emission; + + if default_alpha_in_i > alpha_emission_i { + alpha_in_i = alpha_emission_i; + tao_in_i = alpha_in_i.saturating_mul(price_i); let difference_tao: U96F32 = default_tao_in_i.saturating_sub(tao_in_i); - // Difference becomes buy. - let buy_swap_result = Self::swap_tao_for_alpha( - *netuid_i, - tou64!(difference_tao).into(), - T::SwapInterface::max_price(), - true, - ); - if let Ok(buy_swap_result_ok) = buy_swap_result { - let bought_alpha = AlphaCurrency::from(buy_swap_result_ok.amount_paid_out); - SubnetAlphaOut::::mutate(*netuid_i, |total| { - *total = total.saturating_sub(bought_alpha); - }); - } - is_subsidized.insert(*netuid_i, true); + excess_tao.insert(*netuid_i, difference_tao); } else { tao_in_i = default_tao_in_i; - alpha_in_i = tao_in_i.safe_div_or(price_i, alpha_emission_i); - is_subsidized.insert(*netuid_i, false); + alpha_in_i = default_alpha_in_i; + excess_tao.insert(*netuid_i, U96F32::from_num(0.0)); } log::debug!("alpha_in_i: {alpha_in_i:?}"); @@ -109,10 +98,34 @@ impl Pallet { log::debug!("tao_in: {tao_in:?}"); log::debug!("alpha_in: {alpha_in:?}"); log::debug!("alpha_out: {alpha_out:?}"); + log::debug!("excess_tao: {excess_tao:?}"); + log::debug!("root_sell_flag: {root_sell_flag:?}"); + + // --- 4. Inject and buy Alpha with any excess TAO. + for netuid_i in subnets_to_emit_to.iter() { + let tao_in_i: TaoCurrency = + tou64!(*tao_in.get(netuid_i).unwrap_or(&asfloat!(0))).into(); + let alpha_in_i: AlphaCurrency = + AlphaCurrency::from(tou64!(*alpha_in.get(netuid_i).unwrap_or(&asfloat!(0)))); + let difference_tao: U96F32 = *excess_tao.get(netuid_i).unwrap_or(&asfloat!(0)); + + T::SwapInterface::adjust_protocol_liquidity(*netuid_i, tao_in_i, alpha_in_i); + + if difference_tao > asfloat!(0) { + let buy_swap_result = Self::swap_tao_for_alpha( + *netuid_i, + tou64!(difference_tao).into(), + T::SwapInterface::max_price(), + true, + ); + if let Ok(buy_swap_result_ok) = buy_swap_result { + let bought_alpha = AlphaCurrency::from(buy_swap_result_ok.amount_paid_out); + Self::recycle_subnet_alpha(*netuid_i, bought_alpha); + } + } + } - // --- 4. Injection. - // Actually perform the injection of alpha_in, alpha_out and tao_in into the subnet pool. - // This operation changes the pool liquidity each block. + // --- 5. Update counters for netuid_i in subnets_to_emit_to.iter() { // Inject Alpha in. let alpha_in_i = @@ -138,14 +151,15 @@ impl Pallet { TotalStake::::mutate(|total| { *total = total.saturating_add(tao_in_i.into()); }); + + let difference_tao: U96F32 = *excess_tao.get(netuid_i).unwrap_or(&asfloat!(0)); TotalIssuance::::mutate(|total| { *total = total.saturating_add(tao_in_i.into()); + *total = total.saturating_add(tou64!(difference_tao).into()); }); - // Adjust protocol liquidity based on new reserves - T::SwapInterface::adjust_protocol_liquidity(*netuid_i, tao_in_i, alpha_in_i); } - // --- 5. Compute owner cuts and remove them from alpha_out remaining. + // --- 6. Compute owner cuts and remove them from alpha_out remaining. // Remove owner cuts here so that we can properly seperate root dividends in the next step. // Owner cuts are accumulated and then fed to the drain at the end of this func. let cut_percent: U96F32 = Self::get_float_subnet_owner_cut(); @@ -174,51 +188,55 @@ impl Pallet { let tao_weight: U96F32 = root_tao.saturating_mul(Self::get_tao_weight()); log::debug!("tao_weight: {tao_weight:?}"); - // --- 6. Seperate out root dividends in alpha and keep them. + // --- 7. Seperate out root dividends in alpha and keep them. // Then accumulate those dividends for later. for netuid_i in subnets_to_emit_to.iter() { // Get remaining alpha out. let alpha_out_i: U96F32 = *alpha_out.get(netuid_i).unwrap_or(&asfloat!(0.0)); log::debug!("alpha_out_i: {alpha_out_i:?}"); - // Get total ALPHA on subnet. - let alpha_issuance: U96F32 = asfloat!(Self::get_alpha_issuance(*netuid_i)); - log::debug!("alpha_issuance: {alpha_issuance:?}"); - // Get root proportional dividends. - let root_proportion: U96F32 = tao_weight - .checked_div(tao_weight.saturating_add(alpha_issuance)) - .unwrap_or(asfloat!(0.0)); - log::debug!("root_proportion: {root_proportion:?}"); + // Get root proportion of alpha_out dividends. - let root_alpha: U96F32 = root_proportion - .saturating_mul(alpha_out_i) // Total alpha emission per block remaining. - .saturating_mul(asfloat!(0.5)); // 50% to validators. + let mut root_alpha: U96F32 = asfloat!(0.0); + if root_sell_flag { + // Get ALPHA issuance. + let alpha_issuance: U96F32 = asfloat!(Self::get_alpha_issuance(*netuid_i)); + log::debug!("alpha_issuance: {alpha_issuance:?}"); + + // Get root proportional dividends. + let root_proportion: U96F32 = tao_weight + .checked_div(tao_weight.saturating_add(alpha_issuance)) + .unwrap_or(asfloat!(0.0)); + log::debug!("root_proportion: {root_proportion:?}"); + + // Get root alpha from root prop. + root_alpha = root_proportion + .saturating_mul(alpha_out_i) // Total alpha emission per block remaining. + .saturating_mul(asfloat!(0.5)); // 50% to validators. + PendingRootAlphaDivs::::mutate(*netuid_i, |total| { + *total = total.saturating_add(tou64!(root_alpha).into()); + }); + } // Remove root alpha from alpha_out. log::debug!("root_alpha: {root_alpha:?}"); + // Get pending alpha as original alpha_out - root_alpha. let pending_alpha: U96F32 = alpha_out_i.saturating_sub(root_alpha); log::debug!("pending_alpha: {pending_alpha:?}"); - let subsidized: bool = *is_subsidized.get(netuid_i).unwrap_or(&false); - if !subsidized { - PendingRootAlphaDivs::::mutate(*netuid_i, |total| { - *total = total.saturating_add(tou64!(root_alpha).into()); - }); - } - // Accumulate alpha emission in pending. PendingEmission::::mutate(*netuid_i, |total| { *total = total.saturating_add(tou64!(pending_alpha).into()); }); } - // --- 7. Update moving prices after using them in the emission calculation. + // --- 8. Update moving prices after using them in the emission calculation. // Only update price EMA for subnets that we emit to. for netuid_i in subnets_to_emit_to.iter() { // Update moving prices after using them above. Self::update_moving_price(*netuid_i); } - // --- 8. Drain pending emission through the subnet based on tempo. + // --- 9. Drain pending emission through the subnet based on tempo. // Run the epoch for *all* subnets, even if we don't emit anything. for &netuid in subnets.iter() { // Reveal matured weights. @@ -245,8 +263,8 @@ impl Pallet { let owner_cut = PendingOwnerCut::::get(netuid); PendingOwnerCut::::insert(netuid, AlphaCurrency::ZERO); - // Drain pending root alpha divs, alpha emission, and owner cut. - Self::drain_pending_emission(netuid, pending_alpha, pending_root_alpha, owner_cut); + // Distribute the emission. + Self::distribute_emission(netuid, pending_alpha, pending_root_alpha, owner_cut); } else { // Increment BlocksSinceLastStep::::mutate(netuid, |total| *total = total.saturating_add(1)); @@ -254,6 +272,17 @@ impl Pallet { } } + pub fn get_network_root_sell_flag(subnets_to_emit_to: &[NetUid]) -> bool { + let total_ema_price: U96F32 = subnets_to_emit_to + .iter() + .map(|netuid| Self::get_moving_alpha_price(*netuid)) + .sum(); + + // If the total EMA price is less than or equal to 1 + // then we WILL NOT root sell. + total_ema_price > U96F32::saturating_from_num(1) + } + pub fn calculate_dividends_and_incentives( netuid: NetUid, hotkey_emission: Vec<(T::AccountId, AlphaCurrency, AlphaCurrency)>, @@ -482,6 +511,7 @@ impl Pallet { let destination = maybe_dest.clone().unwrap_or(hotkey.clone()); if let Some(dest) = maybe_dest { + log::debug!("incentives: auto staking {incentive:?} to {dest:?}"); Self::deposit_event(Event::::AutoStakeAdded { netuid, destination: dest, @@ -490,6 +520,7 @@ impl Pallet { incentive, }); } + Self::increase_stake_for_hotkey_and_coldkey_on_subnet( &destination, &owner, @@ -600,7 +631,7 @@ impl Pallet { (incentives, (alpha_dividends, root_alpha_dividends)) } - pub fn drain_pending_emission( + pub fn distribute_emission( netuid: NetUid, pending_alpha: AlphaCurrency, pending_root_alpha: AlphaCurrency, @@ -611,10 +642,11 @@ impl Pallet { ); let tao_weight = Self::get_tao_weight(); + let total_alpha = pending_alpha.saturating_add(pending_root_alpha); // Run the epoch. let hotkey_emission: Vec<(T::AccountId, AlphaCurrency, AlphaCurrency)> = - Self::epoch_with_mechanisms(netuid, pending_alpha.saturating_add(pending_root_alpha)); + Self::epoch_with_mechanisms(netuid, total_alpha); log::debug!("hotkey_emission: {hotkey_emission:?}"); // Compute the pending validator alpha. @@ -630,8 +662,7 @@ impl Pallet { log::debug!("incentive_sum: {incentive_sum:?}"); let pending_validator_alpha = if !incentive_sum.is_zero() { - pending_alpha - .saturating_add(pending_root_alpha) + total_alpha .saturating_div(2.into()) .saturating_sub(pending_root_alpha) } else { diff --git a/pallets/subtensor/src/coinbase/subnet_emissions.rs b/pallets/subtensor/src/coinbase/subnet_emissions.rs index 184f27a4ba..895a908ba8 100644 --- a/pallets/subtensor/src/coinbase/subnet_emissions.rs +++ b/pallets/subtensor/src/coinbase/subnet_emissions.rs @@ -1,5 +1,4 @@ use super::*; -use crate::alloc::borrow::ToOwned; use alloc::collections::BTreeMap; use safe_math::FixedExt; use substrate_fixed::transcendental::{exp, ln}; @@ -7,21 +6,21 @@ use substrate_fixed::types::{I32F32, I64F64, U64F64, U96F32}; use subtensor_swap_interface::SwapHandler; impl Pallet { - pub fn get_subnet_block_emissions( - subnets: &[NetUid], - block_emission: U96F32, - ) -> BTreeMap { + pub fn get_subnets_to_emit_to(subnets: &[NetUid]) -> Vec { // Filter out subnets with no first emission block number. - let subnets_to_emit_to: Vec = subnets - .to_owned() - .clone() - .into_iter() + subnets + .iter() .filter(|netuid| FirstEmissionBlockNumber::::get(*netuid).is_some()) - .collect(); - log::debug!("Subnets to emit to: {subnets_to_emit_to:?}"); + .copied() + .collect() + } + pub fn get_subnet_block_emissions( + subnets_to_emit_to: &[NetUid], + block_emission: U96F32, + ) -> BTreeMap { // Get subnet TAO emissions. - let shares = Self::get_shares(&subnets_to_emit_to); + let shares = Self::get_shares(subnets_to_emit_to); log::debug!("Subnet emission shares = {shares:?}"); shares diff --git a/pallets/subtensor/src/staking/remove_stake.rs b/pallets/subtensor/src/staking/remove_stake.rs index 5771029f74..7fdd335556 100644 --- a/pallets/subtensor/src/staking/remove_stake.rs +++ b/pallets/subtensor/src/staking/remove_stake.rs @@ -447,19 +447,13 @@ impl Pallet { let should_refund_owner: bool = reg_at < start_block; // 3) Compute owner's received emission in TAO at current price (ONLY if we may refund). - // Emission:: is Vec. We: - // - sum emitted α, + // We: + // - get the current alpha issuance, // - apply owner fraction to get owner α, // - price that α using a *simulated* AMM swap. let mut owner_emission_tao = TaoCurrency::ZERO; if should_refund_owner && !lock_cost.is_zero() { - let total_emitted_alpha_u128: u128 = - Emission::::get(netuid) - .into_iter() - .fold(0u128, |acc, e_alpha| { - let e_u64: u64 = Into::::into(e_alpha); - acc.saturating_add(e_u64 as u128) - }); + let total_emitted_alpha_u128: u128 = Self::get_alpha_issuance(netuid).to_u64() as u128; if total_emitted_alpha_u128 > 0 { let owner_fraction: U96F32 = Self::get_float_subnet_owner_cut(); @@ -469,22 +463,12 @@ impl Pallet { .saturating_to_num::(); owner_emission_tao = if owner_alpha_u64 > 0 { - let order = GetTaoForAlpha::with_amount(owner_alpha_u64); - match T::SwapInterface::sim_swap(netuid.into(), order) { - Ok(sim) => TaoCurrency::from(sim.amount_paid_out), - Err(e) => { - log::debug!( - "destroy_alpha_in_out_stakes: sim_swap owner α→τ failed (netuid={netuid:?}, alpha={owner_alpha_u64}, err={e:?}); falling back to price multiply.", - ); - let cur_price: U96F32 = - T::SwapInterface::current_alpha_price(netuid.into()); - let val_u64 = U96F32::from_num(owner_alpha_u64) - .saturating_mul(cur_price) - .floor() - .saturating_to_num::(); - TaoCurrency::from(val_u64) - } - } + let cur_price: U96F32 = T::SwapInterface::current_alpha_price(netuid.into()); + let val_u64 = U96F32::from_num(owner_alpha_u64) + .saturating_mul(cur_price) + .floor() + .saturating_to_num::(); + TaoCurrency::from(val_u64) } else { TaoCurrency::ZERO }; diff --git a/pallets/subtensor/src/tests/children.rs b/pallets/subtensor/src/tests/children.rs index e9b8c2aa6b..14b7a0b29d 100644 --- a/pallets/subtensor/src/tests/children.rs +++ b/pallets/subtensor/src/tests/children.rs @@ -2827,7 +2827,7 @@ fn test_set_weights_no_parent() { }); } -/// Test that drain_pending_emission sends childkey take fully to the nominators if childkey +/// Test that distribute_emission sends childkey take fully to the nominators if childkey /// doesn't have its own stake, independently of parent hotkey take. /// cargo test --package pallet-subtensor --lib -- tests::children::test_childkey_take_drain --exact --show-output #[allow(clippy::assertions_on_constants)] diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index e73417a326..951dfa0ae1 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -6,8 +6,8 @@ use crate::tests::mock::{ use crate::{ DefaultMinRootClaimAmount, Error, MAX_NUM_ROOT_CLAIMS, MAX_ROOT_CLAIM_THRESHOLD, NetworksAdded, NumRootClaim, NumStakingColdkeys, PendingRootAlphaDivs, RootClaimable, RootClaimableThreshold, - StakingColdkeys, StakingColdkeysByIndex, SubnetAlphaIn, SubnetMechanism, SubnetTAO, - SubtokenEnabled, Tempo, pallet, + StakingColdkeys, StakingColdkeysByIndex, SubnetAlphaIn, SubnetMechanism, SubnetMovingPrice, + SubnetTAO, SubtokenEnabled, Tempo, pallet, }; use crate::{RootClaimType, RootClaimTypeEnum, RootClaimed}; use approx::assert_abs_diff_eq; @@ -18,7 +18,7 @@ use frame_support::{assert_err, assert_noop, assert_ok}; use sp_core::{H256, U256}; use sp_runtime::DispatchError; use std::collections::BTreeSet; -use substrate_fixed::types::{I96F32, U96F32}; +use substrate_fixed::types::{I96F32, U64F64, U96F32}; use subtensor_runtime_common::{AlphaCurrency, Currency, NetUid, TaoCurrency}; use subtensor_swap_interface::SwapHandler; @@ -72,7 +72,7 @@ fn test_claim_root_with_drain_emissions() { // Distribute pending root alpha let pending_root_alpha = 1_000_000u64; - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), @@ -140,7 +140,7 @@ fn test_claim_root_with_drain_emissions() { // Distribute pending root alpha (round 2) - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), @@ -241,7 +241,7 @@ fn test_claim_root_adding_stake_proportionally_for_two_stakers() { // Distribute pending root alpha let pending_root_alpha = 10_000_000u64; - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), @@ -342,7 +342,7 @@ fn test_claim_root_adding_stake_disproportionally_for_two_stakers() { // Distribute pending root alpha let pending_root_alpha = 10_000_000u64; - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), @@ -433,7 +433,7 @@ fn test_claim_root_with_changed_stake() { // Distribute pending root alpha let pending_root_alpha = 10_000_000u64; - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), @@ -485,7 +485,7 @@ fn test_claim_root_with_changed_stake() { // Distribute pending root alpha let pending_root_alpha = 10_000_000u64; - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), @@ -538,7 +538,7 @@ fn test_claim_root_with_changed_stake() { // Distribute pending root alpha let pending_root_alpha = 10_000_000u64; - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), @@ -630,7 +630,7 @@ fn test_claim_root_with_drain_emissions_and_swap_claim_type() { // Distribute pending root alpha let pending_root_alpha = 10_000_000u64; - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), @@ -674,7 +674,7 @@ fn test_claim_root_with_drain_emissions_and_swap_claim_type() { // Distribute and claim pending root alpha (round 2) - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), @@ -710,7 +710,7 @@ fn test_claim_root_with_drain_emissions_and_swap_claim_type() { ); // Distribute and claim pending root alpha (round 3) - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), @@ -779,6 +779,18 @@ fn test_claim_root_with_run_coinbase() { initial_total_hotkey_alpha.into(), ); + // Set moving price > 1.0 and price > 1.0 + // So we turn ON root sell + SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); + pallet_subtensor_swap::AlphaSqrtPrice::::insert( + netuid, + U64F64::saturating_from_num(10.0), + ); + + // Make sure we are root selling, so we have root alpha divs. + let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); + assert!(root_sell_flag, "Root sell flag should be true"); + // Distribute pending root alpha let initial_stake: u64 = @@ -878,6 +890,18 @@ fn test_claim_root_with_block_emissions() { ); SubtensorModule::maybe_add_coldkey_index(&coldkey); + // Set moving price > 1.0 and price > 1.0 + // So we turn ON root sell + SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); + pallet_subtensor_swap::AlphaSqrtPrice::::insert( + netuid, + U64F64::saturating_from_num(10.0), + ); + + // Make sure we are root selling, so we have root alpha divs. + let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); + assert!(root_sell_flag, "Root sell flag should be true"); + let initial_total_hotkey_alpha = 10_000_000u64; SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, @@ -991,12 +1015,27 @@ fn test_claim_root_coinbase_distribution() { let initial_alpha_issuance = SubtensorModule::get_alpha_issuance(netuid); let alpha_emissions: AlphaCurrency = 1_000_000_000u64.into(); - // Check total issuance (saved to pending alpha divs) + // Set moving price > 1.0 and price > 1.0 + // So we turn ON root sell + SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); + pallet_subtensor_swap::AlphaSqrtPrice::::insert( + netuid, + U64F64::saturating_from_num(10.0), + ); + + // Make sure we are root selling, so we have root alpha divs. + let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); + assert!(root_sell_flag, "Root sell flag should be true"); + // Check total issuance (saved to pending alpha divs) run_to_block(2); let alpha_issuance = SubtensorModule::get_alpha_issuance(netuid); - assert_eq!(initial_alpha_issuance + alpha_emissions, alpha_issuance); + // We went two blocks so we should have 2x the alpha emissions + assert_eq!( + initial_alpha_issuance + alpha_emissions.saturating_mul(2.into()), + alpha_issuance + ); let root_prop = initial_tao as f64 / (u64::from(alpha_issuance) + initial_tao) as f64; let root_validators_share = 0.5f64; @@ -1096,7 +1135,7 @@ fn test_claim_root_with_swap_coldkey() { // Distribute pending root alpha let pending_root_alpha = 1_000_000u64; - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), @@ -1186,7 +1225,7 @@ fn test_claim_root_with_swap_hotkey() { // Distribute pending root alpha let pending_root_alpha = 1_000_000u64; - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), @@ -1302,7 +1341,7 @@ fn test_claim_root_on_network_deregistration() { // Distribute pending root alpha let pending_root_alpha = 10_000_000u64; - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), @@ -1442,7 +1481,7 @@ fn test_claim_root_with_unrelated_subnets() { // Distribute pending root alpha let pending_root_alpha = 1_000_000u64; - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 60644b2a28..5d03afa973 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -245,7 +245,7 @@ fn test_coinbase_tao_issuance_different_prices() { ); // Prices are low => we limit tao issued (buy alpha with it) - let tao_issued = TaoCurrency::from(((0.1 + 0.2) * emission as f64) as u64); + let tao_issued = TaoCurrency::from(((1.0) * emission as f64) as u64); assert_abs_diff_eq!( TotalIssuance::::get(), tao_issued, @@ -661,15 +661,27 @@ fn test_owner_cut_base() { #[test] fn test_pending_emission() { new_test_ext(1).execute_with(|| { - let netuid = NetUid::from(1); let emission: u64 = 1_000_000; - add_network(netuid, 1, 0); + let hotkey = U256::from(1); + let coldkey = U256::from(2); + let netuid = add_dynamic_network(&hotkey, &coldkey); + Tempo::::insert(netuid, 1); + FirstEmissionBlockNumber::::insert(netuid, 0); + mock::setup_reserves(netuid, 1_000_000.into(), 1.into()); SubtensorModule::run_coinbase(U96F32::from_num(0)); SubnetTAO::::insert(NetUid::ROOT, TaoCurrency::from(1_000_000_000)); // Add root weight. SubtensorModule::run_coinbase(U96F32::from_num(0)); SubtensorModule::set_tempo(netuid, 10000); // Large number (dont drain) SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 + + // Set moving price > 1.0 + SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); + + // Make sure we are root selling, so we have root alpha divs. + let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); + assert!(root_sell_flag, "Root sell flag should be true"); + SubtensorModule::run_coinbase(U96F32::from_num(0)); // 1 TAO / ( 1 + 3 ) = 0.25 * 1 / 2 = 125000000 @@ -678,6 +690,12 @@ fn test_pending_emission() { 1_000_000_000 - 125000000, epsilon = 1 ); // 1 - swapped. + + assert_abs_diff_eq!( + u64::from(PendingRootAlphaDivs::::get(netuid)), + 125000000, + epsilon = 1 + ); // 1 / 2 = 125000000 }); } @@ -685,7 +703,7 @@ fn test_pending_emission() { #[test] fn test_drain_base() { new_test_ext(1).execute_with(|| { - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( 0.into(), AlphaCurrency::ZERO, AlphaCurrency::ZERO, @@ -700,7 +718,7 @@ fn test_drain_base_with_subnet() { new_test_ext(1).execute_with(|| { let netuid = NetUid::from(1); add_network(netuid, 1, 0); - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, AlphaCurrency::ZERO, @@ -725,7 +743,7 @@ fn test_drain_base_with_subnet_with_single_staker_not_registered() { stake_before, ); let pending_alpha = AlphaCurrency::from(1_000_000_000); - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, pending_alpha.into(), AlphaCurrency::ZERO, @@ -754,7 +772,7 @@ fn test_drain_base_with_subnet_with_single_staker_registered() { stake_before, ); let pending_alpha = AlphaCurrency::from(1_000_000_000); - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, pending_alpha, AlphaCurrency::ZERO, @@ -798,7 +816,7 @@ fn test_drain_base_with_subnet_with_single_staker_registered_root_weight() { let pending_alpha = AlphaCurrency::from(1_000_000_000); let pending_root_alpha = AlphaCurrency::from(1_000_000_000); assert_eq!(SubnetTAO::::get(NetUid::ROOT), TaoCurrency::ZERO); - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, pending_alpha, pending_root_alpha, @@ -845,7 +863,7 @@ fn test_drain_base_with_subnet_with_two_stakers_registered() { stake_before, ); let pending_alpha = AlphaCurrency::from(1_000_000_000); - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, pending_alpha, AlphaCurrency::ZERO, @@ -910,7 +928,7 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root() { let pending_tao = TaoCurrency::from(1_000_000_000); let pending_alpha = AlphaCurrency::from(1_000_000_000); assert_eq!(SubnetTAO::::get(NetUid::ROOT), TaoCurrency::ZERO); - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, pending_alpha, AlphaCurrency::ZERO, @@ -985,12 +1003,7 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_am let pending_tao = TaoCurrency::from(1_000_000_000); let pending_alpha = AlphaCurrency::from(1_000_000_000); assert_eq!(SubnetTAO::::get(NetUid::ROOT), TaoCurrency::ZERO); - SubtensorModule::drain_pending_emission( - netuid, - pending_alpha, - AlphaCurrency::ZERO, - 0.into(), - ); + SubtensorModule::distribute_emission(netuid, pending_alpha, AlphaCurrency::ZERO, 0.into()); let stake_after1 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey1, &coldkey, netuid); let root_after1 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -1065,7 +1078,7 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_am let pending_tao = TaoCurrency::from(1_000_000_000); let pending_alpha = AlphaCurrency::from(1_000_000_000); assert_eq!(SubnetTAO::::get(NetUid::ROOT), TaoCurrency::ZERO); - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, pending_alpha, AlphaCurrency::ZERO, @@ -1126,7 +1139,7 @@ fn test_drain_alpha_childkey_parentkey() { ChildkeyTake::::insert(child, netuid, u16::MAX / 10); let pending_alpha = AlphaCurrency::from(1_000_000_000); - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, pending_alpha, AlphaCurrency::ZERO, @@ -1351,7 +1364,7 @@ fn test_get_root_children_drain() { // Lets drain let pending_alpha = AlphaCurrency::from(1_000_000_000); - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( alpha, pending_alpha, AlphaCurrency::ZERO, @@ -1374,7 +1387,7 @@ fn test_get_root_children_drain() { // Lets drain let pending_alpha = AlphaCurrency::from(1_000_000_000); let pending_root1 = TaoCurrency::from(1_000_000_000); - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( alpha, pending_alpha, // pending_root1, @@ -1398,7 +1411,7 @@ fn test_get_root_children_drain() { // Lets drain let pending_alpha = AlphaCurrency::from(1_000_000_000); let pending_root2 = TaoCurrency::from(1_000_000_000); - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( alpha, pending_alpha, AlphaCurrency::ZERO, @@ -1486,7 +1499,7 @@ fn test_get_root_children_drain_half_proportion() { // Lets drain! let pending_alpha = AlphaCurrency::from(1_000_000_000); - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( alpha, pending_alpha, AlphaCurrency::ZERO, @@ -1572,7 +1585,7 @@ fn test_get_root_children_drain_with_take() { // Lets drain! let pending_alpha = AlphaCurrency::from(1_000_000_000); - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( alpha, pending_alpha, AlphaCurrency::ZERO, @@ -1659,7 +1672,7 @@ fn test_get_root_children_drain_with_half_take() { // Lets drain! let pending_alpha = AlphaCurrency::from(1_000_000_000); - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( alpha, pending_alpha, AlphaCurrency::ZERO, @@ -1761,7 +1774,7 @@ fn test_get_root_children_drain_with_half_take() { // // Lets drain! // let pending_alpha = AlphaCurrency::from(1_000_000_000); -// SubtensorModule::drain_pending_emission(alpha, pending_alpha, 0, 0.into(), 0.into()); +// SubtensorModule::distribute_emission(alpha, pending_alpha, 0, 0.into(), 0.into()); // // Alice and Bob make the same amount. // close( @@ -2349,7 +2362,7 @@ fn test_calculate_dividends_and_incentives_only_miners() { } #[test] -fn test_drain_pending_emission_no_miners_all_drained() { +fn test_distribute_emission_no_miners_all_drained() { new_test_ext(1).execute_with(|| { let netuid = add_dynamic_network(&U256::from(1), &U256::from(2)); let hotkey = U256::from(3); @@ -2374,7 +2387,7 @@ fn test_drain_pending_emission_no_miners_all_drained() { // Set the emission to be 1 million. let emission = AlphaCurrency::from(1_000_000); // Run drain pending without any miners. - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, emission, AlphaCurrency::ZERO, @@ -2393,9 +2406,9 @@ fn test_drain_pending_emission_no_miners_all_drained() { }); } -// cargo test --package pallet-subtensor --lib -- tests::coinbase::test_drain_pending_emission_zero_emission --exact --show-output +// cargo test --package pallet-subtensor --lib -- tests::coinbase::test_distribute_emission_zero_emission --exact --show-output #[test] -fn test_drain_pending_emission_zero_emission() { +fn test_distribute_emission_zero_emission() { new_test_ext(1).execute_with(|| { let netuid = add_dynamic_network_disable_commit_reveal(&U256::from(1), &U256::from(2)); let hotkey = U256::from(3); @@ -2446,7 +2459,7 @@ fn test_drain_pending_emission_zero_emission() { Dividends::::remove(netuid); // Set the emission to be ZERO. - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, 0.into(), AlphaCurrency::ZERO, @@ -2739,7 +2752,7 @@ fn test_drain_alpha_childkey_parentkey_with_burn() { let child_stake_before = SubtensorModule::get_stake_for_hotkey_on_subnet(&child, netuid); let pending_alpha = AlphaCurrency::from(1_000_000_000); - SubtensorModule::drain_pending_emission( + SubtensorModule::distribute_emission( netuid, pending_alpha, AlphaCurrency::ZERO, @@ -2909,3 +2922,368 @@ fn test_zero_shares_zero_emission() { assert_eq!(SubnetAlphaIn::::get(netuid2), initial.into()); }); } + +#[test] +fn test_mining_emission_distribution_with_no_root_sell() { + new_test_ext(1).execute_with(|| { + let validator_coldkey = U256::from(1); + let validator_hotkey = U256::from(2); + let validator_miner_coldkey = U256::from(3); + let validator_miner_hotkey = U256::from(4); + let miner_coldkey = U256::from(5); + let miner_hotkey = U256::from(6); + let netuid = NetUid::from(1); + let subnet_tempo = 10; + let stake: u64 = 100_000_000_000; + let root_stake: u64 = 200_000_000_000; // 200 TAO + + // Create root network + SubtensorModule::set_tao_weight(0); // Start tao weight at 0 + SubtokenEnabled::::insert(NetUid::ROOT, true); + NetworksAdded::::insert(NetUid::ROOT, true); + + // Add network, register hotkeys, and setup network parameters + add_network(netuid, subnet_tempo, 0); + SubnetMechanism::::insert(netuid, 1); // Set mechanism to 1 + + // Setup large LPs to prevent slippage + SubnetTAO::::insert(netuid, TaoCurrency::from(1_000_000_000_000_000)); + SubnetAlphaIn::::insert(netuid, AlphaCurrency::from(1_000_000_000_000_000)); + + register_ok_neuron(netuid, validator_hotkey, validator_coldkey, 0); + register_ok_neuron(netuid, validator_miner_hotkey, validator_miner_coldkey, 1); + register_ok_neuron(netuid, miner_hotkey, miner_coldkey, 2); + SubtensorModule::add_balance_to_coldkey_account( + &validator_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::add_balance_to_coldkey_account( + &validator_miner_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::add_balance_to_coldkey_account( + &miner_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::set_weights_set_rate_limit(netuid, 0); + step_block(subnet_tempo); + SubnetOwnerCut::::set(u16::MAX / 10); + // There are two validators and three neurons + MaxAllowedUids::::set(netuid, 3); + SubtensorModule::set_max_allowed_validators(netuid, 2); + + // Setup stakes: + // Stake from validator + // Stake from valiminer + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_coldkey), + validator_hotkey, + netuid, + stake.into() + )); + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_miner_coldkey), + validator_miner_hotkey, + netuid, + stake.into() + )); + + // Setup YUMA so that it creates emissions + Weights::::insert(NetUidStorageIndex::from(netuid), 0, vec![(1, 0xFFFF)]); + Weights::::insert(NetUidStorageIndex::from(netuid), 1, vec![(2, 0xFFFF)]); + BlockAtRegistration::::set(netuid, 0, 1); + BlockAtRegistration::::set(netuid, 1, 1); + BlockAtRegistration::::set(netuid, 2, 1); + LastUpdate::::set(NetUidStorageIndex::from(netuid), vec![2, 2, 2]); + Kappa::::set(netuid, u16::MAX / 5); + ActivityCutoff::::set(netuid, u16::MAX); // makes all stake active + ValidatorPermit::::insert(netuid, vec![true, true, false]); + + // Run run_coinbase until emissions are drained + step_block(subnet_tempo); + + // Add stake to validator so it has root stake + SubtensorModule::add_balance_to_coldkey_account(&validator_coldkey, root_stake.into()); + // init root + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_coldkey), + validator_hotkey, + NetUid::ROOT, + root_stake.into() + )); + // Set tao weight non zero + SubtensorModule::set_tao_weight(u64::MAX / 10); + + // Make root sell NOT happen + // set price very low, e.g. a lot of alpha in + //SubnetAlphaIn::::insert(netuid, AlphaCurrency::from(1_000_000_000_000_000)); + pallet_subtensor_swap::AlphaSqrtPrice::::insert( + netuid, + U64F64::saturating_from_num(0.01), + ); + + // Make sure we ARE NOT root selling, so we do not have root alpha divs. + let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); + assert!(!root_sell_flag, "Root sell flag should be false"); + + // Run run_coinbase until emissions are drained + step_block(subnet_tempo); + + let old_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); + let per_block_emission = SubtensorModule::get_block_emission_for_issuance( + SubtensorModule::get_alpha_issuance(netuid).into(), + ) + .unwrap_or(0); + + // step by one block + step_block(1); + // Verify that root alpha divs + let new_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); + // Check that we are indeed NOT root selling, i.e. that root alpha divs are NOT increasing + assert_eq!( + new_root_alpha_divs, old_root_alpha_divs, + "Root alpha divs should not increase" + ); + // Check root divs are zero + assert_eq!( + new_root_alpha_divs, + AlphaCurrency::ZERO, + "Root alpha divs should be zero" + ); + let miner_stake_before_epoch = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &miner_hotkey, + &miner_coldkey, + netuid, + ); + // Run again but with some root stake + step_block(subnet_tempo - 2); + assert_abs_diff_eq!( + PendingEmission::::get(netuid).to_u64(), + U96F32::saturating_from_num(per_block_emission) + .saturating_mul(U96F32::saturating_from_num(subnet_tempo as u64)) + .saturating_mul(U96F32::saturating_from_num(0.90)) + .saturating_to_num::(), + epsilon = 100_000_u64.into() + ); + step_block(1); + assert!( + BlocksSinceLastStep::::get(netuid) == 0, + "Blocks since last step should be 0" + ); + + let miner_uid = Uids::::get(netuid, miner_hotkey).unwrap_or(0); + log::info!("Miner uid: {miner_uid:?}"); + let miner_incentive: AlphaCurrency = { + let miner_incentive = Incentive::::get(NetUidStorageIndex::from(netuid)) + .get(miner_uid as usize) + .copied(); + + assert!(miner_incentive.is_some()); + + (miner_incentive.unwrap_or_default() as u64).into() + }; + log::info!("Miner incentive: {miner_incentive:?}"); + + // Miner emissions + let miner_emission_1: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &miner_hotkey, + &miner_coldkey, + netuid, + ) + .to_u64() + - miner_stake_before_epoch.to_u64(); + + assert_abs_diff_eq!( + Incentive::::get(NetUidStorageIndex::from(netuid)) + .iter() + .sum::(), + u16::MAX, + epsilon = 10 + ); + + assert_abs_diff_eq!( + miner_emission_1, + U96F32::saturating_from_num(miner_incentive) + .saturating_div(u16::MAX.into()) + .saturating_mul(U96F32::saturating_from_num(per_block_emission)) + .saturating_mul(U96F32::saturating_from_num(subnet_tempo + 1)) + .saturating_mul(U96F32::saturating_from_num(0.45)) // miner cut + .saturating_to_num::(), + epsilon = 1_000_000_u64 + ); + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_mining_emission_distribution_with_root_sell --exact --show-output --nocapture +#[test] +fn test_mining_emission_distribution_with_root_sell() { + new_test_ext(1).execute_with(|| { + let validator_coldkey = U256::from(1); + let validator_hotkey = U256::from(2); + let validator_miner_coldkey = U256::from(3); + let validator_miner_hotkey = U256::from(4); + let miner_coldkey = U256::from(5); + let miner_hotkey = U256::from(6); + let subnet_tempo = 10; + let stake: u64 = 100_000_000_000; + let root_stake: u64 = 200_000_000_000; // 200 TAO + + // Create root network + SubtensorModule::set_tao_weight(0); // Start tao weight at 0 + SubtokenEnabled::::insert(NetUid::ROOT, true); + NetworksAdded::::insert(NetUid::ROOT, true); + + // Add network, register hotkeys, and setup network parameters + let owner_hotkey = U256::from(10); + let owner_coldkey = U256::from(11); + let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); + Tempo::::insert(netuid, 1); + FirstEmissionBlockNumber::::insert(netuid, 0); + + // Setup large LPs to prevent slippage + SubnetTAO::::insert(netuid, TaoCurrency::from(1_000_000_000_000_000)); + SubnetAlphaIn::::insert(netuid, AlphaCurrency::from(1_000_000_000_000_000)); + + register_ok_neuron(netuid, validator_hotkey, validator_coldkey, 0); + register_ok_neuron(netuid, validator_miner_hotkey, validator_miner_coldkey, 1); + register_ok_neuron(netuid, miner_hotkey, miner_coldkey, 2); + SubtensorModule::add_balance_to_coldkey_account( + &validator_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::add_balance_to_coldkey_account( + &validator_miner_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::add_balance_to_coldkey_account( + &miner_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::set_weights_set_rate_limit(netuid, 0); + step_block(subnet_tempo); + SubnetOwnerCut::::set(u16::MAX / 10); + // There are two validators and three neurons + MaxAllowedUids::::set(netuid, 3); + SubtensorModule::set_max_allowed_validators(netuid, 2); + + // Setup stakes: + // Stake from validator + // Stake from valiminer + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_coldkey), + validator_hotkey, + netuid, + stake.into() + )); + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_miner_coldkey), + validator_miner_hotkey, + netuid, + stake.into() + )); + + // Setup YUMA so that it creates emissions + Weights::::insert(NetUidStorageIndex::from(netuid), 0, vec![(1, 0xFFFF)]); + Weights::::insert(NetUidStorageIndex::from(netuid), 1, vec![(2, 0xFFFF)]); + BlockAtRegistration::::set(netuid, 0, 1); + BlockAtRegistration::::set(netuid, 1, 1); + BlockAtRegistration::::set(netuid, 2, 1); + LastUpdate::::set(NetUidStorageIndex::from(netuid), vec![2, 2, 2]); + Kappa::::set(netuid, u16::MAX / 5); + ActivityCutoff::::set(netuid, u16::MAX); // makes all stake active + ValidatorPermit::::insert(netuid, vec![true, true, false]); + + // Run run_coinbase until emissions are drained + step_block(subnet_tempo); + + // Add stake to validator so it has root stake + SubtensorModule::add_balance_to_coldkey_account(&validator_coldkey, root_stake.into()); + // init root + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_coldkey), + validator_hotkey, + NetUid::ROOT, + root_stake.into() + )); + // Set tao weight non zero + SubtensorModule::set_tao_weight(u64::MAX / 10); + + // Make root sell happen + // Set moving price > 1.0 + // Set price > 1.0 + pallet_subtensor_swap::AlphaSqrtPrice::::insert( + netuid, + U64F64::saturating_from_num(10.0), + ); + + SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); + + // Make sure we are root selling, so we have root alpha divs. + let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); + assert!(root_sell_flag, "Root sell flag should be true"); + + // Run run_coinbase until emissions are drained + step_block(subnet_tempo); + + let old_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); + let miner_stake_before_epoch = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &miner_hotkey, + &miner_coldkey, + netuid, + ); + + // step by one block + step_block(1); + // Verify root alpha divs + let new_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); + // Check that we ARE root selling, i.e. that root alpha divs are changing + assert_ne!( + new_root_alpha_divs, old_root_alpha_divs, + "Root alpha divs should be changing" + ); + assert!( + new_root_alpha_divs > AlphaCurrency::ZERO, + "Root alpha divs should be greater than 0" + ); + + // Run again but with some root stake + step_block(subnet_tempo - 1); + + let miner_uid = Uids::::get(netuid, miner_hotkey).unwrap_or(0); + let miner_incentive: AlphaCurrency = { + let miner_incentive = Incentive::::get(NetUidStorageIndex::from(netuid)) + .get(miner_uid as usize) + .copied(); + + assert!(miner_incentive.is_some()); + + (miner_incentive.unwrap_or_default() as u64).into() + }; + log::info!("Miner incentive: {miner_incentive:?}"); + + let per_block_emission = SubtensorModule::get_block_emission_for_issuance( + SubtensorModule::get_alpha_issuance(netuid).into(), + ) + .unwrap_or(0); + + // Miner emissions + let miner_emission_1: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &miner_hotkey, + &miner_coldkey, + netuid, + ) + .to_u64() + - miner_stake_before_epoch.to_u64(); + + assert_abs_diff_eq!( + miner_emission_1, + U96F32::saturating_from_num(miner_incentive) + .saturating_div(u16::MAX.into()) + .saturating_mul(U96F32::saturating_from_num(per_block_emission)) + .saturating_mul(U96F32::saturating_from_num(subnet_tempo + 1)) + .saturating_mul(U96F32::saturating_from_num(0.45)) // miner cut + .saturating_to_num::(), + epsilon = 1_000_000_u64 + ); + }); +} diff --git a/pallets/subtensor/src/tests/networks.rs b/pallets/subtensor/src/tests/networks.rs index 0449c67f86..3706878bb6 100644 --- a/pallets/subtensor/src/tests/networks.rs +++ b/pallets/subtensor/src/tests/networks.rs @@ -216,43 +216,49 @@ fn dissolve_owner_cut_refund_logic() { // One staker and a TAO pot (not relevant to refund amount). let sh = U256::from(77); let sc = U256::from(88); - Alpha::::insert((sh, sc, net), U64F64::from_num(100u128)); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &sh, + &sc, + net, + AlphaCurrency::from(800u64), + ); SubnetTAO::::insert(net, TaoCurrency::from(1_000)); // Lock & emissions: total emitted α = 800. let lock: TaoCurrency = TaoCurrency::from(2_000); SubtensorModule::set_subnet_locked_balance(net, lock); - Emission::::insert( - net, - vec![AlphaCurrency::from(200), AlphaCurrency::from(600)], - ); + // ensure there was some Alpha issued + assert!(SubtensorModule::get_alpha_issuance(net).to_u64() > 0); // Owner cut = 11796 / 65535 (about 18%). SubnetOwnerCut::::put(11_796u16); // Compute expected refund with the SAME math as the pallet. let frac: U96F32 = SubtensorModule::get_float_subnet_owner_cut(); - let total_emitted_alpha: u64 = 800; + let total_emitted_alpha: u64 = SubtensorModule::get_alpha_issuance(net).to_u64(); let owner_alpha_u64: u64 = U96F32::from_num(total_emitted_alpha) .saturating_mul(frac) .floor() .saturating_to_num::(); - // Current α→τ price for this subnet. - let price: U96F32 = - ::SwapInterface::current_alpha_price(net.into()); - let owner_emission_tao_u64: u64 = U96F32::from_num(owner_alpha_u64) - .saturating_mul(price) - .floor() - .saturating_to_num::(); + // Use the current alpha price to estimate the TAO equivalent. + let owner_emission_tao = { + let price: U96F32 = + ::SwapInterface::current_alpha_price(net.into()); + U96F32::from_num(owner_alpha_u64) + .saturating_mul(price) + .floor() + .saturating_to_num::() + .into() + }; - let expected_refund: TaoCurrency = - lock.saturating_sub(TaoCurrency::from(owner_emission_tao_u64)); + let expected_refund: TaoCurrency = lock.saturating_sub(owner_emission_tao); let before = SubtensorModule::get_coldkey_balance(&oc); assert_ok!(SubtensorModule::do_dissolve_network(net)); let after = SubtensorModule::get_coldkey_balance(&oc); + assert!(after > before); // some refund is expected assert_eq!( TaoCurrency::from(after), TaoCurrency::from(before) + expected_refund @@ -841,15 +847,10 @@ fn destroy_alpha_out_many_stakers_complex_distribution() { SubnetTAO::::insert(netuid, TaoCurrency::from(tao_pot)); SubtensorModule::set_subnet_locked_balance(netuid, TaoCurrency::from(lock)); + // ensure there was some Alpha issued + assert!(SubtensorModule::get_alpha_issuance(netuid).to_u64() > 0); + // Owner already earned some emission; owner-cut = 50 % - Emission::::insert( - netuid, - vec![ - AlphaCurrency::from(1_000), - AlphaCurrency::from(2_000), - AlphaCurrency::from(1_500), - ], - ); SubnetOwnerCut::::put(32_768u16); // ~ 0.5 in fixed-point // ── 4) balances before ────────────────────────────────────────────── @@ -879,28 +880,23 @@ fn destroy_alpha_out_many_stakers_complex_distribution() { // ── 5b) expected owner refund with price-aware emission deduction ─── let frac: U96F32 = SubtensorModule::get_float_subnet_owner_cut(); - let total_emitted_alpha: u64 = 1_000 + 2_000 + 1_500; // 4500 α + let total_emitted_alpha: u64 = SubtensorModule::get_alpha_issuance(netuid).to_u64(); let owner_alpha_u64: u64 = U96F32::from_num(total_emitted_alpha) .saturating_mul(frac) .floor() .saturating_to_num::(); - let order = GetTaoForAlpha::::with_amount(owner_alpha_u64); - let owner_emission_tao = - ::SwapInterface::sim_swap(netuid.into(), order) - .map(|res| res.amount_paid_out) - .unwrap_or_else(|_| { - // Fallback matches the pallet's fallback - let price: U96F32 = - ::SwapInterface::current_alpha_price(netuid.into()); - U96F32::from_num(owner_alpha_u64) - .saturating_mul(price) - .floor() - .saturating_to_num::() - .into() - }); - - let expected_refund = lock.saturating_sub(owner_emission_tao.to_u64()); + let owner_emission_tao: u64 = { + // Fallback matches the pallet's fallback + let price: U96F32 = + ::SwapInterface::current_alpha_price(netuid.into()); + U96F32::from_num(owner_alpha_u64) + .saturating_mul(price) + .floor() + .saturating_to_num::() + }; + + let expected_refund = lock.saturating_sub(owner_emission_tao); // ── 6) run distribution (credits τ to coldkeys, wipes α state) ───── assert_ok!(SubtensorModule::destroy_alpha_in_out_stakes(netuid)); @@ -947,34 +943,38 @@ fn destroy_alpha_out_refund_gating_by_registration_block() { // Lock and (nonzero) emissions let lock_u64: u64 = 50_000; SubtensorModule::set_subnet_locked_balance(netuid, TaoCurrency::from(lock_u64)); - Emission::::insert( - netuid, - vec![AlphaCurrency::from(1_500u64), AlphaCurrency::from(3_000u64)], // total 4_500 α - ); // Owner cut ≈ 50% SubnetOwnerCut::::put(32_768u16); + // give some stake to other key + let other_cold = U256::from(1_234); + let other_hot = U256::from(2_345); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &other_hot, + &other_cold, + netuid, + AlphaCurrency::from(30u64), // not nearly enough to cover the lock + ); + + // ensure there was some Alpha issued + assert!(SubtensorModule::get_alpha_issuance(netuid).to_u64() > 0); + // Compute expected refund using the same math as the pallet let frac: U96F32 = SubtensorModule::get_float_subnet_owner_cut(); - let total_emitted_alpha: u64 = 1_500 + 3_000; // 4_500 α + let total_emitted_alpha: u64 = SubtensorModule::get_alpha_issuance(netuid).to_u64(); let owner_alpha_u64: u64 = U96F32::from_num(total_emitted_alpha) .saturating_mul(frac) .floor() .saturating_to_num::(); - // Prefer sim_swap; fall back to current price if unavailable. - let order = GetTaoForAlpha::::with_amount(owner_alpha_u64); - let owner_emission_tao_u64 = - ::SwapInterface::sim_swap(netuid.into(), order) - .map(|res| res.amount_paid_out.to_u64()) - .unwrap_or_else(|_| { - let price: U96F32 = - ::SwapInterface::current_alpha_price(netuid.into()); - U96F32::from_num(owner_alpha_u64) - .saturating_mul(price) - .floor() - .saturating_to_num::() - }); + let owner_emission_tao_u64 = { + let price: U96F32 = + ::SwapInterface::current_alpha_price(netuid.into()); + U96F32::from_num(owner_alpha_u64) + .saturating_mul(price) + .floor() + .saturating_to_num::() + }; let expected_refund: u64 = lock_u64.saturating_sub(owner_emission_tao_u64); @@ -1011,7 +1011,17 @@ fn destroy_alpha_out_refund_gating_by_registration_block() { // Lock and emissions present (should be ignored for refund) let lock_u64: u64 = 42_000; SubtensorModule::set_subnet_locked_balance(netuid, TaoCurrency::from(lock_u64)); - Emission::::insert(netuid, vec![AlphaCurrency::from(5_000u64)]); + // give some stake to other key + let other_cold = U256::from(1_234); + let other_hot = U256::from(2_345); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &other_hot, + &other_cold, + netuid, + AlphaCurrency::from(300u64), // not nearly enough to cover the lock + ); + // ensure there was some Alpha issued + assert!(SubtensorModule::get_alpha_issuance(netuid).to_u64() > 0); SubnetOwnerCut::::put(32_768u16); // ~50% // Balances before @@ -1046,7 +1056,9 @@ fn destroy_alpha_out_refund_gating_by_registration_block() { // lock = 0; emissions present (must not matter) SubtensorModule::set_subnet_locked_balance(netuid, TaoCurrency::from(0u64)); - Emission::::insert(netuid, vec![AlphaCurrency::from(10_000u64)]); + SubnetAlphaOut::::insert(netuid, AlphaCurrency::from(10_000)); + // ensure there was some Alpha issued + assert!(SubtensorModule::get_alpha_issuance(netuid).to_u64() > 0); SubnetOwnerCut::::put(32_768u16); // ~50% let owner_before = SubtensorModule::get_coldkey_balance(&owner_cold); diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 266a755708..fe9bda8b6c 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 338, + spec_version: 342, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From e9c94240019fd3bccb3b76dbf066b2e785b5f009 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 6 Nov 2025 22:25:13 -0500 Subject: [PATCH 108/210] add totalemission counter --- pallets/subtensor/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 5df8a9c429..c536873128 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1433,6 +1433,10 @@ pub mod pallet { /// --- MAP ( netuid ) --> pending_emission pub type PendingEmission = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultPendingEmission>; + #[pallet::storage] + /// --- MAP ( netuid ) --> total_emission + pub type TotalEmission = + StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultZeroAlpha>; /// --- MAP ( netuid ) --> pending_root_alpha_emission #[pallet::storage] pub type PendingRootAlphaDivs = From 5cafe0a3bae5c3734dc7a293be2fc54ac071aa80 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 6 Nov 2025 22:34:26 -0500 Subject: [PATCH 109/210] burn alpha not sold by root --- pallets/subtensor/src/benchmarks.rs | 1 + .../subtensor/src/coinbase/run_coinbase.rs | 52 ++++++++++++------- pallets/subtensor/src/tests/claim_root.rs | 14 +++++ pallets/subtensor/src/tests/coinbase.rs | 29 +++++++++-- 4 files changed, 73 insertions(+), 23 deletions(-) diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index 1e7d6991c1..223c086419 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -1619,6 +1619,7 @@ mod pallet_benchmarks { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), AlphaCurrency::ZERO, ); diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index d998ad1f7d..c8169c317f 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -195,29 +195,27 @@ impl Pallet { let alpha_out_i: U96F32 = *alpha_out.get(netuid_i).unwrap_or(&asfloat!(0.0)); log::debug!("alpha_out_i: {alpha_out_i:?}"); - // Get root proportion of alpha_out dividends. - let mut root_alpha: U96F32 = asfloat!(0.0); + // Get ALPHA issuance. + let alpha_issuance: U96F32 = asfloat!(Self::get_alpha_issuance(*netuid_i)); + log::debug!("alpha_issuance: {alpha_issuance:?}"); + + // Get root proportional dividends. + let root_proportion: U96F32 = tao_weight + .checked_div(tao_weight.saturating_add(alpha_issuance)) + .unwrap_or(asfloat!(0.0)); + log::debug!("root_proportion: {root_proportion:?}"); + + // Get root alpha from root prop. + let root_alpha: U96F32 = root_proportion + .saturating_mul(alpha_out_i) // Total alpha emission per block remaining. + .saturating_mul(asfloat!(0.5)); // 50% to validators. + log::debug!("root_alpha: {root_alpha:?}"); + if root_sell_flag { - // Get ALPHA issuance. - let alpha_issuance: U96F32 = asfloat!(Self::get_alpha_issuance(*netuid_i)); - log::debug!("alpha_issuance: {alpha_issuance:?}"); - - // Get root proportional dividends. - let root_proportion: U96F32 = tao_weight - .checked_div(tao_weight.saturating_add(alpha_issuance)) - .unwrap_or(asfloat!(0.0)); - log::debug!("root_proportion: {root_proportion:?}"); - - // Get root alpha from root prop. - root_alpha = root_proportion - .saturating_mul(alpha_out_i) // Total alpha emission per block remaining. - .saturating_mul(asfloat!(0.5)); // 50% to validators. PendingRootAlphaDivs::::mutate(*netuid_i, |total| { *total = total.saturating_add(tou64!(root_alpha).into()); }); } - // Remove root alpha from alpha_out. - log::debug!("root_alpha: {root_alpha:?}"); // Get pending alpha as original alpha_out - root_alpha. let pending_alpha: U96F32 = alpha_out_i.saturating_sub(root_alpha); @@ -227,6 +225,10 @@ impl Pallet { PendingEmission::::mutate(*netuid_i, |total| { *total = total.saturating_add(tou64!(pending_alpha).into()); }); + // Accumulate total alpha emission, including burned alpha from root prop. + TotalEmission::::mutate(*netuid_i, |total| { + *total = total.saturating_add(tou64!(alpha_out_i).into()); + }); } // --- 8. Update moving prices after using them in the emission calculation. @@ -263,8 +265,18 @@ impl Pallet { let owner_cut = PendingOwnerCut::::get(netuid); PendingOwnerCut::::insert(netuid, AlphaCurrency::ZERO); + // Get total emission and drain. + let total_emission = TotalEmission::::get(netuid); + TotalEmission::::insert(netuid, AlphaCurrency::ZERO); + // Distribute the emission. - Self::distribute_emission(netuid, pending_alpha, pending_root_alpha, owner_cut); + Self::distribute_emission( + netuid, + pending_alpha, + pending_root_alpha, + total_emission, + owner_cut, + ); } else { // Increment BlocksSinceLastStep::::mutate(netuid, |total| *total = total.saturating_add(1)); @@ -635,6 +647,7 @@ impl Pallet { netuid: NetUid, pending_alpha: AlphaCurrency, pending_root_alpha: AlphaCurrency, + total_alpha: AlphaCurrency, owner_cut: AlphaCurrency, ) { log::debug!( @@ -642,7 +655,6 @@ impl Pallet { ); let tao_weight = Self::get_tao_weight(); - let total_alpha = pending_alpha.saturating_add(pending_root_alpha); // Run the epoch. let hotkey_emission: Vec<(T::AccountId, AlphaCurrency, AlphaCurrency)> = diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index 951dfa0ae1..5e8f0a8907 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -76,6 +76,7 @@ fn test_claim_root_with_drain_emissions() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), AlphaCurrency::ZERO, ); @@ -144,6 +145,7 @@ fn test_claim_root_with_drain_emissions() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), AlphaCurrency::ZERO, ); @@ -245,6 +247,7 @@ fn test_claim_root_adding_stake_proportionally_for_two_stakers() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), AlphaCurrency::ZERO, ); @@ -346,6 +349,7 @@ fn test_claim_root_adding_stake_disproportionally_for_two_stakers() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), AlphaCurrency::ZERO, ); @@ -437,6 +441,7 @@ fn test_claim_root_with_changed_stake() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), AlphaCurrency::ZERO, ); @@ -489,6 +494,7 @@ fn test_claim_root_with_changed_stake() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), AlphaCurrency::ZERO, ); @@ -542,6 +548,7 @@ fn test_claim_root_with_changed_stake() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), AlphaCurrency::ZERO, ); @@ -634,6 +641,7 @@ fn test_claim_root_with_drain_emissions_and_swap_claim_type() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), AlphaCurrency::ZERO, ); @@ -678,6 +686,7 @@ fn test_claim_root_with_drain_emissions_and_swap_claim_type() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), AlphaCurrency::ZERO, ); @@ -714,6 +723,7 @@ fn test_claim_root_with_drain_emissions_and_swap_claim_type() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), AlphaCurrency::ZERO, ); @@ -1139,6 +1149,7 @@ fn test_claim_root_with_swap_coldkey() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), AlphaCurrency::ZERO, ); @@ -1229,6 +1240,7 @@ fn test_claim_root_with_swap_hotkey() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), AlphaCurrency::ZERO, ); @@ -1345,6 +1357,7 @@ fn test_claim_root_on_network_deregistration() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), AlphaCurrency::ZERO, ); @@ -1485,6 +1498,7 @@ fn test_claim_root_with_unrelated_subnets() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), AlphaCurrency::ZERO, ); diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 5d03afa973..d97fbfb037 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -708,6 +708,7 @@ fn test_drain_base() { AlphaCurrency::ZERO, AlphaCurrency::ZERO, AlphaCurrency::ZERO, + AlphaCurrency::ZERO, ) }); } @@ -723,6 +724,7 @@ fn test_drain_base_with_subnet() { AlphaCurrency::ZERO, AlphaCurrency::ZERO, AlphaCurrency::ZERO, + AlphaCurrency::ZERO, ) }); } @@ -747,6 +749,7 @@ fn test_drain_base_with_subnet_with_single_staker_not_registered() { netuid, pending_alpha.into(), AlphaCurrency::ZERO, + pending_alpha.into(), AlphaCurrency::ZERO, ); let stake_after = @@ -776,6 +779,7 @@ fn test_drain_base_with_subnet_with_single_staker_registered() { netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); let stake_after = @@ -820,6 +824,7 @@ fn test_drain_base_with_subnet_with_single_staker_registered_root_weight() { netuid, pending_alpha, pending_root_alpha, + pending_alpha.saturating_add(pending_root_alpha), AlphaCurrency::ZERO, ); let stake_after = @@ -867,6 +872,7 @@ fn test_drain_base_with_subnet_with_two_stakers_registered() { netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); let stake_after1 = @@ -932,6 +938,7 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root() { netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); let stake_after1 = @@ -1003,7 +1010,13 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_am let pending_tao = TaoCurrency::from(1_000_000_000); let pending_alpha = AlphaCurrency::from(1_000_000_000); assert_eq!(SubnetTAO::::get(NetUid::ROOT), TaoCurrency::ZERO); - SubtensorModule::distribute_emission(netuid, pending_alpha, AlphaCurrency::ZERO, 0.into()); + SubtensorModule::distribute_emission( + netuid, + pending_alpha, + AlphaCurrency::ZERO, + pending_alpha, + 0.into(), + ); let stake_after1 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey1, &coldkey, netuid); let root_after1 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -1082,6 +1095,7 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_am netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); let stake_after1 = @@ -1143,6 +1157,7 @@ fn test_drain_alpha_childkey_parentkey() { netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); let parent_stake_after = SubtensorModule::get_stake_for_hotkey_on_subnet(&parent, netuid); @@ -1368,6 +1383,7 @@ fn test_get_root_children_drain() { alpha, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); @@ -1390,8 +1406,8 @@ fn test_get_root_children_drain() { SubtensorModule::distribute_emission( alpha, pending_alpha, - // pending_root1, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); @@ -1415,6 +1431,7 @@ fn test_get_root_children_drain() { alpha, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); @@ -1503,6 +1520,7 @@ fn test_get_root_children_drain_half_proportion() { alpha, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); @@ -1589,6 +1607,7 @@ fn test_get_root_children_drain_with_take() { alpha, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); @@ -1676,6 +1695,7 @@ fn test_get_root_children_drain_with_half_take() { alpha, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); @@ -2391,6 +2411,7 @@ fn test_distribute_emission_no_miners_all_drained() { netuid, emission, AlphaCurrency::ZERO, + emission, AlphaCurrency::ZERO, ); @@ -2461,7 +2482,8 @@ fn test_distribute_emission_zero_emission() { // Set the emission to be ZERO. SubtensorModule::distribute_emission( netuid, - 0.into(), + AlphaCurrency::ZERO, + AlphaCurrency::ZERO, AlphaCurrency::ZERO, AlphaCurrency::ZERO, ); @@ -2756,6 +2778,7 @@ fn test_drain_alpha_childkey_parentkey_with_burn() { netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); let parent_stake_after = SubtensorModule::get_stake_for_hotkey_on_subnet(&parent, netuid); From 16822b44b2c6eb23ecb246a16add8ead6187f653 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 22:42:04 -0500 Subject: [PATCH 110/210] wip --- pallets/subtensor/src/tests/coinbase.rs | 139 ++++++++++++++++++++++++ 1 file changed, 139 insertions(+) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 726c7c608e..4c0f9877f8 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -3593,3 +3593,142 @@ fn test_coinbase_drain_pending_gets_counters_and_resets_them() { assert_eq!(PendingOwnerCut::::get(netuid0), AlphaCurrency::ZERO); }); } + +#[test] +fn test_coinbase_emit_to_subnets_with_no_root_sell() { + new_test_ext(1).execute_with(|| { + let zero = U96F32::saturating_from_num(0); + let netuid0 = add_dynamic_network(&U256::from(1), &U256::from(2)); + // Set owner cut to ~10% + SubnetOwnerCut::::set(u16::MAX / 10); + mock::setup_reserves( + netuid0, + TaoCurrency::from(1_000_000_000_000_000), + AlphaCurrency::from(1_000_000_000_000_000), + ); + // Initialize swap v3 + Swap::maybe_initialize_v3(netuid0); + + let tao_emission = U96F32::saturating_from_num(12345678); + let subnet_emissions = BTreeMap::from([(netuid0, tao_emission)]); + + // NO root sell + let root_sell_flag = false; + + let alpha_emission = U96F32::saturating_from_num( + SubtensorModule::get_block_emission_for_issuance( + SubtensorModule::get_alpha_issuance(netuid0).into(), + ) + .unwrap_or(0), + ); + let price: U96F32 = Swap::current_alpha_price(netuid0); + let (tao_in, alpha_in, alpha_out, excess_tao) = + SubtensorModule::get_subnet_terms(&subnet_emissions); + // Based on the price, we should have NO excess TAO + assert!(tao_emission / price <= alpha_emission); + + // ==== Run the emit to subnets ===== + SubtensorModule::emit_to_subnets(&[netuid0], &subnet_emissions, root_sell_flag); + + // Find the owner cut expected + let owner_cut: U96F32 = SubtensorModule::get_float_subnet_owner_cut(); + let owner_cut_expected: U96F32 = owner_cut.saturating_mul(alpha_emission); + log::info!("owner_cut_expected: {owner_cut_expected:?}"); + log::info!("alpha_emission: {alpha_emission:?}"); + log::info!("owner_cut: {owner_cut:?}"); + + // ===== Check that the pending emissions are set correctly ===== + // Owner cut is as expected + assert_abs_diff_eq!( + PendingOwnerCut::::get(netuid0).to_u64(), + owner_cut_expected.saturating_to_num::(), + epsilon = 200_u64 + ); + // NO root sell, so no root alpha divs + assert_eq!( + PendingRootAlphaDivs::::get(netuid0), + AlphaCurrency::ZERO + ); + // Should be alpha_emission minus the owner cut, + // we don't deduct any root alpha b/c NO root sell + assert_abs_diff_eq!( + PendingEmission::::get(netuid0).to_u64(), + alpha_emission + .saturating_sub(owner_cut_expected) + .saturating_to_num::(), + epsilon = 200_u64 + ); + }); +} + +#[test] +fn test_coinbase_emit_to_subnets_with_root_sell() { + new_test_ext(1).execute_with(|| { + let zero = U96F32::saturating_from_num(0); + let netuid0 = add_dynamic_network(&U256::from(1), &U256::from(2)); + // Set owner cut to ~10% + SubnetOwnerCut::::set(u16::MAX / 10); + mock::setup_reserves( + netuid0, + TaoCurrency::from(1_000_000_000_000_000), + AlphaCurrency::from(1_000_000_000_000_000), + ); + // Initialize swap v3 + Swap::maybe_initialize_v3(netuid0); + + let tao_emission = U96F32::saturating_from_num(12345678); + let subnet_emissions = BTreeMap::from([(netuid0, tao_emission)]); + + // NO root sell + let root_sell_flag = true; + + let alpha_emission: U96F32 = U96F32::saturating_from_num( + SubtensorModule::get_block_emission_for_issuance( + SubtensorModule::get_alpha_issuance(netuid0).into(), + ) + .unwrap_or(0), + ); + let price: U96F32 = Swap::current_alpha_price(netuid0); + let (tao_in, alpha_in, alpha_out, excess_tao) = + SubtensorModule::get_subnet_terms(&subnet_emissions); + // Based on the price, we should have NO excess TAO + assert!(tao_emission / price <= alpha_emission); + + // ==== Run the emit to subnets ===== + SubtensorModule::emit_to_subnets(&[netuid0], &subnet_emissions, root_sell_flag); + + // Find the owner cut expected + let owner_cut: U96F32 = SubtensorModule::get_float_subnet_owner_cut(); + let owner_cut_expected: U96F32 = owner_cut.saturating_mul(alpha_emission); + log::info!("owner_cut_expected: {owner_cut_expected:?}"); + log::info!("alpha_emission: {alpha_emission:?}"); + log::info!("owner_cut: {owner_cut:?}"); + + let expected_root_alpha_divs: AlphaCurrency = AlphaCurrency::from(12345678); + + let expected_emission: U96F32 = alpha_emission + .saturating_sub(owner_cut_expected) + .saturating_sub(U96F32::saturating_from_num(expected_root_alpha_divs.to_u64()).into()); + + // ===== Check that the pending emissions are set correctly ===== + // Owner cut is as expected + assert_abs_diff_eq!( + PendingOwnerCut::::get(netuid0).to_u64(), + owner_cut_expected.saturating_to_num::(), + epsilon = 200_u64 + ); + // YES root sell, so we have root alpha divs + assert_abs_diff_eq!( + PendingRootAlphaDivs::::get(netuid0).to_u64(), + expected_root_alpha_divs.to_u64(), + epsilon = 200_u64 + ); + // Should be alpha_emission minus the owner cut, + // minus the root alpha divs + assert_abs_diff_eq!( + PendingEmission::::get(netuid0).to_u64(), + expected_emission.saturating_to_num::(), + epsilon = 200_u64 + ); + }); +} From cfca5d09232b2c8b632c76ce166b0cf10bf774d3 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 22:56:06 -0500 Subject: [PATCH 111/210] chore: fmt --- pallets/subtensor/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index c536873128..3989733558 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1433,7 +1433,7 @@ pub mod pallet { /// --- MAP ( netuid ) --> pending_emission pub type PendingEmission = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultPendingEmission>; - #[pallet::storage] + #[pallet::storage] /// --- MAP ( netuid ) --> total_emission pub type TotalEmission = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultZeroAlpha>; From 3975359ddd0bf023a39788527fe9c3eeffea398d Mon Sep 17 00:00:00 2001 From: camfairchild Date: Thu, 6 Nov 2025 22:56:24 -0500 Subject: [PATCH 112/210] bump spec --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index fe9bda8b6c..45e470be80 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 342, + spec_version: 343, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From abef5e0223ec9441814adbd0cc1a5e2a30761516 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Fri, 7 Nov 2025 14:01:03 -0500 Subject: [PATCH 113/210] add pending server and pending vali emissions --- .../subtensor/src/coinbase/run_coinbase.rs | 90 ++++++++++++------- pallets/subtensor/src/lib.rs | 8 +- 2 files changed, 65 insertions(+), 33 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index c8169c317f..428c23e41e 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -212,22 +212,31 @@ impl Pallet { log::debug!("root_alpha: {root_alpha:?}"); if root_sell_flag { + // Only accumulate root alpha divs if root sell is allowed. PendingRootAlphaDivs::::mutate(*netuid_i, |total| { *total = total.saturating_add(tou64!(root_alpha).into()); }); + } else { + // If we are not selling the root alpha, we should recycle it. + Self::recycle_subnet_alpha(*netuid_i, AlphaCurrency::from(tou64!(root_alpha))); } - // Get pending alpha as original alpha_out - root_alpha. - let pending_alpha: U96F32 = alpha_out_i.saturating_sub(root_alpha); - log::debug!("pending_alpha: {pending_alpha:?}"); + // Get pending server alpha, which is the miner cut of the alpha out. + // Currently miner cut is 50% of the alpha out. + let pending_server_alpha = alpha_out_i.saturating_mul(asfloat!(0.5)); + // The total validator alpha is the remaining alpha out minus the server alpha. + let total_validator_alpha = alpha_out_i.saturating_sub(pending_server_alpha); + + // The alpha validators don't get the root alpha. + let pending_validator_alpha = total_validator_alpha.saturating_sub(root_alpha); - // Accumulate alpha emission in pending. - PendingEmission::::mutate(*netuid_i, |total| { - *total = total.saturating_add(tou64!(pending_alpha).into()); + // Accumulate the server alpha emission. + PendingServerEmission::::mutate(*netuid_i, |total| { + *total = total.saturating_add(tou64!(pending_server_alpha).into()); }); - // Accumulate total alpha emission, including burned alpha from root prop. - TotalEmission::::mutate(*netuid_i, |total| { - *total = total.saturating_add(tou64!(alpha_out_i).into()); + // Accumulate the validator alpha emission. + PendingValidatorEmission::::mutate(*netuid_i, |total| { + *total = total.saturating_add(tou64!(pending_validator_alpha).into()); }); } @@ -253,9 +262,29 @@ impl Pallet { BlocksSinceLastStep::::insert(netuid, 0); LastMechansimStepBlock::::insert(netuid, current_block); - // Get and drain the subnet pending emission. - let pending_alpha = PendingEmission::::get(netuid); + // Get and drain the PendingEmission. + // deprecated + let pending_emission = PendingEmission::::get(netuid); PendingEmission::::insert(netuid, AlphaCurrency::ZERO); + // If pending_emission is not zero, we split it between the server emission and the validator emission. + if !pending_emission.is_zero() { + let server_alpha = pending_emission.saturating_div(2.into()); + let validator_alpha = pending_emission.saturating_sub(server_alpha); + + PendingServerEmission::::mutate(netuid, |total| { + *total = total.saturating_add(server_alpha) + }); + PendingValidatorEmission::::mutate(netuid, |total| { + *total = total.saturating_add(validator_alpha) + }); + } + + // Get and drain the subnet pending emission. + let pending_server_alpha = PendingServerEmission::::get(netuid); + PendingServerEmission::::insert(netuid, AlphaCurrency::ZERO); + + let pending_validator_alpha = PendingValidatorEmission::::get(netuid); + PendingValidatorEmission::::insert(netuid, AlphaCurrency::ZERO); // Get and drain the subnet pending root alpha divs. let pending_root_alpha = PendingRootAlphaDivs::::get(netuid); @@ -265,16 +294,12 @@ impl Pallet { let owner_cut = PendingOwnerCut::::get(netuid); PendingOwnerCut::::insert(netuid, AlphaCurrency::ZERO); - // Get total emission and drain. - let total_emission = TotalEmission::::get(netuid); - TotalEmission::::insert(netuid, AlphaCurrency::ZERO); - // Distribute the emission. Self::distribute_emission( netuid, - pending_alpha, + pending_server_alpha, + pending_validator_alpha, pending_root_alpha, - total_emission, owner_cut, ); } else { @@ -645,20 +670,23 @@ impl Pallet { pub fn distribute_emission( netuid: NetUid, - pending_alpha: AlphaCurrency, + pending_server_alpha: AlphaCurrency, + pending_validator_alpha: AlphaCurrency, pending_root_alpha: AlphaCurrency, - total_alpha: AlphaCurrency, - owner_cut: AlphaCurrency, + pending_owner_cut: AlphaCurrency, ) { log::debug!( - "Draining pending alpha emission for netuid {netuid:?}, pending_alpha: {pending_alpha:?}, pending_root_alpha: {pending_root_alpha:?}, owner_cut: {owner_cut:?}" + "Draining pending alpha emission for netuid {netuid:?}, pending_server_alpha: {pending_server_alpha:?}, pending_validator_alpha: {pending_validator_alpha:?}, pending_root_alpha: {pending_root_alpha:?}, pending_owner_cut: {pending_owner_cut:?}" ); let tao_weight = Self::get_tao_weight(); + let total_alpha_minus_owner_cut = pending_server_alpha + .saturating_add(pending_validator_alpha) + .saturating_add(pending_root_alpha); - // Run the epoch. + // Run the epoch, using the alpha going to both the servers and the validators. let hotkey_emission: Vec<(T::AccountId, AlphaCurrency, AlphaCurrency)> = - Self::epoch_with_mechanisms(netuid, total_alpha); + Self::epoch_with_mechanisms(netuid, total_alpha_minus_owner_cut); log::debug!("hotkey_emission: {hotkey_emission:?}"); // Compute the pending validator alpha. @@ -673,20 +701,20 @@ impl Pallet { }); log::debug!("incentive_sum: {incentive_sum:?}"); - let pending_validator_alpha = if !incentive_sum.is_zero() { - total_alpha - .saturating_div(2.into()) - .saturating_sub(pending_root_alpha) + let validator_alpha = if !incentive_sum.is_zero() { + pending_validator_alpha } else { - // If the incentive is 0, then Validators get 100% of the alpha. - pending_alpha + // If the incentive is 0, then Alpha Validators get . + pending_validator_alpha.saturating_add(pending_server_alpha) }; + let root_alpha = pending_root_alpha; + let owner_cut = pending_owner_cut; let (incentives, (alpha_dividends, root_alpha_dividends)) = Self::calculate_dividend_and_incentive_distribution( netuid, - pending_root_alpha, - pending_validator_alpha, + root_alpha, + validator_alpha, hotkey_emission, tao_weight, ); diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 3989733558..93471186e9 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1434,8 +1434,12 @@ pub mod pallet { pub type PendingEmission = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultPendingEmission>; #[pallet::storage] - /// --- MAP ( netuid ) --> total_emission - pub type TotalEmission = + /// --- MAP ( netuid ) --> pending_server_emission + pub type PendingServerEmission = + StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultZeroAlpha>; + #[pallet::storage] + /// --- MAP ( netuid ) --> pending_validator_emission + pub type PendingValidatorEmission = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultZeroAlpha>; /// --- MAP ( netuid ) --> pending_root_alpha_emission #[pallet::storage] From 6418911d66346df709f42153dfc9e18c43295696 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Fri, 7 Nov 2025 14:08:04 -0500 Subject: [PATCH 114/210] fix args for em distr in tests --- pallets/subtensor/src/tests/claim_root.rs | 28 +++++----- pallets/subtensor/src/tests/coinbase.rs | 66 +++++++++++------------ 2 files changed, 47 insertions(+), 47 deletions(-) diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index 5e8f0a8907..c11d88033f 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -75,7 +75,7 @@ fn test_claim_root_with_drain_emissions() { SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, - pending_root_alpha.into(), + AlphaCurrency::ZERO, pending_root_alpha.into(), AlphaCurrency::ZERO, ); @@ -144,7 +144,7 @@ fn test_claim_root_with_drain_emissions() { SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, - pending_root_alpha.into(), + AlphaCurrency::ZERO, pending_root_alpha.into(), AlphaCurrency::ZERO, ); @@ -246,7 +246,7 @@ fn test_claim_root_adding_stake_proportionally_for_two_stakers() { SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, - pending_root_alpha.into(), + AlphaCurrency::ZERO, pending_root_alpha.into(), AlphaCurrency::ZERO, ); @@ -348,7 +348,7 @@ fn test_claim_root_adding_stake_disproportionally_for_two_stakers() { SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, - pending_root_alpha.into(), + AlphaCurrency::ZERO, pending_root_alpha.into(), AlphaCurrency::ZERO, ); @@ -440,7 +440,7 @@ fn test_claim_root_with_changed_stake() { SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, - pending_root_alpha.into(), + AlphaCurrency::ZERO, pending_root_alpha.into(), AlphaCurrency::ZERO, ); @@ -493,7 +493,7 @@ fn test_claim_root_with_changed_stake() { SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, - pending_root_alpha.into(), + AlphaCurrency::ZERO, pending_root_alpha.into(), AlphaCurrency::ZERO, ); @@ -547,7 +547,7 @@ fn test_claim_root_with_changed_stake() { SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, - pending_root_alpha.into(), + AlphaCurrency::ZERO, pending_root_alpha.into(), AlphaCurrency::ZERO, ); @@ -640,7 +640,7 @@ fn test_claim_root_with_drain_emissions_and_swap_claim_type() { SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, - pending_root_alpha.into(), + AlphaCurrency::ZERO, pending_root_alpha.into(), AlphaCurrency::ZERO, ); @@ -685,7 +685,7 @@ fn test_claim_root_with_drain_emissions_and_swap_claim_type() { SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, - pending_root_alpha.into(), + AlphaCurrency::ZERO, pending_root_alpha.into(), AlphaCurrency::ZERO, ); @@ -722,7 +722,7 @@ fn test_claim_root_with_drain_emissions_and_swap_claim_type() { SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, - pending_root_alpha.into(), + AlphaCurrency::ZERO, pending_root_alpha.into(), AlphaCurrency::ZERO, ); @@ -1148,7 +1148,7 @@ fn test_claim_root_with_swap_coldkey() { SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, - pending_root_alpha.into(), + AlphaCurrency::ZERO, pending_root_alpha.into(), AlphaCurrency::ZERO, ); @@ -1239,7 +1239,7 @@ fn test_claim_root_with_swap_hotkey() { SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, - pending_root_alpha.into(), + AlphaCurrency::ZERO, pending_root_alpha.into(), AlphaCurrency::ZERO, ); @@ -1356,7 +1356,7 @@ fn test_claim_root_on_network_deregistration() { SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, - pending_root_alpha.into(), + AlphaCurrency::ZERO, pending_root_alpha.into(), AlphaCurrency::ZERO, ); @@ -1497,7 +1497,7 @@ fn test_claim_root_with_unrelated_subnets() { SubtensorModule::distribute_emission( netuid, AlphaCurrency::ZERO, - pending_root_alpha.into(), + AlphaCurrency::ZERO, pending_root_alpha.into(), AlphaCurrency::ZERO, ); diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index d97fbfb037..55706b47e3 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -747,9 +747,9 @@ fn test_drain_base_with_subnet_with_single_staker_not_registered() { let pending_alpha = AlphaCurrency::from(1_000_000_000); SubtensorModule::distribute_emission( netuid, - pending_alpha.into(), + pending_alpha.saturating_div(2.into()).into(), + pending_alpha.saturating_div(2.into()).into(), AlphaCurrency::ZERO, - pending_alpha.into(), AlphaCurrency::ZERO, ); let stake_after = @@ -777,9 +777,9 @@ fn test_drain_base_with_subnet_with_single_staker_registered() { let pending_alpha = AlphaCurrency::from(1_000_000_000); SubtensorModule::distribute_emission( netuid, - pending_alpha, + pending_alpha.saturating_div(2.into()).into(), + pending_alpha.saturating_div(2.into()).into(), AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); let stake_after = @@ -822,9 +822,9 @@ fn test_drain_base_with_subnet_with_single_staker_registered_root_weight() { assert_eq!(SubnetTAO::::get(NetUid::ROOT), TaoCurrency::ZERO); SubtensorModule::distribute_emission( netuid, - pending_alpha, + pending_alpha.saturating_div(2.into()).into(), + pending_alpha.saturating_div(2.into()).into(), pending_root_alpha, - pending_alpha.saturating_add(pending_root_alpha), AlphaCurrency::ZERO, ); let stake_after = @@ -870,9 +870,9 @@ fn test_drain_base_with_subnet_with_two_stakers_registered() { let pending_alpha = AlphaCurrency::from(1_000_000_000); SubtensorModule::distribute_emission( netuid, - pending_alpha, + pending_alpha.saturating_div(2.into()).into(), + pending_alpha.saturating_div(2.into()).into(), AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); let stake_after1 = @@ -936,9 +936,9 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root() { assert_eq!(SubnetTAO::::get(NetUid::ROOT), TaoCurrency::ZERO); SubtensorModule::distribute_emission( netuid, - pending_alpha, + pending_alpha.saturating_div(2.into()).into(), + pending_alpha.saturating_div(2.into()).into(), AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); let stake_after1 = @@ -1012,10 +1012,10 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_am assert_eq!(SubnetTAO::::get(NetUid::ROOT), TaoCurrency::ZERO); SubtensorModule::distribute_emission( netuid, - pending_alpha, + pending_alpha.saturating_div(2.into()).into(), + pending_alpha.saturating_div(2.into()).into(), + AlphaCurrency::ZERO, AlphaCurrency::ZERO, - pending_alpha, - 0.into(), ); let stake_after1 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey1, &coldkey, netuid); @@ -1093,9 +1093,9 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_am assert_eq!(SubnetTAO::::get(NetUid::ROOT), TaoCurrency::ZERO); SubtensorModule::distribute_emission( netuid, - pending_alpha, + pending_alpha.saturating_div(2.into()).into(), + pending_alpha.saturating_div(2.into()).into(), AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); let stake_after1 = @@ -1155,9 +1155,9 @@ fn test_drain_alpha_childkey_parentkey() { let pending_alpha = AlphaCurrency::from(1_000_000_000); SubtensorModule::distribute_emission( netuid, - pending_alpha, + pending_alpha.saturating_div(2.into()).into(), + pending_alpha.saturating_div(2.into()).into(), AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); let parent_stake_after = SubtensorModule::get_stake_for_hotkey_on_subnet(&parent, netuid); @@ -1381,9 +1381,9 @@ fn test_get_root_children_drain() { let pending_alpha = AlphaCurrency::from(1_000_000_000); SubtensorModule::distribute_emission( alpha, - pending_alpha, + pending_alpha.saturating_div(2.into()).into(), + pending_alpha.saturating_div(2.into()).into(), AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); @@ -1405,9 +1405,9 @@ fn test_get_root_children_drain() { let pending_root1 = TaoCurrency::from(1_000_000_000); SubtensorModule::distribute_emission( alpha, - pending_alpha, + pending_alpha.saturating_div(2.into()).into(), + pending_alpha.saturating_div(2.into()).into(), AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); @@ -1429,9 +1429,9 @@ fn test_get_root_children_drain() { let pending_root2 = TaoCurrency::from(1_000_000_000); SubtensorModule::distribute_emission( alpha, - pending_alpha, + pending_alpha.saturating_div(2.into()).into(), + pending_alpha.saturating_div(2.into()).into(), AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); @@ -1518,9 +1518,9 @@ fn test_get_root_children_drain_half_proportion() { let pending_alpha = AlphaCurrency::from(1_000_000_000); SubtensorModule::distribute_emission( alpha, - pending_alpha, + pending_alpha.saturating_div(2.into()).into(), + pending_alpha.saturating_div(2.into()).into(), AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); @@ -1605,9 +1605,9 @@ fn test_get_root_children_drain_with_take() { let pending_alpha = AlphaCurrency::from(1_000_000_000); SubtensorModule::distribute_emission( alpha, - pending_alpha, + pending_alpha.saturating_div(2.into()).into(), + pending_alpha.saturating_div(2.into()).into(), AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); @@ -1693,9 +1693,9 @@ fn test_get_root_children_drain_with_half_take() { let pending_alpha = AlphaCurrency::from(1_000_000_000); SubtensorModule::distribute_emission( alpha, - pending_alpha, + pending_alpha.saturating_div(2.into()).into(), + pending_alpha.saturating_div(2.into()).into(), AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); @@ -2409,9 +2409,9 @@ fn test_distribute_emission_no_miners_all_drained() { // Run drain pending without any miners. SubtensorModule::distribute_emission( netuid, - emission, + emission.saturating_div(2.into()).into(), + emission.saturating_div(2.into()).into(), AlphaCurrency::ZERO, - emission, AlphaCurrency::ZERO, ); @@ -2776,9 +2776,9 @@ fn test_drain_alpha_childkey_parentkey_with_burn() { let pending_alpha = AlphaCurrency::from(1_000_000_000); SubtensorModule::distribute_emission( netuid, - pending_alpha, + pending_alpha.saturating_div(2.into()).into(), + pending_alpha.saturating_div(2.into()).into(), AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); let parent_stake_after = SubtensorModule::get_stake_for_hotkey_on_subnet(&parent, netuid); From 264d9c337427783e8efc6ac8aa40571fd53d96dc Mon Sep 17 00:00:00 2001 From: camfairchild Date: Fri, 7 Nov 2025 14:19:12 -0500 Subject: [PATCH 115/210] use new pending em storages --- pallets/subtensor/src/coinbase/root.rs | 2 ++ pallets/subtensor/src/rpc_info/dynamic_info.rs | 4 +++- pallets/subtensor/src/rpc_info/metagraph.rs | 10 ++++++++-- pallets/subtensor/src/tests/children.rs | 3 ++- pallets/subtensor/src/tests/coinbase.rs | 17 ++++++++++++----- pallets/subtensor/src/tests/networks.rs | 6 ++++-- pallets/subtensor/src/utils/misc.rs | 3 --- 7 files changed, 31 insertions(+), 14 deletions(-) diff --git a/pallets/subtensor/src/coinbase/root.rs b/pallets/subtensor/src/coinbase/root.rs index 80965f0bbb..42f04b5754 100644 --- a/pallets/subtensor/src/coinbase/root.rs +++ b/pallets/subtensor/src/coinbase/root.rs @@ -315,6 +315,8 @@ impl Pallet { // --- 15. Mechanism step / emissions bookkeeping. FirstEmissionBlockNumber::::remove(netuid); PendingEmission::::remove(netuid); + PendingValidatorEmission::::remove(netuid); + PendingServerEmission::::remove(netuid); PendingRootAlphaDivs::::remove(netuid); PendingOwnerCut::::remove(netuid); BlocksSinceLastStep::::remove(netuid); diff --git a/pallets/subtensor/src/rpc_info/dynamic_info.rs b/pallets/subtensor/src/rpc_info/dynamic_info.rs index 3bfbda8676..d4f99176ac 100644 --- a/pallets/subtensor/src/rpc_info/dynamic_info.rs +++ b/pallets/subtensor/src/rpc_info/dynamic_info.rs @@ -62,7 +62,9 @@ impl Pallet { alpha_out_emission: SubnetAlphaOutEmission::::get(netuid).into(), alpha_in_emission: SubnetAlphaInEmission::::get(netuid).into(), tao_in_emission: SubnetTaoInEmission::::get(netuid).into(), - pending_alpha_emission: PendingEmission::::get(netuid).into(), + pending_alpha_emission: PendingValidatorEmission::::get(netuid) + .saturating_add(PendingServerEmission::::get(netuid)) + .into(), pending_root_emission: TaoCurrency::from(0u64).into(), subnet_volume: SubnetVolume::::get(netuid).into(), network_registered_at: NetworkRegisteredAt::::get(netuid).into(), diff --git a/pallets/subtensor/src/rpc_info/metagraph.rs b/pallets/subtensor/src/rpc_info/metagraph.rs index df0c8023b0..ea24657aeb 100644 --- a/pallets/subtensor/src/rpc_info/metagraph.rs +++ b/pallets/subtensor/src/rpc_info/metagraph.rs @@ -694,7 +694,9 @@ impl Pallet { alpha_out_emission: SubnetAlphaOutEmission::::get(netuid).into(), // amount injected in alpha reserves per block alpha_in_emission: SubnetAlphaInEmission::::get(netuid).into(), // amount injected outstanding per block tao_in_emission: SubnetTaoInEmission::::get(netuid).into(), // amount of tao injected per block - pending_alpha_emission: PendingEmission::::get(netuid).into(), // pending alpha to be distributed + pending_alpha_emission: PendingValidatorEmission::::get(netuid) + .saturating_add(PendingServerEmission::::get(netuid)) + .into(), // pending alpha to be distributed pending_root_emission: TaoCurrency::from(0u64).into(), // panding tao for root divs to be distributed subnet_volume: subnet_volume.into(), moving_price: SubnetMovingPrice::::get(netuid), @@ -1000,7 +1002,11 @@ impl Pallet { }, Some(SelectiveMetagraphIndex::PendingAlphaEmission) => SelectiveMetagraph { netuid: netuid.into(), - pending_alpha_emission: Some(PendingEmission::::get(netuid).into()), + pending_alpha_emission: Some( + PendingValidatorEmission::::get(netuid) + .saturating_add(PendingServerEmission::::get(netuid)) + .into(), + ), ..Default::default() }, Some(SelectiveMetagraphIndex::PendingRootEmission) => SelectiveMetagraph { diff --git a/pallets/subtensor/src/tests/children.rs b/pallets/subtensor/src/tests/children.rs index 14b7a0b29d..e8be57f021 100644 --- a/pallets/subtensor/src/tests/children.rs +++ b/pallets/subtensor/src/tests/children.rs @@ -3095,7 +3095,8 @@ fn test_parent_child_chain_emission() { ); // Set pending emission to 0 - PendingEmission::::insert(netuid, AlphaCurrency::ZERO); + PendingValidatorEmission::::insert(netuid, AlphaCurrency::ZERO); + PendingServerEmission::::insert(netuid, AlphaCurrency::ZERO); // Run epoch with emission value SubtensorModule::run_coinbase(emission); diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 55706b47e3..5366b3310b 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -686,16 +686,22 @@ fn test_pending_emission() { // 1 TAO / ( 1 + 3 ) = 0.25 * 1 / 2 = 125000000 assert_abs_diff_eq!( - u64::from(PendingEmission::::get(netuid)), - 1_000_000_000 - 125000000, + u64::from(PendingServerEmission::::get(netuid)), + 500_000_000, epsilon = 1 - ); // 1 - swapped. + ); // 1 / 2. + + assert_abs_diff_eq!( + u64::from(PendingValidatorEmission::::get(netuid)), + 500_000_000 - 125000000, + epsilon = 1 + ); // 1 / 2 - swapped. assert_abs_diff_eq!( u64::from(PendingRootAlphaDivs::::get(netuid)), 125000000, epsilon = 1 - ); // 1 / 2 = 125000000 + ); // 1 / 2 * 0.25 --> (from root_prop) }); } @@ -3081,9 +3087,10 @@ fn test_mining_emission_distribution_with_no_root_sell() { // Run again but with some root stake step_block(subnet_tempo - 2); assert_abs_diff_eq!( - PendingEmission::::get(netuid).to_u64(), + PendingServerEmission::::get(netuid).to_u64(), U96F32::saturating_from_num(per_block_emission) .saturating_mul(U96F32::saturating_from_num(subnet_tempo as u64)) + .saturating_mul(U96F32::saturating_from_num(0.5)) // miner cut .saturating_mul(U96F32::saturating_from_num(0.90)) .saturating_to_num::(), epsilon = 100_000_u64.into() diff --git a/pallets/subtensor/src/tests/networks.rs b/pallets/subtensor/src/tests/networks.rs index 3706878bb6..0fa27a99d6 100644 --- a/pallets/subtensor/src/tests/networks.rs +++ b/pallets/subtensor/src/tests/networks.rs @@ -374,7 +374,8 @@ fn dissolve_clears_all_per_subnet_storages() { SubnetMechanism::::insert(net, 1u16); NetworkRegistrationAllowed::::insert(net, true); NetworkPowRegistrationAllowed::::insert(net, true); - PendingEmission::::insert(net, AlphaCurrency::from(1)); + PendingServerEmission::::insert(net, AlphaCurrency::from(1)); + PendingValidatorEmission::::insert(net, AlphaCurrency::from(1)); PendingRootAlphaDivs::::insert(net, AlphaCurrency::from(1)); PendingOwnerCut::::insert(net, AlphaCurrency::from(1)); BlocksSinceLastStep::::insert(net, 1u64); @@ -529,7 +530,8 @@ fn dissolve_clears_all_per_subnet_storages() { assert!(!SubnetMechanism::::contains_key(net)); assert!(!NetworkRegistrationAllowed::::contains_key(net)); assert!(!NetworkPowRegistrationAllowed::::contains_key(net)); - assert!(!PendingEmission::::contains_key(net)); + assert!(!PendingServerEmission::::contains_key(net)); + assert!(!PendingValidatorEmission::::contains_key(net)); assert!(!PendingRootAlphaDivs::::contains_key(net)); assert!(!PendingOwnerCut::::contains_key(net)); assert!(!BlocksSinceLastStep::::contains_key(net)); diff --git a/pallets/subtensor/src/utils/misc.rs b/pallets/subtensor/src/utils/misc.rs index 0ba3df1103..46dc8e42c2 100644 --- a/pallets/subtensor/src/utils/misc.rs +++ b/pallets/subtensor/src/utils/misc.rs @@ -334,9 +334,6 @@ impl Pallet { pub fn get_tempo(netuid: NetUid) -> u16 { Tempo::::get(netuid) } - pub fn get_pending_emission(netuid: NetUid) -> AlphaCurrency { - PendingEmission::::get(netuid) - } pub fn get_last_adjustment_block(netuid: NetUid) -> u64 { LastAdjustmentBlock::::get(netuid) } From 94d093bf61e0d8a24ff7b408dfd4f6cb2717b050 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Fri, 7 Nov 2025 14:45:34 -0500 Subject: [PATCH 116/210] use migration to clear the old pending em map --- pallets/subtensor/src/coinbase/root.rs | 1 - .../subtensor/src/coinbase/run_coinbase.rs | 17 ----- pallets/subtensor/src/lib.rs | 4 - pallets/subtensor/src/macros/hooks.rs | 4 +- .../migrations/migrate_pending_emissions.rs | 73 +++++++++++++++++++ pallets/subtensor/src/migrations/mod.rs | 1 + 6 files changed, 77 insertions(+), 23 deletions(-) create mode 100644 pallets/subtensor/src/migrations/migrate_pending_emissions.rs diff --git a/pallets/subtensor/src/coinbase/root.rs b/pallets/subtensor/src/coinbase/root.rs index 42f04b5754..893f855f3e 100644 --- a/pallets/subtensor/src/coinbase/root.rs +++ b/pallets/subtensor/src/coinbase/root.rs @@ -314,7 +314,6 @@ impl Pallet { // --- 15. Mechanism step / emissions bookkeeping. FirstEmissionBlockNumber::::remove(netuid); - PendingEmission::::remove(netuid); PendingValidatorEmission::::remove(netuid); PendingServerEmission::::remove(netuid); PendingRootAlphaDivs::::remove(netuid); diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 428c23e41e..e25ccf6e27 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -262,23 +262,6 @@ impl Pallet { BlocksSinceLastStep::::insert(netuid, 0); LastMechansimStepBlock::::insert(netuid, current_block); - // Get and drain the PendingEmission. - // deprecated - let pending_emission = PendingEmission::::get(netuid); - PendingEmission::::insert(netuid, AlphaCurrency::ZERO); - // If pending_emission is not zero, we split it between the server emission and the validator emission. - if !pending_emission.is_zero() { - let server_alpha = pending_emission.saturating_div(2.into()); - let validator_alpha = pending_emission.saturating_sub(server_alpha); - - PendingServerEmission::::mutate(netuid, |total| { - *total = total.saturating_add(server_alpha) - }); - PendingValidatorEmission::::mutate(netuid, |total| { - *total = total.saturating_add(validator_alpha) - }); - } - // Get and drain the subnet pending emission. let pending_server_alpha = PendingServerEmission::::get(netuid); PendingServerEmission::::insert(netuid, AlphaCurrency::ZERO); diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 93471186e9..8e53fb9c4e 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1430,10 +1430,6 @@ pub mod pallet { pub type NetworkRegisteredAt = StorageMap<_, Identity, NetUid, u64, ValueQuery, DefaultNetworkRegisteredAt>; #[pallet::storage] - /// --- MAP ( netuid ) --> pending_emission - pub type PendingEmission = - StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultPendingEmission>; - #[pallet::storage] /// --- MAP ( netuid ) --> pending_server_emission pub type PendingServerEmission = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultZeroAlpha>; diff --git a/pallets/subtensor/src/macros/hooks.rs b/pallets/subtensor/src/macros/hooks.rs index 7095ce8ecb..692de0e3fa 100644 --- a/pallets/subtensor/src/macros/hooks.rs +++ b/pallets/subtensor/src/macros/hooks.rs @@ -161,7 +161,9 @@ mod hooks { // Remove obsolete map entries .saturating_add(migrations::migrate_remove_tao_dividends::migrate_remove_tao_dividends::()) // Re-init tao flows - .saturating_add(migrations::migrate_init_tao_flow::migrate_init_tao_flow::()); + .saturating_add(migrations::migrate_init_tao_flow::migrate_init_tao_flow::()) + // Migrate pending emissions + .saturating_add(migrations::migrate_pending_emissions::migrate_pending_emissions::()); weight } diff --git a/pallets/subtensor/src/migrations/migrate_pending_emissions.rs b/pallets/subtensor/src/migrations/migrate_pending_emissions.rs new file mode 100644 index 0000000000..416080f5b2 --- /dev/null +++ b/pallets/subtensor/src/migrations/migrate_pending_emissions.rs @@ -0,0 +1,73 @@ +use super::*; +use frame_support::{storage_alias, traits::Get, weights::Weight}; +use substrate_fixed::types::U96F32; + +pub mod deprecated_pending_emission_format { + use super::*; + + #[storage_alias] + pub(super) type PendingEmission = + StorageMap, Identity, NetUid, AlphaCurrency, ValueQuery>; +} + +pub fn migrate_pending_emissions() -> Weight { + let migration_name = b"migrate_pending_emissions".to_vec(); + let mut weight: Weight = T::DbWeight::get().reads(1); + + // Skip if already executed + if HasMigrationRun::::get(&migration_name) { + log::info!( + target: "runtime", + "Migration '{}' already run - skipping.", + String::from_utf8_lossy(&migration_name) + ); + return weight; + } + log::info!( + "Running migration '{}'", + String::from_utf8_lossy(&migration_name) + ); + + // Pull from PendingEmission and distribute to PendingValidatorEmission and PendingServerEmission + for (netuid, pending_emission) in + deprecated_pending_emission_format::PendingEmission::::iter() + { + // Split up the pending emission into server and validator emission + // Server emission is pending+root_alpha times the 50% miner cut. + let root_alpha: U96F32 = + U96F32::saturating_from_num(PendingRootAlphaDivs::::get(netuid).to_u64()); + let server_emission_float: U96F32 = U96F32::saturating_from_num(pending_emission.to_u64()) + .saturating_add(root_alpha) + .saturating_div(U96F32::saturating_from_num(2)); + let server_emission: AlphaCurrency = + server_emission_float.saturating_to_num::().into(); + let validator_emission = pending_emission.saturating_sub(server_emission); + + PendingValidatorEmission::::mutate(netuid, |total| { + *total = total.saturating_add(validator_emission) + }); + PendingServerEmission::::mutate(netuid, |total| { + *total = total.saturating_add(server_emission) + }); + + weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 2)); + } + + // Kill the map + let removal_result = + deprecated_pending_emission_format::PendingEmission::::clear(u32::MAX, None); + weight = weight.saturating_add( + T::DbWeight::get().reads_writes(removal_result.loops as u64, removal_result.backend as u64), + ); + + // Mark Migration as Completed + HasMigrationRun::::insert(&migration_name, true); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + + log::info!( + "Migration '{:?}' completed successfully.", + String::from_utf8_lossy(&migration_name) + ); + + weight +} diff --git a/pallets/subtensor/src/migrations/mod.rs b/pallets/subtensor/src/migrations/mod.rs index 7fe37617fd..b6aac3d7d8 100644 --- a/pallets/subtensor/src/migrations/mod.rs +++ b/pallets/subtensor/src/migrations/mod.rs @@ -27,6 +27,7 @@ pub mod migrate_network_immunity_period; pub mod migrate_network_lock_cost_2500; pub mod migrate_network_lock_reduction_interval; pub mod migrate_orphaned_storage_items; +pub mod migrate_pending_emissions; pub mod migrate_populate_owned_hotkeys; pub mod migrate_rao; pub mod migrate_rate_limit_keys; From c2e6ab702190f1678898dca890e79e1655f93281 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Fri, 7 Nov 2025 15:39:49 -0500 Subject: [PATCH 117/210] amend comment --- pallets/subtensor/src/coinbase/run_coinbase.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index e25ccf6e27..33f05b68a4 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -687,7 +687,7 @@ impl Pallet { let validator_alpha = if !incentive_sum.is_zero() { pending_validator_alpha } else { - // If the incentive is 0, then Alpha Validators get . + // If the incentive is 0, then Alpha Validators get both the server and validator alpha. pending_validator_alpha.saturating_add(pending_server_alpha) }; let root_alpha = pending_root_alpha; From e931123e664877ffdd326eb44056c3a453717158 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Fri, 7 Nov 2025 17:58:32 -0500 Subject: [PATCH 118/210] Update pallets/subtensor/src/lib.rs Co-authored-by: Loris Moulin <45130584+l0r1s@users.noreply.github.com> --- pallets/subtensor/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 8e53fb9c4e..6c3ee1d84e 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1433,8 +1433,8 @@ pub mod pallet { /// --- MAP ( netuid ) --> pending_server_emission pub type PendingServerEmission = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultZeroAlpha>; - #[pallet::storage] /// --- MAP ( netuid ) --> pending_validator_emission + #[pallet::storage] pub type PendingValidatorEmission = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultZeroAlpha>; /// --- MAP ( netuid ) --> pending_root_alpha_emission From 53db5e08fd2119b580682166fd63f24986b4ee51 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Sat, 8 Nov 2025 13:18:57 -0500 Subject: [PATCH 119/210] use get_sn_to_emit_to fn and test --- .../subtensor/src/coinbase/run_coinbase.rs | 7 +- pallets/subtensor/src/tests/coinbase.rs | 81 +++++++++++++++++++ 2 files changed, 86 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 33f05b68a4..45eec7ddec 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -31,9 +31,12 @@ impl Pallet { .collect(); log::debug!("All subnet netuids: {subnets:?}"); + let subnets_to_emit_to: Vec = Self::get_subnets_to_emit_to(&subnets); + log::debug!("Subnets to emit to: {subnets_to_emit_to:?}"); + // 2. Get subnets to emit to and emissions - let subnet_emissions = Self::get_subnet_block_emissions(&subnets, block_emission); - let subnets_to_emit_to: Vec = subnet_emissions.keys().copied().collect(); + let subnet_emissions = + Self::get_subnet_block_emissions(&subnets_to_emit_to, block_emission); let root_sell_flag = Self::get_network_root_sell_flag(&subnets_to_emit_to); // --- 3. Get subnet terms (tao_in, alpha_in, and alpha_out) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 5366b3310b..796506dffc 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -3317,3 +3317,84 @@ fn test_mining_emission_distribution_with_root_sell() { ); }); } + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_pending_emission_start_call_not_done --exact --show-output --nocapture +#[test] +fn test_pending_emission_start_call_not_done() { + new_test_ext(1).execute_with(|| { + let validator_coldkey = U256::from(1); + let validator_hotkey = U256::from(2); + let subnet_tempo = 10; + let stake: u64 = 100_000_000_000; + let root_stake: u64 = 200_000_000_000; // 200 TAO + + // Create root network + NetworksAdded::::insert(NetUid::ROOT, true); + // enabled root + SubtokenEnabled::::insert(NetUid::ROOT, true); + + // Add network, register hotkeys, and setup network parameters + let owner_hotkey = U256::from(10); + let owner_coldkey = U256::from(11); + let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); + Tempo::::insert(netuid, subnet_tempo); + + register_ok_neuron(netuid, validator_hotkey, validator_coldkey, 0); + SubtensorModule::add_balance_to_coldkey_account( + &validator_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::set_weights_set_rate_limit(netuid, 0); + step_block(subnet_tempo); + SubnetOwnerCut::::set(u16::MAX / 10); + // There are two validators and three neurons + MaxAllowedUids::::set(netuid, 3); + SubtensorModule::set_max_allowed_validators(netuid, 2); + + // Add stake to validator so it has root stake + SubtensorModule::add_balance_to_coldkey_account(&validator_coldkey, root_stake.into()); + // init root + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_coldkey), + validator_hotkey, + NetUid::ROOT, + root_stake.into() + )); + // Set tao weight non zero + SubtensorModule::set_tao_weight(u64::MAX / 10); + + // Make root sell happen + // Set moving price > 1.0 + // Set price > 1.0 + pallet_subtensor_swap::AlphaSqrtPrice::::insert( + netuid, + U64F64::saturating_from_num(10.0), + ); + + SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); + + // Make sure we are root selling, so we have root alpha divs. + let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); + assert!(root_sell_flag, "Root sell flag should be true"); + + // !!! Check that the subnet FirstEmissionBlockNumber is None -- no entry + assert!(FirstEmissionBlockNumber::::get(netuid).is_none()); + + // Run run_coinbase until emissions are accumulated + step_block(subnet_tempo - 2); + + // Verify that all pending emissions are zero + assert_eq!( + PendingServerEmission::::get(netuid), + AlphaCurrency::ZERO + ); + assert_eq!( + PendingValidatorEmission::::get(netuid), + AlphaCurrency::ZERO + ); + assert_eq!( + PendingRootAlphaDivs::::get(netuid), + AlphaCurrency::ZERO + ); + }); +} From 0c564183ac706a3ce17427c731ac4e1406f68473 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Sat, 8 Nov 2025 13:36:31 -0500 Subject: [PATCH 120/210] spec bump --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 45e470be80..3db1c38291 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 343, + spec_version: 344, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 5e0028d41c9179a2defa3626bcce9368db4d9112 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Sat, 8 Nov 2025 13:36:45 -0500 Subject: [PATCH 121/210] add mirgation to reset inactive SNs --- pallets/subtensor/src/macros/hooks.rs | 4 +- .../migrations/migrate_reset_unactive_sn.rs | 99 +++++++++++ pallets/subtensor/src/migrations/mod.rs | 1 + pallets/subtensor/src/tests/migration.rs | 163 +++++++++++++++++- 4 files changed, 265 insertions(+), 2 deletions(-) create mode 100644 pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs diff --git a/pallets/subtensor/src/macros/hooks.rs b/pallets/subtensor/src/macros/hooks.rs index 692de0e3fa..56980b8a24 100644 --- a/pallets/subtensor/src/macros/hooks.rs +++ b/pallets/subtensor/src/macros/hooks.rs @@ -163,7 +163,9 @@ mod hooks { // Re-init tao flows .saturating_add(migrations::migrate_init_tao_flow::migrate_init_tao_flow::()) // Migrate pending emissions - .saturating_add(migrations::migrate_pending_emissions::migrate_pending_emissions::()); + .saturating_add(migrations::migrate_pending_emissions::migrate_pending_emissions::()) + // Reset unactive subnets + .saturating_add(migrations::migrate_reset_unactive_sn::migrate_reset_unactive_sn::()); weight } diff --git a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs new file mode 100644 index 0000000000..3549fee2f0 --- /dev/null +++ b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs @@ -0,0 +1,99 @@ +use super::*; + +pub fn migrate_reset_unactive_sn() -> Weight { + let migration_name = b"migrate_reset_unactive_sn".to_vec(); + let mut weight: Weight = T::DbWeight::get().reads(1); + + // Skip if already executed + if HasMigrationRun::::get(&migration_name) { + log::info!( + target: "runtime", + "Migration '{}' already run - skipping.", + String::from_utf8_lossy(&migration_name) + ); + return weight; + } + log::info!( + "Running migration '{}'", + String::from_utf8_lossy(&migration_name) + ); + + // Loop over all subnets, if the AlphaIssuance is > 10, but FirstEmissionBlockNumber is None + // then we reset the subnet + for netuid in Pallet::::get_all_subnet_netuids() + .iter() + .filter(|&netuid| !netuid.is_root()) + { + let alpha_issuance = Pallet::::get_alpha_issuance(*netuid); + let first_emission_block_number = FirstEmissionBlockNumber::::get(*netuid); + weight = weight.saturating_add(T::DbWeight::get().reads(3)); + if alpha_issuance > AlphaCurrency::from(1_000_000_000) + && first_emission_block_number.is_none() + { + // Reset the subnet as it shouldn'ty have any emissions + PendingServerEmission::::remove(*netuid); + PendingValidatorEmission::::remove(*netuid); + PendingRootAlphaDivs::::remove(*netuid); + PendingOwnerCut::::remove(*netuid); + + // Reset pool + let pool_initial_tao: TaoCurrency = Pallet::::get_network_min_lock(); + let pool_initial_alpha: AlphaCurrency = pool_initial_tao.to_u64().into(); + + // Recycle already emitted TAO + let subnet_tao = SubnetTAO::::get(*netuid); + if subnet_tao > pool_initial_tao { + Pallet::::recycle_tao(subnet_tao.saturating_sub(pool_initial_tao)); + } + SubnetTAO::::insert(*netuid, pool_initial_tao); + + // Reset pool alpha + SubnetAlphaIn::::insert(*netuid, pool_initial_alpha); + SubnetAlphaOut::::insert(*netuid, AlphaCurrency::ZERO); + // Reset volume + SubnetVolume::::insert(*netuid, 0u128); + + // Reset Alpha stake entries for this subnet + let mut to_reset = Vec::new(); + for (hotkey, _, alpha) in + TotalHotkeyAlpha::::iter().filter(|(_, netuid_, _)| *netuid_ == *netuid) + { + if alpha > AlphaCurrency::from(0) { + to_reset.push((hotkey, netuid, alpha)); + } + } + for (hotkey, netuid, _) in to_reset { + TotalHotkeyAlpha::::remove(&hotkey, netuid); + TotalHotkeyShares::::remove(&hotkey, netuid); + TotalHotkeyAlphaLastEpoch::::remove(&hotkey, netuid); + + // Reset root claimable and claimed + RootClaimable::::mutate(&hotkey, |claimable| { + claimable.remove(netuid); + }); + let _ = RootClaimed::::clear_prefix((netuid, &hotkey), u32::MAX, None); + + let mut to_reset_alpha: Vec<(&T::AccountId, T::AccountId, NetUid)> = Vec::new(); + for ((coldkey, _), _) in Alpha::::iter_prefix((&hotkey,)) + .filter(|((_, netuid_), _)| *netuid_ == *netuid) + { + to_reset_alpha.push((&hotkey, coldkey, *netuid)); + } + for (hotkey, coldkey, netuid) in to_reset_alpha { + Alpha::::remove((hotkey, coldkey, netuid)); + } + } + } + } + + // Mark Migration as Completed + HasMigrationRun::::insert(&migration_name, true); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + + log::info!( + "Migration '{:?}' completed successfully.", + String::from_utf8_lossy(&migration_name) + ); + + weight +} diff --git a/pallets/subtensor/src/migrations/mod.rs b/pallets/subtensor/src/migrations/mod.rs index b6aac3d7d8..f5ffa8c434 100644 --- a/pallets/subtensor/src/migrations/mod.rs +++ b/pallets/subtensor/src/migrations/mod.rs @@ -41,6 +41,7 @@ pub mod migrate_remove_unused_maps_and_values; pub mod migrate_remove_zero_total_hotkey_alpha; pub mod migrate_reset_bonds_moving_average; pub mod migrate_reset_max_burn; +pub mod migrate_reset_unactive_sn; pub mod migrate_set_first_emission_block_number; pub mod migrate_set_min_burn; pub mod migrate_set_min_difficulty; diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index 28159d41bc..2137503de3 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -26,8 +26,8 @@ use scale_info::prelude::collections::VecDeque; use sp_core::{H256, U256, crypto::Ss58Codec}; use sp_io::hashing::twox_128; use sp_runtime::traits::Zero; -use substrate_fixed::types::I96F32; use substrate_fixed::types::extra::U2; +use substrate_fixed::types::{I96F32, U64F64}; use subtensor_runtime_common::{NetUidStorageIndex, TaoCurrency}; #[allow(clippy::arithmetic_side_effects)] @@ -2397,3 +2397,164 @@ fn test_migrate_remove_tao_dividends() { 200_000, ); } + +#[test] +fn test_migrate_reset_unactive_sn() { + new_test_ext(1).execute_with(|| { + // Register some subnets + let netuid0 = add_dynamic_network(&U256::from(0), &U256::from(0)); + let netuid1 = add_dynamic_network(&U256::from(1), &U256::from(1)); + let netuid2 = add_dynamic_network(&U256::from(2), &U256::from(2)); + let inactive_netuids = vec![netuid0, netuid1, netuid2]; + // Add active subnets + let netuid3 = add_dynamic_network(&U256::from(3), &U256::from(3)); + let netuid4 = add_dynamic_network(&U256::from(4), &U256::from(4)); + let netuid5 = add_dynamic_network(&U256::from(5), &U256::from(5)); + let active_netuids = vec![netuid3, netuid4, netuid5]; + let netuids: Vec = inactive_netuids + .iter() + .chain(active_netuids.iter()) + .copied() + .collect(); + + // Add stake to the subnet pools + for netuid in &netuids { + SubnetTAO::::insert(netuid, TaoCurrency::from(123123_u64)); + SubnetAlphaIn::::insert(netuid, AlphaCurrency::from(123123_u64)); + SubnetAlphaOut::::insert(netuid, AlphaCurrency::from(123123_u64)); + SubnetVolume::::insert(netuid, 123123_u128); + SubnetLocked::::insert(netuid, TaoCurrency::from(399999_u64)); + + // Make sure the FirstEmissionBlockNumber is None + assert!(FirstEmissionBlockNumber::::get(netuid).is_none()); + } + for netuid in &active_netuids { + // Set the FirstEmissionBlockNumber for the active subnet + FirstEmissionBlockNumber::::insert(netuid, 100); + } + + let alpha_amt = AlphaCurrency::from(123123_u64); + let tao_amt = TaoCurrency::from(333333_u64); + // Create some Stake entries + for netuid in &netuids { + for hotkey in 0..10 { + let hk = U256::from(hotkey); + TotalHotkeyAlpha::::insert(hk, netuid, alpha_amt); + TotalHotkeyShares::::insert(hk, netuid, U64F64::from(123123_u64)); + TotalHotkeyAlphaLastEpoch::::insert(hk, netuid, alpha_amt); + + let mut claimable: BTreeMap = BTreeMap::new(); + claimable.insert(*netuid, I96F32::from(alpha_amt.to_u64())); + RootClaimable::::insert(hk, claimable); + for coldkey in 0..10 { + let ck = U256::from(coldkey); + Alpha::::insert((hk, ck, netuid), U64F64::from(123_u64)); + RootClaimed::::insert((netuid, hk, ck), 222_u128); + } + } + } + // Add some pending emissions + let alpha_em_amt = AlphaCurrency::from(355555_u64); + for netuid in &netuids { + PendingServerEmission::::insert(netuid, alpha_em_amt); + PendingValidatorEmission::::insert(netuid, alpha_em_amt); + PendingRootAlphaDivs::::insert(netuid, alpha_em_amt); + PendingOwnerCut::::insert(netuid, alpha_em_amt); + } + + // Run the migration + let w = crate::migrations::migrate_reset_unactive_sn::migrate_reset_unactive_sn::(); + assert!(!w.is_zero(), "weight must be non-zero"); + + // Verify the results + let initial_tao = Pallet::::get_network_min_lock(); + let initial_alpha = initial_tao.to_u64().into(); + for netuid in &inactive_netuids { + assert_eq!( + PendingServerEmission::::get(netuid), + AlphaCurrency::ZERO + ); + assert_eq!( + PendingValidatorEmission::::get(netuid), + AlphaCurrency::ZERO + ); + assert_eq!( + PendingRootAlphaDivs::::get(netuid), + AlphaCurrency::ZERO + ); + assert_eq!(PendingOwnerCut::::get(netuid), AlphaCurrency::ZERO); + assert_eq!(SubnetTAO::::get(netuid), initial_tao); + assert_eq!(SubnetAlphaIn::::get(netuid), initial_alpha); + assert_eq!(SubnetAlphaOut::::get(netuid), AlphaCurrency::ZERO); + assert_eq!(SubnetVolume::::get(netuid), 0u128); + for hotkey in 0..10 { + let hk = U256::from(hotkey); + assert_eq!( + TotalHotkeyAlpha::::get(hk, netuid), + AlphaCurrency::ZERO + ); + assert_eq!( + TotalHotkeyShares::::get(hk, netuid), + U64F64::from_num(0.0) + ); + assert_eq!( + TotalHotkeyAlphaLastEpoch::::get(hk, netuid), + AlphaCurrency::ZERO + ); + assert_eq!(RootClaimable::::get(hk).get(netuid), None); + for coldkey in 0..10 { + let ck = U256::from(coldkey); + assert_eq!(Alpha::::get((hk, ck, netuid)), U64F64::from_num(0.0)); + assert_eq!(RootClaimed::::get((netuid, hk, ck)), 0u128); + } + } + + // Don't touch SubnetLocked + assert_ne!(SubnetLocked::::get(netuid), TaoCurrency::ZERO); + } + + // !!! Make sure the active subnets were not reset + for netuid in &active_netuids { + assert_ne!( + PendingServerEmission::::get(netuid), + AlphaCurrency::ZERO + ); + assert_ne!( + PendingValidatorEmission::::get(netuid), + AlphaCurrency::ZERO + ); + assert_ne!( + PendingRootAlphaDivs::::get(netuid), + AlphaCurrency::ZERO + ); + assert_ne!(PendingOwnerCut::::get(netuid), AlphaCurrency::ZERO); + assert_ne!(SubnetTAO::::get(netuid), initial_tao); + assert_ne!(SubnetAlphaIn::::get(netuid), initial_alpha); + assert_ne!(SubnetAlphaOut::::get(netuid), AlphaCurrency::ZERO); + assert_ne!(SubnetVolume::::get(netuid), 0u128); + for hotkey in 0..10 { + let hk = U256::from(hotkey); + assert_ne!( + TotalHotkeyAlpha::::get(hk, netuid), + AlphaCurrency::ZERO + ); + assert_ne!( + TotalHotkeyShares::::get(hk, netuid), + U64F64::from_num(0.0) + ); + assert_ne!( + TotalHotkeyAlphaLastEpoch::::get(hk, netuid), + AlphaCurrency::ZERO + ); + assert_ne!(RootClaimable::::get(hk).get(netuid), None); + for coldkey in 0..10 { + let ck = U256::from(coldkey); + assert_ne!(Alpha::::get((hk, ck, netuid)), U64F64::from_num(0.0)); + assert_ne!(RootClaimed::::get((netuid, hk, ck)), 0u128); + } + } + // Don't touch SubnetLocked + assert_ne!(SubnetLocked::::get(netuid), TaoCurrency::ZERO); + } + }); +} From 1346bca206057e2fc87870f1379c07c3e27c5ab1 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Sat, 8 Nov 2025 13:40:47 -0500 Subject: [PATCH 122/210] set firstemissionblock in tests --- pallets/subtensor/src/tests/coinbase.rs | 2 ++ pallets/subtensor/src/tests/migration.rs | 7 +++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 796506dffc..2ac4a24c4e 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -3337,6 +3337,8 @@ fn test_pending_emission_start_call_not_done() { let owner_hotkey = U256::from(10); let owner_coldkey = U256::from(11); let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); + // Remove FirstEmissionBlockNumber + FirstEmissionBlockNumber::::remove(netuid); Tempo::::insert(netuid, subnet_tempo); register_ok_neuron(netuid, validator_hotkey, validator_coldkey, 0); diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index 2137503de3..5790f06c15 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -2425,12 +2425,15 @@ fn test_migrate_reset_unactive_sn() { SubnetVolume::::insert(netuid, 123123_u128); SubnetLocked::::insert(netuid, TaoCurrency::from(399999_u64)); - // Make sure the FirstEmissionBlockNumber is None - assert!(FirstEmissionBlockNumber::::get(netuid).is_none()); + // Remove the FirstEmissionBlockNumber + FirstEmissionBlockNumber::::remove(netuid); + SubtokenEnabled::::remove(netuid); } for netuid in &active_netuids { // Set the FirstEmissionBlockNumber for the active subnet FirstEmissionBlockNumber::::insert(netuid, 100); + // Also set SubtokenEnabled to true + SubtokenEnabled::::insert(netuid, true); } let alpha_amt = AlphaCurrency::from(123123_u64); From 0fb1ea71478986470af241885a6449ece5597e69 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Sat, 8 Nov 2025 13:44:27 -0500 Subject: [PATCH 123/210] use pool initial alpha --- .../src/migrations/migrate_reset_unactive_sn.rs | 10 +++++----- pallets/subtensor/src/tests/migration.rs | 9 +++++---- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs index 3549fee2f0..3614b808cc 100644 --- a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs +++ b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs @@ -18,6 +18,10 @@ pub fn migrate_reset_unactive_sn() -> Weight { String::from_utf8_lossy(&migration_name) ); + // From init_new_network + let pool_initial_tao: TaoCurrency = Pallet::::get_network_min_lock(); + let pool_initial_alpha: AlphaCurrency = pool_initial_tao.to_u64().into(); + // Loop over all subnets, if the AlphaIssuance is > 10, but FirstEmissionBlockNumber is None // then we reset the subnet for netuid in Pallet::::get_all_subnet_netuids() @@ -27,9 +31,7 @@ pub fn migrate_reset_unactive_sn() -> Weight { let alpha_issuance = Pallet::::get_alpha_issuance(*netuid); let first_emission_block_number = FirstEmissionBlockNumber::::get(*netuid); weight = weight.saturating_add(T::DbWeight::get().reads(3)); - if alpha_issuance > AlphaCurrency::from(1_000_000_000) - && first_emission_block_number.is_none() - { + if alpha_issuance != pool_initial_alpha && first_emission_block_number.is_none() { // Reset the subnet as it shouldn'ty have any emissions PendingServerEmission::::remove(*netuid); PendingValidatorEmission::::remove(*netuid); @@ -37,8 +39,6 @@ pub fn migrate_reset_unactive_sn() -> Weight { PendingOwnerCut::::remove(*netuid); // Reset pool - let pool_initial_tao: TaoCurrency = Pallet::::get_network_min_lock(); - let pool_initial_alpha: AlphaCurrency = pool_initial_tao.to_u64().into(); // Recycle already emitted TAO let subnet_tao = SubnetTAO::::get(*netuid); diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index 5790f06c15..b0c3b658c2 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -2417,10 +2417,13 @@ fn test_migrate_reset_unactive_sn() { .copied() .collect(); + let initial_tao = Pallet::::get_network_min_lock(); + let initial_alpha = initial_tao.to_u64().into(); + // Add stake to the subnet pools for netuid in &netuids { - SubnetTAO::::insert(netuid, TaoCurrency::from(123123_u64)); - SubnetAlphaIn::::insert(netuid, AlphaCurrency::from(123123_u64)); + SubnetTAO::::insert(netuid, initial_tao + TaoCurrency::from(123123_u64)); + SubnetAlphaIn::::insert(netuid, initial_alpha + AlphaCurrency::from(123123_u64)); SubnetAlphaOut::::insert(netuid, AlphaCurrency::from(123123_u64)); SubnetVolume::::insert(netuid, 123123_u128); SubnetLocked::::insert(netuid, TaoCurrency::from(399999_u64)); @@ -2470,8 +2473,6 @@ fn test_migrate_reset_unactive_sn() { assert!(!w.is_zero(), "weight must be non-zero"); // Verify the results - let initial_tao = Pallet::::get_network_min_lock(); - let initial_alpha = initial_tao.to_u64().into(); for netuid in &inactive_netuids { assert_eq!( PendingServerEmission::::get(netuid), From e974c0bbf0eaf274eec593b30368a24a63ed4e96 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Sat, 8 Nov 2025 13:47:46 -0500 Subject: [PATCH 124/210] comment --- pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs index 3614b808cc..fb28af6d80 100644 --- a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs +++ b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs @@ -22,7 +22,8 @@ pub fn migrate_reset_unactive_sn() -> Weight { let pool_initial_tao: TaoCurrency = Pallet::::get_network_min_lock(); let pool_initial_alpha: AlphaCurrency = pool_initial_tao.to_u64().into(); - // Loop over all subnets, if the AlphaIssuance is > 10, but FirstEmissionBlockNumber is None + // Loop over all subnets, if the AlphaIssuance is > pool_initial_alpha + // but FirstEmissionBlockNumber is None // then we reset the subnet for netuid in Pallet::::get_all_subnet_netuids() .iter() From 92736710591a35a0c75e2f4e892bde0e766b2510 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Sat, 8 Nov 2025 13:50:41 -0500 Subject: [PATCH 125/210] insert properly to the btreemap --- pallets/subtensor/src/tests/migration.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index b0c3b658c2..3f05852b0f 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -2450,7 +2450,10 @@ fn test_migrate_reset_unactive_sn() { TotalHotkeyAlphaLastEpoch::::insert(hk, netuid, alpha_amt); let mut claimable: BTreeMap = BTreeMap::new(); - claimable.insert(*netuid, I96F32::from(alpha_amt.to_u64())); + claimable + .entry(*netuid) + .and_modify(|v| *v = I96F32::from(alpha_amt.to_u64())) + .or_insert(I96F32::from(alpha_amt.to_u64())); RootClaimable::::insert(hk, claimable); for coldkey in 0..10 { let ck = U256::from(coldkey); From b68847e0028f1541b3d9830986988e72095f6ee4 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Sat, 8 Nov 2025 13:51:22 -0500 Subject: [PATCH 126/210] docs: typo --- pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs index fb28af6d80..0ef0bb3b42 100644 --- a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs +++ b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs @@ -33,7 +33,7 @@ pub fn migrate_reset_unactive_sn() -> Weight { let first_emission_block_number = FirstEmissionBlockNumber::::get(*netuid); weight = weight.saturating_add(T::DbWeight::get().reads(3)); if alpha_issuance != pool_initial_alpha && first_emission_block_number.is_none() { - // Reset the subnet as it shouldn'ty have any emissions + // Reset the subnet as it shouldn't have any emissions PendingServerEmission::::remove(*netuid); PendingValidatorEmission::::remove(*netuid); PendingRootAlphaDivs::::remove(*netuid); From 9c07bd0687582789cabe9b10d1e064ea4b0a25fa Mon Sep 17 00:00:00 2001 From: camfairchild Date: Sat, 8 Nov 2025 14:05:30 -0500 Subject: [PATCH 127/210] use mutate instead --- .../migrations/migrate_reset_unactive_sn.rs | 18 +++++++++--------- pallets/subtensor/src/tests/migration.rs | 11 ++++------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs index 0ef0bb3b42..167121693e 100644 --- a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs +++ b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs @@ -63,25 +63,25 @@ pub fn migrate_reset_unactive_sn() -> Weight { to_reset.push((hotkey, netuid, alpha)); } } - for (hotkey, netuid, _) in to_reset { - TotalHotkeyAlpha::::remove(&hotkey, netuid); - TotalHotkeyShares::::remove(&hotkey, netuid); - TotalHotkeyAlphaLastEpoch::::remove(&hotkey, netuid); + for (hotkey, netuid_, _) in to_reset { + TotalHotkeyAlpha::::remove(&hotkey, netuid_); + TotalHotkeyShares::::remove(&hotkey, netuid_); + TotalHotkeyAlphaLastEpoch::::remove(&hotkey, netuid_); // Reset root claimable and claimed RootClaimable::::mutate(&hotkey, |claimable| { - claimable.remove(netuid); + claimable.remove(netuid_); }); - let _ = RootClaimed::::clear_prefix((netuid, &hotkey), u32::MAX, None); + let _ = RootClaimed::::clear_prefix((netuid_, &hotkey), u32::MAX, None); let mut to_reset_alpha: Vec<(&T::AccountId, T::AccountId, NetUid)> = Vec::new(); for ((coldkey, _), _) in Alpha::::iter_prefix((&hotkey,)) .filter(|((_, netuid_), _)| *netuid_ == *netuid) { - to_reset_alpha.push((&hotkey, coldkey, *netuid)); + to_reset_alpha.push((&hotkey, coldkey, *netuid_)); } - for (hotkey, coldkey, netuid) in to_reset_alpha { - Alpha::::remove((hotkey, coldkey, netuid)); + for (hotkey, coldkey, netuid_) in to_reset_alpha { + Alpha::::remove((hotkey, coldkey, netuid_)); } } } diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index 3f05852b0f..ec8a349542 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -2449,12 +2449,9 @@ fn test_migrate_reset_unactive_sn() { TotalHotkeyShares::::insert(hk, netuid, U64F64::from(123123_u64)); TotalHotkeyAlphaLastEpoch::::insert(hk, netuid, alpha_amt); - let mut claimable: BTreeMap = BTreeMap::new(); - claimable - .entry(*netuid) - .and_modify(|v| *v = I96F32::from(alpha_amt.to_u64())) - .or_insert(I96F32::from(alpha_amt.to_u64())); - RootClaimable::::insert(hk, claimable); + RootClaimable::::mutate(hk, |claimable| { + claimable.insert(*netuid, I96F32::from(alpha_amt.to_u64())); + }); for coldkey in 0..10 { let ck = U256::from(coldkey); Alpha::::insert((hk, ck, netuid), U64F64::from(123_u64)); @@ -2553,7 +2550,7 @@ fn test_migrate_reset_unactive_sn() { TotalHotkeyAlphaLastEpoch::::get(hk, netuid), AlphaCurrency::ZERO ); - assert_ne!(RootClaimable::::get(hk).get(netuid), None); + assert!(RootClaimable::::get(hk).contains_key(netuid)); for coldkey in 0..10 { let ck = U256::from(coldkey); assert_ne!(Alpha::::get((hk, ck, netuid)), U64F64::from_num(0.0)); From 5be2d44f8ec4206f2d1a5655b2bcc69c18e6fed9 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Sat, 8 Nov 2025 14:58:32 -0500 Subject: [PATCH 128/210] use explicit filter for correct SNs and test --- .../migrations/migrate_reset_unactive_sn.rs | 43 +++++++---- pallets/subtensor/src/tests/migration.rs | 73 ++++++++++++++++++- 2 files changed, 99 insertions(+), 17 deletions(-) diff --git a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs index 167121693e..089fba3292 100644 --- a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs +++ b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs @@ -1,5 +1,28 @@ use super::*; +pub fn get_unactive_sn_netuids( + pool_initial_alpha: AlphaCurrency, +) -> (Vec, Weight) { + // Loop over all subnets, if the AlphaIssuance is > pool_initial_alpha + // but FirstEmissionBlockNumber is None + // then this subnet should be reset + let mut weight = T::DbWeight::get().reads(1); + let unactive_netuids = Pallet::::get_all_subnet_netuids() + .iter() + .filter(|&netuid| !netuid.is_root()) + .filter(|&netuid| { + let alpha_issuance = Pallet::::get_alpha_issuance(*netuid); + let first_emission_block_number = FirstEmissionBlockNumber::::get(*netuid); + alpha_issuance != pool_initial_alpha && first_emission_block_number.is_none() + }) + .copied() + .collect::>(); + weight = weight + .saturating_add(T::DbWeight::get().reads(unactive_netuids.len().saturating_mul(3) as u64)); + + (unactive_netuids, weight) +} + pub fn migrate_reset_unactive_sn() -> Weight { let migration_name = b"migrate_reset_unactive_sn".to_vec(); let mut weight: Weight = T::DbWeight::get().reads(1); @@ -22,22 +45,10 @@ pub fn migrate_reset_unactive_sn() -> Weight { let pool_initial_tao: TaoCurrency = Pallet::::get_network_min_lock(); let pool_initial_alpha: AlphaCurrency = pool_initial_tao.to_u64().into(); - // Loop over all subnets, if the AlphaIssuance is > pool_initial_alpha - // but FirstEmissionBlockNumber is None - // then we reset the subnet - for netuid in Pallet::::get_all_subnet_netuids() - .iter() - .filter(|&netuid| !netuid.is_root()) - { - let alpha_issuance = Pallet::::get_alpha_issuance(*netuid); - let first_emission_block_number = FirstEmissionBlockNumber::::get(*netuid); - weight = weight.saturating_add(T::DbWeight::get().reads(3)); - if alpha_issuance != pool_initial_alpha && first_emission_block_number.is_none() { - // Reset the subnet as it shouldn't have any emissions - PendingServerEmission::::remove(*netuid); - PendingValidatorEmission::::remove(*netuid); - PendingRootAlphaDivs::::remove(*netuid); - PendingOwnerCut::::remove(*netuid); + let (unactive_netuids, w) = get_unactive_sn_netuids::(pool_initial_alpha); + weight = weight.saturating_add(w); + + for netuid in unactive_netuids.iter() { // Reset pool diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index ec8a349542..add0c1f05b 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -2399,7 +2399,7 @@ fn test_migrate_remove_tao_dividends() { } #[test] -fn test_migrate_reset_unactive_sn() { +fn test_migrate_reset_unactive_sn_get_unactive_netuids() { new_test_ext(1).execute_with(|| { // Register some subnets let netuid0 = add_dynamic_network(&U256::from(0), &U256::from(0)); @@ -2419,7 +2419,78 @@ fn test_migrate_reset_unactive_sn() { let initial_tao = Pallet::::get_network_min_lock(); let initial_alpha = initial_tao.to_u64().into(); + let actual_tao_lock_amount = TaoCurrency::from(322222229_u64); + let actual_tao_lock_amount_less_pool_tao = + actual_tao_lock_amount.saturating_sub(initial_tao); + // Add stake to the subnet pools + for netuid in &netuids { + SubnetTAO::::insert(netuid, initial_tao + TaoCurrency::from(123123_u64)); + SubnetAlphaIn::::insert(netuid, initial_alpha + AlphaCurrency::from(123123_u64)); + SubnetAlphaOut::::insert(netuid, AlphaCurrency::from(123123_u64)); + SubnetVolume::::insert(netuid, 123123_u128); + SubnetLocked::::insert(netuid, actual_tao_lock_amount); + RAORecycledForRegistration::::insert( + netuid, + actual_tao_lock_amount_less_pool_tao, + ); + + // Set AlphaSqrtPrice + pallet_subtensor_swap::AlphaSqrtPrice::::insert(netuid, U64F64::from(123123_u64)); + // Remove the FirstEmissionBlockNumber + FirstEmissionBlockNumber::::remove(netuid); + SubtokenEnabled::::remove(netuid); + } + for netuid in &active_netuids { + // Set the FirstEmissionBlockNumber for the active subnet + FirstEmissionBlockNumber::::insert(netuid, 100); + // Also set SubtokenEnabled to true + SubtokenEnabled::::insert(netuid, true); + } + + let (unactive_netuids, w) = + crate::migrations::migrate_reset_unactive_sn::get_unactive_sn_netuids::( + initial_alpha, + ); + // Make sure ALL the inactive subnets are in the unactive netuids + assert!( + inactive_netuids + .iter() + .all(|netuid| unactive_netuids.contains(netuid)) + ); + // Make sure the active subnets are not in the unactive netuids + assert!( + active_netuids + .iter() + .all(|netuid| !unactive_netuids.contains(netuid)) + ); + }); +} + +#[test] +fn test_migrate_reset_unactive_sn() { + new_test_ext(1).execute_with(|| { + // Register some subnets + let netuid0 = add_dynamic_network(&U256::from(0), &U256::from(0)); + let netuid1 = add_dynamic_network(&U256::from(1), &U256::from(1)); + let netuid2 = add_dynamic_network(&U256::from(2), &U256::from(2)); + let inactive_netuids = vec![netuid0, netuid1, netuid2]; + // Add active subnets + let netuid3 = add_dynamic_network(&U256::from(3), &U256::from(3)); + let netuid4 = add_dynamic_network(&U256::from(4), &U256::from(4)); + let netuid5 = add_dynamic_network(&U256::from(5), &U256::from(5)); + let active_netuids = vec![netuid3, netuid4, netuid5]; + let netuids: Vec = inactive_netuids + .iter() + .chain(active_netuids.iter()) + .copied() + .collect(); + + let initial_tao = Pallet::::get_network_min_lock(); + let initial_alpha = initial_tao.to_u64().into(); + let actual_tao_lock_amount = TaoCurrency::from(322222229_u64); + let actual_tao_lock_amount_less_pool_tao = + actual_tao_lock_amount.saturating_sub(initial_tao); // Add stake to the subnet pools for netuid in &netuids { SubnetTAO::::insert(netuid, initial_tao + TaoCurrency::from(123123_u64)); From 6e37470096490f3e5e276e835b1de9dc66b75a3e Mon Sep 17 00:00:00 2001 From: camfairchild Date: Sat, 8 Nov 2025 15:01:33 -0500 Subject: [PATCH 129/210] clear protocol liq and raorecycled for reg --- .../migrations/migrate_reset_unactive_sn.rs | 103 +++++++++++------- pallets/subtensor/src/tests/migration.rs | 23 +++- 2 files changed, 82 insertions(+), 44 deletions(-) diff --git a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs index 089fba3292..69fbe6c259 100644 --- a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs +++ b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs @@ -1,5 +1,7 @@ use super::*; +use subtensor_swap_interface::SwapHandler; + pub fn get_unactive_sn_netuids( pool_initial_alpha: AlphaCurrency, ) -> (Vec, Weight) { @@ -49,51 +51,68 @@ pub fn migrate_reset_unactive_sn() -> Weight { weight = weight.saturating_add(w); for netuid in unactive_netuids.iter() { - - // Reset pool - - // Recycle already emitted TAO - let subnet_tao = SubnetTAO::::get(*netuid); - if subnet_tao > pool_initial_tao { - Pallet::::recycle_tao(subnet_tao.saturating_sub(pool_initial_tao)); + // Reset the subnet as it shouldn't have any emissions + PendingServerEmission::::remove(*netuid); + PendingValidatorEmission::::remove(*netuid); + PendingRootAlphaDivs::::remove(*netuid); + PendingOwnerCut::::remove(*netuid); + + // Reset pool + let actual_tao_lock_amount = SubnetLocked::::get(*netuid); + let actual_tao_lock_amount_less_pool_tao = + actual_tao_lock_amount.saturating_sub(pool_initial_tao); + + // Recycle already emitted TAO + let subnet_tao = SubnetTAO::::get(*netuid); + if subnet_tao > pool_initial_tao { + Pallet::::recycle_tao(subnet_tao.saturating_sub(pool_initial_tao)); + } + SubnetTAO::::insert(*netuid, pool_initial_tao); + + // Reset pool alpha + SubnetAlphaIn::::insert(*netuid, pool_initial_alpha); + SubnetAlphaOut::::insert(*netuid, AlphaCurrency::ZERO); + // Reset volume + SubnetVolume::::insert(*netuid, 0u128); + // Reset recycled (from init_new_network) + RAORecycledForRegistration::::insert(*netuid, actual_tao_lock_amount_less_pool_tao); + + // Reset v3 pool + T::SwapInterface::clear_protocol_liquidity(*netuid).unwrap_or_else(|e| { + log::error!( + "Failed to clear protocol liquidity for netuid {netuid:?}: {:?}", + e + ); + }); + + // Reset Alpha stake entries for this subnet + let mut to_reset = Vec::new(); + for (hotkey, _, alpha) in + TotalHotkeyAlpha::::iter().filter(|(_, netuid_, _)| *netuid_ == *netuid) + { + if alpha > AlphaCurrency::from(0) { + to_reset.push((hotkey, netuid, alpha)); } - SubnetTAO::::insert(*netuid, pool_initial_tao); - - // Reset pool alpha - SubnetAlphaIn::::insert(*netuid, pool_initial_alpha); - SubnetAlphaOut::::insert(*netuid, AlphaCurrency::ZERO); - // Reset volume - SubnetVolume::::insert(*netuid, 0u128); - - // Reset Alpha stake entries for this subnet - let mut to_reset = Vec::new(); - for (hotkey, _, alpha) in - TotalHotkeyAlpha::::iter().filter(|(_, netuid_, _)| *netuid_ == *netuid) + } + for (hotkey, netuid_, _) in to_reset { + TotalHotkeyAlpha::::remove(&hotkey, netuid_); + TotalHotkeyShares::::remove(&hotkey, netuid_); + TotalHotkeyAlphaLastEpoch::::remove(&hotkey, netuid_); + + // Reset root claimable and claimed + RootClaimable::::mutate(&hotkey, |claimable| { + claimable.remove(netuid_); + }); + let _ = RootClaimed::::clear_prefix((netuid_, &hotkey), u32::MAX, None); + + let mut to_reset_alpha: Vec<(&T::AccountId, T::AccountId, NetUid)> = Vec::new(); + for ((coldkey, _), _) in + Alpha::::iter_prefix((&hotkey,)).filter(|((_, netuid_), _)| *netuid_ == *netuid) { - if alpha > AlphaCurrency::from(0) { - to_reset.push((hotkey, netuid, alpha)); - } + to_reset_alpha.push((&hotkey, coldkey, *netuid_)); } - for (hotkey, netuid_, _) in to_reset { - TotalHotkeyAlpha::::remove(&hotkey, netuid_); - TotalHotkeyShares::::remove(&hotkey, netuid_); - TotalHotkeyAlphaLastEpoch::::remove(&hotkey, netuid_); - - // Reset root claimable and claimed - RootClaimable::::mutate(&hotkey, |claimable| { - claimable.remove(netuid_); - }); - let _ = RootClaimed::::clear_prefix((netuid_, &hotkey), u32::MAX, None); - - let mut to_reset_alpha: Vec<(&T::AccountId, T::AccountId, NetUid)> = Vec::new(); - for ((coldkey, _), _) in Alpha::::iter_prefix((&hotkey,)) - .filter(|((_, netuid_), _)| *netuid_ == *netuid) - { - to_reset_alpha.push((&hotkey, coldkey, *netuid_)); - } - for (hotkey, coldkey, netuid_) in to_reset_alpha { - Alpha::::remove((hotkey, coldkey, netuid_)); - } + for (hotkey, coldkey, netuid_) in to_reset_alpha { + Alpha::::remove((hotkey, coldkey, netuid_)); } } } diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index add0c1f05b..81f899f8ca 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -2488,7 +2488,7 @@ fn test_migrate_reset_unactive_sn() { let initial_tao = Pallet::::get_network_min_lock(); let initial_alpha = initial_tao.to_u64().into(); - let actual_tao_lock_amount = TaoCurrency::from(322222229_u64); + let actual_tao_lock_amount = initial_tao.saturating_add(TaoCurrency::from(12345678_u64)); let actual_tao_lock_amount_less_pool_tao = actual_tao_lock_amount.saturating_sub(initial_tao); // Add stake to the subnet pools @@ -2497,7 +2497,14 @@ fn test_migrate_reset_unactive_sn() { SubnetAlphaIn::::insert(netuid, initial_alpha + AlphaCurrency::from(123123_u64)); SubnetAlphaOut::::insert(netuid, AlphaCurrency::from(123123_u64)); SubnetVolume::::insert(netuid, 123123_u128); - SubnetLocked::::insert(netuid, TaoCurrency::from(399999_u64)); + SubnetLocked::::insert(netuid, actual_tao_lock_amount); + RAORecycledForRegistration::::insert( + netuid, + actual_tao_lock_amount_less_pool_tao + TaoCurrency::from(887777_u64), + ); + + // Set AlphaSqrtPrice + pallet_subtensor_swap::AlphaSqrtPrice::::insert(netuid, U64F64::from(123123_u64)); // Remove the FirstEmissionBlockNumber FirstEmissionBlockNumber::::remove(netuid); @@ -2557,6 +2564,11 @@ fn test_migrate_reset_unactive_sn() { PendingRootAlphaDivs::::get(netuid), AlphaCurrency::ZERO ); + assert_eq!( + RAORecycledForRegistration::::get(netuid), + actual_tao_lock_amount_less_pool_tao + ); + assert!(!pallet_subtensor_swap::AlphaSqrtPrice::::contains_key(*netuid)); assert_eq!(PendingOwnerCut::::get(netuid), AlphaCurrency::ZERO); assert_eq!(SubnetTAO::::get(netuid), initial_tao); assert_eq!(SubnetAlphaIn::::get(netuid), initial_alpha); @@ -2602,6 +2614,13 @@ fn test_migrate_reset_unactive_sn() { PendingRootAlphaDivs::::get(netuid), AlphaCurrency::ZERO ); + assert_ne!( + RAORecycledForRegistration::::get(netuid), + actual_tao_lock_amount_less_pool_tao + ); + assert!(pallet_subtensor_swap::AlphaSqrtPrice::::contains_key( + *netuid + )); assert_ne!(PendingOwnerCut::::get(netuid), AlphaCurrency::ZERO); assert_ne!(SubnetTAO::::get(netuid), initial_tao); assert_ne!(SubnetAlphaIn::::get(netuid), initial_alpha); From eea9be4e1fbd543f86640e3f3944e941b18c14e0 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Sat, 8 Nov 2025 15:04:25 -0500 Subject: [PATCH 130/210] chore: clippy --- .../subtensor/src/migrations/migrate_reset_unactive_sn.rs | 5 +---- pallets/subtensor/src/tests/migration.rs | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs index 69fbe6c259..73608b1ae9 100644 --- a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs +++ b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs @@ -79,10 +79,7 @@ pub fn migrate_reset_unactive_sn() -> Weight { // Reset v3 pool T::SwapInterface::clear_protocol_liquidity(*netuid).unwrap_or_else(|e| { - log::error!( - "Failed to clear protocol liquidity for netuid {netuid:?}: {:?}", - e - ); + log::error!("Failed to clear protocol liquidity for netuid {netuid:?}: {e:?}"); }); // Reset Alpha stake entries for this subnet diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index 81f899f8ca..e64210a36f 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -2405,12 +2405,12 @@ fn test_migrate_reset_unactive_sn_get_unactive_netuids() { let netuid0 = add_dynamic_network(&U256::from(0), &U256::from(0)); let netuid1 = add_dynamic_network(&U256::from(1), &U256::from(1)); let netuid2 = add_dynamic_network(&U256::from(2), &U256::from(2)); - let inactive_netuids = vec![netuid0, netuid1, netuid2]; + let inactive_netuids = [netuid0, netuid1, netuid2]; // Add active subnets let netuid3 = add_dynamic_network(&U256::from(3), &U256::from(3)); let netuid4 = add_dynamic_network(&U256::from(4), &U256::from(4)); let netuid5 = add_dynamic_network(&U256::from(5), &U256::from(5)); - let active_netuids = vec![netuid3, netuid4, netuid5]; + let active_netuids = [netuid3, netuid4, netuid5]; let netuids: Vec = inactive_netuids .iter() .chain(active_netuids.iter()) From 79f5dd9fdb242575ebb2e4327e4931046a29bd45 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Sat, 8 Nov 2025 15:21:48 -0500 Subject: [PATCH 131/210] add weights --- .../migrations/migrate_reset_unactive_sn.rs | 51 +++++++++++++------ 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs index 73608b1ae9..7d2764f59d 100644 --- a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs +++ b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs @@ -56,11 +56,13 @@ pub fn migrate_reset_unactive_sn() -> Weight { PendingValidatorEmission::::remove(*netuid); PendingRootAlphaDivs::::remove(*netuid); PendingOwnerCut::::remove(*netuid); + weight = weight.saturating_add(T::DbWeight::get().writes(4)); // Reset pool let actual_tao_lock_amount = SubnetLocked::::get(*netuid); let actual_tao_lock_amount_less_pool_tao = actual_tao_lock_amount.saturating_sub(pool_initial_tao); + weight = weight.saturating_add(T::DbWeight::get().reads(1)); // Recycle already emitted TAO let subnet_tao = SubnetTAO::::get(*netuid); @@ -68,6 +70,7 @@ pub fn migrate_reset_unactive_sn() -> Weight { Pallet::::recycle_tao(subnet_tao.saturating_sub(pool_initial_tao)); } SubnetTAO::::insert(*netuid, pool_initial_tao); + weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); // Reset pool alpha SubnetAlphaIn::::insert(*netuid, pool_initial_alpha); @@ -76,40 +79,58 @@ pub fn migrate_reset_unactive_sn() -> Weight { SubnetVolume::::insert(*netuid, 0u128); // Reset recycled (from init_new_network) RAORecycledForRegistration::::insert(*netuid, actual_tao_lock_amount_less_pool_tao); + weight = weight.saturating_add(T::DbWeight::get().writes(4)); // Reset v3 pool T::SwapInterface::clear_protocol_liquidity(*netuid).unwrap_or_else(|e| { log::error!("Failed to clear protocol liquidity for netuid {netuid:?}: {e:?}"); }); + // might be based on ticks but this is a rough estimate + weight = weight.saturating_add(T::DbWeight::get().reads_writes(6, 14)); // Reset Alpha stake entries for this subnet let mut to_reset = Vec::new(); - for (hotkey, _, alpha) in - TotalHotkeyAlpha::::iter().filter(|(_, netuid_, _)| *netuid_ == *netuid) - { + for (hotkey, netuid_, alpha) in TotalHotkeyAlpha::::iter() { + weight = weight.saturating_add(T::DbWeight::get().reads(1)); + if netuid_ != *netuid { + // skip netuids that are not the subnet we are resetting + continue; + } + if alpha > AlphaCurrency::from(0) { to_reset.push((hotkey, netuid, alpha)); } } - for (hotkey, netuid_, _) in to_reset { - TotalHotkeyAlpha::::remove(&hotkey, netuid_); - TotalHotkeyShares::::remove(&hotkey, netuid_); - TotalHotkeyAlphaLastEpoch::::remove(&hotkey, netuid_); + + for (hotkey, netuid_i, _) in to_reset { + TotalHotkeyAlpha::::remove(&hotkey, netuid_i); + TotalHotkeyShares::::remove(&hotkey, netuid_i); + TotalHotkeyAlphaLastEpoch::::remove(&hotkey, netuid_i); + weight = weight.saturating_add(T::DbWeight::get().writes(3)); // Reset root claimable and claimed RootClaimable::::mutate(&hotkey, |claimable| { - claimable.remove(netuid_); + claimable.remove(netuid_i); }); - let _ = RootClaimed::::clear_prefix((netuid_, &hotkey), u32::MAX, None); + weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); + let removal_result = + RootClaimed::::clear_prefix((netuid_i, &hotkey), u32::MAX, None); + weight = weight.saturating_add( + T::DbWeight::get() + .reads_writes(removal_result.loops as u64, removal_result.backend as u64), + ); let mut to_reset_alpha: Vec<(&T::AccountId, T::AccountId, NetUid)> = Vec::new(); - for ((coldkey, _), _) in - Alpha::::iter_prefix((&hotkey,)).filter(|((_, netuid_), _)| *netuid_ == *netuid) - { - to_reset_alpha.push((&hotkey, coldkey, *netuid_)); + for ((coldkey, netuid_j), _) in Alpha::::iter_prefix((&hotkey,)) { + weight = weight.saturating_add(T::DbWeight::get().reads(1)); + if netuid_j != *netuid_i { + continue; + } + to_reset_alpha.push((&hotkey, coldkey, netuid_j)); } - for (hotkey, coldkey, netuid_) in to_reset_alpha { - Alpha::::remove((hotkey, coldkey, netuid_)); + for (hotkey, coldkey, netuid_j) in to_reset_alpha { + Alpha::::remove((hotkey, coldkey, netuid_j)); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); } } } From 6c1b39fb457089968c68c451f3608cdc3d257ec0 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Sat, 8 Nov 2025 15:50:13 -0500 Subject: [PATCH 132/210] optimize migration --- .../migrations/migrate_reset_unactive_sn.rs | 80 ++++++++++++------- 1 file changed, 50 insertions(+), 30 deletions(-) diff --git a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs index 7d2764f59d..ed0fa36b16 100644 --- a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs +++ b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs @@ -1,4 +1,5 @@ use super::*; +use sp_std::collections::{btree_map::BTreeMap, btree_set::BTreeSet}; use subtensor_swap_interface::SwapHandler; @@ -50,6 +51,37 @@ pub fn migrate_reset_unactive_sn() -> Weight { let (unactive_netuids, w) = get_unactive_sn_netuids::(pool_initial_alpha); weight = weight.saturating_add(w); + // Collect the hotkeys to remove for each subnet + let mut to_remove_alpha_hotkeys: BTreeMap> = BTreeMap::new(); + let mut to_remove_alpha_coldkeys: BTreeMap> = + BTreeMap::new(); + let mut all_hotkeys_set = BTreeSet::new(); + for (hotkey, netuid_i, _) in TotalHotkeyAlpha::::iter() { + weight = weight.saturating_add(T::DbWeight::get().reads(1)); + if unactive_netuids.contains(&netuid_i) { + // Only for unactive subnets + to_remove_alpha_hotkeys + .entry(netuid_i) + .or_insert(Vec::new()) + .push(hotkey.clone()); + all_hotkeys_set.insert(hotkey); + } + } + + // Collect the coldkeys to remove for each subnet + for hotkey in all_hotkeys_set.iter() { + for ((coldkey, netuid_i), _) in Alpha::::iter_prefix((&hotkey,)) { + weight = weight.saturating_add(T::DbWeight::get().reads(1)); + if unactive_netuids.contains(&netuid_i) { + // Only for unactive subnets + to_remove_alpha_coldkeys + .entry(netuid_i) + .or_insert(Vec::new()) + .push((hotkey.clone(), coldkey)); + } + } + } + for netuid in unactive_netuids.iter() { // Reset the subnet as it shouldn't have any emissions PendingServerEmission::::remove(*netuid); @@ -89,47 +121,35 @@ pub fn migrate_reset_unactive_sn() -> Weight { weight = weight.saturating_add(T::DbWeight::get().reads_writes(6, 14)); // Reset Alpha stake entries for this subnet - let mut to_reset = Vec::new(); - for (hotkey, netuid_, alpha) in TotalHotkeyAlpha::::iter() { - weight = weight.saturating_add(T::DbWeight::get().reads(1)); - if netuid_ != *netuid { - // skip netuids that are not the subnet we are resetting - continue; - } - - if alpha > AlphaCurrency::from(0) { - to_reset.push((hotkey, netuid, alpha)); - } - } - - for (hotkey, netuid_i, _) in to_reset { - TotalHotkeyAlpha::::remove(&hotkey, netuid_i); - TotalHotkeyShares::::remove(&hotkey, netuid_i); - TotalHotkeyAlphaLastEpoch::::remove(&hotkey, netuid_i); + let to_reset: Vec = match to_remove_alpha_hotkeys.get(netuid) { + Some(hotkeys) => hotkeys.clone(), + None => Vec::new(), + }; + + for hotkey in to_reset { + TotalHotkeyAlpha::::remove(&hotkey, *netuid); + TotalHotkeyShares::::remove(&hotkey, *netuid); + TotalHotkeyAlphaLastEpoch::::remove(&hotkey, *netuid); weight = weight.saturating_add(T::DbWeight::get().writes(3)); // Reset root claimable and claimed RootClaimable::::mutate(&hotkey, |claimable| { - claimable.remove(netuid_i); + claimable.remove(netuid); }); weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); - let removal_result = - RootClaimed::::clear_prefix((netuid_i, &hotkey), u32::MAX, None); + let removal_result = RootClaimed::::clear_prefix((*netuid, &hotkey), u32::MAX, None); weight = weight.saturating_add( T::DbWeight::get() .reads_writes(removal_result.loops as u64, removal_result.backend as u64), ); - let mut to_reset_alpha: Vec<(&T::AccountId, T::AccountId, NetUid)> = Vec::new(); - for ((coldkey, netuid_j), _) in Alpha::::iter_prefix((&hotkey,)) { - weight = weight.saturating_add(T::DbWeight::get().reads(1)); - if netuid_j != *netuid_i { - continue; - } - to_reset_alpha.push((&hotkey, coldkey, netuid_j)); - } - for (hotkey, coldkey, netuid_j) in to_reset_alpha { - Alpha::::remove((hotkey, coldkey, netuid_j)); + let to_reset_alpha: Vec<(T::AccountId, T::AccountId)> = + match to_remove_alpha_coldkeys.get(netuid) { + Some(coldkeys) => coldkeys.clone(), + None => Vec::new(), + }; + for (hotkey, coldkey) in to_reset_alpha { + Alpha::::remove((hotkey, coldkey, netuid)); weight = weight.saturating_add(T::DbWeight::get().writes(1)); } } From f74517b68c9450434e87b217e9715d4d28218f7d Mon Sep 17 00:00:00 2001 From: camfairchild Date: Sat, 8 Nov 2025 15:52:40 -0500 Subject: [PATCH 133/210] also dec totalstake for recycle --- pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs index ed0fa36b16..64f3f849ac 100644 --- a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs +++ b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs @@ -100,6 +100,10 @@ pub fn migrate_reset_unactive_sn() -> Weight { let subnet_tao = SubnetTAO::::get(*netuid); if subnet_tao > pool_initial_tao { Pallet::::recycle_tao(subnet_tao.saturating_sub(pool_initial_tao)); + TotalStake::::mutate(|total| { + *total = total.saturating_sub(subnet_tao.saturating_sub(pool_initial_tao)); + }); + weight = weight.saturating_add(T::DbWeight::get().reads_writes(2, 2)); } SubnetTAO::::insert(*netuid, pool_initial_tao); weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); From b9c597c886a2caa85e89121880538853bd078b4c Mon Sep 17 00:00:00 2001 From: camfairchild Date: Sat, 8 Nov 2025 16:20:39 -0500 Subject: [PATCH 134/210] move clear liq up --- .../src/migrations/migrate_reset_unactive_sn.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs index 64f3f849ac..8dcfaa7465 100644 --- a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs +++ b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs @@ -96,6 +96,13 @@ pub fn migrate_reset_unactive_sn() -> Weight { actual_tao_lock_amount.saturating_sub(pool_initial_tao); weight = weight.saturating_add(T::DbWeight::get().reads(1)); + // Reset v3 pool + T::SwapInterface::clear_protocol_liquidity(*netuid).unwrap_or_else(|e| { + log::error!("Failed to clear protocol liquidity for netuid {netuid:?}: {e:?}"); + }); + // might be based on ticks but this is a rough estimate + weight = weight.saturating_add(T::DbWeight::get().reads_writes(6, 14)); + // Recycle already emitted TAO let subnet_tao = SubnetTAO::::get(*netuid); if subnet_tao > pool_initial_tao { @@ -117,13 +124,6 @@ pub fn migrate_reset_unactive_sn() -> Weight { RAORecycledForRegistration::::insert(*netuid, actual_tao_lock_amount_less_pool_tao); weight = weight.saturating_add(T::DbWeight::get().writes(4)); - // Reset v3 pool - T::SwapInterface::clear_protocol_liquidity(*netuid).unwrap_or_else(|e| { - log::error!("Failed to clear protocol liquidity for netuid {netuid:?}: {e:?}"); - }); - // might be based on ticks but this is a rough estimate - weight = weight.saturating_add(T::DbWeight::get().reads_writes(6, 14)); - // Reset Alpha stake entries for this subnet let to_reset: Vec = match to_remove_alpha_hotkeys.get(netuid) { Some(hotkeys) => hotkeys.clone(), From a1a92a7337f1d4cabd032b53dfc8b0a15820bf9f Mon Sep 17 00:00:00 2001 From: camfairchild Date: Sat, 8 Nov 2025 16:22:53 -0500 Subject: [PATCH 135/210] use var --- .../subtensor/src/migrations/migrate_reset_unactive_sn.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs index 8dcfaa7465..ffdc3b2669 100644 --- a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs +++ b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs @@ -106,9 +106,10 @@ pub fn migrate_reset_unactive_sn() -> Weight { // Recycle already emitted TAO let subnet_tao = SubnetTAO::::get(*netuid); if subnet_tao > pool_initial_tao { - Pallet::::recycle_tao(subnet_tao.saturating_sub(pool_initial_tao)); + let tao_to_recycle = subnet_tao.saturating_sub(pool_initial_tao); + Pallet::::recycle_tao(tao_to_recycle); TotalStake::::mutate(|total| { - *total = total.saturating_sub(subnet_tao.saturating_sub(pool_initial_tao)); + *total = total.saturating_sub(tao_to_recycle); }); weight = weight.saturating_add(T::DbWeight::get().reads_writes(2, 2)); } From fb7c4c527818fc1d647547dca6fb7b0e7f8eaa0b Mon Sep 17 00:00:00 2001 From: camfairchild Date: Sat, 8 Nov 2025 16:27:07 -0500 Subject: [PATCH 136/210] add burn reg to migration test --- pallets/subtensor/src/tests/migration.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index e64210a36f..fac699fad8 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -2440,6 +2440,19 @@ fn test_migrate_reset_unactive_sn_get_unactive_netuids() { // Remove the FirstEmissionBlockNumber FirstEmissionBlockNumber::::remove(netuid); SubtokenEnabled::::remove(netuid); + + // Try registering on the subnet to simulate a real network + // give balance to the coldkey + let coldkey_account_id = U256::from(1111); + let hotkey_account_id = U256::from(1111); + let burn_cost = SubtensorModule::get_burn(*netuid); + SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, burn_cost.into()); + // register the neuron + assert_ok!(SubtensorModule::burned_register( + <::RuntimeOrigin>::signed(coldkey_account_id), + *netuid, + hotkey_account_id + )); } for netuid in &active_netuids { // Set the FirstEmissionBlockNumber for the active subnet From 8b8f763a700021d24e2b5223110832cbc7997c5c Mon Sep 17 00:00:00 2001 From: camfairchild Date: Sat, 8 Nov 2025 17:18:04 -0500 Subject: [PATCH 137/210] move reg disabled filter to sn to emit to --- .../subtensor/src/coinbase/run_coinbase.rs | 50 +++++++------------ .../src/coinbase/subnet_emissions.rs | 10 +++- 2 files changed, 25 insertions(+), 35 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 6b0d59ac0a..2b8ba6d14e 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -127,45 +127,29 @@ impl Pallet { // Only calculate for subnets that we are emitting to. for (&netuid_i, &tao_emission_i) in subnet_emissions.iter() { - let mut tao_in_i: U96F32; - let mut alpha_in_i: U96F32; - let alpha_out_i: U96F32; - // Only emit if the subnetwork allows registration. - if !Self::get_network_registration_allowed(netuid_i) - && !Self::get_network_pow_registration_allowed(netuid_i) - { - tao_in_i = asfloat!(0.0); - alpha_in_i = asfloat!(0.0); - alpha_out_i = asfloat!(0.0); - } else { - // Get alpha_emission total - let alpha_emission_i: U96F32 = asfloat!( - Self::get_block_emission_for_issuance( - Self::get_alpha_issuance(netuid_i).into() - ) + // Get alpha_emission this block. + let alpha_emission_i: U96F32 = asfloat!( + Self::get_block_emission_for_issuance(Self::get_alpha_issuance(netuid_i).into()) .unwrap_or(0) - ); - log::debug!("alpha_emission_i: {alpha_emission_i:?}"); - - // Get alpha_out. - alpha_out_i = alpha_emission_i; - - // Get subnet price. - let price_i: U96F32 = T::SwapInterface::current_alpha_price(netuid_i.into()); - log::debug!("price_i: {price_i:?}"); + ); + log::debug!("alpha_emission_i: {alpha_emission_i:?}"); - tao_in_i = tao_emission_i; - alpha_in_i = tao_emission_i.safe_div_or(price_i, U96F32::saturating_from_num(0.0)); + // Get subnet price. + let price_i: U96F32 = T::SwapInterface::current_alpha_price(netuid_i.into()); + log::debug!("price_i: {price_i:?}"); - if alpha_in_i > alpha_emission_i { - alpha_in_i = alpha_emission_i; - tao_in_i = alpha_in_i.saturating_mul(price_i); - } + let mut tao_in_i: U96F32 = tao_emission_i; + let alpha_out_i: U96F32 = alpha_emission_i; + let mut alpha_in_i: U96F32 = tao_emission_i.safe_div_or(price_i, U96F32::from_num(0.0)); - let excess_amount: U96F32 = tao_emission_i.saturating_sub(tao_in_i); - excess_tao.insert(netuid_i, excess_amount); + if alpha_in_i > alpha_emission_i { + alpha_in_i = alpha_emission_i; + tao_in_i = alpha_in_i.saturating_mul(price_i); } + let excess_amount: U96F32 = tao_emission_i.saturating_sub(tao_in_i); + excess_tao.insert(netuid_i, excess_amount); + // Insert values into maps tao_in.insert(netuid_i, tao_in_i); alpha_in.insert(netuid_i, alpha_in_i); diff --git a/pallets/subtensor/src/coinbase/subnet_emissions.rs b/pallets/subtensor/src/coinbase/subnet_emissions.rs index 3f06092084..1a5a8ec402 100644 --- a/pallets/subtensor/src/coinbase/subnet_emissions.rs +++ b/pallets/subtensor/src/coinbase/subnet_emissions.rs @@ -11,8 +11,14 @@ impl Pallet { // Filter out subnets with no first emission block number. subnets .iter() - .filter(|&netuid| *netuid != NetUid::ROOT) - .filter(|&netuid| FirstEmissionBlockNumber::::get(*netuid).is_some()) + .filter(|netuid| !netuid.is_root()) + .filter(|netuid| FirstEmissionBlockNumber::::get(*netuid).is_some()) + .filter(|netuid| SubtokenEnabled::::get(*netuid)) + .filter(|&netuid| { + // Only emit TAO if the subnetwork allows registration. + Self::get_network_registration_allowed(*netuid) + || Self::get_network_pow_registration_allowed(*netuid) + }) .copied() .collect() } From 717be510141cb3077023671910afb5a0cc5b4448 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Sat, 8 Nov 2025 18:47:20 -0500 Subject: [PATCH 138/210] remove extra counters --- .../migrations/migrate_reset_unactive_sn.rs | 5 +- pallets/subtensor/src/tests/migration.rs | 240 +++++++++--------- 2 files changed, 117 insertions(+), 128 deletions(-) diff --git a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs index ffdc3b2669..2561bdc2bf 100644 --- a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs +++ b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs @@ -88,7 +88,10 @@ pub fn migrate_reset_unactive_sn() -> Weight { PendingValidatorEmission::::remove(*netuid); PendingRootAlphaDivs::::remove(*netuid); PendingOwnerCut::::remove(*netuid); - weight = weight.saturating_add(T::DbWeight::get().writes(4)); + SubnetTaoInEmission::::remove(*netuid); + SubnetAlphaInEmission::::remove(*netuid); + SubnetAlphaOutEmission::::remove(*netuid); + weight = weight.saturating_add(T::DbWeight::get().writes(7)); // Reset pool let actual_tao_lock_amount = SubnetLocked::::get(*netuid); diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index fac699fad8..f48f444221 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -2398,68 +2398,116 @@ fn test_migrate_remove_tao_dividends() { ); } +fn do_setup_unactive_sn() -> (Vec, Vec) { + // Register some subnets + let netuid0 = add_dynamic_network(&U256::from(0), &U256::from(0)); + let netuid1 = add_dynamic_network(&U256::from(1), &U256::from(1)); + let netuid2 = add_dynamic_network(&U256::from(2), &U256::from(2)); + let inactive_netuids = vec![netuid0, netuid1, netuid2]; + // Add active subnets + let netuid3 = add_dynamic_network(&U256::from(3), &U256::from(3)); + let netuid4 = add_dynamic_network(&U256::from(4), &U256::from(4)); + let netuid5 = add_dynamic_network(&U256::from(5), &U256::from(5)); + let active_netuids = vec![netuid3, netuid4, netuid5]; + let netuids: Vec = inactive_netuids + .iter() + .chain(active_netuids.iter()) + .copied() + .collect(); + + let initial_tao = Pallet::::get_network_min_lock(); + let initial_alpha: AlphaCurrency = initial_tao.to_u64().into(); + let actual_tao_lock_amount = initial_tao.saturating_add(TaoCurrency::from(12345678_u64)); + let actual_tao_lock_amount_less_pool_tao = actual_tao_lock_amount.saturating_sub(initial_tao); + + // Set total issuance to the total amount of TAO locked + TotalIssuance::::set( + actual_tao_lock_amount.saturating_mul((netuids.len() as u64).into()), + ); + // Add stake to the subnet pools + for netuid in &netuids { + let stake_in_pool = initial_tao + TaoCurrency::from(123123_u64); + SubnetTAO::::insert(netuid, stake_in_pool); + TotalStake::::mutate(|total_stake| { + *total_stake = total_stake.saturating_add(stake_in_pool); + }); + + SubnetAlphaIn::::insert(netuid, initial_alpha + AlphaCurrency::from(123123_u64)); + SubnetAlphaOut::::insert(netuid, AlphaCurrency::from(123123_u64)); + SubnetVolume::::insert(netuid, 123123_u128); + SubnetLocked::::insert(netuid, actual_tao_lock_amount); + + // Remove the FirstEmissionBlockNumber + FirstEmissionBlockNumber::::remove(netuid); + SubtokenEnabled::::remove(netuid); + + // Try registering on the subnet to simulate a real network + // give balance to the coldkey + let coldkey_account_id = U256::from(1111); + let hotkey_account_id = U256::from(1111); + let burn_cost = SubtensorModule::get_burn(*netuid); + SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, burn_cost.into()); + TotalIssuance::::mutate(|total_issuance| { + *total_issuance = total_issuance.saturating_add(burn_cost); + }); + // register the neuron + assert_ok!(SubtensorModule::burned_register( + <::RuntimeOrigin>::signed(coldkey_account_id), + *netuid, + hotkey_account_id + )); + } + for netuid in &active_netuids { + // Set the FirstEmissionBlockNumber for the active subnet + FirstEmissionBlockNumber::::insert(netuid, 100); + // Also set SubtokenEnabled to true + SubtokenEnabled::::insert(netuid, true); + } + + let alpha_amt = AlphaCurrency::from(123123_u64); + let tao_amt = TaoCurrency::from(333333_u64); + // Create some Stake entries + for netuid in &netuids { + for hotkey in 0..10 { + let hk = U256::from(hotkey); + TotalHotkeyAlpha::::insert(hk, netuid, alpha_amt); + TotalHotkeyShares::::insert(hk, netuid, U64F64::from(123123_u64)); + TotalHotkeyAlphaLastEpoch::::insert(hk, netuid, alpha_amt); + + RootClaimable::::mutate(hk, |claimable| { + claimable.insert(*netuid, I96F32::from(alpha_amt.to_u64())); + }); + for coldkey in 0..10 { + let ck = U256::from(coldkey); + Alpha::::insert((hk, ck, netuid), U64F64::from(123_u64)); + RootClaimed::::insert((netuid, hk, ck), 222_u128); + } + } + } + // Add some pending emissions + let alpha_em_amt = AlphaCurrency::from(355555_u64); + for netuid in &netuids { + PendingServerEmission::::insert(netuid, alpha_em_amt); + PendingValidatorEmission::::insert(netuid, alpha_em_amt); + PendingRootAlphaDivs::::insert(netuid, alpha_em_amt); + PendingOwnerCut::::insert(netuid, alpha_em_amt); + + SubnetTaoInEmission::::insert(netuid, TaoCurrency::from(12345678_u64)); + SubnetAlphaInEmission::::insert(netuid, AlphaCurrency::from(12345678_u64)); + SubnetAlphaOutEmission::::insert(netuid, AlphaCurrency::from(12345678_u64)); + + } + + return (active_netuids, inactive_netuids); +} + #[test] fn test_migrate_reset_unactive_sn_get_unactive_netuids() { new_test_ext(1).execute_with(|| { - // Register some subnets - let netuid0 = add_dynamic_network(&U256::from(0), &U256::from(0)); - let netuid1 = add_dynamic_network(&U256::from(1), &U256::from(1)); - let netuid2 = add_dynamic_network(&U256::from(2), &U256::from(2)); - let inactive_netuids = [netuid0, netuid1, netuid2]; - // Add active subnets - let netuid3 = add_dynamic_network(&U256::from(3), &U256::from(3)); - let netuid4 = add_dynamic_network(&U256::from(4), &U256::from(4)); - let netuid5 = add_dynamic_network(&U256::from(5), &U256::from(5)); - let active_netuids = [netuid3, netuid4, netuid5]; - let netuids: Vec = inactive_netuids - .iter() - .chain(active_netuids.iter()) - .copied() - .collect(); + let (active_netuids, inactive_netuids) = do_setup_unactive_sn(); let initial_tao = Pallet::::get_network_min_lock(); - let initial_alpha = initial_tao.to_u64().into(); - let actual_tao_lock_amount = TaoCurrency::from(322222229_u64); - let actual_tao_lock_amount_less_pool_tao = - actual_tao_lock_amount.saturating_sub(initial_tao); - // Add stake to the subnet pools - for netuid in &netuids { - SubnetTAO::::insert(netuid, initial_tao + TaoCurrency::from(123123_u64)); - SubnetAlphaIn::::insert(netuid, initial_alpha + AlphaCurrency::from(123123_u64)); - SubnetAlphaOut::::insert(netuid, AlphaCurrency::from(123123_u64)); - SubnetVolume::::insert(netuid, 123123_u128); - SubnetLocked::::insert(netuid, actual_tao_lock_amount); - RAORecycledForRegistration::::insert( - netuid, - actual_tao_lock_amount_less_pool_tao, - ); - - // Set AlphaSqrtPrice - pallet_subtensor_swap::AlphaSqrtPrice::::insert(netuid, U64F64::from(123123_u64)); - - // Remove the FirstEmissionBlockNumber - FirstEmissionBlockNumber::::remove(netuid); - SubtokenEnabled::::remove(netuid); - - // Try registering on the subnet to simulate a real network - // give balance to the coldkey - let coldkey_account_id = U256::from(1111); - let hotkey_account_id = U256::from(1111); - let burn_cost = SubtensorModule::get_burn(*netuid); - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, burn_cost.into()); - // register the neuron - assert_ok!(SubtensorModule::burned_register( - <::RuntimeOrigin>::signed(coldkey_account_id), - *netuid, - hotkey_account_id - )); - } - for netuid in &active_netuids { - // Set the FirstEmissionBlockNumber for the active subnet - FirstEmissionBlockNumber::::insert(netuid, 100); - // Also set SubtokenEnabled to true - SubtokenEnabled::::insert(netuid, true); - } + let initial_alpha: AlphaCurrency = initial_tao.to_u64().into(); let (unactive_netuids, w) = crate::migrations::migrate_reset_unactive_sn::get_unactive_sn_netuids::( @@ -2483,81 +2531,13 @@ fn test_migrate_reset_unactive_sn_get_unactive_netuids() { #[test] fn test_migrate_reset_unactive_sn() { new_test_ext(1).execute_with(|| { - // Register some subnets - let netuid0 = add_dynamic_network(&U256::from(0), &U256::from(0)); - let netuid1 = add_dynamic_network(&U256::from(1), &U256::from(1)); - let netuid2 = add_dynamic_network(&U256::from(2), &U256::from(2)); - let inactive_netuids = vec![netuid0, netuid1, netuid2]; - // Add active subnets - let netuid3 = add_dynamic_network(&U256::from(3), &U256::from(3)); - let netuid4 = add_dynamic_network(&U256::from(4), &U256::from(4)); - let netuid5 = add_dynamic_network(&U256::from(5), &U256::from(5)); - let active_netuids = vec![netuid3, netuid4, netuid5]; - let netuids: Vec = inactive_netuids - .iter() - .chain(active_netuids.iter()) - .copied() - .collect(); + let (active_netuids, inactive_netuids) = do_setup_unactive_sn(); let initial_tao = Pallet::::get_network_min_lock(); - let initial_alpha = initial_tao.to_u64().into(); + let initial_alpha: AlphaCurrency = initial_tao.to_u64().into(); let actual_tao_lock_amount = initial_tao.saturating_add(TaoCurrency::from(12345678_u64)); let actual_tao_lock_amount_less_pool_tao = actual_tao_lock_amount.saturating_sub(initial_tao); - // Add stake to the subnet pools - for netuid in &netuids { - SubnetTAO::::insert(netuid, initial_tao + TaoCurrency::from(123123_u64)); - SubnetAlphaIn::::insert(netuid, initial_alpha + AlphaCurrency::from(123123_u64)); - SubnetAlphaOut::::insert(netuid, AlphaCurrency::from(123123_u64)); - SubnetVolume::::insert(netuid, 123123_u128); - SubnetLocked::::insert(netuid, actual_tao_lock_amount); - RAORecycledForRegistration::::insert( - netuid, - actual_tao_lock_amount_less_pool_tao + TaoCurrency::from(887777_u64), - ); - - // Set AlphaSqrtPrice - pallet_subtensor_swap::AlphaSqrtPrice::::insert(netuid, U64F64::from(123123_u64)); - - // Remove the FirstEmissionBlockNumber - FirstEmissionBlockNumber::::remove(netuid); - SubtokenEnabled::::remove(netuid); - } - for netuid in &active_netuids { - // Set the FirstEmissionBlockNumber for the active subnet - FirstEmissionBlockNumber::::insert(netuid, 100); - // Also set SubtokenEnabled to true - SubtokenEnabled::::insert(netuid, true); - } - - let alpha_amt = AlphaCurrency::from(123123_u64); - let tao_amt = TaoCurrency::from(333333_u64); - // Create some Stake entries - for netuid in &netuids { - for hotkey in 0..10 { - let hk = U256::from(hotkey); - TotalHotkeyAlpha::::insert(hk, netuid, alpha_amt); - TotalHotkeyShares::::insert(hk, netuid, U64F64::from(123123_u64)); - TotalHotkeyAlphaLastEpoch::::insert(hk, netuid, alpha_amt); - - RootClaimable::::mutate(hk, |claimable| { - claimable.insert(*netuid, I96F32::from(alpha_amt.to_u64())); - }); - for coldkey in 0..10 { - let ck = U256::from(coldkey); - Alpha::::insert((hk, ck, netuid), U64F64::from(123_u64)); - RootClaimed::::insert((netuid, hk, ck), 222_u128); - } - } - } - // Add some pending emissions - let alpha_em_amt = AlphaCurrency::from(355555_u64); - for netuid in &netuids { - PendingServerEmission::::insert(netuid, alpha_em_amt); - PendingValidatorEmission::::insert(netuid, alpha_em_amt); - PendingRootAlphaDivs::::insert(netuid, alpha_em_amt); - PendingOwnerCut::::insert(netuid, alpha_em_amt); - } // Run the migration let w = crate::migrations::migrate_reset_unactive_sn::migrate_reset_unactive_sn::(); @@ -2586,6 +2566,9 @@ fn test_migrate_reset_unactive_sn() { assert_eq!(SubnetTAO::::get(netuid), initial_tao); assert_eq!(SubnetAlphaIn::::get(netuid), initial_alpha); assert_eq!(SubnetAlphaOut::::get(netuid), AlphaCurrency::ZERO); + assert_eq!(SubnetTaoInEmission::::get(netuid), TaoCurrency::ZERO); + assert_eq!(SubnetAlphaInEmission::::get(netuid), AlphaCurrency::ZERO); + assert_eq!(SubnetAlphaOutEmission::::get(netuid), AlphaCurrency::ZERO); assert_eq!(SubnetVolume::::get(netuid), 0u128); for hotkey in 0..10 { let hk = U256::from(hotkey); @@ -2631,6 +2614,9 @@ fn test_migrate_reset_unactive_sn() { RAORecycledForRegistration::::get(netuid), actual_tao_lock_amount_less_pool_tao ); + assert_ne!(SubnetTaoInEmission::::get(netuid), TaoCurrency::ZERO); + assert_ne!(SubnetAlphaInEmission::::get(netuid), AlphaCurrency::ZERO); + assert_ne!(SubnetAlphaOutEmission::::get(netuid), AlphaCurrency::ZERO); assert!(pallet_subtensor_swap::AlphaSqrtPrice::::contains_key( *netuid )); From 1ca583a01da5609b5807df9216d57caebc6c7af3 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Sat, 8 Nov 2025 19:02:20 -0500 Subject: [PATCH 139/210] test for idempotence with ti migration: wip --- .../migrations/migrate_reset_unactive_sn.rs | 18 +++-- pallets/subtensor/src/tests/migration.rs | 72 ++++++++++++++++--- pallets/swap-interface/src/lib.rs | 4 +- pallets/swap/src/pallet/impls.rs | 61 ++++++++-------- 4 files changed, 110 insertions(+), 45 deletions(-) diff --git a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs index 2561bdc2bf..e3b627354e 100644 --- a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs +++ b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs @@ -100,9 +100,14 @@ pub fn migrate_reset_unactive_sn() -> Weight { weight = weight.saturating_add(T::DbWeight::get().reads(1)); // Reset v3 pool - T::SwapInterface::clear_protocol_liquidity(*netuid).unwrap_or_else(|e| { - log::error!("Failed to clear protocol liquidity for netuid {netuid:?}: {e:?}"); - }); + let burned_tao = match T::SwapInterface::clear_protocol_liquidity(*netuid) { + Ok((_tao, fee_tao, _alpha, _fee_alpha)) => fee_tao, + Err(e) => { + log::error!("Failed to clear protocol liquidity for netuid {netuid:?}: {e:?}"); + TaoCurrency::ZERO + } + }; + Pallet::::recycle_tao(burned_tao); // might be based on ticks but this is a rough estimate weight = weight.saturating_add(T::DbWeight::get().reads_writes(6, 14)); @@ -114,10 +119,11 @@ pub fn migrate_reset_unactive_sn() -> Weight { TotalStake::::mutate(|total| { *total = total.saturating_sub(tao_to_recycle); }); - weight = weight.saturating_add(T::DbWeight::get().reads_writes(2, 2)); + SubnetTAO::::mutate(*netuid, |amount| { + *amount = amount.saturating_sub(tao_to_recycle); + }); + weight = weight.saturating_add(T::DbWeight::get().reads_writes(3, 3)); } - SubnetTAO::::insert(*netuid, pool_initial_tao); - weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); // Reset pool alpha SubnetAlphaIn::::insert(*netuid, pool_initial_alpha); diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index f48f444221..03eb2985fa 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -2420,10 +2420,6 @@ fn do_setup_unactive_sn() -> (Vec, Vec) { let actual_tao_lock_amount = initial_tao.saturating_add(TaoCurrency::from(12345678_u64)); let actual_tao_lock_amount_less_pool_tao = actual_tao_lock_amount.saturating_sub(initial_tao); - // Set total issuance to the total amount of TAO locked - TotalIssuance::::set( - actual_tao_lock_amount.saturating_mul((netuids.len() as u64).into()), - ); // Add stake to the subnet pools for netuid in &netuids { let stake_in_pool = initial_tao + TaoCurrency::from(123123_u64); @@ -2431,6 +2427,9 @@ fn do_setup_unactive_sn() -> (Vec, Vec) { TotalStake::::mutate(|total_stake| { *total_stake = total_stake.saturating_add(stake_in_pool); }); + TotalIssuance::::mutate(|total_issuance| { + *total_issuance = total_issuance.saturating_add(stake_in_pool); + }); SubnetAlphaIn::::insert(netuid, initial_alpha + AlphaCurrency::from(123123_u64)); SubnetAlphaOut::::insert(netuid, AlphaCurrency::from(123123_u64)); @@ -2450,6 +2449,7 @@ fn do_setup_unactive_sn() -> (Vec, Vec) { TotalIssuance::::mutate(|total_issuance| { *total_issuance = total_issuance.saturating_add(burn_cost); }); + // register the neuron assert_ok!(SubtensorModule::burned_register( <::RuntimeOrigin>::signed(coldkey_account_id), @@ -2495,7 +2495,6 @@ fn do_setup_unactive_sn() -> (Vec, Vec) { SubnetTaoInEmission::::insert(netuid, TaoCurrency::from(12345678_u64)); SubnetAlphaInEmission::::insert(netuid, AlphaCurrency::from(12345678_u64)); SubnetAlphaOutEmission::::insert(netuid, AlphaCurrency::from(12345678_u64)); - } return (active_netuids, inactive_netuids); @@ -2567,8 +2566,14 @@ fn test_migrate_reset_unactive_sn() { assert_eq!(SubnetAlphaIn::::get(netuid), initial_alpha); assert_eq!(SubnetAlphaOut::::get(netuid), AlphaCurrency::ZERO); assert_eq!(SubnetTaoInEmission::::get(netuid), TaoCurrency::ZERO); - assert_eq!(SubnetAlphaInEmission::::get(netuid), AlphaCurrency::ZERO); - assert_eq!(SubnetAlphaOutEmission::::get(netuid), AlphaCurrency::ZERO); + assert_eq!( + SubnetAlphaInEmission::::get(netuid), + AlphaCurrency::ZERO + ); + assert_eq!( + SubnetAlphaOutEmission::::get(netuid), + AlphaCurrency::ZERO + ); assert_eq!(SubnetVolume::::get(netuid), 0u128); for hotkey in 0..10 { let hk = U256::from(hotkey); @@ -2615,8 +2620,14 @@ fn test_migrate_reset_unactive_sn() { actual_tao_lock_amount_less_pool_tao ); assert_ne!(SubnetTaoInEmission::::get(netuid), TaoCurrency::ZERO); - assert_ne!(SubnetAlphaInEmission::::get(netuid), AlphaCurrency::ZERO); - assert_ne!(SubnetAlphaOutEmission::::get(netuid), AlphaCurrency::ZERO); + assert_ne!( + SubnetAlphaInEmission::::get(netuid), + AlphaCurrency::ZERO + ); + assert_ne!( + SubnetAlphaOutEmission::::get(netuid), + AlphaCurrency::ZERO + ); assert!(pallet_subtensor_swap::AlphaSqrtPrice::::contains_key( *netuid )); @@ -2651,3 +2662,46 @@ fn test_migrate_reset_unactive_sn() { } }); } + +#[test] +fn test_migrate_reset_unactive_sn_idempotence() { + new_test_ext(1).execute_with(|| { + let (active_netuids, inactive_netuids) = do_setup_unactive_sn(); + let netuids = inactive_netuids + .iter() + .chain(active_netuids.iter()) + .copied() + .collect::>(); + + let initial_tao = Pallet::::get_network_min_lock(); + let initial_alpha: AlphaCurrency = initial_tao.to_u64().into(); + let actual_tao_lock_amount = initial_tao.saturating_add(TaoCurrency::from(12345678_u64)); + let actual_tao_lock_amount_less_pool_tao = + actual_tao_lock_amount.saturating_sub(initial_tao); + + // Run the migration + let w = crate::migrations::migrate_reset_unactive_sn::migrate_reset_unactive_sn::(); + assert!(!w.is_zero(), "weight must be non-zero"); + + let mut subnet_tao_before = BTreeMap::new(); + for netuid in &netuids { + subnet_tao_before.insert(netuid, SubnetTAO::::get(netuid)); + } + let total_stake_before = TotalStake::::get(); + let total_issuance_before = TotalIssuance::::get(); + + // Run total issuance migration + crate::migrations::migrate_init_total_issuance::migrate_init_total_issuance::(); + crate::migrations::migrate_total_issuance::migrate_total_issuance::(true); + + // Verify that none of the values are different + for netuid in &netuids { + assert_eq!( + SubnetTAO::::get(netuid), + *subnet_tao_before.get(netuid).unwrap_or(&TaoCurrency::ZERO) + ); + } + assert_eq!(TotalStake::::get(), total_stake_before); + assert_eq!(TotalIssuance::::get(), total_issuance_before); + }); +} diff --git a/pallets/swap-interface/src/lib.rs b/pallets/swap-interface/src/lib.rs index ae7d375f97..408bb842eb 100644 --- a/pallets/swap-interface/src/lib.rs +++ b/pallets/swap-interface/src/lib.rs @@ -49,7 +49,9 @@ pub trait SwapHandler { fn is_user_liquidity_enabled(netuid: NetUid) -> bool; fn dissolve_all_liquidity_providers(netuid: NetUid) -> DispatchResult; fn toggle_user_liquidity(netuid: NetUid, enabled: bool); - fn clear_protocol_liquidity(netuid: NetUid) -> DispatchResult; + fn clear_protocol_liquidity( + netuid: NetUid, + ) -> Result<(TaoCurrency, TaoCurrency, AlphaCurrency, AlphaCurrency), DispatchError>; } pub trait DefaultPriceLimit diff --git a/pallets/swap/src/pallet/impls.rs b/pallets/swap/src/pallet/impls.rs index 34b5e624e6..fe19be2eaa 100644 --- a/pallets/swap/src/pallet/impls.rs +++ b/pallets/swap/src/pallet/impls.rs @@ -511,7 +511,7 @@ impl Pallet { Ok((position, tao, alpha)) } - /// Remove liquidity and credit balances back to (coldkey_account_id, hotkey_account_id) stake. + /// Remove liquidity and return the tao and alpha amounts. /// Removing is allowed even when user liquidity is enabled. /// /// Account ID and Position ID identify position in the storage map @@ -878,12 +878,13 @@ impl Pallet { rm.alpha.saturating_add(rm.fee_alpha); // ---------------- USER: refund τ and convert α → stake ---------------- - + let tao_total_from_pool = rm.tao.saturating_add(rm.fee_tao); // 1) Refund τ principal directly. - if rm.tao > TaoCurrency::ZERO { - T::BalanceOps::increase_balance(&owner, rm.tao); - user_refunded_tao = user_refunded_tao.saturating_add(rm.tao); - T::TaoReserve::decrease_provided(netuid, rm.tao); + if tao_total_from_pool > TaoCurrency::ZERO { + T::BalanceOps::increase_balance(&owner, tao_total_from_pool); + user_refunded_tao = + user_refunded_tao.saturating_add(tao_total_from_pool); + T::TaoReserve::decrease_provided(netuid, tao_total_from_pool); } // 2) Stake ALL withdrawn α (principal + fees) to the best permitted validator. @@ -944,12 +945,18 @@ impl Pallet { } /// Clear **protocol-owned** liquidity and wipe all swap state for `netuid`. - pub fn do_clear_protocol_liquidity(netuid: NetUid) -> DispatchResult { + /// # Returns + /// * `(TaoCurrency, AlphaCurrency)` - The amount of TAO and ALPHA burned + pub fn do_clear_protocol_liquidity( + netuid: NetUid, + ) -> Result<(TaoCurrency, TaoCurrency, AlphaCurrency, AlphaCurrency), DispatchError> { let protocol_account = Self::protocol_account_id(); // 1) Force-close only protocol positions, burning proceeds. - let mut burned_tao = TaoCurrency::ZERO; - let mut burned_alpha = AlphaCurrency::ZERO; + let mut tao = TaoCurrency::ZERO; + let mut fee_tao = TaoCurrency::ZERO; + let mut alpha = AlphaCurrency::ZERO; + let mut fee_alpha = AlphaCurrency::ZERO; // Collect protocol position IDs first to avoid mutating while iterating. let protocol_pos_ids: sp_std::vec::Vec = Positions::::iter_prefix((netuid,)) @@ -965,27 +972,20 @@ impl Pallet { for pos_id in protocol_pos_ids { match Self::do_remove_liquidity(netuid, &protocol_account, pos_id) { Ok(rm) => { - let alpha_total_from_pool: AlphaCurrency = - rm.alpha.saturating_add(rm.fee_alpha); - let tao = rm.tao; - - if tao > TaoCurrency::ZERO { - burned_tao = burned_tao.saturating_add(tao); - } - if alpha_total_from_pool > AlphaCurrency::ZERO { - burned_alpha = burned_alpha.saturating_add(alpha_total_from_pool); - } + tao = tao.saturating_add(rm.tao); + fee_tao = fee_tao.saturating_add(rm.fee_tao); + alpha = alpha.saturating_add(rm.alpha); + fee_alpha = fee_alpha.saturating_add(rm.fee_alpha); log::debug!( - "clear_protocol_liquidity: burned protocol pos: netuid={netuid:?}, pos_id={pos_id:?}, τ={tao:?}, α_total={alpha_total_from_pool:?}" + "clear_protocol_liquidity: burned protocol pos: netuid={netuid:?}, \ + pos_id={pos_id:?}, τ={fee_tao:?}, α={fee_alpha:?}; \ + protocol liquidity: {tao:?}, {alpha:?}" ); } - Err(e) => { - log::debug!( - "clear_protocol_liquidity: force-close failed: netuid={netuid:?}, pos_id={pos_id:?}, err={e:?}" - ); - continue; - } + Err(e) => log::debug!( + "clear_protocol_liquidity: force-close failed: netuid={netuid:?}, pos_id={pos_id:?}, err={e:?}" + ), } } @@ -1011,10 +1011,11 @@ impl Pallet { EnabledUserLiquidity::::remove(netuid); log::debug!( - "clear_protocol_liquidity: netuid={netuid:?}, protocol_burned: τ={burned_tao:?}, α={burned_alpha:?}; state cleared" + "clear_protocol_liquidity: netuid={netuid:?}, protocol_fees: τ={fee_tao:?}, α={fee_alpha:?}; \ + protocol liquidity: {tao:?}, {alpha:?}; state cleared" ); - Ok(()) + Ok((tao, fee_tao, alpha, fee_alpha)) } } @@ -1137,7 +1138,9 @@ impl SwapHandler for Pallet { fn toggle_user_liquidity(netuid: NetUid, enabled: bool) { EnabledUserLiquidity::::insert(netuid, enabled) } - fn clear_protocol_liquidity(netuid: NetUid) -> DispatchResult { + fn clear_protocol_liquidity( + netuid: NetUid, + ) -> Result<(TaoCurrency, TaoCurrency, AlphaCurrency, AlphaCurrency), DispatchError> { Self::do_clear_protocol_liquidity(netuid) } } From 67634bab51d22a1dde2ac6544537e40bc824c95e Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Sat, 8 Nov 2025 20:03:12 -0500 Subject: [PATCH 140/210] commit Cargo.lock --- pallets/subtensor/src/tests/migration.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index 03eb2985fa..805da58d56 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -2497,7 +2497,7 @@ fn do_setup_unactive_sn() -> (Vec, Vec) { SubnetAlphaOutEmission::::insert(netuid, AlphaCurrency::from(12345678_u64)); } - return (active_netuids, inactive_netuids); + (active_netuids, inactive_netuids) } #[test] From 3a8baf19b6381286f2ce11b81fd347f83fcfdb85 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Sat, 8 Nov 2025 20:09:37 -0500 Subject: [PATCH 141/210] fix test --- pallets/subtensor/src/tests/migration.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index 805da58d56..ad5552ad0a 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -2422,7 +2422,7 @@ fn do_setup_unactive_sn() -> (Vec, Vec) { // Add stake to the subnet pools for netuid in &netuids { - let stake_in_pool = initial_tao + TaoCurrency::from(123123_u64); + let stake_in_pool = initial_tao.saturating_add(TaoCurrency::from(123123_u64)); SubnetTAO::::insert(netuid, stake_in_pool); TotalStake::::mutate(|total_stake| { *total_stake = total_stake.saturating_add(stake_in_pool); @@ -2431,7 +2431,7 @@ fn do_setup_unactive_sn() -> (Vec, Vec) { *total_issuance = total_issuance.saturating_add(stake_in_pool); }); - SubnetAlphaIn::::insert(netuid, initial_alpha + AlphaCurrency::from(123123_u64)); + SubnetAlphaIn::::insert(netuid, initial_alpha.saturating_add(AlphaCurrency::from(123123_u64))); SubnetAlphaOut::::insert(netuid, AlphaCurrency::from(123123_u64)); SubnetVolume::::insert(netuid, 123123_u128); SubnetLocked::::insert(netuid, actual_tao_lock_amount); From 3493ec98993f4736f3ab5fe64d656a6fa9efb7fa Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Sat, 8 Nov 2025 20:12:01 -0500 Subject: [PATCH 142/210] cargo fmt --- pallets/subtensor/src/tests/migration.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index ad5552ad0a..fee0f5a7d2 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -2431,7 +2431,10 @@ fn do_setup_unactive_sn() -> (Vec, Vec) { *total_issuance = total_issuance.saturating_add(stake_in_pool); }); - SubnetAlphaIn::::insert(netuid, initial_alpha.saturating_add(AlphaCurrency::from(123123_u64))); + SubnetAlphaIn::::insert( + netuid, + initial_alpha.saturating_add(AlphaCurrency::from(123123_u64)), + ); SubnetAlphaOut::::insert(netuid, AlphaCurrency::from(123123_u64)); SubnetVolume::::insert(netuid, 123123_u128); SubnetLocked::::insert(netuid, actual_tao_lock_amount); From a8a1498e33ee916d0806cc914844def1f0c5c6da Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Sat, 8 Nov 2025 21:02:03 -0500 Subject: [PATCH 143/210] fix tests --- .../migrations/migrate_reset_unactive_sn.rs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs index e3b627354e..0ab46fe310 100644 --- a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs +++ b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs @@ -1,4 +1,5 @@ use super::*; +use frame_support::traits::tokens::fungible::Inspect; use sp_std::collections::{btree_map::BTreeMap, btree_set::BTreeSet}; use subtensor_swap_interface::SwapHandler; @@ -169,6 +170,35 @@ pub fn migrate_reset_unactive_sn() -> Weight { } } + // Recalculate total stake to ensure it matches the updated subnet pots. + let recalculated_total_stake = + SubnetTAO::::iter().fold(TaoCurrency::ZERO, |acc, (_, stake)| acc.saturating_add(stake)); + let subnet_count = SubnetTAO::::iter().count() as u64; + TotalStake::::put(recalculated_total_stake); + weight = weight.saturating_add(T::DbWeight::get().reads_writes(subnet_count, 1)); + + // Recalculate total issuance so subsequent migrations become no-ops. + let stake_sum = Owner::::iter().fold(TaoCurrency::ZERO, |acc, (hotkey, _)| { + acc.saturating_add(Pallet::::get_total_stake_for_hotkey(&hotkey)) + }); + let owner_count = Owner::::iter().count() as u64; + let locked_sum = + SubnetLocked::::iter().fold(TaoCurrency::ZERO, |acc, (_, locked)| acc.saturating_add(locked)); + let locked_count = SubnetLocked::::iter().count() as u64; + let currency_total = ::Currency::total_issuance(); + let total_issuance_value = stake_sum + .saturating_add(locked_sum) + .saturating_add(currency_total.into()); + TotalIssuance::::put(total_issuance_value); + weight = weight.saturating_add( + T::DbWeight::get().reads_writes( + owner_count + .saturating_add(locked_count) + .saturating_add(1), + 1, + ), + ); + // Mark Migration as Completed HasMigrationRun::::insert(&migration_name, true); weight = weight.saturating_add(T::DbWeight::get().writes(1)); From e52cccdee0b84e655bed28eaedb363e0b4664b04 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Sat, 8 Nov 2025 21:04:50 -0500 Subject: [PATCH 144/210] cargo fmt --- .../migrations/migrate_reset_unactive_sn.rs | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs index 0ab46fe310..17213701ee 100644 --- a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs +++ b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs @@ -171,8 +171,10 @@ pub fn migrate_reset_unactive_sn() -> Weight { } // Recalculate total stake to ensure it matches the updated subnet pots. - let recalculated_total_stake = - SubnetTAO::::iter().fold(TaoCurrency::ZERO, |acc, (_, stake)| acc.saturating_add(stake)); + let recalculated_total_stake = SubnetTAO::::iter() + .fold(TaoCurrency::ZERO, |acc, (_, stake)| { + acc.saturating_add(stake) + }); let subnet_count = SubnetTAO::::iter().count() as u64; TotalStake::::put(recalculated_total_stake); weight = weight.saturating_add(T::DbWeight::get().reads_writes(subnet_count, 1)); @@ -182,22 +184,19 @@ pub fn migrate_reset_unactive_sn() -> Weight { acc.saturating_add(Pallet::::get_total_stake_for_hotkey(&hotkey)) }); let owner_count = Owner::::iter().count() as u64; - let locked_sum = - SubnetLocked::::iter().fold(TaoCurrency::ZERO, |acc, (_, locked)| acc.saturating_add(locked)); + let locked_sum = SubnetLocked::::iter().fold(TaoCurrency::ZERO, |acc, (_, locked)| { + acc.saturating_add(locked) + }); let locked_count = SubnetLocked::::iter().count() as u64; let currency_total = ::Currency::total_issuance(); let total_issuance_value = stake_sum .saturating_add(locked_sum) .saturating_add(currency_total.into()); TotalIssuance::::put(total_issuance_value); - weight = weight.saturating_add( - T::DbWeight::get().reads_writes( - owner_count - .saturating_add(locked_count) - .saturating_add(1), - 1, - ), - ); + weight = weight.saturating_add(T::DbWeight::get().reads_writes( + owner_count.saturating_add(locked_count).saturating_add(1), + 1, + )); // Mark Migration as Completed HasMigrationRun::::insert(&migration_name, true); From fed50dadae7bcb88fb7b131d99e47a2d8cf13627 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Sat, 8 Nov 2025 21:11:17 -0500 Subject: [PATCH 145/210] fix custom lints --- pallets/subtensor/src/tests/migration.rs | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index fee0f5a7d2..f4416e8c68 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -2417,12 +2417,27 @@ fn do_setup_unactive_sn() -> (Vec, Vec) { let initial_tao = Pallet::::get_network_min_lock(); let initial_alpha: AlphaCurrency = initial_tao.to_u64().into(); - let actual_tao_lock_amount = initial_tao.saturating_add(TaoCurrency::from(12345678_u64)); - let actual_tao_lock_amount_less_pool_tao = actual_tao_lock_amount.saturating_sub(initial_tao); + let actual_tao_lock_amount = TaoCurrency::from( + initial_tao + .to_u64() + .checked_add(12_345_678_u64) + .expect("initial_tao + 12_345_678_u64 should not overflow"), + ); + let actual_tao_lock_amount_less_pool_tao = TaoCurrency::from( + actual_tao_lock_amount + .to_u64() + .checked_sub(initial_tao.to_u64()) + .expect("actual_tao_lock_amount >= initial_tao"), + ); // Add stake to the subnet pools for netuid in &netuids { - let stake_in_pool = initial_tao.saturating_add(TaoCurrency::from(123123_u64)); + let stake_in_pool = TaoCurrency::from( + initial_tao + .to_u64() + .checked_add(123_123_u64) + .expect("initial_tao + 123_123 must stay in range"), + ); SubnetTAO::::insert(netuid, stake_in_pool); TotalStake::::mutate(|total_stake| { *total_stake = total_stake.saturating_add(stake_in_pool); From 16605175670d60fc0ec3867498fcc9cdaa4f8e6e Mon Sep 17 00:00:00 2001 From: camfairchild Date: Sun, 9 Nov 2025 03:00:08 -0500 Subject: [PATCH 146/210] remove locks from ti migration --- .../src/migrations/migrate_total_issuance.rs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/pallets/subtensor/src/migrations/migrate_total_issuance.rs b/pallets/subtensor/src/migrations/migrate_total_issuance.rs index bd69c60436..e87337b74e 100644 --- a/pallets/subtensor/src/migrations/migrate_total_issuance.rs +++ b/pallets/subtensor/src/migrations/migrate_total_issuance.rs @@ -51,14 +51,6 @@ pub fn migrate_total_issuance(test: bool) -> Weight { T::DbWeight::get().reads((Owner::::iter().count() as u64).saturating_mul(2)), ); - // Calculate the sum of all locked subnet values - let locked_sum = SubnetLocked::::iter().fold(TaoCurrency::ZERO, |acc, (_, locked)| { - acc.saturating_add(locked) - }); - // Add weight for reading all subnet locked entries - weight = weight - .saturating_add(T::DbWeight::get().reads(SubnetLocked::::iter().count() as u64)); - // Retrieve the total balance sum let total_balance = ::Currency::total_issuance(); // Add weight for reading total issuance @@ -69,8 +61,7 @@ pub fn migrate_total_issuance(test: bool) -> Weight { Ok(total_balance_sum) => { // Compute the total issuance value let total_issuance_value = stake_sum - .saturating_add(total_balance_sum.into()) - .saturating_add(locked_sum.into()); + .saturating_add(total_balance_sum.into()); // Update the total issuance in storage TotalIssuance::::put(total_issuance_value); From 86cbbdc7e50a210c3785b7ca81c1391d3db5eb89 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Sun, 9 Nov 2025 03:00:19 -0500 Subject: [PATCH 147/210] remove extra ti migration --- pallets/subtensor/src/macros/hooks.rs | 3 --- pallets/subtensor/src/migrations/mod.rs | 1 - 2 files changed, 4 deletions(-) diff --git a/pallets/subtensor/src/macros/hooks.rs b/pallets/subtensor/src/macros/hooks.rs index 56980b8a24..1b7d5fd77e 100644 --- a/pallets/subtensor/src/macros/hooks.rs +++ b/pallets/subtensor/src/macros/hooks.rs @@ -72,9 +72,6 @@ mod hooks { .saturating_add(migrations::migrate_delete_subnet_21::migrate_delete_subnet_21::()) // Storage version v4 -> v5 .saturating_add(migrations::migrate_delete_subnet_3::migrate_delete_subnet_3::()) - // Doesn't check storage version. TODO: Remove after upgrade - // Storage version v5 -> v6 - .saturating_add(migrations::migrate_total_issuance::migrate_total_issuance::(false)) // Populate OwnedHotkeys map for coldkey swap. Doesn't update storage vesion. // Storage version v6 -> v7 .saturating_add(migrations::migrate_populate_owned_hotkeys::migrate_populate_owned::()) diff --git a/pallets/subtensor/src/migrations/mod.rs b/pallets/subtensor/src/migrations/mod.rs index f5ffa8c434..41c1333a89 100644 --- a/pallets/subtensor/src/migrations/mod.rs +++ b/pallets/subtensor/src/migrations/mod.rs @@ -56,7 +56,6 @@ pub mod migrate_subnet_symbols; pub mod migrate_subnet_volume; pub mod migrate_to_v1_separate_emission; pub mod migrate_to_v2_fixed_total_stake; -pub mod migrate_total_issuance; pub mod migrate_transfer_ownership_to_foundation; pub mod migrate_upgrade_revealed_commitments; From 08ad9f0029f7b78313d919ed84cfed1f47b28ecb Mon Sep 17 00:00:00 2001 From: camfairchild Date: Sun, 9 Nov 2025 03:00:47 -0500 Subject: [PATCH 148/210] bump ti in test helper --- pallets/subtensor/src/tests/mock.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index 9d028d76ab..ddd259441e 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -718,6 +718,9 @@ pub fn add_dynamic_network(hotkey: &U256, coldkey: &U256) -> NetUid { let netuid = SubtensorModule::get_next_netuid(); let lock_cost = SubtensorModule::get_network_lock_cost(); SubtensorModule::add_balance_to_coldkey_account(coldkey, lock_cost.into()); + TotalIssuance::::mutate(|total_issuance| { + *total_issuance = total_issuance.saturating_add(lock_cost); + }); assert_ok!(SubtensorModule::register_network( RawOrigin::Signed(*coldkey).into(), @@ -735,6 +738,9 @@ pub fn add_dynamic_network_without_emission_block(hotkey: &U256, coldkey: &U256) let netuid = SubtensorModule::get_next_netuid(); let lock_cost = SubtensorModule::get_network_lock_cost(); SubtensorModule::add_balance_to_coldkey_account(coldkey, lock_cost.into()); + TotalIssuance::::mutate(|total_issuance| { + *total_issuance = total_issuance.saturating_add(lock_cost); + }); assert_ok!(SubtensorModule::register_network( RawOrigin::Signed(*coldkey).into(), From 39a3b5628e2ccef50be2beab01f41d84ea77e4ea Mon Sep 17 00:00:00 2001 From: camfairchild Date: Sun, 9 Nov 2025 03:01:28 -0500 Subject: [PATCH 149/210] dont need subnet lock --- .../subtensor/src/migrations/migrate_reset_unactive_sn.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs index 17213701ee..29928e3c3d 100644 --- a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs +++ b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs @@ -94,12 +94,6 @@ pub fn migrate_reset_unactive_sn() -> Weight { SubnetAlphaOutEmission::::remove(*netuid); weight = weight.saturating_add(T::DbWeight::get().writes(7)); - // Reset pool - let actual_tao_lock_amount = SubnetLocked::::get(*netuid); - let actual_tao_lock_amount_less_pool_tao = - actual_tao_lock_amount.saturating_sub(pool_initial_tao); - weight = weight.saturating_add(T::DbWeight::get().reads(1)); - // Reset v3 pool let burned_tao = match T::SwapInterface::clear_protocol_liquidity(*netuid) { Ok((_tao, fee_tao, _alpha, _fee_alpha)) => fee_tao, From 42508751fda24e5481704c90152a6ec4acfe3f3e Mon Sep 17 00:00:00 2001 From: camfairchild Date: Sun, 9 Nov 2025 03:02:08 -0500 Subject: [PATCH 150/210] ust use total issuance migration --- .../migrations/migrate_reset_unactive_sn.rs | 29 ++----------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs index 29928e3c3d..de676acf4a 100644 --- a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs +++ b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs @@ -164,33 +164,8 @@ pub fn migrate_reset_unactive_sn() -> Weight { } } - // Recalculate total stake to ensure it matches the updated subnet pots. - let recalculated_total_stake = SubnetTAO::::iter() - .fold(TaoCurrency::ZERO, |acc, (_, stake)| { - acc.saturating_add(stake) - }); - let subnet_count = SubnetTAO::::iter().count() as u64; - TotalStake::::put(recalculated_total_stake); - weight = weight.saturating_add(T::DbWeight::get().reads_writes(subnet_count, 1)); - - // Recalculate total issuance so subsequent migrations become no-ops. - let stake_sum = Owner::::iter().fold(TaoCurrency::ZERO, |acc, (hotkey, _)| { - acc.saturating_add(Pallet::::get_total_stake_for_hotkey(&hotkey)) - }); - let owner_count = Owner::::iter().count() as u64; - let locked_sum = SubnetLocked::::iter().fold(TaoCurrency::ZERO, |acc, (_, locked)| { - acc.saturating_add(locked) - }); - let locked_count = SubnetLocked::::iter().count() as u64; - let currency_total = ::Currency::total_issuance(); - let total_issuance_value = stake_sum - .saturating_add(locked_sum) - .saturating_add(currency_total.into()); - TotalIssuance::::put(total_issuance_value); - weight = weight.saturating_add(T::DbWeight::get().reads_writes( - owner_count.saturating_add(locked_count).saturating_add(1), - 1, - )); + // Run total issuance migration + crate::migrations::migrate_init_total_issuance::migrate_init_total_issuance::(); // Mark Migration as Completed HasMigrationRun::::insert(&migration_name, true); From f3c7319d92503b4f7d4723f26a048253e5024d34 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Sun, 9 Nov 2025 03:02:38 -0500 Subject: [PATCH 151/210] mint TAO in the case that the SubnetTAO is lower than init --- .../src/migrations/migrate_reset_unactive_sn.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs index de676acf4a..1bde30dbd4 100644 --- a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs +++ b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs @@ -1,5 +1,4 @@ use super::*; -use frame_support::traits::tokens::fungible::Inspect; use sp_std::collections::{btree_map::BTreeMap, btree_set::BTreeSet}; use subtensor_swap_interface::SwapHandler; @@ -107,6 +106,7 @@ pub fn migrate_reset_unactive_sn() -> Weight { weight = weight.saturating_add(T::DbWeight::get().reads_writes(6, 14)); // Recycle already emitted TAO + // or mint 1 TAO to the pool let subnet_tao = SubnetTAO::::get(*netuid); if subnet_tao > pool_initial_tao { let tao_to_recycle = subnet_tao.saturating_sub(pool_initial_tao); @@ -118,6 +118,18 @@ pub fn migrate_reset_unactive_sn() -> Weight { *amount = amount.saturating_sub(tao_to_recycle); }); weight = weight.saturating_add(T::DbWeight::get().reads_writes(3, 3)); + } else if subnet_tao < pool_initial_tao { + let tao_to_add = pool_initial_tao.saturating_sub(subnet_tao); + TotalStake::::mutate(|total| { + *total = total.saturating_add(tao_to_add); + }); + SubnetTAO::::mutate(*netuid, |amount| { + *amount = amount.saturating_add(tao_to_add); + }); + TotalIssuance::::mutate(|total| { + *total = total.saturating_add(tao_to_add); + }); + weight = weight.saturating_add(T::DbWeight::get().reads_writes(3, 3)); } // Reset pool alpha @@ -125,8 +137,6 @@ pub fn migrate_reset_unactive_sn() -> Weight { SubnetAlphaOut::::insert(*netuid, AlphaCurrency::ZERO); // Reset volume SubnetVolume::::insert(*netuid, 0u128); - // Reset recycled (from init_new_network) - RAORecycledForRegistration::::insert(*netuid, actual_tao_lock_amount_less_pool_tao); weight = weight.saturating_add(T::DbWeight::get().writes(4)); // Reset Alpha stake entries for this subnet From 97065fe1192ed45c9d61edb597d2a047ce0433bb Mon Sep 17 00:00:00 2001 From: camfairchild Date: Sun, 9 Nov 2025 03:03:05 -0500 Subject: [PATCH 152/210] cleanup test --- pallets/subtensor/src/tests/migration.rs | 63 ++++++++---------------- 1 file changed, 20 insertions(+), 43 deletions(-) diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index f4416e8c68..55b7166948 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -2400,14 +2400,14 @@ fn test_migrate_remove_tao_dividends() { fn do_setup_unactive_sn() -> (Vec, Vec) { // Register some subnets - let netuid0 = add_dynamic_network(&U256::from(0), &U256::from(0)); - let netuid1 = add_dynamic_network(&U256::from(1), &U256::from(1)); - let netuid2 = add_dynamic_network(&U256::from(2), &U256::from(2)); + let netuid0 = add_dynamic_network_without_emission_block(&U256::from(0), &U256::from(0)); + let netuid1 = add_dynamic_network_without_emission_block(&U256::from(1), &U256::from(1)); + let netuid2 = add_dynamic_network_without_emission_block(&U256::from(2), &U256::from(2)); let inactive_netuids = vec![netuid0, netuid1, netuid2]; // Add active subnets - let netuid3 = add_dynamic_network(&U256::from(3), &U256::from(3)); - let netuid4 = add_dynamic_network(&U256::from(4), &U256::from(4)); - let netuid5 = add_dynamic_network(&U256::from(5), &U256::from(5)); + let netuid3 = add_dynamic_network_without_emission_block(&U256::from(3), &U256::from(3)); + let netuid4 = add_dynamic_network_without_emission_block(&U256::from(4), &U256::from(4)); + let netuid5 = add_dynamic_network_without_emission_block(&U256::from(5), &U256::from(5)); let active_netuids = vec![netuid3, netuid4, netuid5]; let netuids: Vec = inactive_netuids .iter() @@ -2417,33 +2417,17 @@ fn do_setup_unactive_sn() -> (Vec, Vec) { let initial_tao = Pallet::::get_network_min_lock(); let initial_alpha: AlphaCurrency = initial_tao.to_u64().into(); - let actual_tao_lock_amount = TaoCurrency::from( - initial_tao - .to_u64() - .checked_add(12_345_678_u64) - .expect("initial_tao + 12_345_678_u64 should not overflow"), - ); - let actual_tao_lock_amount_less_pool_tao = TaoCurrency::from( - actual_tao_lock_amount - .to_u64() - .checked_sub(initial_tao.to_u64()) - .expect("actual_tao_lock_amount >= initial_tao"), - ); // Add stake to the subnet pools for netuid in &netuids { - let stake_in_pool = TaoCurrency::from( - initial_tao - .to_u64() - .checked_add(123_123_u64) - .expect("initial_tao + 123_123 must stay in range"), - ); + let extra_for_pool = TaoCurrency::from(123_123_u64); + let stake_in_pool = initial_tao.saturating_add(extra_for_pool); SubnetTAO::::insert(netuid, stake_in_pool); TotalStake::::mutate(|total_stake| { - *total_stake = total_stake.saturating_add(stake_in_pool); + *total_stake = total_stake.saturating_add(extra_for_pool); }); TotalIssuance::::mutate(|total_issuance| { - *total_issuance = total_issuance.saturating_add(stake_in_pool); + *total_issuance = total_issuance.saturating_add(extra_for_pool); }); SubnetAlphaIn::::insert( @@ -2452,11 +2436,6 @@ fn do_setup_unactive_sn() -> (Vec, Vec) { ); SubnetAlphaOut::::insert(netuid, AlphaCurrency::from(123123_u64)); SubnetVolume::::insert(netuid, 123123_u128); - SubnetLocked::::insert(netuid, actual_tao_lock_amount); - - // Remove the FirstEmissionBlockNumber - FirstEmissionBlockNumber::::remove(netuid); - SubtokenEnabled::::remove(netuid); // Try registering on the subnet to simulate a real network // give balance to the coldkey @@ -2475,6 +2454,7 @@ fn do_setup_unactive_sn() -> (Vec, Vec) { hotkey_account_id )); } + for netuid in &active_netuids { // Set the FirstEmissionBlockNumber for the active subnet FirstEmissionBlockNumber::::insert(netuid, 100); @@ -2483,7 +2463,6 @@ fn do_setup_unactive_sn() -> (Vec, Vec) { } let alpha_amt = AlphaCurrency::from(123123_u64); - let tao_amt = TaoCurrency::from(333333_u64); // Create some Stake entries for netuid in &netuids { for hotkey in 0..10 { @@ -2552,9 +2531,6 @@ fn test_migrate_reset_unactive_sn() { let initial_tao = Pallet::::get_network_min_lock(); let initial_alpha: AlphaCurrency = initial_tao.to_u64().into(); - let actual_tao_lock_amount = initial_tao.saturating_add(TaoCurrency::from(12345678_u64)); - let actual_tao_lock_amount_less_pool_tao = - actual_tao_lock_amount.saturating_sub(initial_tao); // Run the migration let w = crate::migrations::migrate_reset_unactive_sn::migrate_reset_unactive_sn::(); @@ -2562,6 +2538,9 @@ fn test_migrate_reset_unactive_sn() { // Verify the results for netuid in &inactive_netuids { + let actual_tao_lock_amount = SubnetLocked::::get(*netuid); + let actual_tao_lock_amount_less_pool_tao = + actual_tao_lock_amount.saturating_sub(initial_tao); assert_eq!( PendingServerEmission::::get(netuid), AlphaCurrency::ZERO @@ -2575,6 +2554,7 @@ fn test_migrate_reset_unactive_sn() { AlphaCurrency::ZERO ); assert_eq!( + // not modified RAORecycledForRegistration::::get(netuid), actual_tao_lock_amount_less_pool_tao ); @@ -2621,6 +2601,9 @@ fn test_migrate_reset_unactive_sn() { // !!! Make sure the active subnets were not reset for netuid in &active_netuids { + let actual_tao_lock_amount = SubnetLocked::::get(*netuid); + let actual_tao_lock_amount_less_pool_tao = + actual_tao_lock_amount.saturating_sub(initial_tao); assert_ne!( PendingServerEmission::::get(netuid), AlphaCurrency::ZERO @@ -2633,7 +2616,8 @@ fn test_migrate_reset_unactive_sn() { PendingRootAlphaDivs::::get(netuid), AlphaCurrency::ZERO ); - assert_ne!( + assert_eq!( + // not modified RAORecycledForRegistration::::get(netuid), actual_tao_lock_amount_less_pool_tao ); @@ -2691,12 +2675,6 @@ fn test_migrate_reset_unactive_sn_idempotence() { .copied() .collect::>(); - let initial_tao = Pallet::::get_network_min_lock(); - let initial_alpha: AlphaCurrency = initial_tao.to_u64().into(); - let actual_tao_lock_amount = initial_tao.saturating_add(TaoCurrency::from(12345678_u64)); - let actual_tao_lock_amount_less_pool_tao = - actual_tao_lock_amount.saturating_sub(initial_tao); - // Run the migration let w = crate::migrations::migrate_reset_unactive_sn::migrate_reset_unactive_sn::(); assert!(!w.is_zero(), "weight must be non-zero"); @@ -2710,7 +2688,6 @@ fn test_migrate_reset_unactive_sn_idempotence() { // Run total issuance migration crate::migrations::migrate_init_total_issuance::migrate_init_total_issuance::(); - crate::migrations::migrate_total_issuance::migrate_total_issuance::(true); // Verify that none of the values are different for netuid in &netuids { From d9aac6e92d94e6f2597bd04cab1257e87d204691 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Sun, 9 Nov 2025 15:35:38 -0500 Subject: [PATCH 153/210] fix test --- pallets/subtensor/src/tests/coinbase.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 2ac4a24c4e..66779012b2 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -89,12 +89,16 @@ fn test_coinbase_tao_issuance_base() { let subnet_owner_ck = U256::from(1001); let subnet_owner_hk = U256::from(1002); let netuid = add_dynamic_network(&subnet_owner_hk, &subnet_owner_ck); + let total_issuance_before = TotalIssuance::::get(); SubnetMovingPrice::::insert(netuid, I96F32::from(3141) / I96F32::from(1000)); let tao_in_before = SubnetTAO::::get(netuid); let total_stake_before = TotalStake::::get(); SubtensorModule::run_coinbase(U96F32::from_num(emission)); assert_eq!(SubnetTAO::::get(netuid), tao_in_before + emission); - assert_eq!(TotalIssuance::::get(), emission); + assert_eq!( + TotalIssuance::::get(), + total_issuance_before + emission + ); assert_eq!(TotalStake::::get(), total_stake_before + emission); }); } From e20ad6fa85bd6eddcde381750c399ffb7cbdc66c Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Sun, 9 Nov 2025 15:59:09 -0500 Subject: [PATCH 154/210] fix custom lints --- pallets/subtensor/src/tests/migration.rs | 28 ++++++++++-------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index 55b7166948..e45eca2ca9 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -2016,7 +2016,7 @@ fn test_migrate_network_lock_reduction_interval_and_decay() { // last_lock_block should be set one week in the future let last_lock_block = Pallet::::get_network_last_lock_block(); - let expected_block = current_block_before.saturating_add(ONE_WEEK_BLOCKS); + let expected_block = current_block_before + ONE_WEEK_BLOCKS; assert_eq!( last_lock_block, expected_block, @@ -2243,10 +2243,8 @@ fn test_migrate_network_lock_cost_2500_sets_price_and_decay() { let min_lock_rao: u64 = Pallet::::get_network_min_lock().to_u64(); step_block(1); - let expected_after_1: u64 = core::cmp::max( - min_lock_rao, - TARGET_COST_RAO.saturating_sub(per_block_decrement), - ); + let expected_after_1: u64 = + core::cmp::max(min_lock_rao, TARGET_COST_RAO - per_block_decrement); let lock_cost_after_1 = Pallet::::get_network_lock_cost(); assert_eq!( lock_cost_after_1, @@ -2293,9 +2291,9 @@ fn test_migrate_kappa_map_to_default() { let default: u16 = DefaultKappa::::get(); let not_default: u16 = if default == u16::MAX { - default.saturating_sub(1) + default - 1 } else { - default.saturating_add(1) + default + 1 }; // ------------------------------ @@ -2421,18 +2419,18 @@ fn do_setup_unactive_sn() -> (Vec, Vec) { // Add stake to the subnet pools for netuid in &netuids { let extra_for_pool = TaoCurrency::from(123_123_u64); - let stake_in_pool = initial_tao.saturating_add(extra_for_pool); + let stake_in_pool = initial_tao + extra_for_pool; SubnetTAO::::insert(netuid, stake_in_pool); TotalStake::::mutate(|total_stake| { - *total_stake = total_stake.saturating_add(extra_for_pool); + *total_stake += extra_for_pool; }); TotalIssuance::::mutate(|total_issuance| { - *total_issuance = total_issuance.saturating_add(extra_for_pool); + *total_issuance += extra_for_pool; }); SubnetAlphaIn::::insert( netuid, - initial_alpha.saturating_add(AlphaCurrency::from(123123_u64)), + initial_alpha + AlphaCurrency::from(123_123_u64), ); SubnetAlphaOut::::insert(netuid, AlphaCurrency::from(123123_u64)); SubnetVolume::::insert(netuid, 123123_u128); @@ -2444,7 +2442,7 @@ fn do_setup_unactive_sn() -> (Vec, Vec) { let burn_cost = SubtensorModule::get_burn(*netuid); SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, burn_cost.into()); TotalIssuance::::mutate(|total_issuance| { - *total_issuance = total_issuance.saturating_add(burn_cost); + *total_issuance += burn_cost; }); // register the neuron @@ -2539,8 +2537,7 @@ fn test_migrate_reset_unactive_sn() { // Verify the results for netuid in &inactive_netuids { let actual_tao_lock_amount = SubnetLocked::::get(*netuid); - let actual_tao_lock_amount_less_pool_tao = - actual_tao_lock_amount.saturating_sub(initial_tao); + let actual_tao_lock_amount_less_pool_tao = actual_tao_lock_amount - initial_tao; assert_eq!( PendingServerEmission::::get(netuid), AlphaCurrency::ZERO @@ -2602,8 +2599,7 @@ fn test_migrate_reset_unactive_sn() { // !!! Make sure the active subnets were not reset for netuid in &active_netuids { let actual_tao_lock_amount = SubnetLocked::::get(*netuid); - let actual_tao_lock_amount_less_pool_tao = - actual_tao_lock_amount.saturating_sub(initial_tao); + let actual_tao_lock_amount_less_pool_tao = actual_tao_lock_amount - initial_tao; assert_ne!( PendingServerEmission::::get(netuid), AlphaCurrency::ZERO From d3564f26e8df3d9f8368c433589a59b449ffaa89 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Sun, 9 Nov 2025 16:10:29 -0500 Subject: [PATCH 155/210] fix more lints --- pallets/subtensor/src/tests/migration.rs | 36 ++++++++++++++++++------ 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index e45eca2ca9..e91fc49a6d 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -2416,23 +2416,38 @@ fn do_setup_unactive_sn() -> (Vec, Vec) { let initial_tao = Pallet::::get_network_min_lock(); let initial_alpha: AlphaCurrency = initial_tao.to_u64().into(); + const EXTRA_POOL_TAO: u64 = 123_123_u64; + const EXTRA_POOL_ALPHA: u64 = 123_123_u64; + // Add stake to the subnet pools for netuid in &netuids { - let extra_for_pool = TaoCurrency::from(123_123_u64); - let stake_in_pool = initial_tao + extra_for_pool; + let extra_for_pool = TaoCurrency::from(EXTRA_POOL_TAO); + let stake_in_pool = TaoCurrency::from( + u64::from(initial_tao) + .checked_add(EXTRA_POOL_TAO) + .expect("initial_tao + extra_for_pool overflow"), + ); SubnetTAO::::insert(netuid, stake_in_pool); TotalStake::::mutate(|total_stake| { - *total_stake += extra_for_pool; + let updated_total = u64::from(*total_stake) + .checked_add(EXTRA_POOL_TAO) + .expect("total stake overflow"); + *total_stake = updated_total.into(); }); TotalIssuance::::mutate(|total_issuance| { - *total_issuance += extra_for_pool; + let updated_total = u64::from(*total_issuance) + .checked_add(EXTRA_POOL_TAO) + .expect("total issuance overflow"); + *total_issuance = updated_total.into(); }); - SubnetAlphaIn::::insert( - netuid, - initial_alpha + AlphaCurrency::from(123_123_u64), + let subnet_alpha_in = AlphaCurrency::from( + u64::from(initial_alpha) + .checked_add(EXTRA_POOL_ALPHA) + .expect("initial alpha + extra alpha overflow"), ); - SubnetAlphaOut::::insert(netuid, AlphaCurrency::from(123123_u64)); + SubnetAlphaIn::::insert(netuid, subnet_alpha_in); + SubnetAlphaOut::::insert(netuid, AlphaCurrency::from(EXTRA_POOL_ALPHA)); SubnetVolume::::insert(netuid, 123123_u128); // Try registering on the subnet to simulate a real network @@ -2442,7 +2457,10 @@ fn do_setup_unactive_sn() -> (Vec, Vec) { let burn_cost = SubtensorModule::get_burn(*netuid); SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, burn_cost.into()); TotalIssuance::::mutate(|total_issuance| { - *total_issuance += burn_cost; + let updated_total = u64::from(*total_issuance) + .checked_add(u64::from(burn_cost)) + .expect("total issuance overflow (burn)"); + *total_issuance = updated_total.into(); }); // register the neuron From 500891aeb40810e6da5ba9d8276c94b5504d39c0 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Sun, 9 Nov 2025 17:28:41 -0500 Subject: [PATCH 156/210] use if instead of saturating --- pallets/subtensor/src/tests/migration.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index e91fc49a6d..c3d0d7f42d 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -2555,7 +2555,11 @@ fn test_migrate_reset_unactive_sn() { // Verify the results for netuid in &inactive_netuids { let actual_tao_lock_amount = SubnetLocked::::get(*netuid); - let actual_tao_lock_amount_less_pool_tao = actual_tao_lock_amount - initial_tao; + let actual_tao_lock_amount_less_pool_tao = if (actual_tao_lock_amount < initial_tao) { + TaoCurrency::ZERO + } else { + actual_tao_lock_amount - initial_tao + }; assert_eq!( PendingServerEmission::::get(netuid), AlphaCurrency::ZERO From 3a82a7364e988086586751d5bde3edcc5cfdacdc Mon Sep 17 00:00:00 2001 From: camfairchild Date: Sun, 9 Nov 2025 20:29:29 -0500 Subject: [PATCH 157/210] update evm e2e tests --- evm-tests/test/neuron.precompile.emission-check.test.ts | 2 +- evm-tests/test/staking.precompile.reward.test.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/evm-tests/test/neuron.precompile.emission-check.test.ts b/evm-tests/test/neuron.precompile.emission-check.test.ts index e54cb1ec88..8abafec907 100644 --- a/evm-tests/test/neuron.precompile.emission-check.test.ts +++ b/evm-tests/test/neuron.precompile.emission-check.test.ts @@ -63,7 +63,7 @@ describe("Test the Neuron precompile with emission", () => { let i = 0; while (i < 10) { - const emission = await api.query.SubtensorModule.PendingEmission.getValue(netuid) + const emission = await api.query.SubtensorModule.ServerEmission.getValue(netuid) console.log("emission is ", emission); await new Promise((resolve) => setTimeout(resolve, 2000)); diff --git a/evm-tests/test/staking.precompile.reward.test.ts b/evm-tests/test/staking.precompile.reward.test.ts index 55a1312313..1b47239e1d 100644 --- a/evm-tests/test/staking.precompile.reward.test.ts +++ b/evm-tests/test/staking.precompile.reward.test.ts @@ -74,14 +74,14 @@ describe("Test neuron precompile reward", () => { let index = 0; while (index < 60) { - const pending = await api.query.SubtensorModule.PendingEmission.getValue(netuid); + const pending = await api.query.SubtensorModule.ValidatorEmission.getValue(netuid); if (pending > 0) { console.log("pending amount is ", pending); break; } await new Promise((resolve) => setTimeout(resolve, 1000)); - console.log("wait for the pendingEmission update"); + console.log("wait for the ValidatorEmission update"); index += 1; } From 1c4729af37abaa4716b4bb4ee4a89b8ee66bd48c Mon Sep 17 00:00:00 2001 From: open-junius Date: Mon, 10 Nov 2025 23:10:07 +0800 Subject: [PATCH 158/210] commit Cargo.lock --- pallets/subtensor/src/tests/coinbase.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 56f7d2171f..a54dedf4ce 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -3080,6 +3080,7 @@ fn test_mining_emission_distribution_with_subsidy() { let miner_incentive: AlphaCurrency = (*Incentive::::get(NetUidStorageIndex::from(netuid)) .get(miner_uid as usize) + .ok_or("Miner uid should be present") .expect("Miner uid should be present") as u64) .into(); log::info!("Miner incentive: {miner_incentive:?}"); @@ -3248,6 +3249,7 @@ fn test_mining_emission_distribution_with_no_subsidy() { let miner_incentive: AlphaCurrency = (*Incentive::::get(NetUidStorageIndex::from(netuid)) .get(miner_uid as usize) + .ok_or("Miner uid should be present") .expect("Miner uid should be present") as u64) .into(); log::info!("Miner incentive: {miner_incentive:?}"); From 41f4d1542ec2e0115f0c2b2ab05313b18445858d Mon Sep 17 00:00:00 2001 From: open-junius Date: Mon, 10 Nov 2025 23:12:51 +0800 Subject: [PATCH 159/210] commit Cargo.lock --- pallets/subtensor/src/tests/coinbase.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index a54dedf4ce..56f7d2171f 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -3080,7 +3080,6 @@ fn test_mining_emission_distribution_with_subsidy() { let miner_incentive: AlphaCurrency = (*Incentive::::get(NetUidStorageIndex::from(netuid)) .get(miner_uid as usize) - .ok_or("Miner uid should be present") .expect("Miner uid should be present") as u64) .into(); log::info!("Miner incentive: {miner_incentive:?}"); @@ -3249,7 +3248,6 @@ fn test_mining_emission_distribution_with_no_subsidy() { let miner_incentive: AlphaCurrency = (*Incentive::::get(NetUidStorageIndex::from(netuid)) .get(miner_uid as usize) - .ok_or("Miner uid should be present") .expect("Miner uid should be present") as u64) .into(); log::info!("Miner incentive: {miner_incentive:?}"); From 448c5f128763f59034c4ac9defae907cee0c8cb2 Mon Sep 17 00:00:00 2001 From: open-junius Date: Mon, 10 Nov 2025 23:15:25 +0800 Subject: [PATCH 160/210] fix CI --- pallets/subtensor/src/tests/coinbase.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 56f7d2171f..efa0e4c957 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -1,4 +1,10 @@ -#![allow(unused, clippy::indexing_slicing, clippy::panic, clippy::unwrap_used)] +#![allow( + unused, + clippy::indexing_slicing, + clippy::panic, + clippy::unwrap_used, + clippy::expect_used +)] use super::mock::*; use crate::tests::mock; From 830ca3736404c35f877329426e9ddf63d27e5873 Mon Sep 17 00:00:00 2001 From: open-junius Date: Mon, 10 Nov 2025 23:15:58 +0800 Subject: [PATCH 161/210] bump version --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 42f35cbc80..fe9bda8b6c 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 341, + spec_version: 342, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 6053ed5ebe6147b5fee4fdd7a13db6bd497ad90b Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Mon, 10 Nov 2025 10:52:48 -0500 Subject: [PATCH 162/210] don't clear alpha maps --- .../migrations/migrate_reset_unactive_sn.rs | 35 ------------------- pallets/subtensor/src/tests/migration.rs | 14 ++++---- runtime/src/lib.rs | 2 +- 3 files changed, 8 insertions(+), 43 deletions(-) diff --git a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs index 1bde30dbd4..bee715c8d4 100644 --- a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs +++ b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs @@ -134,44 +134,9 @@ pub fn migrate_reset_unactive_sn() -> Weight { // Reset pool alpha SubnetAlphaIn::::insert(*netuid, pool_initial_alpha); - SubnetAlphaOut::::insert(*netuid, AlphaCurrency::ZERO); // Reset volume SubnetVolume::::insert(*netuid, 0u128); weight = weight.saturating_add(T::DbWeight::get().writes(4)); - - // Reset Alpha stake entries for this subnet - let to_reset: Vec = match to_remove_alpha_hotkeys.get(netuid) { - Some(hotkeys) => hotkeys.clone(), - None => Vec::new(), - }; - - for hotkey in to_reset { - TotalHotkeyAlpha::::remove(&hotkey, *netuid); - TotalHotkeyShares::::remove(&hotkey, *netuid); - TotalHotkeyAlphaLastEpoch::::remove(&hotkey, *netuid); - weight = weight.saturating_add(T::DbWeight::get().writes(3)); - - // Reset root claimable and claimed - RootClaimable::::mutate(&hotkey, |claimable| { - claimable.remove(netuid); - }); - weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); - let removal_result = RootClaimed::::clear_prefix((*netuid, &hotkey), u32::MAX, None); - weight = weight.saturating_add( - T::DbWeight::get() - .reads_writes(removal_result.loops as u64, removal_result.backend as u64), - ); - - let to_reset_alpha: Vec<(T::AccountId, T::AccountId)> = - match to_remove_alpha_coldkeys.get(netuid) { - Some(coldkeys) => coldkeys.clone(), - None => Vec::new(), - }; - for (hotkey, coldkey) in to_reset_alpha { - Alpha::::remove((hotkey, coldkey, netuid)); - weight = weight.saturating_add(T::DbWeight::get().writes(1)); - } - } } // Run total issuance migration diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index c3d0d7f42d..aaece8d526 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -2581,7 +2581,7 @@ fn test_migrate_reset_unactive_sn() { assert_eq!(PendingOwnerCut::::get(netuid), AlphaCurrency::ZERO); assert_eq!(SubnetTAO::::get(netuid), initial_tao); assert_eq!(SubnetAlphaIn::::get(netuid), initial_alpha); - assert_eq!(SubnetAlphaOut::::get(netuid), AlphaCurrency::ZERO); + assert_ne!(SubnetAlphaOut::::get(netuid), AlphaCurrency::ZERO); assert_eq!(SubnetTaoInEmission::::get(netuid), TaoCurrency::ZERO); assert_eq!( SubnetAlphaInEmission::::get(netuid), @@ -2594,23 +2594,23 @@ fn test_migrate_reset_unactive_sn() { assert_eq!(SubnetVolume::::get(netuid), 0u128); for hotkey in 0..10 { let hk = U256::from(hotkey); - assert_eq!( + assert_ne!( TotalHotkeyAlpha::::get(hk, netuid), AlphaCurrency::ZERO ); - assert_eq!( + assert_ne!( TotalHotkeyShares::::get(hk, netuid), U64F64::from_num(0.0) ); - assert_eq!( + assert_ne!( TotalHotkeyAlphaLastEpoch::::get(hk, netuid), AlphaCurrency::ZERO ); - assert_eq!(RootClaimable::::get(hk).get(netuid), None); + assert_ne!(RootClaimable::::get(hk).get(netuid), None); for coldkey in 0..10 { let ck = U256::from(coldkey); - assert_eq!(Alpha::::get((hk, ck, netuid)), U64F64::from_num(0.0)); - assert_eq!(RootClaimed::::get((netuid, hk, ck)), 0u128); + assert_ne!(Alpha::::get((hk, ck, netuid)), U64F64::from_num(0.0)); + assert_ne!(RootClaimed::::get((netuid, hk, ck)), 0u128); } } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 3db1c38291..5e17dc6f7d 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 344, + spec_version: 345, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 7a40853ddc14ccd26d226c67090f753f98ce6a3e Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Mon, 10 Nov 2025 11:04:12 -0500 Subject: [PATCH 163/210] only reset the pending em --- .../migrations/migrate_reset_unactive_sn.rs | 82 +------------------ pallets/subtensor/src/tests/migration.rs | 10 ++- 2 files changed, 8 insertions(+), 84 deletions(-) diff --git a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs index bee715c8d4..0ae7c5604b 100644 --- a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs +++ b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs @@ -1,7 +1,4 @@ use super::*; -use sp_std::collections::{btree_map::BTreeMap, btree_set::BTreeSet}; - -use subtensor_swap_interface::SwapHandler; pub fn get_unactive_sn_netuids( pool_initial_alpha: AlphaCurrency, @@ -51,37 +48,6 @@ pub fn migrate_reset_unactive_sn() -> Weight { let (unactive_netuids, w) = get_unactive_sn_netuids::(pool_initial_alpha); weight = weight.saturating_add(w); - // Collect the hotkeys to remove for each subnet - let mut to_remove_alpha_hotkeys: BTreeMap> = BTreeMap::new(); - let mut to_remove_alpha_coldkeys: BTreeMap> = - BTreeMap::new(); - let mut all_hotkeys_set = BTreeSet::new(); - for (hotkey, netuid_i, _) in TotalHotkeyAlpha::::iter() { - weight = weight.saturating_add(T::DbWeight::get().reads(1)); - if unactive_netuids.contains(&netuid_i) { - // Only for unactive subnets - to_remove_alpha_hotkeys - .entry(netuid_i) - .or_insert(Vec::new()) - .push(hotkey.clone()); - all_hotkeys_set.insert(hotkey); - } - } - - // Collect the coldkeys to remove for each subnet - for hotkey in all_hotkeys_set.iter() { - for ((coldkey, netuid_i), _) in Alpha::::iter_prefix((&hotkey,)) { - weight = weight.saturating_add(T::DbWeight::get().reads(1)); - if unactive_netuids.contains(&netuid_i) { - // Only for unactive subnets - to_remove_alpha_coldkeys - .entry(netuid_i) - .or_insert(Vec::new()) - .push((hotkey.clone(), coldkey)); - } - } - } - for netuid in unactive_netuids.iter() { // Reset the subnet as it shouldn't have any emissions PendingServerEmission::::remove(*netuid); @@ -92,55 +58,11 @@ pub fn migrate_reset_unactive_sn() -> Weight { SubnetAlphaInEmission::::remove(*netuid); SubnetAlphaOutEmission::::remove(*netuid); weight = weight.saturating_add(T::DbWeight::get().writes(7)); - - // Reset v3 pool - let burned_tao = match T::SwapInterface::clear_protocol_liquidity(*netuid) { - Ok((_tao, fee_tao, _alpha, _fee_alpha)) => fee_tao, - Err(e) => { - log::error!("Failed to clear protocol liquidity for netuid {netuid:?}: {e:?}"); - TaoCurrency::ZERO - } - }; - Pallet::::recycle_tao(burned_tao); - // might be based on ticks but this is a rough estimate - weight = weight.saturating_add(T::DbWeight::get().reads_writes(6, 14)); - - // Recycle already emitted TAO - // or mint 1 TAO to the pool - let subnet_tao = SubnetTAO::::get(*netuid); - if subnet_tao > pool_initial_tao { - let tao_to_recycle = subnet_tao.saturating_sub(pool_initial_tao); - Pallet::::recycle_tao(tao_to_recycle); - TotalStake::::mutate(|total| { - *total = total.saturating_sub(tao_to_recycle); - }); - SubnetTAO::::mutate(*netuid, |amount| { - *amount = amount.saturating_sub(tao_to_recycle); - }); - weight = weight.saturating_add(T::DbWeight::get().reads_writes(3, 3)); - } else if subnet_tao < pool_initial_tao { - let tao_to_add = pool_initial_tao.saturating_sub(subnet_tao); - TotalStake::::mutate(|total| { - *total = total.saturating_add(tao_to_add); - }); - SubnetTAO::::mutate(*netuid, |amount| { - *amount = amount.saturating_add(tao_to_add); - }); - TotalIssuance::::mutate(|total| { - *total = total.saturating_add(tao_to_add); - }); - weight = weight.saturating_add(T::DbWeight::get().reads_writes(3, 3)); - } - - // Reset pool alpha - SubnetAlphaIn::::insert(*netuid, pool_initial_alpha); - // Reset volume - SubnetVolume::::insert(*netuid, 0u128); - weight = weight.saturating_add(T::DbWeight::get().writes(4)); } // Run total issuance migration - crate::migrations::migrate_init_total_issuance::migrate_init_total_issuance::(); + let ti_w = crate::migrations::migrate_init_total_issuance::migrate_init_total_issuance::(); + weight = weight.saturating_add(ti_w); // Mark Migration as Completed HasMigrationRun::::insert(&migration_name, true); diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index aaece8d526..4aa320327f 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -2577,10 +2577,12 @@ fn test_migrate_reset_unactive_sn() { RAORecycledForRegistration::::get(netuid), actual_tao_lock_amount_less_pool_tao ); - assert!(!pallet_subtensor_swap::AlphaSqrtPrice::::contains_key(*netuid)); + assert!(pallet_subtensor_swap::AlphaSqrtPrice::::contains_key( + *netuid + )); assert_eq!(PendingOwnerCut::::get(netuid), AlphaCurrency::ZERO); - assert_eq!(SubnetTAO::::get(netuid), initial_tao); - assert_eq!(SubnetAlphaIn::::get(netuid), initial_alpha); + assert_ne!(SubnetTAO::::get(netuid), initial_tao); + assert_ne!(SubnetAlphaIn::::get(netuid), initial_alpha); assert_ne!(SubnetAlphaOut::::get(netuid), AlphaCurrency::ZERO); assert_eq!(SubnetTaoInEmission::::get(netuid), TaoCurrency::ZERO); assert_eq!( @@ -2591,7 +2593,7 @@ fn test_migrate_reset_unactive_sn() { SubnetAlphaOutEmission::::get(netuid), AlphaCurrency::ZERO ); - assert_eq!(SubnetVolume::::get(netuid), 0u128); + assert_ne!(SubnetVolume::::get(netuid), 0u128); for hotkey in 0..10 { let hk = U256::from(hotkey); assert_ne!( From 2c37bc2410839449ba9ca76987a1abdd010d5fdc Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Mon, 10 Nov 2025 11:05:22 -0500 Subject: [PATCH 164/210] dont call ti migration --- pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs index 0ae7c5604b..e2e8ddc296 100644 --- a/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs +++ b/pallets/subtensor/src/migrations/migrate_reset_unactive_sn.rs @@ -60,10 +60,6 @@ pub fn migrate_reset_unactive_sn() -> Weight { weight = weight.saturating_add(T::DbWeight::get().writes(7)); } - // Run total issuance migration - let ti_w = crate::migrations::migrate_init_total_issuance::migrate_init_total_issuance::(); - weight = weight.saturating_add(ti_w); - // Mark Migration as Completed HasMigrationRun::::insert(&migration_name, true); weight = weight.saturating_add(T::DbWeight::get().writes(1)); From e64150b06d1bbca7ff2de739dc6e849db2d0e687 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Mon, 10 Nov 2025 13:18:18 -0500 Subject: [PATCH 165/210] revert changes to swap interface --- pallets/swap-interface/src/lib.rs | 4 +-- pallets/swap/src/pallet/impls.rs | 56 +++++++++++++++---------------- 2 files changed, 28 insertions(+), 32 deletions(-) diff --git a/pallets/swap-interface/src/lib.rs b/pallets/swap-interface/src/lib.rs index 408bb842eb..ae7d375f97 100644 --- a/pallets/swap-interface/src/lib.rs +++ b/pallets/swap-interface/src/lib.rs @@ -49,9 +49,7 @@ pub trait SwapHandler { fn is_user_liquidity_enabled(netuid: NetUid) -> bool; fn dissolve_all_liquidity_providers(netuid: NetUid) -> DispatchResult; fn toggle_user_liquidity(netuid: NetUid, enabled: bool); - fn clear_protocol_liquidity( - netuid: NetUid, - ) -> Result<(TaoCurrency, TaoCurrency, AlphaCurrency, AlphaCurrency), DispatchError>; + fn clear_protocol_liquidity(netuid: NetUid) -> DispatchResult; } pub trait DefaultPriceLimit diff --git a/pallets/swap/src/pallet/impls.rs b/pallets/swap/src/pallet/impls.rs index fe19be2eaa..c455dd19f8 100644 --- a/pallets/swap/src/pallet/impls.rs +++ b/pallets/swap/src/pallet/impls.rs @@ -878,13 +878,11 @@ impl Pallet { rm.alpha.saturating_add(rm.fee_alpha); // ---------------- USER: refund τ and convert α → stake ---------------- - let tao_total_from_pool = rm.tao.saturating_add(rm.fee_tao); // 1) Refund τ principal directly. - if tao_total_from_pool > TaoCurrency::ZERO { - T::BalanceOps::increase_balance(&owner, tao_total_from_pool); - user_refunded_tao = - user_refunded_tao.saturating_add(tao_total_from_pool); - T::TaoReserve::decrease_provided(netuid, tao_total_from_pool); + if rm.tao > TaoCurrency::ZERO { + T::BalanceOps::increase_balance(&owner, rm.tao); + user_refunded_tao = user_refunded_tao.saturating_add(rm.tao); + T::TaoReserve::decrease_provided(netuid, rm.tao); } // 2) Stake ALL withdrawn α (principal + fees) to the best permitted validator. @@ -947,16 +945,12 @@ impl Pallet { /// Clear **protocol-owned** liquidity and wipe all swap state for `netuid`. /// # Returns /// * `(TaoCurrency, AlphaCurrency)` - The amount of TAO and ALPHA burned - pub fn do_clear_protocol_liquidity( - netuid: NetUid, - ) -> Result<(TaoCurrency, TaoCurrency, AlphaCurrency, AlphaCurrency), DispatchError> { + pub fn do_clear_protocol_liquidity(netuid: NetUid) -> DispatchResult { let protocol_account = Self::protocol_account_id(); // 1) Force-close only protocol positions, burning proceeds. - let mut tao = TaoCurrency::ZERO; - let mut fee_tao = TaoCurrency::ZERO; - let mut alpha = AlphaCurrency::ZERO; - let mut fee_alpha = AlphaCurrency::ZERO; + let mut burned_tao = TaoCurrency::ZERO; + let mut burned_alpha = AlphaCurrency::ZERO; // Collect protocol position IDs first to avoid mutating while iterating. let protocol_pos_ids: sp_std::vec::Vec = Positions::::iter_prefix((netuid,)) @@ -972,20 +966,27 @@ impl Pallet { for pos_id in protocol_pos_ids { match Self::do_remove_liquidity(netuid, &protocol_account, pos_id) { Ok(rm) => { - tao = tao.saturating_add(rm.tao); - fee_tao = fee_tao.saturating_add(rm.fee_tao); - alpha = alpha.saturating_add(rm.alpha); - fee_alpha = fee_alpha.saturating_add(rm.fee_alpha); + let alpha_total_from_pool: AlphaCurrency = + rm.alpha.saturating_add(rm.fee_alpha); + let tao = rm.tao; + + if tao > TaoCurrency::ZERO { + burned_tao = burned_tao.saturating_add(tao); + } + if alpha_total_from_pool > AlphaCurrency::ZERO { + burned_alpha = burned_alpha.saturating_add(alpha_total_from_pool); + } log::debug!( - "clear_protocol_liquidity: burned protocol pos: netuid={netuid:?}, \ - pos_id={pos_id:?}, τ={fee_tao:?}, α={fee_alpha:?}; \ - protocol liquidity: {tao:?}, {alpha:?}" + "clear_protocol_liquidity: burned protocol pos: netuid={netuid:?}, pos_id={pos_id:?}, τ={tao:?}, α_total={alpha_total_from_pool:?}" ); } - Err(e) => log::debug!( - "clear_protocol_liquidity: force-close failed: netuid={netuid:?}, pos_id={pos_id:?}, err={e:?}" - ), + Err(e) => { + log::debug!( + "clear_protocol_liquidity: force-close failed: netuid={netuid:?}, pos_id={pos_id:?}, err={e:?}" + ); + continue; + } } } @@ -1011,11 +1012,10 @@ impl Pallet { EnabledUserLiquidity::::remove(netuid); log::debug!( - "clear_protocol_liquidity: netuid={netuid:?}, protocol_fees: τ={fee_tao:?}, α={fee_alpha:?}; \ - protocol liquidity: {tao:?}, {alpha:?}; state cleared" + "clear_protocol_liquidity: netuid={netuid:?}, protocol_burned: τ={burned_tao:?}, α={burned_alpha:?}; state cleared" ); - Ok((tao, fee_tao, alpha, fee_alpha)) + Ok(()) } } @@ -1138,9 +1138,7 @@ impl SwapHandler for Pallet { fn toggle_user_liquidity(netuid: NetUid, enabled: bool) { EnabledUserLiquidity::::insert(netuid, enabled) } - fn clear_protocol_liquidity( - netuid: NetUid, - ) -> Result<(TaoCurrency, TaoCurrency, AlphaCurrency, AlphaCurrency), DispatchError> { + fn clear_protocol_liquidity(netuid: NetUid) -> DispatchResult { Self::do_clear_protocol_liquidity(netuid) } } From c01f1bb67f5cec1bf2c57594f200641567041d5f Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Mon, 10 Nov 2025 13:21:28 -0500 Subject: [PATCH 166/210] undo comment/fmt changes in swap --- pallets/swap/src/pallet/impls.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pallets/swap/src/pallet/impls.rs b/pallets/swap/src/pallet/impls.rs index c455dd19f8..34b5e624e6 100644 --- a/pallets/swap/src/pallet/impls.rs +++ b/pallets/swap/src/pallet/impls.rs @@ -511,7 +511,7 @@ impl Pallet { Ok((position, tao, alpha)) } - /// Remove liquidity and return the tao and alpha amounts. + /// Remove liquidity and credit balances back to (coldkey_account_id, hotkey_account_id) stake. /// Removing is allowed even when user liquidity is enabled. /// /// Account ID and Position ID identify position in the storage map @@ -878,6 +878,7 @@ impl Pallet { rm.alpha.saturating_add(rm.fee_alpha); // ---------------- USER: refund τ and convert α → stake ---------------- + // 1) Refund τ principal directly. if rm.tao > TaoCurrency::ZERO { T::BalanceOps::increase_balance(&owner, rm.tao); @@ -943,8 +944,6 @@ impl Pallet { } /// Clear **protocol-owned** liquidity and wipe all swap state for `netuid`. - /// # Returns - /// * `(TaoCurrency, AlphaCurrency)` - The amount of TAO and ALPHA burned pub fn do_clear_protocol_liquidity(netuid: NetUid) -> DispatchResult { let protocol_account = Self::protocol_account_id(); From 4fdcdbc59687e1baa0bcb858fac8c5c85a5bbcce Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Mon, 10 Nov 2025 19:05:50 -0300 Subject: [PATCH 167/210] fix leasing to distribute alpha only --- pallets/subtensor/src/subnets/leasing.rs | 94 ++++--- pallets/subtensor/src/tests/leasing.rs | 322 +++++++++++++++++------ 2 files changed, 280 insertions(+), 136 deletions(-) diff --git a/pallets/subtensor/src/subnets/leasing.rs b/pallets/subtensor/src/subnets/leasing.rs index cf263a1335..bc8fd352cb 100644 --- a/pallets/subtensor/src/subnets/leasing.rs +++ b/pallets/subtensor/src/subnets/leasing.rs @@ -24,8 +24,7 @@ use frame_system::pallet_prelude::*; use sp_core::blake2_256; use sp_runtime::{Percent, traits::TrailingZeroInput}; use substrate_fixed::types::U64F64; -use subtensor_runtime_common::{AlphaCurrency, NetUid, TaoCurrency}; -use subtensor_swap_interface::SwapHandler; +use subtensor_runtime_common::{AlphaCurrency, NetUid}; pub type LeaseId = u32; @@ -130,6 +129,9 @@ impl Pallet { ); SubnetUidToLeaseId::::insert(netuid, lease_id); + // The lease take should be 0% to allow all contributors to receive dividends entirely. + Self::delegate_hotkey(&lease_hotkey, 0); + // Get all the contributions to the crowdloan except for the beneficiary // because its share will be computed as the dividends are distributed let contributions = pallet_crowdloan::Contributions::::iter_prefix(crowdloan_id) @@ -249,9 +251,8 @@ impl Pallet { /// Hook used when the subnet owner's cut is distributed to split the amount into dividends /// for the contributors and the beneficiary in shares relative to their initial contributions. - /// - /// It will ensure the subnet has enough alpha in its liquidity pool before swapping it to tao to be distributed, - /// and if not enough liquidity is available, it will accumulate the dividends for later distribution. + /// It accumulates dividends to be distributed later when the interval for distribution is reached. + /// Distribution is made in alpha and stake to the contributor coldkey and lease hotkey. pub fn distribute_leased_network_dividends(lease_id: LeaseId, owner_cut_alpha: AlphaCurrency) { // Ensure the lease exists let Some(lease) = SubnetLeases::::get(lease_id) else { @@ -290,55 +291,48 @@ impl Pallet { return; } - // Ensure there is enough liquidity to unstake the contributors cut - if let Err(err) = Self::validate_remove_stake( - &lease.coldkey, - &lease.hotkey, - lease.netuid, - total_contributors_cut_alpha, - total_contributors_cut_alpha, - false, - ) { - log::debug!("Couldn't distributing dividends for lease {lease_id}: {err:?}"); - AccumulatedLeaseDividends::::set(lease_id, total_contributors_cut_alpha); - return; - } - - // Unstake the contributors cut from the subnet as tao to the lease coldkey - let tao_unstaked = match Self::unstake_from_subnet( - &lease.hotkey, - &lease.coldkey, - lease.netuid, - total_contributors_cut_alpha, - T::SwapInterface::min_price(), - false, - ) { - Ok(tao_unstaked) => tao_unstaked, - Err(err) => { - log::debug!("Couldn't distributing dividends for lease {lease_id}: {err:?}"); - AccumulatedLeaseDividends::::set(lease_id, total_contributors_cut_alpha); - return; + // We use a storage layer to ensure the distribution is atomic. + if let Err(err) = frame_support::storage::with_storage_layer(|| { + let mut alpha_distributed = AlphaCurrency::ZERO; + + // Distribute the contributors cut to the contributors and accumulate the alpha + // distributed so far to obtain how much alpha is left to distribute to the beneficiary + for (contributor, share) in SubnetLeaseShares::::iter_prefix(lease_id) { + let alpha_for_contributor = share + .saturating_mul(U64F64::from(total_contributors_cut_alpha.to_u64())) + .ceil() + .saturating_to_num::(); + Self::transfer_stake_within_subnet( + &lease.coldkey, + &lease.hotkey, + &contributor, + &lease.hotkey, + lease.netuid, + alpha_for_contributor.into(), + )?; + alpha_distributed = alpha_distributed.saturating_add(alpha_for_contributor.into()); } - }; - // Distribute the contributors cut to the contributors and accumulate the tao - // distributed so far to obtain how much tao is left to distribute to the beneficiary - let mut tao_distributed = TaoCurrency::ZERO; - for (contributor, share) in SubnetLeaseShares::::iter_prefix(lease_id) { - let tao_for_contributor = share - .saturating_mul(U64F64::from(tao_unstaked.to_u64())) - .floor() - .saturating_to_num::(); - Self::add_balance_to_coldkey_account(&contributor, tao_for_contributor); - tao_distributed = tao_distributed.saturating_add(tao_for_contributor.into()); - } + // Distribute the leftover alpha to the beneficiary + let beneficiary_cut_alpha = + total_contributors_cut_alpha.saturating_sub(alpha_distributed); + Self::transfer_stake_within_subnet( + &lease.coldkey, + &lease.hotkey, + &lease.beneficiary, + &lease.hotkey, + lease.netuid, + beneficiary_cut_alpha.into(), + )?; - // Distribute the leftover tao to the beneficiary - let beneficiary_cut_tao = tao_unstaked.saturating_sub(tao_distributed); - Self::add_balance_to_coldkey_account(&lease.beneficiary, beneficiary_cut_tao.into()); + // Reset the accumulated dividends + AccumulatedLeaseDividends::::insert(lease_id, AlphaCurrency::ZERO); - // Reset the accumulated dividends - AccumulatedLeaseDividends::::insert(lease_id, AlphaCurrency::ZERO); + Ok::<(), DispatchError>(()) + }) { + log::debug!("Couldn't distributing dividends for lease {lease_id}: {err:?}"); + AccumulatedLeaseDividends::::set(lease_id, total_contributors_cut_alpha); + }; } fn lease_coldkey(lease_id: LeaseId) -> Result { diff --git a/pallets/subtensor/src/tests/leasing.rs b/pallets/subtensor/src/tests/leasing.rs index 4ffc18230a..0ee268e2b2 100644 --- a/pallets/subtensor/src/tests/leasing.rs +++ b/pallets/subtensor/src/tests/leasing.rs @@ -65,6 +65,9 @@ fn test_register_leased_network_works() { contributor2_share ); + // Ensure the lease hotkey has 0 take from staking + assert_eq!(SubtensorModule::get_hotkey_take(&lease.hotkey), 0); + // Ensure each contributor and beneficiary has been refunded their share of the leftover cap let leftover_cap = cap.saturating_sub(lease.cost); @@ -502,54 +505,84 @@ fn test_distribute_lease_network_dividends_multiple_contributors_works() { // Setup the correct block to distribute dividends run_to_block(::LeaseDividendsDistributionInterval::get() as u64); - // Get the initial subnet tao after stake and ensure all contributor - // balances are in initial state - let subnet_tao_before = SubnetTAO::::get(lease.netuid); - let contributor1_balance_before = SubtensorModule::get_coldkey_balance(&contributions[0].0); - let contributor2_balance_before = SubtensorModule::get_coldkey_balance(&contributions[1].0); - let beneficiary_balance_before = SubtensorModule::get_coldkey_balance(&beneficiary); + // Get the initial alpha for the contributors and beneficiary and ensure they are zero + let contributor1_alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &lease.hotkey, + &contributions[0].0, + lease.netuid, + ); + assert_eq!(contributor1_alpha_before, AlphaCurrency::ZERO); + let contributor2_alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &lease.hotkey, + &contributions[1].0, + lease.netuid, + ); + assert_eq!(contributor2_alpha_before, AlphaCurrency::ZERO); + let beneficiary_alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &lease.hotkey, + &beneficiary, + lease.netuid, + ); + assert_eq!(beneficiary_alpha_before, AlphaCurrency::ZERO); // Setup some previously accumulated dividends - let accumulated_dividends = AlphaCurrency::from(5_000_000); + let accumulated_dividends = AlphaCurrency::from(10_000_000_000); AccumulatedLeaseDividends::::insert(lease_id, accumulated_dividends); // Distribute the dividends - let owner_cut_alpha = AlphaCurrency::from(5_000_000); + let owner_cut_alpha = AlphaCurrency::from(5_000_000_000); SubtensorModule::distribute_leased_network_dividends(lease_id, owner_cut_alpha); // Ensure the dividends were distributed correctly relative to their shares - let distributed_tao = subnet_tao_before - SubnetTAO::::get(lease.netuid); - let contributor1_balance_delta = SubtensorModule::get_coldkey_balance(&contributions[0].0) - .saturating_sub(contributor1_balance_before); - let contributor2_balance_delta = SubtensorModule::get_coldkey_balance(&contributions[1].0) - .saturating_sub(contributor2_balance_before); - let beneficiary_balance_delta = SubtensorModule::get_coldkey_balance(&beneficiary) - .saturating_sub(beneficiary_balance_before); - + let distributed_alpha = + accumulated_dividends + emissions_share.mul_ceil(owner_cut_alpha.to_u64()).into(); + assert_ne!(distributed_alpha, AlphaCurrency::ZERO); + let contributor1_alpha_delta = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &lease.hotkey, + &contributions[0].0, + lease.netuid, + ) + .saturating_sub(contributor1_alpha_before); + assert_ne!(contributor1_alpha_delta, AlphaCurrency::ZERO); + let contributor2_alpha_delta = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &lease.hotkey, + &contributions[1].0, + lease.netuid, + ) + .saturating_sub(contributor2_alpha_before); + assert_ne!(contributor2_alpha_delta, AlphaCurrency::ZERO); + let beneficiary_alpha_delta = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &lease.hotkey, + &beneficiary, + lease.netuid, + ) + .saturating_sub(beneficiary_alpha_before); + assert_ne!(beneficiary_alpha_delta, AlphaCurrency::ZERO); + + // What has been distributed should be equal to the sum of all contributors received alpha assert_eq!( - distributed_tao, - (beneficiary_balance_delta + contributor1_balance_delta + contributor2_balance_delta) - .into() + distributed_alpha, + (beneficiary_alpha_delta + contributor1_alpha_delta + contributor2_alpha_delta).into() ); - let expected_contributor1_balance = + let expected_contributor1_alpha = SubnetLeaseShares::::get(lease_id, contributions[0].0) - .saturating_mul(U64F64::from(distributed_tao.to_u64())) - .floor() + .saturating_mul(U64F64::from(distributed_alpha.to_u64())) + .ceil() .to_num::(); - assert_eq!(contributor1_balance_delta, expected_contributor1_balance); + assert_eq!(contributor1_alpha_delta, expected_contributor1_alpha.into()); - let expected_contributor2_balance = + let expected_contributor2_alpha = SubnetLeaseShares::::get(lease_id, contributions[1].0) - .saturating_mul(U64F64::from(distributed_tao.to_u64())) - .floor() + .saturating_mul(U64F64::from(distributed_alpha.to_u64())) + .ceil() .to_num::(); - assert_eq!(contributor2_balance_delta, expected_contributor2_balance); + assert_eq!(contributor2_alpha_delta, expected_contributor2_alpha.into()); // The beneficiary should have received the remaining dividends - let expected_beneficiary_balance = distributed_tao.to_u64() - - (expected_contributor1_balance + expected_contributor2_balance); - assert_eq!(beneficiary_balance_delta, expected_beneficiary_balance); + let expected_beneficiary_alpha = distributed_alpha.to_u64() + - (expected_contributor1_alpha + expected_contributor2_alpha); + assert_eq!(beneficiary_alpha_delta, expected_beneficiary_alpha.into()); // Ensure nothing was accumulated for later distribution assert_eq!( @@ -584,23 +617,33 @@ fn test_distribute_lease_network_dividends_only_beneficiary_works() { // Setup the correct block to distribute dividends run_to_block(::LeaseDividendsDistributionInterval::get() as u64); - // Get the initial subnet tao after stake and beneficiary balance - let subnet_tao_before = SubnetTAO::::get(lease.netuid); - let beneficiary_balance_before = SubtensorModule::get_coldkey_balance(&beneficiary); + // Get the initial alpha for the beneficiary and ensure it is zero + let beneficiary_alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &lease.hotkey, + &beneficiary, + lease.netuid, + ); + assert_eq!(beneficiary_alpha_before, AlphaCurrency::ZERO); // Setup some previously accumulated dividends - let accumulated_dividends = AlphaCurrency::from(5_000_000); + let accumulated_dividends = AlphaCurrency::from(10_000_000_000); AccumulatedLeaseDividends::::insert(lease_id, accumulated_dividends); // Distribute the dividends - let owner_cut_alpha = AlphaCurrency::from(5_000_000); + let owner_cut_alpha = AlphaCurrency::from(5_000_000_000); SubtensorModule::distribute_leased_network_dividends(lease_id, owner_cut_alpha); // Ensure the dividends were distributed correctly relative to their shares - let distributed_tao = subnet_tao_before - SubnetTAO::::get(lease.netuid); - let beneficiary_balance_delta = SubtensorModule::get_coldkey_balance(&beneficiary) - .saturating_sub(beneficiary_balance_before); - assert_eq!(distributed_tao, beneficiary_balance_delta.into()); + let distributed_alpha = + accumulated_dividends + emissions_share.mul_ceil(owner_cut_alpha.to_u64()).into(); + assert_ne!(distributed_alpha, AlphaCurrency::ZERO); + let beneficiary_alpha_delta = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &lease.hotkey, + &beneficiary, + lease.netuid, + ) + .saturating_sub(beneficiary_alpha_before); + assert_eq!(beneficiary_alpha_delta, distributed_alpha.into()); // Ensure nothing was accumulated for later distribution assert_eq!( @@ -628,7 +671,7 @@ fn test_distribute_lease_network_dividends_accumulates_if_not_the_correct_block( let end_block = 500; let emissions_share = Percent::from_percent(30); let tao_to_stake = 100_000_000_000; // 100 TAO - let (lease_id, _) = setup_leased_network( + let (lease_id, lease) = setup_leased_network( beneficiary, emissions_share, Some(end_block), @@ -638,31 +681,58 @@ fn test_distribute_lease_network_dividends_accumulates_if_not_the_correct_block( // Setup incorrect block to distribute dividends run_to_block(::LeaseDividendsDistributionInterval::get() as u64 + 1); - // Get the initial subnet tao after stake and ensure all contributor - let contributor1_balance_before = SubtensorModule::get_coldkey_balance(&contributions[0].0); - let contributor2_balance_before = SubtensorModule::get_coldkey_balance(&contributions[1].0); - let beneficiary_balance_before = SubtensorModule::get_coldkey_balance(&beneficiary); + // Get the initial alpha for the contributors and beneficiary and ensure they are zero + let contributor1_alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &lease.hotkey, + &contributions[0].0, + lease.netuid, + ); + assert_eq!(contributor1_alpha_before, AlphaCurrency::ZERO); + let contributor2_alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &lease.hotkey, + &contributions[1].0, + lease.netuid, + ); + assert_eq!(contributor2_alpha_before, AlphaCurrency::ZERO); + let beneficiary_alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &lease.hotkey, + &beneficiary, + lease.netuid, + ); + assert_eq!(beneficiary_alpha_before, AlphaCurrency::ZERO); // Setup some previously accumulated dividends - let accumulated_dividends = AlphaCurrency::from(5_000_000); + let accumulated_dividends = AlphaCurrency::from(10_000_000_000); AccumulatedLeaseDividends::::insert(lease_id, accumulated_dividends); // Distribute the dividends - let owner_cut_alpha = AlphaCurrency::from(5_000_000); + let owner_cut_alpha = AlphaCurrency::from(5_000_000_000); SubtensorModule::distribute_leased_network_dividends(lease_id, owner_cut_alpha); // Ensure the dividends were not distributed assert_eq!( - SubtensorModule::get_coldkey_balance(&contributions[0].0), - contributor1_balance_before + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &lease.hotkey, + &contributions[0].0, + lease.netuid + ), + contributor1_alpha_before ); assert_eq!( - SubtensorModule::get_coldkey_balance(&contributions[1].0), - contributor2_balance_before + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &lease.hotkey, + &contributions[1].0, + lease.netuid + ), + contributor2_alpha_before ); assert_eq!( - SubtensorModule::get_coldkey_balance(&beneficiary), - beneficiary_balance_before + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &lease.hotkey, + &beneficiary, + lease.netuid + ), + beneficiary_alpha_before ); // Ensure we correctly accumulated the dividends @@ -711,29 +781,58 @@ fn test_distribute_lease_network_dividends_does_nothing_if_lease_has_ended() { // Run to the end of the lease run_to_block(end_block); - let subnet_tao_before = SubnetTAO::::get(lease.netuid); - let contributor1_balance_before = SubtensorModule::get_coldkey_balance(&contributions[0].0); - let contributor2_balance_before = SubtensorModule::get_coldkey_balance(&contributions[1].0); - let beneficiary_balance_before = SubtensorModule::get_coldkey_balance(&beneficiary); + // Get the initial alpha for the contributors and beneficiary and ensure they are zero + let contributor1_alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &lease.hotkey, + &contributions[0].0, + lease.netuid, + ); + assert_eq!(contributor1_alpha_before, AlphaCurrency::ZERO); + let contributor2_alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &lease.hotkey, + &contributions[1].0, + lease.netuid, + ); + assert_eq!(contributor2_alpha_before, AlphaCurrency::ZERO); + let beneficiary_alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &lease.hotkey, + &beneficiary, + lease.netuid, + ); + assert_eq!(beneficiary_alpha_before, AlphaCurrency::ZERO); + + // No dividends are present, lease is new let accumulated_dividends_before = AccumulatedLeaseDividends::::get(lease_id); + assert_eq!(accumulated_dividends_before, AlphaCurrency::ZERO); // Try to distribute the dividends - let owner_cut_alpha = AlphaCurrency::from(5_000_000); + let owner_cut_alpha = AlphaCurrency::from(5_000_000_000); SubtensorModule::distribute_leased_network_dividends(lease_id, owner_cut_alpha); // Ensure the dividends were not distributed - assert_eq!(SubnetTAO::::get(lease.netuid), subnet_tao_before); assert_eq!( - SubtensorModule::get_coldkey_balance(&contributions[0].0), - contributor1_balance_before + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &lease.hotkey, + &contributions[0].0, + lease.netuid + ), + contributor1_alpha_before ); assert_eq!( - SubtensorModule::get_coldkey_balance(&contributions[1].0), - contributor2_balance_before + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &lease.hotkey, + &contributions[1].0, + lease.netuid + ), + contributor2_alpha_before ); assert_eq!( - SubtensorModule::get_coldkey_balance(&beneficiary), - beneficiary_balance_before + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &lease.hotkey, + &beneficiary, + lease.netuid + ), + beneficiary_alpha_before ); // Ensure nothing was accumulated for later distribution assert_eq!( @@ -768,28 +867,54 @@ fn test_distribute_lease_network_dividends_accumulates_if_amount_is_too_low() { None, // We don't add any liquidity ); - let subnet_tao_before = SubnetTAO::::get(lease.netuid); - let contributor1_balance_before = SubtensorModule::get_coldkey_balance(&contributions[0].0); - let contributor2_balance_before = SubtensorModule::get_coldkey_balance(&contributions[1].0); - let beneficiary_balance_before = SubtensorModule::get_coldkey_balance(&beneficiary); + // Get the initial alpha for the contributors and beneficiary and ensure they are zero + let contributor1_alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &lease.hotkey, + &contributions[0].0, + lease.netuid, + ); + assert_eq!(contributor1_alpha_before, AlphaCurrency::ZERO); + let contributor2_alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &lease.hotkey, + &contributions[1].0, + lease.netuid, + ); + assert_eq!(contributor2_alpha_before, AlphaCurrency::ZERO); + let beneficiary_alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &lease.hotkey, + &beneficiary, + lease.netuid, + ); + assert_eq!(beneficiary_alpha_before, AlphaCurrency::ZERO); // Try to distribute the dividends let owner_cut_alpha = AlphaCurrency::from(5_000); SubtensorModule::distribute_leased_network_dividends(lease_id, owner_cut_alpha); // Ensure the dividends were not distributed - assert_eq!(SubnetTAO::::get(lease.netuid), subnet_tao_before); assert_eq!( - SubtensorModule::get_coldkey_balance(&contributions[0].0), - contributor1_balance_before + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &lease.hotkey, + &contributions[0].0, + lease.netuid + ), + contributor1_alpha_before ); assert_eq!( - SubtensorModule::get_coldkey_balance(&contributions[1].0), - contributor2_balance_before + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &lease.hotkey, + &contributions[1].0, + lease.netuid + ), + contributor2_alpha_before ); assert_eq!( - SubtensorModule::get_coldkey_balance(&beneficiary), - beneficiary_balance_before + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &lease.hotkey, + &beneficiary, + lease.netuid + ), + beneficiary_alpha_before ); // Ensure the correct amount of alpha was accumulated for later dividends distribution assert_eq!( @@ -824,28 +949,53 @@ fn test_distribute_lease_network_dividends_accumulates_if_insufficient_liquidity None, // We don't add any liquidity ); - let subnet_tao_before = SubnetTAO::::get(lease.netuid); - let contributor1_balance_before = SubtensorModule::get_coldkey_balance(&contributions[0].0); - let contributor2_balance_before = SubtensorModule::get_coldkey_balance(&contributions[1].0); - let beneficiary_balance_before = SubtensorModule::get_coldkey_balance(&beneficiary); + let contributor1_alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &lease.hotkey, + &contributions[0].0, + lease.netuid, + ); + assert_eq!(contributor1_alpha_before, AlphaCurrency::ZERO); + let contributor2_alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &lease.hotkey, + &contributions[1].0, + lease.netuid, + ); + assert_eq!(contributor2_alpha_before, AlphaCurrency::ZERO); + let beneficiary_alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &lease.hotkey, + &beneficiary, + lease.netuid, + ); + assert_eq!(beneficiary_alpha_before, AlphaCurrency::ZERO); // Try to distribute the dividends let owner_cut_alpha = AlphaCurrency::from(5_000_000); SubtensorModule::distribute_leased_network_dividends(lease_id, owner_cut_alpha); // Ensure the dividends were not distributed - assert_eq!(SubnetTAO::::get(lease.netuid), subnet_tao_before); assert_eq!( - SubtensorModule::get_coldkey_balance(&contributions[0].0), - contributor1_balance_before + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &lease.hotkey, + &contributions[0].0, + lease.netuid + ), + contributor1_alpha_before ); assert_eq!( - SubtensorModule::get_coldkey_balance(&contributions[1].0), - contributor2_balance_before + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &lease.hotkey, + &contributions[1].0, + lease.netuid + ), + contributor2_alpha_before ); assert_eq!( - SubtensorModule::get_coldkey_balance(&beneficiary), - beneficiary_balance_before + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &lease.hotkey, + &beneficiary, + lease.netuid + ), + beneficiary_alpha_before ); // Ensure the correct amount of alpha was accumulated for later dividends distribution assert_eq!( From 0e3b8cc65d0fc7d4dd1cdb3a74f867acd9c0dff5 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 10 Nov 2025 19:50:58 -0500 Subject: [PATCH 168/210] bump CI From d968a93ba95a5c91a3251d1f6a9ae066498b9f3d Mon Sep 17 00:00:00 2001 From: camfairchild Date: Mon, 10 Nov 2025 21:18:09 -0500 Subject: [PATCH 169/210] add run ti migration before migration in idemp test. --- pallets/subtensor/src/tests/migration.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index 4aa320327f..b694459eaa 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -2695,10 +2695,14 @@ fn test_migrate_reset_unactive_sn_idempotence() { .copied() .collect::>(); + // Run total issuance migration *before* running the migration. + crate::migrations::migrate_init_total_issuance::migrate_init_total_issuance::(); + // Run the migration let w = crate::migrations::migrate_reset_unactive_sn::migrate_reset_unactive_sn::(); assert!(!w.is_zero(), "weight must be non-zero"); + // Store the values after running the migration let mut subnet_tao_before = BTreeMap::new(); for netuid in &netuids { subnet_tao_before.insert(netuid, SubnetTAO::::get(netuid)); @@ -2706,7 +2710,7 @@ fn test_migrate_reset_unactive_sn_idempotence() { let total_stake_before = TotalStake::::get(); let total_issuance_before = TotalIssuance::::get(); - // Run total issuance migration + // Run total issuance migration again, to make sure no changes happen from it. crate::migrations::migrate_init_total_issuance::migrate_init_total_issuance::(); // Verify that none of the values are different From 5ac488054fb3be94d300178864174fcdbcff5b9c Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Tue, 11 Nov 2025 20:33:51 -0300 Subject: [PATCH 170/210] add dividends emission event --- pallets/subtensor/src/macros/events.rs | 10 +++++++ pallets/subtensor/src/subnets/leasing.rs | 12 +++++++++ pallets/subtensor/src/tests/leasing.rs | 34 ++++++++++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index c2931024ee..d015205d4d 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -467,5 +467,15 @@ mod events { /// Claim type root_claim_type: RootClaimTypeEnum, }, + + /// Subnet lease dividends have been distributed. + SubnetLeaseDividendsDistributed { + /// The lease ID + lease_id: LeaseId, + /// The contributor + contributor: T::AccountId, + /// The amount of alpha distributed + alpha: AlphaCurrency, + }, } } diff --git a/pallets/subtensor/src/subnets/leasing.rs b/pallets/subtensor/src/subnets/leasing.rs index bc8fd352cb..a202a22a45 100644 --- a/pallets/subtensor/src/subnets/leasing.rs +++ b/pallets/subtensor/src/subnets/leasing.rs @@ -302,6 +302,7 @@ impl Pallet { .saturating_mul(U64F64::from(total_contributors_cut_alpha.to_u64())) .ceil() .saturating_to_num::(); + Self::transfer_stake_within_subnet( &lease.coldkey, &lease.hotkey, @@ -311,6 +312,12 @@ impl Pallet { alpha_for_contributor.into(), )?; alpha_distributed = alpha_distributed.saturating_add(alpha_for_contributor.into()); + + Self::deposit_event(Event::SubnetLeaseDividendsDistributed { + lease_id, + contributor, + alpha: alpha_for_contributor.into(), + }); } // Distribute the leftover alpha to the beneficiary @@ -324,6 +331,11 @@ impl Pallet { lease.netuid, beneficiary_cut_alpha.into(), )?; + Self::deposit_event(Event::SubnetLeaseDividendsDistributed { + lease_id, + contributor: lease.beneficiary.clone(), + alpha: beneficiary_cut_alpha.into(), + }); // Reset the accumulated dividends AccumulatedLeaseDividends::::insert(lease_id, AlphaCurrency::ZERO); diff --git a/pallets/subtensor/src/tests/leasing.rs b/pallets/subtensor/src/tests/leasing.rs index 0ee268e2b2..4c48d2c341 100644 --- a/pallets/subtensor/src/tests/leasing.rs +++ b/pallets/subtensor/src/tests/leasing.rs @@ -537,6 +537,7 @@ fn test_distribute_lease_network_dividends_multiple_contributors_works() { let distributed_alpha = accumulated_dividends + emissions_share.mul_ceil(owner_cut_alpha.to_u64()).into(); assert_ne!(distributed_alpha, AlphaCurrency::ZERO); + let contributor1_alpha_delta = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &lease.hotkey, &contributions[0].0, @@ -544,6 +545,7 @@ fn test_distribute_lease_network_dividends_multiple_contributors_works() { ) .saturating_sub(contributor1_alpha_before); assert_ne!(contributor1_alpha_delta, AlphaCurrency::ZERO); + let contributor2_alpha_delta = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &lease.hotkey, &contributions[1].0, @@ -551,6 +553,7 @@ fn test_distribute_lease_network_dividends_multiple_contributors_works() { ) .saturating_sub(contributor2_alpha_before); assert_ne!(contributor2_alpha_delta, AlphaCurrency::ZERO); + let beneficiary_alpha_delta = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &lease.hotkey, &beneficiary, @@ -571,6 +574,14 @@ fn test_distribute_lease_network_dividends_multiple_contributors_works() { .ceil() .to_num::(); assert_eq!(contributor1_alpha_delta, expected_contributor1_alpha.into()); + assert_eq!( + System::events().get(2).expect("Event not found").event, + RuntimeEvent::SubtensorModule(Event::SubnetLeaseDividendsDistributed { + lease_id, + contributor: contributions[0].0.into(), + alpha: expected_contributor1_alpha.into(), + },) + ); let expected_contributor2_alpha = SubnetLeaseShares::::get(lease_id, contributions[1].0) @@ -578,11 +589,27 @@ fn test_distribute_lease_network_dividends_multiple_contributors_works() { .ceil() .to_num::(); assert_eq!(contributor2_alpha_delta, expected_contributor2_alpha.into()); + assert_eq!( + System::events().get(5).expect("Event not found").event, + RuntimeEvent::SubtensorModule(Event::SubnetLeaseDividendsDistributed { + lease_id, + contributor: contributions[1].0.into(), + alpha: expected_contributor2_alpha.into(), + },) + ); // The beneficiary should have received the remaining dividends let expected_beneficiary_alpha = distributed_alpha.to_u64() - (expected_contributor1_alpha + expected_contributor2_alpha); assert_eq!(beneficiary_alpha_delta, expected_beneficiary_alpha.into()); + assert_eq!( + System::events().get(8).expect("Event not found").event, + RuntimeEvent::SubtensorModule(Event::SubnetLeaseDividendsDistributed { + lease_id, + contributor: beneficiary.into(), + alpha: expected_beneficiary_alpha.into(), + },) + ); // Ensure nothing was accumulated for later distribution assert_eq!( @@ -644,6 +671,13 @@ fn test_distribute_lease_network_dividends_only_beneficiary_works() { ) .saturating_sub(beneficiary_alpha_before); assert_eq!(beneficiary_alpha_delta, distributed_alpha.into()); + assert_last_event::(RuntimeEvent::SubtensorModule( + Event::SubnetLeaseDividendsDistributed { + lease_id, + contributor: beneficiary.into(), + alpha: distributed_alpha, + }, + )); // Ensure nothing was accumulated for later distribution assert_eq!( From 5dfdf475361b320dd6a8f82ab88ad3992fe96153 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Tue, 11 Nov 2025 21:37:16 -0500 Subject: [PATCH 171/210] fix test for change from main --- pallets/subtensor/src/tests/coinbase.rs | 61 ++++++++++++------------- 1 file changed, 28 insertions(+), 33 deletions(-) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 2bf260be5c..f600cc4f2b 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -3326,47 +3326,42 @@ fn test_mining_emission_distribution_with_root_sell() { } #[test] -fn test_coinbase_subnet_terms_with_no_reg_get_no_emission() { +fn test_coinbase_subnets_with_no_reg_get_no_emission() { new_test_ext(1).execute_with(|| { let zero = U96F32::saturating_from_num(0); let netuid0 = add_dynamic_network(&U256::from(1), &U256::from(2)); let netuid1 = add_dynamic_network(&U256::from(3), &U256::from(4)); - let subnet_emissions = BTreeMap::from([ - (netuid0, U96F32::saturating_from_num(1)), - (netuid1, U96F32::saturating_from_num(1)), - ]); - - let (tao_in, alpha_in, alpha_out, _) = SubtensorModule::get_subnet_terms(&subnet_emissions); - assert_eq!(tao_in.len(), 2); - assert_eq!(alpha_in.len(), 2); - assert_eq!(alpha_out.len(), 2); - - assert!(tao_in[&netuid0] > zero); - assert!(alpha_in[&netuid0] > zero); - assert!(alpha_out[&netuid0] > zero); - - assert!(tao_in[&netuid1] > zero); - assert!(alpha_in[&netuid1] > zero); - assert!(alpha_out[&netuid1] > zero); - - // Disabled registration of both methods + // Setup initial state + SubtokenEnabled::::insert(netuid0, true); + SubtokenEnabled::::insert(netuid1, true); + FirstEmissionBlockNumber::::insert(netuid0, 0); + FirstEmissionBlockNumber::::insert(netuid1, 0); + // Explicitly allow registration for both subnets + NetworkRegistrationAllowed::::insert(netuid0, true); + NetworkRegistrationAllowed::::insert(netuid1, true); + NetworkPowRegistrationAllowed::::insert(netuid0, false); + NetworkPowRegistrationAllowed::::insert(netuid1, true); + + // Note that netuid0 has only one method allowed + // And, netuid1 has *both* methods allowed + // Both should be in the list. + let subnets_to_emit_to_0 = SubtensorModule::get_subnets_to_emit_to(&[netuid0, netuid1]); + // Check that both subnets are in the list + assert_eq!(subnets_to_emit_to_0.len(), 2); + assert!(subnets_to_emit_to_0.contains(&netuid0)); + assert!(subnets_to_emit_to_0.contains(&netuid1)); + + // Disabled registration of both methods on ONLY netuid0 NetworkRegistrationAllowed::::insert(netuid0, false); NetworkPowRegistrationAllowed::::insert(netuid0, false); - let (tao_in_2, alpha_in_2, alpha_out_2, _) = - SubtensorModule::get_subnet_terms(&subnet_emissions); - assert_eq!(tao_in_2.len(), 2); - assert_eq!(alpha_in_2.len(), 2); - assert_eq!(alpha_out_2.len(), 2); - - assert!(tao_in_2[&netuid0] == zero); - assert!(alpha_in_2[&netuid0] == zero); - assert!(alpha_out_2[&netuid0] == zero); - - assert!(tao_in_2[&netuid1] > zero); - assert!(alpha_in_2[&netuid1] > zero); - assert!(alpha_out_2[&netuid1] > zero); + // Check that netuid0 is not in the list + let subnets_to_emit_to_1 = SubtensorModule::get_subnets_to_emit_to(&[netuid0, netuid1]); + assert_eq!(subnets_to_emit_to_1.len(), 1); + assert!(!subnets_to_emit_to_1.contains(&netuid0)); + // Netuid1 still in the list + assert!(subnets_to_emit_to_1.contains(&netuid1)); }); } From 5cfed02e98be5f156c18d9b10e3b006523afbfd3 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Tue, 11 Nov 2025 21:37:33 -0500 Subject: [PATCH 172/210] remove line from merge resolution --- pallets/subtensor/src/coinbase/run_coinbase.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 0fd4974712..a16bda0b69 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -232,9 +232,6 @@ impl Pallet { } log::debug!("root_alpha: {root_alpha:?}"); - // Deduct root alpha from alpha_out. - alpha_out_i = alpha_out_i.saturating_sub(root_alpha); - // Get pending server alpha, which is the miner cut of the alpha out. // Currently miner cut is 50% of the alpha out. let pending_server_alpha = alpha_out_i.saturating_mul(asfloat!(0.5)); From 55e52c1868929207bf4d6eb84d290cf36384557a Mon Sep 17 00:00:00 2001 From: camfairchild Date: Tue, 11 Nov 2025 21:39:05 -0500 Subject: [PATCH 173/210] refactor emit_to_sns --- .../subtensor/src/coinbase/run_coinbase.rs | 25 ++++++++++--------- pallets/subtensor/src/tests/coinbase.rs | 2 +- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index a16bda0b69..5cb2ea45f8 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -221,25 +221,16 @@ impl Pallet { .saturating_mul(asfloat!(0.5)); // 50% to validators. log::debug!("root_alpha: {root_alpha:?}"); - if root_sell_flag { - // Only accumulate root alpha divs if root sell is allowed. - PendingRootAlphaDivs::::mutate(*netuid_i, |total| { - *total = total.saturating_add(tou64!(root_alpha).into()); - }); - } else { - // If we are not selling the root alpha, we should recycle it. - Self::recycle_subnet_alpha(*netuid_i, AlphaCurrency::from(tou64!(root_alpha))); - } - log::debug!("root_alpha: {root_alpha:?}"); - // Get pending server alpha, which is the miner cut of the alpha out. // Currently miner cut is 50% of the alpha out. let pending_server_alpha = alpha_out_i.saturating_mul(asfloat!(0.5)); + log::debug!("pending_server_alpha: {pending_server_alpha:?}"); // The total validator alpha is the remaining alpha out minus the server alpha. let total_validator_alpha = alpha_out_i.saturating_sub(pending_server_alpha); - + log::debug!("total_validator_alpha: {total_validator_alpha:?}"); // The alpha validators don't get the root alpha. let pending_validator_alpha = total_validator_alpha.saturating_sub(root_alpha); + log::debug!("pending_validator_alpha: {pending_validator_alpha:?}"); // Accumulate the server alpha emission. PendingServerEmission::::mutate(*netuid_i, |total| { @@ -249,6 +240,16 @@ impl Pallet { PendingValidatorEmission::::mutate(*netuid_i, |total| { *total = total.saturating_add(tou64!(pending_validator_alpha).into()); }); + + if root_sell_flag { + // Only accumulate root alpha divs if root sell is allowed. + PendingRootAlphaDivs::::mutate(*netuid_i, |total| { + *total = total.saturating_add(tou64!(root_alpha).into()); + }); + } else { + // If we are not selling the root alpha, we should recycle it. + Self::recycle_subnet_alpha(*netuid_i, AlphaCurrency::from(tou64!(root_alpha))); + } } } diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index f600cc4f2b..9388ab6070 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -3361,7 +3361,7 @@ fn test_coinbase_subnets_with_no_reg_get_no_emission() { assert_eq!(subnets_to_emit_to_1.len(), 1); assert!(!subnets_to_emit_to_1.contains(&netuid0)); // Netuid1 still in the list - assert!(subnets_to_emit_to_1.contains(&netuid1)); + assert!(subnets_to_emit_to_1.contains(&netuid1)); }); } From d740a8e6272838966d77c95d082dff6f54db2ecd Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 12 Nov 2025 17:55:08 +0800 Subject: [PATCH 174/210] bump version --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 5e17dc6f7d..f6843c50ee 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 345, + spec_version: 346, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From bfb1111863432a9e659101722a36a6839c0f6196 Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 12 Nov 2025 17:56:33 +0800 Subject: [PATCH 175/210] bump version --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 5e17dc6f7d..f6843c50ee 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 345, + spec_version: 346, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 5c084990ed1260117f3320291ee3b69bea02721b Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 12 Nov 2025 22:10:09 +0800 Subject: [PATCH 176/210] fix evm test --- evm-tests/test/neuron.precompile.emission-check.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/evm-tests/test/neuron.precompile.emission-check.test.ts b/evm-tests/test/neuron.precompile.emission-check.test.ts index 8abafec907..1a2b053ed0 100644 --- a/evm-tests/test/neuron.precompile.emission-check.test.ts +++ b/evm-tests/test/neuron.precompile.emission-check.test.ts @@ -45,7 +45,7 @@ describe("Test the Neuron precompile with emission", () => { it("Burned register and check emission", async () => { let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1 - + const uid = await api.query.SubtensorModule.SubnetworkN.getValue(netuid) const contract = new ethers.Contract(INEURON_ADDRESS, INeuronABI, wallet); @@ -63,7 +63,7 @@ describe("Test the Neuron precompile with emission", () => { let i = 0; while (i < 10) { - const emission = await api.query.SubtensorModule.ServerEmission.getValue(netuid) + const emission = await api.query.SubtensorModule.Emission.getValue(netuid) console.log("emission is ", emission); await new Promise((resolve) => setTimeout(resolve, 2000)); From b65feee2555360e5367b0f01c5443363cd372c8e Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 12 Nov 2025 22:14:31 +0800 Subject: [PATCH 177/210] bump version --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 5e17dc6f7d..f6843c50ee 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 345, + spec_version: 346, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 87b78c5cd7a454b722122bbbfc45e4b5b99f4b5d Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Wed, 12 Nov 2025 11:31:54 -0300 Subject: [PATCH 178/210] fix clippy --- pallets/subtensor/src/tests/leasing.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pallets/subtensor/src/tests/leasing.rs b/pallets/subtensor/src/tests/leasing.rs index 4c48d2c341..9f8bf4d6bf 100644 --- a/pallets/subtensor/src/tests/leasing.rs +++ b/pallets/subtensor/src/tests/leasing.rs @@ -575,7 +575,7 @@ fn test_distribute_lease_network_dividends_multiple_contributors_works() { .to_num::(); assert_eq!(contributor1_alpha_delta, expected_contributor1_alpha.into()); assert_eq!( - System::events().get(2).expect("Event not found").event, + System::events()[2].event, RuntimeEvent::SubtensorModule(Event::SubnetLeaseDividendsDistributed { lease_id, contributor: contributions[0].0.into(), @@ -590,7 +590,7 @@ fn test_distribute_lease_network_dividends_multiple_contributors_works() { .to_num::(); assert_eq!(contributor2_alpha_delta, expected_contributor2_alpha.into()); assert_eq!( - System::events().get(5).expect("Event not found").event, + System::events()[5].event, RuntimeEvent::SubtensorModule(Event::SubnetLeaseDividendsDistributed { lease_id, contributor: contributions[1].0.into(), @@ -603,7 +603,7 @@ fn test_distribute_lease_network_dividends_multiple_contributors_works() { - (expected_contributor1_alpha + expected_contributor2_alpha); assert_eq!(beneficiary_alpha_delta, expected_beneficiary_alpha.into()); assert_eq!( - System::events().get(8).expect("Event not found").event, + System::events()[8].event, RuntimeEvent::SubtensorModule(Event::SubnetLeaseDividendsDistributed { lease_id, contributor: beneficiary.into(), From 5a4f754a8e752d0bb48667586d3071357c387c9d Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Wed, 12 Nov 2025 11:32:16 -0300 Subject: [PATCH 179/210] bump spec version --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 5e17dc6f7d..f6843c50ee 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 345, + spec_version: 346, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From e0e8c9b58b3b0b8ebe7a502427cf8d44fe6bcebc Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Wed, 12 Nov 2025 12:15:30 -0500 Subject: [PATCH 180/210] bump spec --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 5e17dc6f7d..f6843c50ee 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 345, + spec_version: 346, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 5557500e5618220db55f4c9970f3c66f8edee3ed Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 12 Nov 2025 13:41:49 -0500 Subject: [PATCH 181/210] switch e2e tests to github runners --- .github/workflows/check-bittensor-e2e-tests.yml.yml | 12 ++++++------ .github/workflows/evm-tests.yml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/check-bittensor-e2e-tests.yml.yml b/.github/workflows/check-bittensor-e2e-tests.yml.yml index 721eb96994..540238d6d5 100644 --- a/.github/workflows/check-bittensor-e2e-tests.yml.yml +++ b/.github/workflows/check-bittensor-e2e-tests.yml.yml @@ -26,7 +26,7 @@ env: jobs: check-label: - runs-on: [self-hosted, type-ccx13] + runs-on: ubuntu-latest outputs: skip-bittensor-e2e-tests: ${{ steps.get-labels.outputs.skip-bittensor-e2e-tests }} steps: @@ -57,7 +57,7 @@ jobs: find-btcli-e2e-tests: needs: check-label if: needs.check-label.outputs.skip-bittensor-e2e-tests == 'false' - runs-on: [self-hosted, type-ccx13] + runs-on: ubuntu-latest outputs: test-files: ${{ steps.get-btcli-tests.outputs.test-files }} steps: @@ -84,7 +84,7 @@ jobs: find-sdk-e2e-tests: needs: check-label if: needs.check-label.outputs.skip-bittensor-e2e-tests == 'false' - runs-on: [self-hosted, type-ccx13] + runs-on: ubuntu-latest outputs: test-files: ${{ steps.get-sdk-tests.outputs.test-files }} steps: @@ -111,7 +111,7 @@ jobs: build-image-with-current-branch: needs: check-label if: needs.check-label.outputs.skip-bittensor-e2e-tests == 'false' - runs-on: [self-hosted, type-ccx33] + runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 @@ -149,7 +149,7 @@ jobs: - find-btcli-e2e-tests - build-image-with-current-branch if: needs.check-label.outputs.skip-bittensor-e2e-tests == 'false' - runs-on: [self-hosted, type-ccx13] + runs-on: ubuntu-latest strategy: fail-fast: false max-parallel: 16 @@ -243,7 +243,7 @@ jobs: - find-sdk-e2e-tests - build-image-with-current-branch if: needs.check-label.outputs.skip-bittensor-e2e-tests == 'false' - runs-on: [self-hosted, type-ccx13] + runs-on: ubuntu-latest strategy: fail-fast: false max-parallel: 16 diff --git a/.github/workflows/evm-tests.yml b/.github/workflows/evm-tests.yml index b818695237..80debe2517 100644 --- a/.github/workflows/evm-tests.yml +++ b/.github/workflows/evm-tests.yml @@ -24,7 +24,7 @@ permissions: jobs: run: - runs-on: [self-hosted, type-ccx33] + runs-on: ubuntu-latest env: RUST_BACKTRACE: full steps: From d3a662e28508f9d745e711e40bf074bbfc825f61 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Thu, 13 Nov 2025 08:15:26 -0300 Subject: [PATCH 182/210] fix evm test --- evm-tests/test/staking.precompile.reward.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm-tests/test/staking.precompile.reward.test.ts b/evm-tests/test/staking.precompile.reward.test.ts index 1b47239e1d..d04620c91b 100644 --- a/evm-tests/test/staking.precompile.reward.test.ts +++ b/evm-tests/test/staking.precompile.reward.test.ts @@ -74,7 +74,7 @@ describe("Test neuron precompile reward", () => { let index = 0; while (index < 60) { - const pending = await api.query.SubtensorModule.ValidatorEmission.getValue(netuid); + const pending = await api.query.SubtensorModule.PendingValidatorEmission.getValue(netuid); if (pending > 0) { console.log("pending amount is ", pending); break; From 800cddd591838be1eb98d85f4301e85ec41313ae Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Thu, 13 Nov 2025 10:52:26 -0300 Subject: [PATCH 183/210] debug github action --- .github/workflows/check-bittensor-e2e-tests.yml.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/check-bittensor-e2e-tests.yml.yml b/.github/workflows/check-bittensor-e2e-tests.yml.yml index 540238d6d5..5617019c64 100644 --- a/.github/workflows/check-bittensor-e2e-tests.yml.yml +++ b/.github/workflows/check-bittensor-e2e-tests.yml.yml @@ -130,6 +130,12 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 + - name: Setup tmate session + id: setup-tmate-session + uses: mxschmitt/action-tmate@v3 + with: + limit-access-to-actor: true + - name: Build Docker Image run: docker build -f Dockerfile-localnet -t localnet . @@ -181,7 +187,7 @@ jobs: - name: Install uv uses: astral-sh/setup-uv@v5 with: - enable-cache: 'false' + enable-cache: "false" - name: Create Python virtual environment working-directory: ${{ github.workspace }} @@ -275,7 +281,7 @@ jobs: - name: Install uv uses: astral-sh/setup-uv@v5 with: - enable-cache: 'false' + enable-cache: "false" - name: Create Python virtual environment working-directory: ${{ github.workspace }} From e30e8b550e5c48577fd1acda243144d18ee11d56 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Thu, 13 Nov 2025 12:16:58 -0300 Subject: [PATCH 184/210] combine build directly as tar --- .../workflows/check-bittensor-e2e-tests.yml.yml | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/.github/workflows/check-bittensor-e2e-tests.yml.yml b/.github/workflows/check-bittensor-e2e-tests.yml.yml index 5617019c64..f60edbb178 100644 --- a/.github/workflows/check-bittensor-e2e-tests.yml.yml +++ b/.github/workflows/check-bittensor-e2e-tests.yml.yml @@ -130,23 +130,14 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - - name: Setup tmate session - id: setup-tmate-session - uses: mxschmitt/action-tmate@v3 - with: - limit-access-to-actor: true - - - name: Build Docker Image - run: docker build -f Dockerfile-localnet -t localnet . - - - name: Save Docker Image as Tar - run: docker save -o subtensor-localnet.tar localnet + - name: Build Docker Image (direct tar output) + run: docker build -f Dockerfile-localnet -t localnet --output type=docker,dest=/mnt/subtensor-localnet.tar . - name: Upload Docker Image as Artifact uses: actions/upload-artifact@v4 with: name: subtensor-localnet - path: subtensor-localnet.tar + path: /mnt/subtensor-localnet.tar # main btcli job run-btcli-e2e-tests: From d8c42d4a8bc0446783acd1e41075bdbe6a7c5ed8 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Thu, 13 Nov 2025 12:18:53 -0300 Subject: [PATCH 185/210] reset debug --- .../workflows/check-bittensor-e2e-tests.yml.yml | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/.github/workflows/check-bittensor-e2e-tests.yml.yml b/.github/workflows/check-bittensor-e2e-tests.yml.yml index f60edbb178..5617019c64 100644 --- a/.github/workflows/check-bittensor-e2e-tests.yml.yml +++ b/.github/workflows/check-bittensor-e2e-tests.yml.yml @@ -130,14 +130,23 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - - name: Build Docker Image (direct tar output) - run: docker build -f Dockerfile-localnet -t localnet --output type=docker,dest=/mnt/subtensor-localnet.tar . + - name: Setup tmate session + id: setup-tmate-session + uses: mxschmitt/action-tmate@v3 + with: + limit-access-to-actor: true + + - name: Build Docker Image + run: docker build -f Dockerfile-localnet -t localnet . + + - name: Save Docker Image as Tar + run: docker save -o subtensor-localnet.tar localnet - name: Upload Docker Image as Artifact uses: actions/upload-artifact@v4 with: name: subtensor-localnet - path: /mnt/subtensor-localnet.tar + path: subtensor-localnet.tar # main btcli job run-btcli-e2e-tests: From ce25ddc03fce53a3de45537e240e1f580aeb0e91 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Thu, 13 Nov 2025 12:25:40 -0300 Subject: [PATCH 186/210] try fix 2 --- .../check-bittensor-e2e-tests.yml.yml | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/.github/workflows/check-bittensor-e2e-tests.yml.yml b/.github/workflows/check-bittensor-e2e-tests.yml.yml index 5617019c64..778f38ae94 100644 --- a/.github/workflows/check-bittensor-e2e-tests.yml.yml +++ b/.github/workflows/check-bittensor-e2e-tests.yml.yml @@ -129,24 +129,18 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 + + - name: Create directory + run: mkdir -p /mnt/data && chown -R runner:runner /mnt/data - - name: Setup tmate session - id: setup-tmate-session - uses: mxschmitt/action-tmate@v3 - with: - limit-access-to-actor: true - - - name: Build Docker Image - run: docker build -f Dockerfile-localnet -t localnet . - - - name: Save Docker Image as Tar - run: docker save -o subtensor-localnet.tar localnet + - name: Build Docker Image (direct tar output) + run: docker build -f Dockerfile-localnet -t localnet --output type=docker,dest=/mnt/data/subtensor-localnet.tar . - name: Upload Docker Image as Artifact uses: actions/upload-artifact@v4 with: name: subtensor-localnet - path: subtensor-localnet.tar + path: /mnt/data/subtensor-localnet.tar # main btcli job run-btcli-e2e-tests: From 2020da52151096e665a23fd72468c3790e0ae329 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Thu, 13 Nov 2025 12:25:59 -0300 Subject: [PATCH 187/210] missing sudo --- .github/workflows/check-bittensor-e2e-tests.yml.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-bittensor-e2e-tests.yml.yml b/.github/workflows/check-bittensor-e2e-tests.yml.yml index 778f38ae94..a00ed874c2 100644 --- a/.github/workflows/check-bittensor-e2e-tests.yml.yml +++ b/.github/workflows/check-bittensor-e2e-tests.yml.yml @@ -131,7 +131,7 @@ jobs: uses: docker/setup-buildx-action@v3 - name: Create directory - run: mkdir -p /mnt/data && chown -R runner:runner /mnt/data + run: sudo mkdir -p /mnt/data && sudo chown -R runner:runner /mnt/data - name: Build Docker Image (direct tar output) run: docker build -f Dockerfile-localnet -t localnet --output type=docker,dest=/mnt/data/subtensor-localnet.tar . From 2d93e2e3e1ed7612e8a6beefb69c66b2ad080d5f Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Thu, 13 Nov 2025 12:27:59 -0300 Subject: [PATCH 188/210] exporter fix --- .github/workflows/check-bittensor-e2e-tests.yml.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-bittensor-e2e-tests.yml.yml b/.github/workflows/check-bittensor-e2e-tests.yml.yml index a00ed874c2..0f43542618 100644 --- a/.github/workflows/check-bittensor-e2e-tests.yml.yml +++ b/.github/workflows/check-bittensor-e2e-tests.yml.yml @@ -134,7 +134,7 @@ jobs: run: sudo mkdir -p /mnt/data && sudo chown -R runner:runner /mnt/data - name: Build Docker Image (direct tar output) - run: docker build -f Dockerfile-localnet -t localnet --output type=docker,dest=/mnt/data/subtensor-localnet.tar . + run: DOCKER_BUILDKIT=1 docker buildx build -f Dockerfile-localnet -t localnet --output type=docker,dest=/mnt/data/subtensor-localnet.tar . - name: Upload Docker Image as Artifact uses: actions/upload-artifact@v4 From 391397d3b87deadd61352df8b39cf0f9cf6ce873 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Thu, 13 Nov 2025 12:29:46 -0300 Subject: [PATCH 189/210] trigger ci? From ca14a10cc40fc04d4afab9e5b420ca7a14135ab7 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Thu, 13 Nov 2025 13:57:24 -0300 Subject: [PATCH 190/210] try with cache to /mnt --- .../workflows/check-bittensor-e2e-tests.yml.yml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/check-bittensor-e2e-tests.yml.yml b/.github/workflows/check-bittensor-e2e-tests.yml.yml index 0f43542618..b06e8d7953 100644 --- a/.github/workflows/check-bittensor-e2e-tests.yml.yml +++ b/.github/workflows/check-bittensor-e2e-tests.yml.yml @@ -129,18 +129,24 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - + - name: Create directory - run: sudo mkdir -p /mnt/data && sudo chown -R runner:runner /mnt/data + run: sudo mkdir -p /mnt/build-cache && sudo chown -R runner:runner /mnt/build-cache - name: Build Docker Image (direct tar output) - run: DOCKER_BUILDKIT=1 docker buildx build -f Dockerfile-localnet -t localnet --output type=docker,dest=/mnt/data/subtensor-localnet.tar . + run: | + docker buildx build \ + -f Dockerfile-localnet \ + -t localnet \ + --cache-to type=local,dest=/mnt/build-cache \ + --cache-from type=local,src=/mnt/build-cache \ + --output type=docker,dest=subtensor-localnet.tar . - name: Upload Docker Image as Artifact uses: actions/upload-artifact@v4 with: name: subtensor-localnet - path: /mnt/data/subtensor-localnet.tar + path: subtensor-localnet.tar # main btcli job run-btcli-e2e-tests: From 827b81b46e54b91b2c4457cd7ff7cf1b57f32775 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Thu, 13 Nov 2025 15:12:17 -0300 Subject: [PATCH 191/210] build on root but save on mnt --- .../check-bittensor-e2e-tests.yml.yml | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/.github/workflows/check-bittensor-e2e-tests.yml.yml b/.github/workflows/check-bittensor-e2e-tests.yml.yml index b06e8d7953..d3c74f4694 100644 --- a/.github/workflows/check-bittensor-e2e-tests.yml.yml +++ b/.github/workflows/check-bittensor-e2e-tests.yml.yml @@ -129,24 +129,21 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - + - name: Create directory - run: sudo mkdir -p /mnt/build-cache && sudo chown -R runner:runner /mnt/build-cache + run: mkdir -p /mnt/data && chown -R runner:runner /mnt/data - - name: Build Docker Image (direct tar output) - run: | - docker buildx build \ - -f Dockerfile-localnet \ - -t localnet \ - --cache-to type=local,dest=/mnt/build-cache \ - --cache-from type=local,src=/mnt/build-cache \ - --output type=docker,dest=subtensor-localnet.tar . + - name: Build Docker Image + run: docker build -f Dockerfile-localnet -t localnet . + + - name: Save Docker Image as Tar + run: docker save -o /mnt/data/subtensor-localnet.tar localnet - name: Upload Docker Image as Artifact uses: actions/upload-artifact@v4 with: name: subtensor-localnet - path: subtensor-localnet.tar + path: /mnt/data/subtensor-localnet.tar # main btcli job run-btcli-e2e-tests: From a791e754b193cae3c10bcc22af546cdd0cf355bd Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Thu, 13 Nov 2025 15:27:27 -0300 Subject: [PATCH 192/210] missing sudo 2 --- .github/workflows/check-bittensor-e2e-tests.yml.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-bittensor-e2e-tests.yml.yml b/.github/workflows/check-bittensor-e2e-tests.yml.yml index d3c74f4694..2352b77228 100644 --- a/.github/workflows/check-bittensor-e2e-tests.yml.yml +++ b/.github/workflows/check-bittensor-e2e-tests.yml.yml @@ -131,7 +131,7 @@ jobs: uses: docker/setup-buildx-action@v3 - name: Create directory - run: mkdir -p /mnt/data && chown -R runner:runner /mnt/data + run: sudo mkdir -p /mnt/data && sudo chown -R runner:runner /mnt/data - name: Build Docker Image run: docker build -f Dockerfile-localnet -t localnet . From f713dfd22a8001e130044f962285e991454d82bb Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Thu, 13 Nov 2025 16:36:10 -0300 Subject: [PATCH 193/210] try to docker root to /mnt --- .github/workflows/check-bittensor-e2e-tests.yml.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/check-bittensor-e2e-tests.yml.yml b/.github/workflows/check-bittensor-e2e-tests.yml.yml index 2352b77228..8c0f6defab 100644 --- a/.github/workflows/check-bittensor-e2e-tests.yml.yml +++ b/.github/workflows/check-bittensor-e2e-tests.yml.yml @@ -129,9 +129,14 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - - - name: Create directory - run: sudo mkdir -p /mnt/data && sudo chown -R runner:runner /mnt/data + + - name: Move Docker data-root to /mnt/data + run: | + sudo systemctl stop docker + sudo mkdir -p /mnt/data/docker + echo '{"data-root": "/mnt/data/docker"}' | sudo tee /etc/docker/daemon.json + sudo systemctl start docker + docker info | grep "Docker Root Dir" - name: Build Docker Image run: docker build -f Dockerfile-localnet -t localnet . From 2ed1e7705db303f2e5ad670fb3fe29dc7f402c0e Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Thu, 13 Nov 2025 17:47:31 -0300 Subject: [PATCH 194/210] fix perms --- .github/workflows/check-bittensor-e2e-tests.yml.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/check-bittensor-e2e-tests.yml.yml b/.github/workflows/check-bittensor-e2e-tests.yml.yml index 8c0f6defab..c133efab22 100644 --- a/.github/workflows/check-bittensor-e2e-tests.yml.yml +++ b/.github/workflows/check-bittensor-e2e-tests.yml.yml @@ -134,6 +134,8 @@ jobs: run: | sudo systemctl stop docker sudo mkdir -p /mnt/data/docker + sudo chown -R runner:runner /mnt/data + sudo chmod -R 777 /mnt/data echo '{"data-root": "/mnt/data/docker"}' | sudo tee /etc/docker/daemon.json sudo systemctl start docker docker info | grep "Docker Root Dir" From 3e7825c1919a7a0a6c23c5d3602c06c3d7d73a41 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Thu, 13 Nov 2025 20:21:00 -0300 Subject: [PATCH 195/210] fix e2e bittensor pipeline --- .../check-bittensor-e2e-tests.yml.yml | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/.github/workflows/check-bittensor-e2e-tests.yml.yml b/.github/workflows/check-bittensor-e2e-tests.yml.yml index 540238d6d5..c133efab22 100644 --- a/.github/workflows/check-bittensor-e2e-tests.yml.yml +++ b/.github/workflows/check-bittensor-e2e-tests.yml.yml @@ -130,17 +130,27 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 + - name: Move Docker data-root to /mnt/data + run: | + sudo systemctl stop docker + sudo mkdir -p /mnt/data/docker + sudo chown -R runner:runner /mnt/data + sudo chmod -R 777 /mnt/data + echo '{"data-root": "/mnt/data/docker"}' | sudo tee /etc/docker/daemon.json + sudo systemctl start docker + docker info | grep "Docker Root Dir" + - name: Build Docker Image run: docker build -f Dockerfile-localnet -t localnet . - name: Save Docker Image as Tar - run: docker save -o subtensor-localnet.tar localnet + run: docker save -o /mnt/data/subtensor-localnet.tar localnet - name: Upload Docker Image as Artifact uses: actions/upload-artifact@v4 with: name: subtensor-localnet - path: subtensor-localnet.tar + path: /mnt/data/subtensor-localnet.tar # main btcli job run-btcli-e2e-tests: @@ -181,7 +191,7 @@ jobs: - name: Install uv uses: astral-sh/setup-uv@v5 with: - enable-cache: 'false' + enable-cache: "false" - name: Create Python virtual environment working-directory: ${{ github.workspace }} @@ -275,7 +285,7 @@ jobs: - name: Install uv uses: astral-sh/setup-uv@v5 with: - enable-cache: 'false' + enable-cache: "false" - name: Create Python virtual environment working-directory: ${{ github.workspace }} From 89115b93e7f8ee02456ad55f3d7aec76dc40ab46 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Thu, 13 Nov 2025 20:21:55 -0300 Subject: [PATCH 196/210] fix evm test --- evm-tests/test/staking.precompile.reward.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm-tests/test/staking.precompile.reward.test.ts b/evm-tests/test/staking.precompile.reward.test.ts index 1b47239e1d..d04620c91b 100644 --- a/evm-tests/test/staking.precompile.reward.test.ts +++ b/evm-tests/test/staking.precompile.reward.test.ts @@ -74,7 +74,7 @@ describe("Test neuron precompile reward", () => { let index = 0; while (index < 60) { - const pending = await api.query.SubtensorModule.ValidatorEmission.getValue(netuid); + const pending = await api.query.SubtensorModule.PendingValidatorEmission.getValue(netuid); if (pending > 0) { console.log("pending amount is ", pending); break; From 6cb6b9ad21128c617de9b8064c2d072c9ba2b46b Mon Sep 17 00:00:00 2001 From: Pawel Polewicz Date: Sat, 15 Nov 2025 14:28:49 +0000 Subject: [PATCH 197/210] Fifo queue instead of a priority flag --- pallets/subtensor/src/coinbase/root.rs | 17 +++++---- pallets/subtensor/src/lib.rs | 8 ++--- pallets/subtensor/src/macros/dispatches.rs | 26 +++++++++----- pallets/subtensor/src/macros/events.rs | 8 +++-- pallets/subtensor/src/tests/networks.rs | 32 +++++++++-------- pallets/subtensor/src/utils/misc.rs | 42 +++++++++++++++++++++- precompiles/src/solidity/subnet.abi | 3 -- precompiles/src/solidity/subnet.sol | 1 + 8 files changed, 93 insertions(+), 44 deletions(-) diff --git a/pallets/subtensor/src/coinbase/root.rs b/pallets/subtensor/src/coinbase/root.rs index d89db2a514..d8191a1e2e 100644 --- a/pallets/subtensor/src/coinbase/root.rs +++ b/pallets/subtensor/src/coinbase/root.rs @@ -244,7 +244,7 @@ impl Pallet { // --- 5. Remove various network-related storages. NetworkRegisteredAt::::remove(netuid); - SubnetDeregistrationPriority::::remove(netuid); + let _ = Self::remove_subnet_from_deregistration_priority_queue(netuid); // --- 6. Remove incentive mechanism memory. let _ = Uids::::clear_prefix(netuid, u32::MAX, None); @@ -593,8 +593,12 @@ impl Pallet { pub fn get_network_to_prune() -> Option { let current_block: u64 = Self::get_current_block_as_u64(); + if let Some(priority_netuid) = Self::pop_ready_subnet_deregistration_priority(current_block) + { + return Some(priority_netuid); + } + let mut candidate_netuid: Option = None; - let mut candidate_priority: bool = false; let mut candidate_price: U96F32 = U96F32::saturating_from_num(u128::MAX); let mut candidate_timestamp: u64 = u64::MAX; @@ -611,17 +615,12 @@ impl Pallet { } let price: U96F32 = Self::get_moving_alpha_price(netuid); - let priority = SubnetDeregistrationPriority::::get(netuid); - // Prioritize higher deregistration priority, then lowest price, then earliest registration. if candidate_netuid.is_none() - || (priority && !candidate_priority) - || (priority == candidate_priority - && (price < candidate_price - || (price == candidate_price && registered_at < candidate_timestamp))) + || price < candidate_price + || (price == candidate_price && registered_at < candidate_timestamp) { candidate_netuid = Some(netuid); - candidate_priority = priority; candidate_price = price; candidate_timestamp = registered_at; } diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index c7f5693dbb..8ade920792 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1434,11 +1434,11 @@ pub mod pallet { pub type NetworkRegisteredAt = StorageMap<_, Identity, NetUid, u64, ValueQuery, DefaultNetworkRegisteredAt>; #[pallet::storage] - /// --- MAP ( netuid ) --> deregistration priority flag (true pruned before price-based selection) - pub type SubnetDeregistrationPriority = - StorageMap<_, Identity, NetUid, bool, ValueQuery, DefaultFalse>; + /// --- FIFO queue of netuids pending deregistration + pub type SubnetDeregistrationPriorityQueue = + StorageValue<_, sp_std::vec::Vec, ValueQuery>; #[pallet::storage] - /// --- MAP ( netuid ) --> scheduled block for enabling deregistration priority + /// --- MAP ( netuid ) --> scheduled block for enqueuing deregistration priority pub type SubnetDeregistrationPrioritySchedule = StorageMap<_, Identity, NetUid, BlockNumberFor, OptionQuery>; #[pallet::storage] diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 8e6e6f0814..4c8307e14f 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2153,11 +2153,12 @@ mod dispatches { Ok(()) } - /// Manages the deregistration priority flag for a subnet. + /// Manages the deregistration priority queue for a subnet. /// - /// When `schedule_set` is `true`, the flag is scheduled to be set after approximately - /// five days using the scheduler pallet. When `schedule_set` is `false`, any pending - /// schedule is cleared. Only the root origin may clear the flag itself. + /// When `schedule_set` is `true`, the subnet is scheduled to be appended to the + /// deregistration queue after approximately five days using the scheduler pallet. + /// When `schedule_set` is `false`, any pending schedule is cleared. Only the root origin + /// may remove the subnet from the queue itself. /// /// Accessible by the subnet owner (for scheduling or canceling a pending schedule) or root /// origin (full control). @@ -2210,23 +2211,30 @@ mod dispatches { } else { if maybe_owner.is_some() { if SubnetDeregistrationPrioritySchedule::::take(netuid).is_some() { - Self::deposit_event(Event::SubnetDeregistrationPriorityCleared(netuid)); + Self::deposit_event(Event::SubnetDeregistrationPriorityScheduleCleared( + netuid, + )); } } else { - let was_flagged = SubnetDeregistrationPriority::::take(netuid); + let was_queued = Self::remove_subnet_from_deregistration_priority_queue(netuid); let had_schedule = SubnetDeregistrationPrioritySchedule::::take(netuid).is_some(); - if was_flagged || had_schedule { + if was_queued { Self::deposit_event(Event::SubnetDeregistrationPriorityCleared(netuid)); } + if had_schedule { + Self::deposit_event(Event::SubnetDeregistrationPriorityScheduleCleared( + netuid, + )); + } } } Ok(()) } - /// Sets the deregistration priority flag immediately. + /// Enqueues the subnet for deregistration immediately. /// /// This call is intended to be used by the scheduler and requires root origin. #[pallet::call_index(122)] @@ -2248,7 +2256,7 @@ mod dispatches { return Ok(()); } - SubnetDeregistrationPriority::::insert(netuid, true); + let _ = Self::enqueue_subnet_deregistration_priority(netuid); Self::deposit_event(Event::SubnetDeregistrationPrioritySet(netuid)); Ok(()) diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index 27093829f3..d24ce60c47 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -14,12 +14,14 @@ mod events { NetworkAdded(NetUid, u16), /// a network is removed. NetworkRemoved(NetUid), - /// a subnet's deregistration priority flag was scheduled to be set. + /// a subnet's deregistration queue entry was scheduled. SubnetDeregistrationPriorityScheduled(NetUid, BlockNumberFor), - /// a subnet's deregistration priority flag was set. + /// a subnet was enqueued for deregistration. SubnetDeregistrationPrioritySet(NetUid), - /// network deregistration priority flag cleared. + /// network deregistration queue entry cleared or canceled. SubnetDeregistrationPriorityCleared(NetUid), + /// pending deregistration schedule cleared or canceled. + SubnetDeregistrationPriorityScheduleCleared(NetUid), /// stake has been transferred from the a coldkey account onto the hotkey staking account. StakeAdded( T::AccountId, diff --git a/pallets/subtensor/src/tests/networks.rs b/pallets/subtensor/src/tests/networks.rs index f4ba420964..40af90aca9 100644 --- a/pallets/subtensor/src/tests/networks.rs +++ b/pallets/subtensor/src/tests/networks.rs @@ -56,8 +56,8 @@ fn dissolve_no_stakers_no_alpha_no_emission() { SubtensorModule::set_subnet_locked_balance(net, TaoCurrency::from(0)); SubnetTAO::::insert(net, TaoCurrency::from(0)); Emission::::insert(net, Vec::::new()); - SubnetDeregistrationPriority::::insert(net, true); - assert!(SubnetDeregistrationPriority::::contains_key(net)); + SubnetDeregistrationPriorityQueue::::mutate(|queue| queue.push(net)); + assert!(SubnetDeregistrationPriorityQueue::::get().contains(&net)); let before = SubtensorModule::get_coldkey_balance(&cold); assert_ok!(SubtensorModule::do_dissolve_network(net)); @@ -66,7 +66,7 @@ fn dissolve_no_stakers_no_alpha_no_emission() { // Balance should be unchanged (whatever the network-lock bookkeeping left there) assert_eq!(after, before); assert!(!SubtensorModule::if_subnet_exist(net)); - assert!(!SubnetDeregistrationPriority::::contains_key(net)); + assert!(!SubnetDeregistrationPriorityQueue::::get().contains(&net)); }); } @@ -83,7 +83,7 @@ fn manage_priority_schedule_and_force_set() { true )); - assert_eq!(SubnetDeregistrationPriority::::get(net), false); + assert!(!SubnetDeregistrationPriorityQueue::::get().contains(&net)); let when = SubnetDeregistrationPrioritySchedule::::get(net).unwrap(); assert!(when > 0); @@ -92,7 +92,7 @@ fn manage_priority_schedule_and_force_set() { net )); - assert!(SubnetDeregistrationPriority::::get(net)); + assert!(SubnetDeregistrationPriorityQueue::::get().contains(&net)); assert!(!SubnetDeregistrationPrioritySchedule::::contains_key( net )); @@ -106,7 +106,7 @@ fn manage_priority_owner_cancels_schedule_only() { let owner_hot = U256::from(26); let net = add_dynamic_network(&owner_hot, &owner_cold); - SubnetDeregistrationPriority::::insert(net, true); + SubnetDeregistrationPriorityQueue::::mutate(|queue| queue.push(net)); SubnetDeregistrationPrioritySchedule::::insert(net, 42); assert_ok!(SubtensorModule::manage_deregistration_priority( @@ -115,7 +115,7 @@ fn manage_priority_owner_cancels_schedule_only() { false )); - assert!(SubnetDeregistrationPriority::::get(net)); + assert!(SubnetDeregistrationPriorityQueue::::get().contains(&net)); assert!(!SubnetDeregistrationPrioritySchedule::::contains_key( net )); @@ -123,13 +123,13 @@ fn manage_priority_owner_cancels_schedule_only() { } #[test] -fn manage_priority_root_clears_flag() { +fn manage_priority_root_clears_queue_entry() { new_test_ext(0).execute_with(|| { let owner_cold = U256::from(19); let owner_hot = U256::from(38); let net = add_dynamic_network(&owner_hot, &owner_cold); - SubnetDeregistrationPriority::::insert(net, true); + SubnetDeregistrationPriorityQueue::::mutate(|queue| queue.push(net)); SubnetDeregistrationPrioritySchedule::::insert(net, 55); assert_ok!(SubtensorModule::manage_deregistration_priority( @@ -138,7 +138,7 @@ fn manage_priority_root_clears_flag() { false )); - assert!(!SubnetDeregistrationPriority::::get(net)); + assert!(!SubnetDeregistrationPriorityQueue::::get().contains(&net)); assert!(!SubnetDeregistrationPrioritySchedule::::contains_key( net )); @@ -182,7 +182,7 @@ fn force_set_deregistration_priority_is_noop_without_schedule() { net )); - assert_eq!(SubnetDeregistrationPriority::::get(net), false); + assert!(!SubnetDeregistrationPriorityQueue::::get().contains(&net)); }); } @@ -197,7 +197,7 @@ fn schedule_swap_coldkey_cancels_priority_schedule() { let swap_cost = SubtensorModule::get_key_swap_cost(); SubtensorModule::add_balance_to_coldkey_account(&owner_cold, swap_cost.to_u64() + 1_000); - SubnetDeregistrationPriority::::insert(net, true); + SubnetDeregistrationPriorityQueue::::mutate(|queue| queue.push(net)); SubnetDeregistrationPrioritySchedule::::insert(net, 5); assert_ok!(SubtensorModule::schedule_swap_coldkey( @@ -205,7 +205,7 @@ fn schedule_swap_coldkey_cancels_priority_schedule() { new_cold )); - assert!(SubnetDeregistrationPriority::::get(net)); + assert!(SubnetDeregistrationPriorityQueue::::get().contains(&net)); assert!(!SubnetDeregistrationPrioritySchedule::::contains_key( net )); @@ -1302,8 +1302,10 @@ fn prune_prefers_higher_priority_over_price() { SubnetMovingPrice::::insert(n1, I96F32::from_num(100)); SubnetMovingPrice::::insert(n2, I96F32::from_num(1)); - SubnetDeregistrationPriority::::insert(n1, true); - SubnetDeregistrationPriority::::insert(n2, false); + SubnetDeregistrationPriorityQueue::::mutate(|queue| { + queue.push(n1); + queue.push(n2); + }); assert_eq!(SubtensorModule::get_network_to_prune(), Some(n1)); }); diff --git a/pallets/subtensor/src/utils/misc.rs b/pallets/subtensor/src/utils/misc.rs index 2184a4a5d7..edb0392527 100644 --- a/pallets/subtensor/src/utils/misc.rs +++ b/pallets/subtensor/src/utils/misc.rs @@ -128,11 +128,51 @@ impl Pallet { for netuid in nets { if SubnetDeregistrationPrioritySchedule::::take(netuid).is_some() { - Self::deposit_event(Event::SubnetDeregistrationPriorityCleared(netuid)); + Self::deposit_event(Event::SubnetDeregistrationPriorityScheduleCleared(netuid)); } } } + pub fn enqueue_subnet_deregistration_priority(netuid: NetUid) -> bool { + SubnetDeregistrationPriorityQueue::::mutate(|queue| { + if queue.iter().any(|&existing| existing == netuid) { + false + } else { + queue.push(netuid); + true + } + }) + } + + pub fn remove_subnet_from_deregistration_priority_queue(netuid: NetUid) -> bool { + SubnetDeregistrationPriorityQueue::::mutate(|queue| { + let original_len = queue.len(); + queue.retain(|&existing| existing != netuid); + original_len != queue.len() + }) + } + + pub fn pop_ready_subnet_deregistration_priority() -> Option { + SubnetDeregistrationPriorityQueue::::mutate(|queue| { + let mut idx = 0; + while idx < queue.len() { + let netuid = queue[idx]; + queue.remove(idx); + + if !Self::if_subnet_exist(netuid) { + continue; + } + + if netuid == NetUid::ROOT { + continue; + } + + return Some(netuid); + } + None + }) + } + /// If owner is `Some`, record last-blocks for the provided `TransactionType`s. pub fn record_owner_rl( maybe_owner: Option<::AccountId>, diff --git a/precompiles/src/solidity/subnet.abi b/precompiles/src/solidity/subnet.abi index 4531f59246..692f39bf32 100644 --- a/precompiles/src/solidity/subnet.abi +++ b/precompiles/src/solidity/subnet.abi @@ -1028,8 +1028,5 @@ "outputs": [], "stateMutability": "payable", "type": "function" - }, - { - "inputs" } ] diff --git a/precompiles/src/solidity/subnet.sol b/precompiles/src/solidity/subnet.sol index 4e78708d62..91bd6f818a 100644 --- a/precompiles/src/solidity/subnet.sol +++ b/precompiles/src/solidity/subnet.sol @@ -201,4 +201,5 @@ interface ISubnet { uint16 netuid, uint64 commitRevealWeightsInterval ) external payable; + } From e6cc8f76a2fe3269d814b2ec71c07e18341b7bbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Polewicz?= Date: Mon, 17 Nov 2025 05:05:48 +0200 Subject: [PATCH 198/210] tweak a comment --- pallets/subtensor/src/macros/dispatches.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 4c8307e14f..69631c5151 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2252,7 +2252,7 @@ mod dispatches { ensure!(Self::if_subnet_exist(netuid), Error::::SubnetNotExists); if SubnetDeregistrationPrioritySchedule::::take(netuid).is_none() { - // Schedule was cleared, nothing to do. + // Schedule was clear already, nothing to do. return Ok(()); } From b8e96a374bc7ea3afb58a40bf5a2f820a5893200 Mon Sep 17 00:00:00 2001 From: John Reed <87283488+JohnReedV@users.noreply.github.com> Date: Thu, 20 Nov 2025 11:31:30 -0800 Subject: [PATCH 199/210] fix compile errors --- pallets/subtensor/src/coinbase/root.rs | 2 +- pallets/subtensor/src/macros/dispatches.rs | 4 ++-- pallets/subtensor/src/macros/events.rs | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pallets/subtensor/src/coinbase/root.rs b/pallets/subtensor/src/coinbase/root.rs index 166bf5b2d0..7cff890de0 100644 --- a/pallets/subtensor/src/coinbase/root.rs +++ b/pallets/subtensor/src/coinbase/root.rs @@ -593,7 +593,7 @@ impl Pallet { pub fn get_network_to_prune() -> Option { let current_block: u64 = Self::get_current_block_as_u64(); - if let Some(priority_netuid) = Self::pop_ready_subnet_deregistration_priority(current_block) + if let Some(priority_netuid) = Self::pop_ready_subnet_deregistration_priority() { return Some(priority_netuid); } diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 977d512c5c..d9e03d2bcc 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2167,7 +2167,7 @@ mod dispatches { /// * [`Error::SubnetNotExists`] if the subnet does not exist. /// * [`Error::SubnetDeregistrationPriorityAlreadyScheduled`] if a set operation is already /// scheduled. - #[pallet::call_index(121)] + #[pallet::call_index(125)] #[pallet::weight(( Weight::from_parts(40_000_000, 0) .saturating_add(T::DbWeight::get().reads_writes(4, 2)), @@ -2237,7 +2237,7 @@ mod dispatches { /// Enqueues the subnet for deregistration immediately. /// /// This call is intended to be used by the scheduler and requires root origin. - #[pallet::call_index(122)] + #[pallet::call_index(126)] #[pallet::weight(( Weight::from_parts(15_000_000, 0) .saturating_add(T::DbWeight::get().reads_writes(3, 2)), diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index f48e0f9c10..259a36e7e8 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -5,7 +5,6 @@ use frame_support::pallet_macros::pallet_section; #[pallet_section] mod events { use codec::Compact; - use frame_system::pallet_prelude::BlockNumberFor; #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] From 20cb50bf6a7623aab849b1fd6bb63ad83bbf2d4d Mon Sep 17 00:00:00 2001 From: John Reed <87283488+JohnReedV@users.noreply.github.com> Date: Thu, 20 Nov 2025 12:35:22 -0800 Subject: [PATCH 200/210] refactor to address warning --- pallets/subtensor/src/utils/misc.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/pallets/subtensor/src/utils/misc.rs b/pallets/subtensor/src/utils/misc.rs index 5251b01ba2..30204ae2cd 100644 --- a/pallets/subtensor/src/utils/misc.rs +++ b/pallets/subtensor/src/utils/misc.rs @@ -129,22 +129,22 @@ impl Pallet { pub fn pop_ready_subnet_deregistration_priority() -> Option { SubnetDeregistrationPriorityQueue::::mutate(|queue| { - let mut idx = 0; - while idx < queue.len() { - let netuid = queue[idx]; - queue.remove(idx); - if !Self::if_subnet_exist(netuid) { - continue; - } - - if netuid == NetUid::ROOT { - continue; - } - - return Some(netuid); + let first_valid = queue + .iter() + .copied() + .enumerate() + .find(|&(_, netuid)| { + Self::if_subnet_exist(netuid) && netuid != NetUid::ROOT + }); + + if let Some((pos, netuid)) = first_valid { + queue.drain(..=pos); + Some(netuid) + } else { + queue.clear(); + None } - None }) } From dfa5af8c0fceac70814fec571f79bec2c61bb5de Mon Sep 17 00:00:00 2001 From: John Reed <87283488+JohnReedV@users.noreply.github.com> Date: Thu, 20 Nov 2025 12:35:39 -0800 Subject: [PATCH 201/210] fmt --- pallets/subtensor/src/coinbase/root.rs | 3 +-- pallets/subtensor/src/utils/misc.rs | 5 +---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/pallets/subtensor/src/coinbase/root.rs b/pallets/subtensor/src/coinbase/root.rs index 7cff890de0..64ff95f061 100644 --- a/pallets/subtensor/src/coinbase/root.rs +++ b/pallets/subtensor/src/coinbase/root.rs @@ -593,8 +593,7 @@ impl Pallet { pub fn get_network_to_prune() -> Option { let current_block: u64 = Self::get_current_block_as_u64(); - if let Some(priority_netuid) = Self::pop_ready_subnet_deregistration_priority() - { + if let Some(priority_netuid) = Self::pop_ready_subnet_deregistration_priority() { return Some(priority_netuid); } diff --git a/pallets/subtensor/src/utils/misc.rs b/pallets/subtensor/src/utils/misc.rs index 30204ae2cd..3e6acb7c34 100644 --- a/pallets/subtensor/src/utils/misc.rs +++ b/pallets/subtensor/src/utils/misc.rs @@ -129,14 +129,11 @@ impl Pallet { pub fn pop_ready_subnet_deregistration_priority() -> Option { SubnetDeregistrationPriorityQueue::::mutate(|queue| { - let first_valid = queue .iter() .copied() .enumerate() - .find(|&(_, netuid)| { - Self::if_subnet_exist(netuid) && netuid != NetUid::ROOT - }); + .find(|&(_, netuid)| Self::if_subnet_exist(netuid) && netuid != NetUid::ROOT); if let Some((pos, netuid)) = first_valid { queue.drain(..=pos); From 7af9a65f1a8050921340613f8199320eb41f944d Mon Sep 17 00:00:00 2001 From: John Reed <87283488+JohnReedV@users.noreply.github.com> Date: Thu, 20 Nov 2025 12:44:59 -0800 Subject: [PATCH 202/210] clippy --- pallets/subtensor/src/tests/networks.rs | 3 ++- pallets/subtensor/src/utils/misc.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/tests/networks.rs b/pallets/subtensor/src/tests/networks.rs index cd91e7dcc0..a71a76c6fe 100644 --- a/pallets/subtensor/src/tests/networks.rs +++ b/pallets/subtensor/src/tests/networks.rs @@ -84,7 +84,8 @@ fn manage_priority_schedule_and_force_set() { )); assert!(!SubnetDeregistrationPriorityQueue::::get().contains(&net)); - let when = SubnetDeregistrationPrioritySchedule::::get(net).unwrap(); + let when = + SubnetDeregistrationPrioritySchedule::::get(net).expect("Expected to not panic"); assert!(when > 0); assert_ok!(SubtensorModule::force_set_deregistration_priority( diff --git a/pallets/subtensor/src/utils/misc.rs b/pallets/subtensor/src/utils/misc.rs index 3e6acb7c34..1700cfba64 100644 --- a/pallets/subtensor/src/utils/misc.rs +++ b/pallets/subtensor/src/utils/misc.rs @@ -110,7 +110,7 @@ impl Pallet { pub fn enqueue_subnet_deregistration_priority(netuid: NetUid) -> bool { SubnetDeregistrationPriorityQueue::::mutate(|queue| { - if queue.iter().any(|&existing| existing == netuid) { + if queue.contains(&netuid) { false } else { queue.push(netuid); From 3f654da066c7fad59dfcc4d8639e897864f22a9d Mon Sep 17 00:00:00 2001 From: Pawel Polewicz Date: Sat, 22 Nov 2025 02:26:46 +0000 Subject: [PATCH 203/210] remove effects of bad merge --- pallets/subtensor/src/lib.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 2afd608ee5..d6d387c135 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1620,11 +1620,6 @@ pub mod pallet { pub type PendingServerEmission = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultZeroAlpha>; - /// --- MAP ( netuid ) --> pending_emission - #[pallet::storage] - pub type PendingEmission = - StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultPendingEmission>; - /// --- MAP ( netuid ) --> pending_validator_emission #[pallet::storage] pub type PendingValidatorEmission = From e9ad3320c404687180f0000bdf83bd09ffddd7f8 Mon Sep 17 00:00:00 2001 From: Pawel Polewicz Date: Sat, 22 Nov 2025 02:36:43 +0000 Subject: [PATCH 204/210] split manage_deregistration_priority() --- pallets/subtensor/src/macros/dispatches.rs | 133 ++++++++++++--------- pallets/subtensor/src/tests/networks.rs | 34 ++---- 2 files changed, 91 insertions(+), 76 deletions(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index d9e03d2bcc..d8607c766c 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2153,15 +2153,9 @@ mod dispatches { Ok(()) } - /// Manages the deregistration priority queue for a subnet. + /// Schedules the subnet to be appended to the deregistration priority queue. /// - /// When `schedule_set` is `true`, the subnet is scheduled to be appended to the - /// deregistration queue after approximately five days using the scheduler pallet. - /// When `schedule_set` is `false`, any pending schedule is cleared. Only the root origin - /// may remove the subnet from the queue itself. - /// - /// Accessible by the subnet owner (for scheduling or canceling a pending schedule) or root - /// origin (full control). + /// Accessible by the subnet owner or root origin. /// /// # Errors /// * [`Error::SubnetNotExists`] if the subnet does not exist. @@ -2174,62 +2168,37 @@ mod dispatches { DispatchClass::Normal, Pays::Yes ))] - pub fn manage_deregistration_priority( + pub fn schedule_deregistration_priority( origin: OriginFor, netuid: NetUid, - schedule_set: bool, ) -> DispatchResult { ensure!(Self::if_subnet_exist(netuid), Error::::SubnetNotExists); - let maybe_owner = Self::ensure_subnet_owner_or_root(origin, netuid)?; + Self::ensure_subnet_owner_or_root(origin, netuid)?; - if schedule_set { - ensure!( - SubnetDeregistrationPrioritySchedule::::get(netuid).is_none(), - Error::::SubnetDeregistrationPriorityAlreadyScheduled - ); + ensure!( + SubnetDeregistrationPrioritySchedule::::get(netuid).is_none(), + Error::::SubnetDeregistrationPriorityAlreadyScheduled + ); + + let current_block: BlockNumberFor = >::block_number(); + let delay: BlockNumberFor = ColdkeySwapScheduleDuration::::get(); + let when: BlockNumberFor = current_block.saturating_add(delay); - let current_block: BlockNumberFor = >::block_number(); - let delay: BlockNumberFor = ColdkeySwapScheduleDuration::::get(); - let when: BlockNumberFor = current_block.saturating_add(delay); - - let call = Call::::force_set_deregistration_priority { netuid }; - let bound_call = ::Preimages::bound(LocalCallOf::::from(call)) - .map_err(|_| Error::::FailedToSchedule)?; - - T::Scheduler::schedule( - DispatchTime::At(when), - None, - 63, - frame_system::RawOrigin::Root.into(), - bound_call, - ) + let call = Call::::force_set_deregistration_priority { netuid }; + let bound_call = ::Preimages::bound(LocalCallOf::::from(call)) .map_err(|_| Error::::FailedToSchedule)?; - SubnetDeregistrationPrioritySchedule::::insert(netuid, when); + T::Scheduler::schedule( + DispatchTime::At(when), + None, + 63, + frame_system::RawOrigin::Root.into(), + bound_call, + ) + .map_err(|_| Error::::FailedToSchedule)?; - Self::deposit_event(Event::SubnetDeregistrationPriorityScheduled(netuid, when)); - } else { - if maybe_owner.is_some() { - if SubnetDeregistrationPrioritySchedule::::take(netuid).is_some() { - Self::deposit_event(Event::SubnetDeregistrationPriorityScheduleCleared( - netuid, - )); - } - } else { - let was_queued = Self::remove_subnet_from_deregistration_priority_queue(netuid); - let had_schedule = - SubnetDeregistrationPrioritySchedule::::take(netuid).is_some(); - - if was_queued { - Self::deposit_event(Event::SubnetDeregistrationPriorityCleared(netuid)); - } - if had_schedule { - Self::deposit_event(Event::SubnetDeregistrationPriorityScheduleCleared( - netuid, - )); - } - } - } + SubnetDeregistrationPrioritySchedule::::insert(netuid, when); + Self::deposit_event(Event::SubnetDeregistrationPriorityScheduled(netuid, when)); Ok(()) } @@ -2262,6 +2231,60 @@ mod dispatches { Ok(()) } + /// Cancels a pending deregistration priority schedule. + /// + /// Accessible by the subnet owner or root origin. + #[pallet::call_index(127)] + #[pallet::weight(( + Weight::from_parts(20_000_000, 0) + .saturating_add(T::DbWeight::get().reads_writes(2, 1)), + DispatchClass::Normal, + Pays::Yes + ))] + pub fn cancel_deregistration_priority_schedule( + origin: OriginFor, + netuid: NetUid, + ) -> DispatchResult { + ensure!(Self::if_subnet_exist(netuid), Error::::SubnetNotExists); + Self::ensure_subnet_owner_or_root(origin, netuid)?; + + if SubnetDeregistrationPrioritySchedule::::take(netuid).is_some() { + Self::deposit_event(Event::SubnetDeregistrationPriorityScheduleCleared(netuid)); + } + + Ok(()) + } + + /// Clears any deregistration priority queue entry and pending schedule for a subnet. + /// + /// Only callable by root origin. + #[pallet::call_index(128)] + #[pallet::weight(( + Weight::from_parts(20_000_000, 0) + .saturating_add(T::DbWeight::get().reads_writes(2, 2)), + DispatchClass::Normal, + Pays::Yes + ))] + pub fn clear_deregistration_priority( + origin: OriginFor, + netuid: NetUid, + ) -> DispatchResult { + ensure_root(origin)?; + ensure!(Self::if_subnet_exist(netuid), Error::::SubnetNotExists); + + let was_queued = Self::remove_subnet_from_deregistration_priority_queue(netuid); + let had_schedule = SubnetDeregistrationPrioritySchedule::::take(netuid).is_some(); + + if was_queued { + Self::deposit_event(Event::SubnetDeregistrationPriorityCleared(netuid)); + } + if had_schedule { + Self::deposit_event(Event::SubnetDeregistrationPriorityScheduleCleared(netuid)); + } + + Ok(()) + } + /// ---- Used to commit timelock encrypted commit-reveal weight values to later be revealed. /// /// # Args: diff --git a/pallets/subtensor/src/tests/networks.rs b/pallets/subtensor/src/tests/networks.rs index a71a76c6fe..0905035e4a 100644 --- a/pallets/subtensor/src/tests/networks.rs +++ b/pallets/subtensor/src/tests/networks.rs @@ -71,16 +71,15 @@ fn dissolve_no_stakers_no_alpha_no_emission() { } #[test] -fn manage_priority_schedule_and_force_set() { +fn schedule_priority_and_force_set() { new_test_ext(0).execute_with(|| { let owner_cold = U256::from(11); let owner_hot = U256::from(22); let net = add_dynamic_network(&owner_hot, &owner_cold); - assert_ok!(SubtensorModule::manage_deregistration_priority( + assert_ok!(SubtensorModule::schedule_deregistration_priority( RuntimeOrigin::signed(owner_cold), - net, - true + net )); assert!(!SubnetDeregistrationPriorityQueue::::get().contains(&net)); @@ -101,7 +100,7 @@ fn manage_priority_schedule_and_force_set() { } #[test] -fn manage_priority_owner_cancels_schedule_only() { +fn cancel_priority_schedule_only() { new_test_ext(0).execute_with(|| { let owner_cold = U256::from(13); let owner_hot = U256::from(26); @@ -110,10 +109,9 @@ fn manage_priority_owner_cancels_schedule_only() { SubnetDeregistrationPriorityQueue::::mutate(|queue| queue.push(net)); SubnetDeregistrationPrioritySchedule::::insert(net, 42); - assert_ok!(SubtensorModule::manage_deregistration_priority( + assert_ok!(SubtensorModule::cancel_deregistration_priority_schedule( RuntimeOrigin::signed(owner_cold), - net, - false + net )); assert!(SubnetDeregistrationPriorityQueue::::get().contains(&net)); @@ -124,7 +122,7 @@ fn manage_priority_owner_cancels_schedule_only() { } #[test] -fn manage_priority_root_clears_queue_entry() { +fn clear_priority_requires_root() { new_test_ext(0).execute_with(|| { let owner_cold = U256::from(19); let owner_hot = U256::from(38); @@ -133,10 +131,9 @@ fn manage_priority_root_clears_queue_entry() { SubnetDeregistrationPriorityQueue::::mutate(|queue| queue.push(net)); SubnetDeregistrationPrioritySchedule::::insert(net, 55); - assert_ok!(SubtensorModule::manage_deregistration_priority( + assert_ok!(SubtensorModule::clear_deregistration_priority( RuntimeOrigin::root(), - net, - false + net )); assert!(!SubnetDeregistrationPriorityQueue::::get().contains(&net)); @@ -147,7 +144,7 @@ fn manage_priority_root_clears_queue_entry() { } #[test] -fn manage_priority_requires_owner_or_root() { +fn schedule_priority_requires_owner_or_root() { new_test_ext(0).execute_with(|| { let owner_cold = U256::from(15); let owner_hot = U256::from(30); @@ -155,18 +152,13 @@ fn manage_priority_requires_owner_or_root() { let intruder = U256::from(999); assert_err!( - SubtensorModule::manage_deregistration_priority( - RuntimeOrigin::signed(intruder), - net, - false - ), + SubtensorModule::schedule_deregistration_priority(RuntimeOrigin::signed(intruder), net), DispatchError::BadOrigin ); - assert_ok!(SubtensorModule::manage_deregistration_priority( + assert_ok!(SubtensorModule::schedule_deregistration_priority( RuntimeOrigin::root(), - net, - false + net )); }); } From 55e6597b37758cd5f196f72f83c8f60c93648c22 Mon Sep 17 00:00:00 2001 From: Pawel Polewicz Date: Sat, 22 Nov 2025 02:38:18 +0000 Subject: [PATCH 205/210] make force_set_deregistration_priority Pays::Yes --- pallets/subtensor/src/macros/dispatches.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index d8607c766c..43fa2acd00 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2211,7 +2211,7 @@ mod dispatches { Weight::from_parts(15_000_000, 0) .saturating_add(T::DbWeight::get().reads_writes(3, 2)), DispatchClass::Operational, - Pays::No + Pays::Yes ))] pub fn force_set_deregistration_priority( origin: OriginFor, From a8b0d50f80f1f6ce7de0c30fd97a31bc692b801f Mon Sep 17 00:00:00 2001 From: Pawel Polewicz Date: Sat, 22 Nov 2025 02:56:39 +0000 Subject: [PATCH 206/210] rename force_set_deregistration_priority to enqueue_subnet_deregistration --- pallets/subtensor/src/macros/dispatches.rs | 4 ++-- pallets/subtensor/src/tests/networks.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 43fa2acd00..4935be3d37 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2184,7 +2184,7 @@ mod dispatches { let delay: BlockNumberFor = ColdkeySwapScheduleDuration::::get(); let when: BlockNumberFor = current_block.saturating_add(delay); - let call = Call::::force_set_deregistration_priority { netuid }; + let call = Call::::enqueue_subnet_deregistration { netuid }; let bound_call = ::Preimages::bound(LocalCallOf::::from(call)) .map_err(|_| Error::::FailedToSchedule)?; @@ -2213,7 +2213,7 @@ mod dispatches { DispatchClass::Operational, Pays::Yes ))] - pub fn force_set_deregistration_priority( + pub fn enqueue_subnet_deregistration( origin: OriginFor, netuid: NetUid, ) -> DispatchResult { diff --git a/pallets/subtensor/src/tests/networks.rs b/pallets/subtensor/src/tests/networks.rs index 0905035e4a..7c25254a80 100644 --- a/pallets/subtensor/src/tests/networks.rs +++ b/pallets/subtensor/src/tests/networks.rs @@ -87,7 +87,7 @@ fn schedule_priority_and_force_set() { SubnetDeregistrationPrioritySchedule::::get(net).expect("Expected to not panic"); assert!(when > 0); - assert_ok!(SubtensorModule::force_set_deregistration_priority( + assert_ok!(SubtensorModule::enqueue_subnet_deregistration( RuntimeOrigin::root(), net )); @@ -164,13 +164,13 @@ fn schedule_priority_requires_owner_or_root() { } #[test] -fn force_set_deregistration_priority_is_noop_without_schedule() { +fn enqueue_subnet_deregistration_is_noop_without_schedule() { new_test_ext(0).execute_with(|| { let owner_cold = U256::from(17); let owner_hot = U256::from(34); let net = add_dynamic_network(&owner_hot, &owner_cold); - assert_ok!(SubtensorModule::force_set_deregistration_priority( + assert_ok!(SubtensorModule::enqueue_subnet_deregistration( RuntimeOrigin::root(), net )); From 02e528e54f216a85782f53797115acddc89cda2a Mon Sep 17 00:00:00 2001 From: Pawel Polewicz Date: Sat, 22 Nov 2025 02:56:52 +0000 Subject: [PATCH 207/210] Fix enqueue_subnet_deregistration when subnet is not scheduled for dereg --- pallets/subtensor/src/macros/dispatches.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 4935be3d37..560e9e794c 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2220,9 +2220,8 @@ mod dispatches { ensure_root(origin)?; ensure!(Self::if_subnet_exist(netuid), Error::::SubnetNotExists); - if SubnetDeregistrationPrioritySchedule::::take(netuid).is_none() { - // Schedule was clear already, nothing to do. - return Ok(()); + if SubnetDeregistrationPrioritySchedule::::take(netuid).is_some() { + Self::deposit_event(Event::SubnetDeregistrationPriorityScheduleCleared(netuid)); } let _ = Self::enqueue_subnet_deregistration_priority(netuid); From 3dc8a6da1484f7603017a17a1d56104d4685bd2e Mon Sep 17 00:00:00 2001 From: Pawel Polewicz Date: Sat, 22 Nov 2025 03:11:00 +0000 Subject: [PATCH 208/210] new setting for deregistration priority schedule delay --- chain-extensions/src/mock.rs | 2 ++ pallets/admin-utils/src/benchmarking.rs | 6 ++++ pallets/admin-utils/src/lib.rs | 22 ++++++++++++++- pallets/admin-utils/src/tests/mock.rs | 2 ++ pallets/admin-utils/src/tests/mod.rs | 33 ++++++++++++++++++++++ pallets/subtensor/src/lib.rs | 15 ++++++++++ pallets/subtensor/src/macros/config.rs | 3 ++ pallets/subtensor/src/macros/dispatches.rs | 2 +- pallets/subtensor/src/macros/events.rs | 2 ++ pallets/subtensor/src/tests/mock.rs | 2 ++ pallets/subtensor/src/utils/misc.rs | 6 ++++ pallets/transaction-fee/src/tests/mock.rs | 2 ++ runtime/src/lib.rs | 2 ++ 13 files changed, 97 insertions(+), 2 deletions(-) diff --git a/chain-extensions/src/mock.rs b/chain-extensions/src/mock.rs index 98ea096199..0efea56696 100644 --- a/chain-extensions/src/mock.rs +++ b/chain-extensions/src/mock.rs @@ -327,6 +327,7 @@ parameter_types! { pub const InitialYuma3On: bool = false; // Default value for Yuma3On // pub const InitialNetworkMaxStake: u64 = u64::MAX; // (DEPRECATED) pub const InitialColdkeySwapScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days + pub const InitialDeregistrationPriorityScheduleDelay: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days pub const InitialColdkeySwapRescheduleDuration: u64 = 24 * 60 * 60 / 12; // Default as 1 day pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days pub const InitialTaoWeight: u64 = 0; // 100% global weight. @@ -398,6 +399,7 @@ impl pallet_subtensor::Config for Test { type Yuma3On = InitialYuma3On; type Preimages = Preimage; type InitialColdkeySwapScheduleDuration = InitialColdkeySwapScheduleDuration; + type InitialDeregistrationPriorityScheduleDelay = InitialDeregistrationPriorityScheduleDelay; type InitialColdkeySwapRescheduleDuration = InitialColdkeySwapRescheduleDuration; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialTaoWeight = InitialTaoWeight; diff --git a/pallets/admin-utils/src/benchmarking.rs b/pallets/admin-utils/src/benchmarking.rs index 08589e530b..a1b61fa5c8 100644 --- a/pallets/admin-utils/src/benchmarking.rs +++ b/pallets/admin-utils/src/benchmarking.rs @@ -485,6 +485,12 @@ mod benchmarks { _(RawOrigin::Root, 100u32.into()); } + #[benchmark] + fn sudo_set_deregistration_priority_schedule_delay() { + #[extrinsic_call] + _(RawOrigin::Root, 100u32.into()); + } + #[benchmark] fn sudo_set_dissolve_network_schedule_duration() { #[extrinsic_call] diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index de6ac5825b..5f0d845ead 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -1386,6 +1386,26 @@ pub mod pallet { Ok(()) } + /// Sets the delay for deregistration priority scheduling. + #[pallet::call_index(55)] + #[pallet::weight(( + Weight::from_parts(5_000_000, 0) + .saturating_add(T::DbWeight::get().reads(0_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)), + DispatchClass::Operational, + Pays::Yes + ))] + pub fn sudo_set_deregistration_priority_schedule_delay( + origin: OriginFor, + duration: BlockNumberFor, + ) -> DispatchResult { + ensure_root(origin)?; + pallet_subtensor::Pallet::::set_deregistration_priority_schedule_delay(duration); + log::trace!("DeregistrationPriorityScheduleDelaySet( duration: {duration:?} )"); + + Ok(()) + } + /// Sets the duration of the dissolve network schedule. /// /// This extrinsic allows the root account to set the duration for the dissolve network schedule. @@ -1400,7 +1420,7 @@ pub mod pallet { /// /// # Weight /// Weight is handled by the `#[pallet::weight]` attribute. - #[pallet::call_index(55)] + #[pallet::call_index(56)] #[pallet::weight(( Weight::from_parts(5_000_000, 0) .saturating_add(T::DbWeight::get().reads(0_u64)) diff --git a/pallets/admin-utils/src/tests/mock.rs b/pallets/admin-utils/src/tests/mock.rs index 0140808baa..58e00e70ba 100644 --- a/pallets/admin-utils/src/tests/mock.rs +++ b/pallets/admin-utils/src/tests/mock.rs @@ -141,6 +141,7 @@ parameter_types! { // pub const InitialHotkeyEmissionTempo: u64 = 1; // (DEPRECATED) // pub const InitialNetworkMaxStake: u64 = u64::MAX; // (DEPRECATED) pub const InitialColdkeySwapScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // 5 days + pub const InitialDeregistrationPriorityScheduleDelay: u64 = 5 * 24 * 60 * 60 / 12; // 5 days pub const InitialColdkeySwapRescheduleDuration: u64 = 24 * 60 * 60 / 12; // 1 day pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // 5 days pub const InitialTaoWeight: u64 = u64::MAX/10; // 10% global weight. @@ -211,6 +212,7 @@ impl pallet_subtensor::Config for Test { type Yuma3On = InitialYuma3On; type Preimages = (); type InitialColdkeySwapScheduleDuration = InitialColdkeySwapScheduleDuration; + type InitialDeregistrationPriorityScheduleDelay = InitialDeregistrationPriorityScheduleDelay; type InitialColdkeySwapRescheduleDuration = InitialColdkeySwapRescheduleDuration; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialTaoWeight = InitialTaoWeight; diff --git a/pallets/admin-utils/src/tests/mod.rs b/pallets/admin-utils/src/tests/mod.rs index 1aaefc8f8d..be222888e8 100644 --- a/pallets/admin-utils/src/tests/mod.rs +++ b/pallets/admin-utils/src/tests/mod.rs @@ -1418,6 +1418,39 @@ fn test_sudo_set_coldkey_swap_schedule_duration() { }); } +#[test] +fn test_sudo_set_deregistration_priority_schedule_delay() { + new_test_ext().execute_with(|| { + let root = RuntimeOrigin::root(); + let non_root = RuntimeOrigin::signed(U256::from(1)); + let new_duration = 150u32.into(); + + assert_noop!( + AdminUtils::sudo_set_deregistration_priority_schedule_delay(non_root, new_duration), + DispatchError::BadOrigin + ); + + assert_ok!(AdminUtils::sudo_set_deregistration_priority_schedule_delay( + root.clone(), + new_duration + )); + + assert_eq!( + pallet_subtensor::DeregistrationPriorityScheduleDelay::::get(), + new_duration + ); + + System::assert_last_event( + Event::DeregistrationPriorityScheduleDelaySet(new_duration).into(), + ); + + assert_ok!(AdminUtils::sudo_set_deregistration_priority_schedule_delay( + root, + new_duration + )); + }); +} + #[test] fn test_sudo_set_dissolve_network_schedule_duration() { new_test_ext().execute_with(|| { diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index d6d387c135..eba1b7d48d 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -955,6 +955,12 @@ pub mod pallet { T::InitialColdkeySwapScheduleDuration::get() } + /// Default value for deregistration priority schedule delay + #[pallet::type_value] + pub fn DefaultDeregistrationPriorityScheduleDelay() -> BlockNumberFor { + T::InitialDeregistrationPriorityScheduleDelay::get() + } + /// Default value for coldkey swap reschedule duration #[pallet::type_value] pub fn DefaultColdkeySwapRescheduleDuration() -> BlockNumberFor { @@ -1615,6 +1621,15 @@ pub mod pallet { pub type SubnetDeregistrationPrioritySchedule = StorageMap<_, Identity, NetUid, BlockNumberFor, OptionQuery>; + /// --- Global delay for scheduled deregistration priority activations + #[pallet::storage] + pub type DeregistrationPriorityScheduleDelay = StorageValue< + _, + BlockNumberFor, + ValueQuery, + DefaultDeregistrationPriorityScheduleDelay, + >; + /// --- MAP ( netuid ) --> pending_server_emission #[pallet::storage] pub type PendingServerEmission = diff --git a/pallets/subtensor/src/macros/config.rs b/pallets/subtensor/src/macros/config.rs index a735bde1e1..54d94086bc 100644 --- a/pallets/subtensor/src/macros/config.rs +++ b/pallets/subtensor/src/macros/config.rs @@ -221,6 +221,9 @@ mod config { /// Coldkey swap schedule duartion. #[pallet::constant] type InitialColdkeySwapScheduleDuration: Get>; + /// Deregistration priority schedule delay. + #[pallet::constant] + type InitialDeregistrationPriorityScheduleDelay: Get>; /// Coldkey swap reschedule duration. #[pallet::constant] type InitialColdkeySwapRescheduleDuration: Get>; diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 560e9e794c..95bddf881c 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2181,7 +2181,7 @@ mod dispatches { ); let current_block: BlockNumberFor = >::block_number(); - let delay: BlockNumberFor = ColdkeySwapScheduleDuration::::get(); + let delay: BlockNumberFor = DeregistrationPriorityScheduleDelay::::get(); let when: BlockNumberFor = current_block.saturating_add(delay); let call = Call::::enqueue_subnet_deregistration { netuid }; diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index 259a36e7e8..5e3bdcdfa9 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -21,6 +21,8 @@ mod events { SubnetDeregistrationPriorityCleared(NetUid), /// pending deregistration schedule cleared or canceled. SubnetDeregistrationPriorityScheduleCleared(NetUid), + /// deregistration priority schedule delay updated. + DeregistrationPriorityScheduleDelaySet(BlockNumberFor), /// stake has been transferred from the a coldkey account onto the hotkey staking account. StakeAdded( T::AccountId, diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index 090fcf8f75..2ea61ef7f5 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -214,6 +214,7 @@ parameter_types! { pub const InitialYuma3On: bool = false; // Default value for Yuma3On // pub const InitialNetworkMaxStake: u64 = u64::MAX; // (DEPRECATED) pub const InitialColdkeySwapScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days + pub const InitialDeregistrationPriorityScheduleDelay: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days pub const InitialColdkeySwapRescheduleDuration: u64 = 24 * 60 * 60 / 12; // Default as 1 day pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days pub const InitialTaoWeight: u64 = 0; // 100% global weight. @@ -285,6 +286,7 @@ impl crate::Config for Test { type Yuma3On = InitialYuma3On; type Preimages = Preimage; type InitialColdkeySwapScheduleDuration = InitialColdkeySwapScheduleDuration; + type InitialDeregistrationPriorityScheduleDelay = InitialDeregistrationPriorityScheduleDelay; type InitialColdkeySwapRescheduleDuration = InitialColdkeySwapRescheduleDuration; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialTaoWeight = InitialTaoWeight; diff --git a/pallets/subtensor/src/utils/misc.rs b/pallets/subtensor/src/utils/misc.rs index 1700cfba64..314c5a92c6 100644 --- a/pallets/subtensor/src/utils/misc.rs +++ b/pallets/subtensor/src/utils/misc.rs @@ -894,6 +894,12 @@ impl Pallet { Self::deposit_event(Event::ColdkeySwapScheduleDurationSet(duration)); } + /// Set the delay applied when scheduling deregistration priority. + pub fn set_deregistration_priority_schedule_delay(duration: BlockNumberFor) { + DeregistrationPriorityScheduleDelay::::set(duration); + Self::deposit_event(Event::DeregistrationPriorityScheduleDelaySet(duration)); + } + /// Set the duration for dissolve network /// /// # Arguments diff --git a/pallets/transaction-fee/src/tests/mock.rs b/pallets/transaction-fee/src/tests/mock.rs index ee5b1693ba..2cfc87ca85 100644 --- a/pallets/transaction-fee/src/tests/mock.rs +++ b/pallets/transaction-fee/src/tests/mock.rs @@ -206,6 +206,7 @@ parameter_types! { // pub const InitialHotkeyEmissionTempo: u64 = 1; // (DEPRECATED) // pub const InitialNetworkMaxStake: u64 = u64::MAX; // (DEPRECATED) pub const InitialColdkeySwapScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // 5 days + pub const InitialDeregistrationPriorityScheduleDelay: u64 = 5 * 24 * 60 * 60 / 12; // 5 days pub const InitialColdkeySwapRescheduleDuration: u64 = 24 * 60 * 60 / 12; // 1 day pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // 5 days pub const InitialTaoWeight: u64 = u64::MAX/10; // 10% global weight. @@ -276,6 +277,7 @@ impl pallet_subtensor::Config for Test { type Yuma3On = InitialYuma3On; type Preimages = (); type InitialColdkeySwapScheduleDuration = InitialColdkeySwapScheduleDuration; + type InitialDeregistrationPriorityScheduleDelay = InitialDeregistrationPriorityScheduleDelay; type InitialColdkeySwapRescheduleDuration = InitialColdkeySwapRescheduleDuration; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialTaoWeight = InitialTaoWeight; diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index f6843c50ee..9dd95b941b 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -1029,6 +1029,7 @@ parameter_types! { pub const InitialYuma3On: bool = false; // Default value for Yuma3On // pub const SubtensorInitialNetworkMaxStake: u64 = u64::MAX; // (DEPRECATED) pub const InitialColdkeySwapScheduleDuration: BlockNumber = 5 * 24 * 60 * 60 / 12; // 5 days + pub const InitialDeregistrationPriorityScheduleDelay: BlockNumber = 5 * 24 * 60 * 60 / 12; // 5 days pub const InitialColdkeySwapRescheduleDuration: BlockNumber = 24 * 60 * 60 / 12; // 1 day pub const InitialDissolveNetworkScheduleDuration: BlockNumber = 5 * 24 * 60 * 60 / 12; // 5 days pub const SubtensorInitialTaoWeight: u64 = 971_718_665_099_567_868; // 0.05267697438728329% tao weight. @@ -1101,6 +1102,7 @@ impl pallet_subtensor::Config for Runtime { type InitialTaoWeight = SubtensorInitialTaoWeight; type Preimages = Preimage; type InitialColdkeySwapScheduleDuration = InitialColdkeySwapScheduleDuration; + type InitialDeregistrationPriorityScheduleDelay = InitialDeregistrationPriorityScheduleDelay; type InitialColdkeySwapRescheduleDuration = InitialColdkeySwapRescheduleDuration; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; From 8cfd5c6ebde0095ff67ac1af7e05926c6d8cc4d4 Mon Sep 17 00:00:00 2001 From: Pawel Polewicz Date: Sat, 22 Nov 2025 03:26:10 +0000 Subject: [PATCH 209/210] Rename the deregistration-related events --- pallets/subtensor/src/macros/dispatches.rs | 14 ++++++++------ pallets/subtensor/src/macros/events.rs | 8 ++++---- pallets/subtensor/src/utils/misc.rs | 2 +- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 95bddf881c..99f8ed8183 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2198,7 +2198,9 @@ mod dispatches { .map_err(|_| Error::::FailedToSchedule)?; SubnetDeregistrationPrioritySchedule::::insert(netuid, when); - Self::deposit_event(Event::SubnetDeregistrationPriorityScheduled(netuid, when)); + Self::deposit_event(Event::SubnetDeregistrationPriorityScheduleAdded( + netuid, when, + )); Ok(()) } @@ -2221,11 +2223,11 @@ mod dispatches { ensure!(Self::if_subnet_exist(netuid), Error::::SubnetNotExists); if SubnetDeregistrationPrioritySchedule::::take(netuid).is_some() { - Self::deposit_event(Event::SubnetDeregistrationPriorityScheduleCleared(netuid)); + Self::deposit_event(Event::SubnetDeregistrationPriorityScheduleRemoved(netuid)); } let _ = Self::enqueue_subnet_deregistration_priority(netuid); - Self::deposit_event(Event::SubnetDeregistrationPrioritySet(netuid)); + Self::deposit_event(Event::SubnetDeregistrationPriorityQueueAdded(netuid)); Ok(()) } @@ -2248,7 +2250,7 @@ mod dispatches { Self::ensure_subnet_owner_or_root(origin, netuid)?; if SubnetDeregistrationPrioritySchedule::::take(netuid).is_some() { - Self::deposit_event(Event::SubnetDeregistrationPriorityScheduleCleared(netuid)); + Self::deposit_event(Event::SubnetDeregistrationPriorityScheduleRemoved(netuid)); } Ok(()) @@ -2275,10 +2277,10 @@ mod dispatches { let had_schedule = SubnetDeregistrationPrioritySchedule::::take(netuid).is_some(); if was_queued { - Self::deposit_event(Event::SubnetDeregistrationPriorityCleared(netuid)); + Self::deposit_event(Event::SubnetDeregistrationPriorityQueueRemoved(netuid)); } if had_schedule { - Self::deposit_event(Event::SubnetDeregistrationPriorityScheduleCleared(netuid)); + Self::deposit_event(Event::SubnetDeregistrationPriorityScheduleRemoved(netuid)); } Ok(()) diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index 5e3bdcdfa9..61bc7d9e6c 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -14,13 +14,13 @@ mod events { /// a network is removed. NetworkRemoved(NetUid), /// a subnet's deregistration queue entry was scheduled. - SubnetDeregistrationPriorityScheduled(NetUid, BlockNumberFor), + SubnetDeregistrationPriorityScheduleAdded(NetUid, BlockNumberFor), /// a subnet was enqueued for deregistration. - SubnetDeregistrationPrioritySet(NetUid), + SubnetDeregistrationPriorityQueueAdded(NetUid), /// network deregistration queue entry cleared or canceled. - SubnetDeregistrationPriorityCleared(NetUid), + SubnetDeregistrationPriorityQueueRemoved(NetUid), /// pending deregistration schedule cleared or canceled. - SubnetDeregistrationPriorityScheduleCleared(NetUid), + SubnetDeregistrationPriorityScheduleRemoved(NetUid), /// deregistration priority schedule delay updated. DeregistrationPriorityScheduleDelaySet(BlockNumberFor), /// stake has been transferred from the a coldkey account onto the hotkey staking account. diff --git a/pallets/subtensor/src/utils/misc.rs b/pallets/subtensor/src/utils/misc.rs index 314c5a92c6..be447e1283 100644 --- a/pallets/subtensor/src/utils/misc.rs +++ b/pallets/subtensor/src/utils/misc.rs @@ -103,7 +103,7 @@ impl Pallet { for netuid in nets { if SubnetDeregistrationPrioritySchedule::::take(netuid).is_some() { - Self::deposit_event(Event::SubnetDeregistrationPriorityScheduleCleared(netuid)); + Self::deposit_event(Event::SubnetDeregistrationPriorityScheduleRemoved(netuid)); } } } From 7329b01bfe6c28fc82e23eabd2ff72ead8d46284 Mon Sep 17 00:00:00 2001 From: Pawel Polewicz Date: Sat, 22 Nov 2025 04:34:01 +0000 Subject: [PATCH 210/210] Renames --- pallets/subtensor/src/coinbase/root.rs | 2 +- pallets/subtensor/src/macros/dispatches.rs | 4 ++-- pallets/subtensor/src/tests/networks.rs | 2 +- pallets/subtensor/src/utils/misc.rs | 9 +++------ precompiles/src/solidity/subnet.sol | 1 - 5 files changed, 7 insertions(+), 11 deletions(-) diff --git a/pallets/subtensor/src/coinbase/root.rs b/pallets/subtensor/src/coinbase/root.rs index 64ff95f061..15275878a5 100644 --- a/pallets/subtensor/src/coinbase/root.rs +++ b/pallets/subtensor/src/coinbase/root.rs @@ -593,7 +593,7 @@ impl Pallet { pub fn get_network_to_prune() -> Option { let current_block: u64 = Self::get_current_block_as_u64(); - if let Some(priority_netuid) = Self::pop_ready_subnet_deregistration_priority() { + if let Some(priority_netuid) = Self::pop_ready_subnet_from_deregistration_priority_queue() { return Some(priority_netuid); } diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 99f8ed8183..3bfbad7fab 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2226,7 +2226,7 @@ mod dispatches { Self::deposit_event(Event::SubnetDeregistrationPriorityScheduleRemoved(netuid)); } - let _ = Self::enqueue_subnet_deregistration_priority(netuid); + Self::enqueue_subnet_to_deregistration_priority_queue(netuid); Self::deposit_event(Event::SubnetDeregistrationPriorityQueueAdded(netuid)); Ok(()) @@ -2242,7 +2242,7 @@ mod dispatches { DispatchClass::Normal, Pays::Yes ))] - pub fn cancel_deregistration_priority_schedule( + pub fn cancel_deregistration_priority_schedules( origin: OriginFor, netuid: NetUid, ) -> DispatchResult { diff --git a/pallets/subtensor/src/tests/networks.rs b/pallets/subtensor/src/tests/networks.rs index 7c25254a80..80700ad6a1 100644 --- a/pallets/subtensor/src/tests/networks.rs +++ b/pallets/subtensor/src/tests/networks.rs @@ -109,7 +109,7 @@ fn cancel_priority_schedule_only() { SubnetDeregistrationPriorityQueue::::mutate(|queue| queue.push(net)); SubnetDeregistrationPrioritySchedule::::insert(net, 42); - assert_ok!(SubtensorModule::cancel_deregistration_priority_schedule( + assert_ok!(SubtensorModule::cancel_deregistration_priority_schedules( RuntimeOrigin::signed(owner_cold), net )); diff --git a/pallets/subtensor/src/utils/misc.rs b/pallets/subtensor/src/utils/misc.rs index be447e1283..53cab078c4 100644 --- a/pallets/subtensor/src/utils/misc.rs +++ b/pallets/subtensor/src/utils/misc.rs @@ -108,13 +108,10 @@ impl Pallet { } } - pub fn enqueue_subnet_deregistration_priority(netuid: NetUid) -> bool { + pub fn enqueue_subnet_to_deregistration_priority_queue(netuid: NetUid) { SubnetDeregistrationPriorityQueue::::mutate(|queue| { - if queue.contains(&netuid) { - false - } else { + if !queue.contains(&netuid) { queue.push(netuid); - true } }) } @@ -127,7 +124,7 @@ impl Pallet { }) } - pub fn pop_ready_subnet_deregistration_priority() -> Option { + pub fn pop_ready_subnet_from_deregistration_priority_queue() -> Option { SubnetDeregistrationPriorityQueue::::mutate(|queue| { let first_valid = queue .iter() diff --git a/precompiles/src/solidity/subnet.sol b/precompiles/src/solidity/subnet.sol index 91bd6f818a..4e78708d62 100644 --- a/precompiles/src/solidity/subnet.sol +++ b/precompiles/src/solidity/subnet.sol @@ -201,5 +201,4 @@ interface ISubnet { uint16 netuid, uint64 commitRevealWeightsInterval ) external payable; - }