From a00756f6d3b33ce08f44a2fef8ea929c18a9fcb0 Mon Sep 17 00:00:00 2001 From: Elv92 Date: Mon, 13 Jul 2026 15:19:48 +0000 Subject: [PATCH] feat(subtensor): add MinTradeDelay post-start trading delay (#2844) Prevent subnet-start self-snipes: a subnet owner can currently submit batch_all[start_call, add_stake] and buy alpha atomically in the block trading opens (the SN99 case, issue #2844), because do_start_call enables SubtokenEnabled in the same block. Add a sudo-configurable MinTradeDelay (blocks). Staking in/out is rejected with TradingNotOpenYet until current_block >= start_block + MinTradeDelay, where start_block = FirstEmissionBlockNumber - 1. The check lives in ensure_subtoken_enabled (the single gate for add_stake/remove_stake/recycle/ buy_alpha/transfer_staked_alpha); validate_stake_transition is also routed through it so swap_stake/move_stake/transfer_stake cannot bypass the delay. Mirrors the existing StartCallDelay hyperparameter (storage, config const, event, getter/setter, sudo_set_min_trade_delay call_index 97). Runtime default prod_or_fast!(360, 0); spec_version 429->430; ValueQuery default => no migration. Existing/started subnets, root, and emission timing are unaffected. Adds tests. --- chain-extensions/src/mock.rs | 2 + eco-tests/src/mock.rs | 2 + pallets/admin-utils/src/lib.rs | 11 +++ pallets/admin-utils/src/tests/mock.rs | 2 + pallets/admin-utils/src/tests/mod.rs | 30 ++++++++ pallets/subtensor/src/coinbase/root.rs | 7 ++ pallets/subtensor/src/lib.rs | 24 +++++- pallets/subtensor/src/macros/config.rs | 3 + pallets/subtensor/src/macros/errors.rs | 2 + pallets/subtensor/src/macros/events.rs | 2 + pallets/subtensor/src/staking/stake_utils.rs | 15 ++-- pallets/subtensor/src/tests/mock.rs | 2 + pallets/subtensor/src/tests/mock_high_ed.rs | 2 + pallets/subtensor/src/tests/subnet.rs | 78 ++++++++++++++++++++ pallets/transaction-fee/src/tests/mock.rs | 2 + precompiles/src/mock.rs | 2 + runtime/src/lib.rs | 6 +- runtime/src/proxy_filters/call_groups.rs | 1 + 18 files changed, 182 insertions(+), 11 deletions(-) diff --git a/chain-extensions/src/mock.rs b/chain-extensions/src/mock.rs index 67103fd00a..ce4b45ed21 100644 --- a/chain-extensions/src/mock.rs +++ b/chain-extensions/src/mock.rs @@ -348,6 +348,7 @@ parameter_types! { pub const InitialTaoWeight: u64 = 0; // 0% TAO weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks pub const InitialStartCallDelay: u64 = 7 * 24 * 60 * 60 / 12; // Default as 7 days + pub const InitialMinTradeDelay: u64 = 0; // #2844: 0 in mocks pub const InitialKeySwapOnSubnetCost: TaoBalance = TaoBalance::new(10_000_000); pub const HotkeySwapOnSubnetInterval: u64 = 15; // 15 block, should be bigger than subnet number, then trigger clean up for all subnets pub const MaxContributorsPerLeaseToRemove: u32 = 3; @@ -428,6 +429,7 @@ impl pallet_subtensor::Config for Test { type InitialTaoWeight = InitialTaoWeight; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; type InitialStartCallDelay = InitialStartCallDelay; + type InitialMinTradeDelay = InitialMinTradeDelay; type SwapInterface = pallet_subtensor_swap::Pallet; type KeySwapOnSubnetCost = InitialKeySwapOnSubnetCost; type HotkeySwapOnSubnetInterval = HotkeySwapOnSubnetInterval; diff --git a/eco-tests/src/mock.rs b/eco-tests/src/mock.rs index 66ab4b9f9c..f13f809b44 100644 --- a/eco-tests/src/mock.rs +++ b/eco-tests/src/mock.rs @@ -243,6 +243,7 @@ parameter_types! { pub const InitialTaoWeight: u64 = 0; // 0% TAO weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks pub const InitialStartCallDelay: u64 = 0; // 0 days + pub const InitialMinTradeDelay: u64 = 0; // #2844: 0 in mocks pub const InitialKeySwapOnSubnetCost: u64 = 10_000_000; pub const HotkeySwapOnSubnetInterval: u64 = 15; // 15 block, should be bigger than subnet number, then trigger clean up for all subnets pub const MaxContributorsPerLeaseToRemove: u32 = 3; @@ -322,6 +323,7 @@ impl pallet_subtensor::Config for Test { type InitialTaoWeight = InitialTaoWeight; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; type InitialStartCallDelay = InitialStartCallDelay; + type InitialMinTradeDelay = InitialMinTradeDelay; type SwapInterface = pallet_subtensor_swap::Pallet; type KeySwapOnSubnetCost = InitialKeySwapOnSubnetCost; type HotkeySwapOnSubnetInterval = HotkeySwapOnSubnetInterval; diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index f4cb6c0534..41e860bbfa 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -2087,6 +2087,17 @@ pub mod pallet { Ok(()) } + /// Sets the number of blocks after subnet-start during which staking is blocked (#2844). + #[pallet::call_index(98)] + #[pallet::weight(Weight::from_parts(14_000_000, 0) + .saturating_add(::DbWeight::get().writes(1)))] + pub fn sudo_set_min_trade_delay(origin: OriginFor, delay: u64) -> DispatchResult { + ensure_root(origin)?; + pallet_subtensor::Pallet::::set_min_trade_delay(delay); + log::debug!("MinTradeDelay( delay: {delay:?} ) "); + Ok(()) + } + /// Sets the announcement delay for coldkey swap. #[pallet::call_index(86)] #[pallet::weight(::WeightInfo::sudo_set_coldkey_swap_announcement_delay())] diff --git a/pallets/admin-utils/src/tests/mock.rs b/pallets/admin-utils/src/tests/mock.rs index 06e4e611a1..ec7e2d1caf 100644 --- a/pallets/admin-utils/src/tests/mock.rs +++ b/pallets/admin-utils/src/tests/mock.rs @@ -158,6 +158,7 @@ parameter_types! { pub const InitialTaoWeight: u64 = u64::MAX/10; // 10% global weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks pub const InitialStartCallDelay: u64 = 0; // 0 days + pub const InitialMinTradeDelay: u64 = 0; // #2844: 0 in mocks pub const InitialKeySwapOnSubnetCost: TaoBalance = TaoBalance::new(10_000_000); pub const HotkeySwapOnSubnetInterval: u64 = 7 * 24 * 60 * 60 / 12; // 7 days pub const LeaseDividendsDistributionInterval: u32 = 100; // 100 blocks @@ -237,6 +238,7 @@ impl pallet_subtensor::Config for Test { type InitialTaoWeight = InitialTaoWeight; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; type InitialStartCallDelay = InitialStartCallDelay; + type InitialMinTradeDelay = InitialMinTradeDelay; type SwapInterface = Swap; type KeySwapOnSubnetCost = InitialKeySwapOnSubnetCost; type HotkeySwapOnSubnetInterval = HotkeySwapOnSubnetInterval; diff --git a/pallets/admin-utils/src/tests/mod.rs b/pallets/admin-utils/src/tests/mod.rs index a0bf21ed04..121cf0b0a5 100644 --- a/pallets/admin-utils/src/tests/mod.rs +++ b/pallets/admin-utils/src/tests/mod.rs @@ -3383,3 +3383,33 @@ fn test_sudo_set_start_call_delay_permissions_and_zero_delay() { ); }); } + +// #2844 — sudo can set the post-start trading delay; non-root cannot. +#[test] +fn test_sudo_set_min_trade_delay() { + new_test_ext().execute_with(|| { + let non_root = U256::from(1); + + // Mock default is 0. + assert_eq!(pallet_subtensor::MinTradeDelay::::get(), 0); + + // Non-root is rejected. + assert_noop!( + AdminUtils::sudo_set_min_trade_delay( + <::RuntimeOrigin>::signed(non_root), + 360 + ), + DispatchError::BadOrigin + ); + + // Root sets it, storage updates, event is emitted. + assert_ok!(AdminUtils::sudo_set_min_trade_delay( + <::RuntimeOrigin>::root(), + 360 + )); + assert_eq!(pallet_subtensor::MinTradeDelay::::get(), 360); + frame_system::Pallet::::assert_last_event(RuntimeEvent::SubtensorModule( + pallet_subtensor::Event::MinTradeDelaySet(360), + )); + }); +} diff --git a/pallets/subtensor/src/coinbase/root.rs b/pallets/subtensor/src/coinbase/root.rs index 7aeb0430b2..b0788b4b36 100644 --- a/pallets/subtensor/src/coinbase/root.rs +++ b/pallets/subtensor/src/coinbase/root.rs @@ -249,6 +249,13 @@ impl Pallet { StartCallDelay::::set(delay); Self::deposit_event(Event::StartCallDelaySet(delay)); } + pub fn get_min_trade_delay() -> u64 { + MinTradeDelay::::get() + } + pub fn set_min_trade_delay(delay: u64) { + MinTradeDelay::::set(delay); + Self::deposit_event(Event::MinTradeDelaySet(delay)); + } pub fn set_network_min_lock(net_min_lock: TaoBalance) { NetworkMinLockCost::::set(net_min_lock); Self::deposit_event(Event::NetworkMinLockCostSet(net_min_lock)); diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 0d9fa2743d..b7e63a1c19 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1760,6 +1760,13 @@ pub mod pallet { #[pallet::storage] pub type StartCallDelay = StorageValue<_, u64, ValueQuery, T::InitialStartCallDelay>; + /// ITEM( min_trade_delay ) + /// Blocks after subnet-start (`start_call`) during which staking in/out is blocked, so a + /// subnet owner cannot bundle `add_stake` into the `start_call` block and self-snipe the + /// launch. See opentensor/subtensor#2844. + #[pallet::storage] + pub type MinTradeDelay = StorageValue<_, u64, ValueQuery, T::InitialMinTradeDelay>; + /// ITEM( min_network_lock_cost ) #[pallet::storage] pub type NetworkMinLockCost = @@ -2822,12 +2829,27 @@ pub mod pallet { true } - /// Ensure subtoken enalbed + /// Ensure the subtoken is enabled AND the post-start trading delay has elapsed. + /// + /// Staking (in or out) is blocked until `MinTradeDelay` blocks after the subnet-start + /// block, so a subnet owner cannot bundle `add_stake` into the same block as `start_call` + /// and self-snipe the launch (opentensor/subtensor#2844). The subnet-start block is + /// `FirstEmissionBlockNumber - 1` (both set atomically by `do_start_call`). Subnets whose + /// subtoken was enabled without a start block (e.g. root / genesis paths where + /// `FirstEmissionBlockNumber` is unset) are unaffected. pub fn ensure_subtoken_enabled(subnet: NetUid) -> Result<(), Error> { ensure!( SubtokenEnabled::::get(subnet), Error::::SubtokenDisabled ); + if let Some(first_emission_block) = FirstEmissionBlockNumber::::get(subnet) { + let start_call_block = first_emission_block.saturating_sub(1); + let trading_open_block = start_call_block.saturating_add(MinTradeDelay::::get()); + ensure!( + Self::get_current_block_as_u64() >= trading_open_block, + Error::::TradingNotOpenYet + ); + } Ok(()) } } diff --git a/pallets/subtensor/src/macros/config.rs b/pallets/subtensor/src/macros/config.rs index 0e5d660b2d..82a8226dc8 100644 --- a/pallets/subtensor/src/macros/config.rs +++ b/pallets/subtensor/src/macros/config.rs @@ -264,6 +264,9 @@ mod config { /// Delay after which a new subnet can dispatch start call extrinsic. #[pallet::constant] type InitialStartCallDelay: Get; + /// Initial blocks after subnet-start during which staking is blocked (#2844). + #[pallet::constant] + type InitialMinTradeDelay: Get; /// Cost of swapping a hotkey in a subnet. #[pallet::constant] type KeySwapOnSubnetCost: Get; diff --git a/pallets/subtensor/src/macros/errors.rs b/pallets/subtensor/src/macros/errors.rs index 33f8d5e5eb..5248495991 100644 --- a/pallets/subtensor/src/macros/errors.rs +++ b/pallets/subtensor/src/macros/errors.rs @@ -332,5 +332,7 @@ mod errors { StartCallNotReady, /// The caller does not have enough Alpha stake for the operation. InsufficientAlphaBalance, + /// Trading is not open yet: the post-start trading delay (MinTradeDelay) has not elapsed. + TradingNotOpenYet, } } diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index c8a0e613e4..d8a70bafe4 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -154,6 +154,8 @@ mod events { NetworkImmunityPeriodSet(u64), /// the start call delay is set. StartCallDelaySet(u64), + /// the minimum post-start trading delay is set. + MinTradeDelaySet(u64), /// the network minimum locking cost is set. NetworkMinLockCostSet(TaoBalance), /// the maximum number of subnets is set diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index 8ff0e47a4d..5dc50f1633 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -1273,15 +1273,12 @@ impl Pallet { ); } - ensure!( - SubtokenEnabled::::get(origin_netuid), - Error::::SubtokenDisabled - ); - - ensure!( - SubtokenEnabled::::get(destination_netuid), - Error::::SubtokenDisabled - ); + // Route through ensure_subtoken_enabled so the post-start trading delay (#2844) also + // gates swap/move/transfer. Otherwise an owner could pre-stake into an always-open + // subnet (e.g. root) and bundle batch_all[start_call, swap_stake(root -> new)] to + // acquire alpha in the new subnet at launch, bypassing the add_stake gate. + Self::ensure_subtoken_enabled(origin_netuid)?; + Self::ensure_subtoken_enabled(destination_netuid)?; // Ensure that the origin hotkey account exists ensure!( diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index 8f0f7b5e04..d65a255cee 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -294,6 +294,7 @@ parameter_types! { pub const InitialTaoWeight: u64 = 0; // 0% TAO weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks pub const InitialStartCallDelay: u64 = 0; // 0 days + pub const InitialMinTradeDelay: u64 = 0; // #2844: 0 in mocks pub const InitialKeySwapOnSubnetCost: u64 = 10_000_000; pub const HotkeySwapOnSubnetInterval: u64 = 15; // 15 block, should be bigger than subnet number, then trigger clean up for all subnets pub const MaxContributorsPerLeaseToRemove: u32 = 3; @@ -373,6 +374,7 @@ impl crate::Config for Test { type InitialTaoWeight = InitialTaoWeight; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; type InitialStartCallDelay = InitialStartCallDelay; + type InitialMinTradeDelay = InitialMinTradeDelay; type SwapInterface = pallet_subtensor_swap::Pallet; type KeySwapOnSubnetCost = InitialKeySwapOnSubnetCost; type HotkeySwapOnSubnetInterval = HotkeySwapOnSubnetInterval; diff --git a/pallets/subtensor/src/tests/mock_high_ed.rs b/pallets/subtensor/src/tests/mock_high_ed.rs index 94c7d2ad26..ee894a0e83 100644 --- a/pallets/subtensor/src/tests/mock_high_ed.rs +++ b/pallets/subtensor/src/tests/mock_high_ed.rs @@ -216,6 +216,7 @@ parameter_types! { pub const InitialTaoWeight: u64 = 0; // 0% TAO weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks pub const InitialStartCallDelay: u64 = 0; // 0 days + pub const InitialMinTradeDelay: u64 = 0; // #2844: 0 in mocks pub const InitialKeySwapOnSubnetCost: u64 = 10_000_000; pub const HotkeySwapOnSubnetInterval: u64 = 15; // 15 block, should be bigger than subnet number, then trigger clean up for all subnets pub const MaxContributorsPerLeaseToRemove: u32 = 3; @@ -295,6 +296,7 @@ impl crate::Config for Test { type InitialTaoWeight = InitialTaoWeight; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; type InitialStartCallDelay = InitialStartCallDelay; + type InitialMinTradeDelay = InitialMinTradeDelay; type SwapInterface = pallet_subtensor_swap::Pallet; type KeySwapOnSubnetCost = InitialKeySwapOnSubnetCost; type HotkeySwapOnSubnetInterval = HotkeySwapOnSubnetInterval; diff --git a/pallets/subtensor/src/tests/subnet.rs b/pallets/subtensor/src/tests/subnet.rs index be1487b4ec..28e5f6c8e2 100644 --- a/pallets/subtensor/src/tests/subnet.rs +++ b/pallets/subtensor/src/tests/subnet.rs @@ -989,3 +989,81 @@ fn test_register_network_gives_owner_no_initial_alpha_distribution() { ); }); } + +/*************************** + #2844 — post-start trading delay (MinTradeDelay) +*****************************/ + +// A non-zero MinTradeDelay must block staking (including an owner's same-block bundle) until +// `start_call_block + MinTradeDelay`, then open it. This is the fix for the SN99 self-snipe. +#[test] +fn test_min_trade_delay_blocks_stake_until_window_elapses() { + new_test_ext(0).execute_with(|| { + let netuid = NetUid::from(1); + let tempo: u16 = 13; + let coldkey = U256::from(0); // default subnet owner + let hotkey = U256::from(1); + let delay: u64 = 10; + + add_network_without_emission_block(netuid, tempo, 0); + mock::setup_reserves(netuid, 1_000_000_000.into(), 1_000_000_000.into()); + register_ok_neuron(netuid, hotkey, coldkey, 0); + add_balance_to_coldkey_account(&coldkey, 10_000.into()); + + SubtensorModule::set_min_trade_delay(delay); + assert_eq!(MinTradeDelay::::get(), delay); + + // Owner starts the subnet at block B: FirstEmissionBlockNumber = B+1, start block = B. + let start_block = System::block_number(); + assert_ok!(SubtensorModule::start_call( + RuntimeOrigin::signed(coldkey), + netuid + )); + assert!(SubtokenEnabled::::get(netuid)); + + // Same block as start: the owner's bundled add_stake is rejected. + assert_noop!( + SubtensorModule::add_stake( + RuntimeOrigin::signed(coldkey), + hotkey, + netuid, + 1_000_000.into() + ), + Error::::TradingNotOpenYet + ); + + // The gate stays closed for the whole window and opens exactly at start + delay. + assert!(matches!( + SubtensorModule::ensure_subtoken_enabled(netuid), + Err(Error::::TradingNotOpenYet) + )); + System::set_block_number(start_block + delay - 1); + assert!(matches!( + SubtensorModule::ensure_subtoken_enabled(netuid), + Err(Error::::TradingNotOpenYet) + )); + System::set_block_number(start_block + delay); + assert!(SubtensorModule::ensure_subtoken_enabled(netuid).is_ok()); + }); +} + +// A zero MinTradeDelay preserves today's behaviour: trading is open in the start block. +#[test] +fn test_min_trade_delay_zero_preserves_immediate_trading() { + new_test_ext(0).execute_with(|| { + let netuid = NetUid::from(1); + let tempo: u16 = 13; + let coldkey = U256::from(0); + + add_network_without_emission_block(netuid, tempo, 0); + mock::setup_reserves(netuid, 1_000_000_000.into(), 1_000_000_000.into()); + + SubtensorModule::set_min_trade_delay(0); + assert_ok!(SubtensorModule::start_call( + RuntimeOrigin::signed(coldkey), + netuid + )); + // delay = 0 -> trading is open in the start block, exactly as today. + assert!(SubtensorModule::ensure_subtoken_enabled(netuid).is_ok()); + }); +} diff --git a/pallets/transaction-fee/src/tests/mock.rs b/pallets/transaction-fee/src/tests/mock.rs index 98ff259495..04fa5c597c 100644 --- a/pallets/transaction-fee/src/tests/mock.rs +++ b/pallets/transaction-fee/src/tests/mock.rs @@ -228,6 +228,7 @@ parameter_types! { pub const InitialTaoWeight: u64 = u64::MAX/10; // 10% global weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks pub const InitialStartCallDelay: u64 = 0; // 0 days + pub const InitialMinTradeDelay: u64 = 0; // #2844: 0 in mocks pub const InitialKeySwapOnSubnetCost: TaoBalance = TaoBalance::new(10_000_000); pub const HotkeySwapOnSubnetInterval: u64 = 7 * 24 * 60 * 60 / 12; // 7 days pub const LeaseDividendsDistributionInterval: u32 = 100; // 100 blocks @@ -307,6 +308,7 @@ impl pallet_subtensor::Config for Test { type InitialTaoWeight = InitialTaoWeight; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; type InitialStartCallDelay = InitialStartCallDelay; + type InitialMinTradeDelay = InitialMinTradeDelay; type SwapInterface = Swap; type KeySwapOnSubnetCost = InitialKeySwapOnSubnetCost; type HotkeySwapOnSubnetInterval = HotkeySwapOnSubnetInterval; diff --git a/precompiles/src/mock.rs b/precompiles/src/mock.rs index e256761b2a..10a87fe641 100644 --- a/precompiles/src/mock.rs +++ b/precompiles/src/mock.rs @@ -150,6 +150,7 @@ parameter_types! { pub const InitialTaoWeight: u64 = u64::MAX / 10; pub const InitialEmaPriceHalvingPeriod: u64 = 201_600; pub const InitialStartCallDelay: u64 = 0; + pub const InitialMinTradeDelay: u64 = 0; // #2844: 0 in mocks pub const InitialKeySwapOnSubnetCost: TaoBalance = TaoBalance::new(10_000_000); pub const HotkeySwapOnSubnetInterval: u64 = 50_400; pub const LeaseDividendsDistributionInterval: u32 = 100; @@ -484,6 +485,7 @@ impl pallet_subtensor::Config for Runtime { type InitialTaoWeight = InitialTaoWeight; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; type InitialStartCallDelay = InitialStartCallDelay; + type InitialMinTradeDelay = InitialMinTradeDelay; type SwapInterface = Swap; type KeySwapOnSubnetCost = InitialKeySwapOnSubnetCost; type HotkeySwapOnSubnetInterval = HotkeySwapOnSubnetInterval; diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 9eb98732e4..9150201571 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -235,7 +235,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: 431, + spec_version: 432, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, @@ -848,6 +848,9 @@ parameter_types! { pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks // 0 days pub const InitialStartCallDelay: u64 = 0; + // #2844: block staking for a predictable window after subnet-start (prod ~72 min; 0 on + // fast-blocks). The value is the policy knob — sudo-tunable via sudo_set_min_trade_delay. + pub const InitialMinTradeDelay: u64 = prod_or_fast!(360, 0); pub const SubtensorInitialKeySwapOnSubnetCost: TaoBalance = TaoBalance::new(1_000_000); // 0.001 TAO pub const HotkeySwapOnSubnetInterval : BlockNumber = prod_or_fast!(24 * 60 * 60 / 12, 1); // 1 day pub const LeaseDividendsDistributionInterval: BlockNumber = 100; // 100 blocks @@ -926,6 +929,7 @@ impl pallet_subtensor::Config for Runtime { type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; type InitialStartCallDelay = InitialStartCallDelay; + type InitialMinTradeDelay = InitialMinTradeDelay; type SwapInterface = Swap; type KeySwapOnSubnetCost = SubtensorInitialKeySwapOnSubnetCost; type HotkeySwapOnSubnetInterval = HotkeySwapOnSubnetInterval; diff --git a/runtime/src/proxy_filters/call_groups.rs b/runtime/src/proxy_filters/call_groups.rs index b23fc2d5b9..eb4b1848cb 100644 --- a/runtime/src/proxy_filters/call_groups.rs +++ b/runtime/src/proxy_filters/call_groups.rs @@ -578,6 +578,7 @@ call_filter_group!( RuntimeCall::AdminUtils(AdminUtilsCall::sudo_set_net_tao_flow_enabled), RuntimeCall::AdminUtils(AdminUtilsCall::sudo_set_max_mechanism_count), RuntimeCall::AdminUtils(AdminUtilsCall::sudo_set_start_call_delay), + RuntimeCall::AdminUtils(AdminUtilsCall::sudo_set_min_trade_delay), RuntimeCall::AdminUtils(AdminUtilsCall::sudo_set_coldkey_swap_announcement_delay), RuntimeCall::AdminUtils(AdminUtilsCall::sudo_set_coldkey_swap_reannouncement_delay), RuntimeCall::AdminUtils(AdminUtilsCall::sudo_set_subnet_emission_enabled),