diff --git a/Cargo.lock b/Cargo.lock index a9b72f109e..c2650935ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8393,6 +8393,7 @@ dependencies = [ "hex", "log", "pallet-admin-utils", + "pallet-alpha-assets", "pallet-aura", "pallet-authority-discovery", "pallet-authorship", @@ -8842,6 +8843,7 @@ dependencies = [ "frame-support", "frame-system", "log", + "pallet-alpha-assets", "pallet-balances", "pallet-crowdloan", "pallet-drand", @@ -8887,6 +8889,22 @@ dependencies = [ "sp-runtime", ] +[[package]] +name = "pallet-alpha-assets" +version = "0.1.0" +dependencies = [ + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "subtensor-macros", + "subtensor-runtime-common", +] + [[package]] name = "pallet-asset-conversion" version = "23.0.0" @@ -10817,6 +10835,7 @@ dependencies = [ "log", "ndarray", "num-traits", + "pallet-alpha-assets", "pallet-aura", "pallet-balances", "pallet-commitments", @@ -18156,6 +18175,7 @@ dependencies = [ "frame-system", "log", "num_enum", + "pallet-alpha-assets", "pallet-balances", "pallet-contracts", "pallet-crowdloan", @@ -18199,6 +18219,7 @@ dependencies = [ "parity-scale-codec", "sp-api", "sp-runtime", + "substrate-fixed", "subtensor-runtime-common", ] @@ -18232,10 +18253,12 @@ dependencies = [ "frame-system", "log", "pallet-admin-utils", + "pallet-alpha-assets", "pallet-balances", "pallet-crowdloan", "pallet-drand", "pallet-evm", + "pallet-evm-chain-id", "pallet-evm-precompile-bn128", "pallet-evm-precompile-dispatch", "pallet-evm-precompile-modexp", @@ -18312,6 +18335,7 @@ dependencies = [ "frame-support", "frame-system", "log", + "pallet-alpha-assets", "pallet-balances", "pallet-crowdloan", "pallet-drand", diff --git a/Cargo.toml b/Cargo.toml index a55a874ef0..14ded6a4f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,6 +54,7 @@ unwrap-used = "deny" useless_conversion = "allow" # until polkadot is patched [workspace.dependencies] +pallet-alpha-assets = { path = "pallets/alpha-assets", default-features = false } node-subtensor-runtime = { path = "runtime", default-features = false } pallet-admin-utils = { path = "pallets/admin-utils", default-features = false } pallet-commitments = { path = "pallets/commitments", default-features = false } @@ -318,4 +319,3 @@ pow-faucet = [] [patch.crates-io] w3f-bls = { git = "https://github.com/opentensor/bls", branch = "fix-no-std" } - diff --git a/chain-extensions/Cargo.toml b/chain-extensions/Cargo.toml index e7445e5fb2..ecc30878b5 100644 --- a/chain-extensions/Cargo.toml +++ b/chain-extensions/Cargo.toml @@ -24,6 +24,7 @@ subtensor-runtime-common.workspace = true pallet-contracts.workspace = true pallet-subtensor.workspace = true pallet-subtensor-swap.workspace = true +pallet-alpha-assets.workspace = true pallet-balances.workspace = true pallet-scheduler.workspace = true pallet-preimage.workspace = true @@ -52,6 +53,7 @@ std = [ "codec/std", "scale-info/std", "subtensor-runtime-common/std", + "pallet-alpha-assets/std", "pallet-contracts/std", "pallet-subtensor/std", "pallet-subtensor-swap/std", diff --git a/chain-extensions/src/lib.rs b/chain-extensions/src/lib.rs index 14ea23d9c8..1aa1a2f966 100644 --- a/chain-extensions/src/lib.rs +++ b/chain-extensions/src/lib.rs @@ -14,6 +14,7 @@ use frame_system::RawOrigin; use pallet_contracts::chain_extension::{ BufInBufOutState, ChainExtension, Environment, Ext, InitState, RetVal, SysConfig, }; +use pallet_subtensor::weights::WeightInfo as SubtensorWeightInfo; use pallet_subtensor_proxy as pallet_proxy; use pallet_subtensor_proxy::WeightInfo; use sp_runtime::{DispatchError, Weight, traits::StaticLookup}; @@ -61,478 +62,821 @@ where + pallet_subtensor_swap::Config, T::AccountId: Clone, { - fn dispatch(env: &mut Env) -> Result + fn dispatch_add_stake_v1( + env: &mut Env, + origin: RawOrigin, + ) -> Result where - Env: SubtensorExtensionEnv, + Env: SubtensorExtensionEnv, <::Lookup as StaticLookup>::Source: From<::AccountId>, { - let func_id: FunctionId = env.func_id().try_into().map_err(|_| { - DispatchError::Other( - "Invalid function id - does not correspond to any registered function", - ) - })?; + let (hotkey, netuid, amount_staked): (T::AccountId, NetUid, TaoBalance) = env + .read_as() + .map_err(|_| DispatchError::Other("Failed to decode input parameters"))?; - match func_id { - FunctionId::GetStakeInfoForHotkeyColdkeyNetuidV1 => { - let (hotkey, coldkey, netuid): (T::AccountId, T::AccountId, NetUid) = env - .read_as() - .map_err(|_| DispatchError::Other("Failed to decode input parameters"))?; + let weight = + <::WeightInfo as SubtensorWeightInfo>::add_stake(); - let stake_info = - pallet_subtensor::Pallet::::get_stake_info_for_hotkey_coldkey_netuid( - hotkey, coldkey, netuid, - ); + env.charge_weight(weight)?; - let encoded_result = stake_info.encode(); + let call_result = + pallet_subtensor::Pallet::::add_stake(origin.into(), hotkey, netuid, amount_staked); - env.write_output(&encoded_result) - .map_err(|_| DispatchError::Other("Failed to write output"))?; + match call_result { + Ok(_) => Ok(RetVal::Converging(Output::Success as u32)), + Err(e) => { + let error_code = Output::from(e) as u32; + Ok(RetVal::Converging(error_code)) + } + } + } - Ok(RetVal::Converging(Output::Success as u32)) + fn dispatch_remove_stake_v1( + env: &mut Env, + origin: RawOrigin, + ) -> Result + where + Env: SubtensorExtensionEnv, + <::Lookup as StaticLookup>::Source: From<::AccountId>, + { + let (hotkey, netuid, amount_unstaked): (T::AccountId, NetUid, AlphaBalance) = env + .read_as() + .map_err(|_| DispatchError::Other("Failed to decode input parameters"))?; + + // weight for remove_stake is not defined in the Subtensor pallet's WeightInfo + let weight = + <::WeightInfo as SubtensorWeightInfo>::remove_stake(); + + env.charge_weight(weight)?; + + let call_result = pallet_subtensor::Pallet::::remove_stake( + origin.into(), + hotkey, + netuid, + amount_unstaked, + ); + + match call_result { + Ok(_) => Ok(RetVal::Converging(Output::Success as u32)), + Err(e) => { + let error_code = Output::from(e) as u32; + Ok(RetVal::Converging(error_code)) } - FunctionId::AddStakeV1 => { - let weight = Weight::from_parts(340_800_000, 0) - .saturating_add(T::DbWeight::get().reads(24_u64)) - .saturating_add(T::DbWeight::get().writes(15)); + } + } - env.charge_weight(weight)?; + fn dispatch_unstake_all_v1( + env: &mut Env, + origin: RawOrigin, + ) -> Result + where + Env: SubtensorExtensionEnv, + <::Lookup as StaticLookup>::Source: From<::AccountId>, + { + let hotkey: T::AccountId = env + .read_as() + .map_err(|_| DispatchError::Other("Failed to decode input parameters"))?; - let (hotkey, netuid, amount_staked): (T::AccountId, NetUid, TaoBalance) = env - .read_as() - .map_err(|_| DispatchError::Other("Failed to decode input parameters"))?; + let weight = + <::WeightInfo as SubtensorWeightInfo>::unstake_all(); - let call_result = pallet_subtensor::Pallet::::add_stake( - RawOrigin::Signed(env.caller()).into(), - hotkey, - netuid, - amount_staked, - ); + env.charge_weight(weight)?; - match call_result { - Ok(_) => Ok(RetVal::Converging(Output::Success as u32)), - Err(e) => { - let error_code = Output::from(e) as u32; - Ok(RetVal::Converging(error_code)) - } - } - } - FunctionId::RemoveStakeV1 => { - let weight = Weight::from_parts(196_800_000, 0) - .saturating_add(T::DbWeight::get().reads(19)) - .saturating_add(T::DbWeight::get().writes(10)); + let call_result = pallet_subtensor::Pallet::::unstake_all(origin.into(), hotkey); - env.charge_weight(weight)?; + match call_result { + Ok(_) => Ok(RetVal::Converging(Output::Success as u32)), + Err(e) => { + let error_code = Output::from(e) as u32; + Ok(RetVal::Converging(error_code)) + } + } + } - let (hotkey, netuid, amount_unstaked): (T::AccountId, NetUid, AlphaBalance) = env - .read_as() - .map_err(|_| DispatchError::Other("Failed to decode input parameters"))?; + fn dispatch_unstake_all_alpha_v1( + env: &mut Env, + origin: RawOrigin, + ) -> Result + where + Env: SubtensorExtensionEnv, + <::Lookup as StaticLookup>::Source: From<::AccountId>, + { + let hotkey: T::AccountId = env + .read_as() + .map_err(|_| DispatchError::Other("Failed to decode input parameters"))?; - let call_result = pallet_subtensor::Pallet::::remove_stake( - RawOrigin::Signed(env.caller()).into(), - hotkey, - netuid, - amount_unstaked, - ); + let weight = + <::WeightInfo as SubtensorWeightInfo>::unstake_all_alpha( + ); - match call_result { - Ok(_) => Ok(RetVal::Converging(Output::Success as u32)), - Err(e) => { - let error_code = Output::from(e) as u32; - Ok(RetVal::Converging(error_code)) - } - } - } - FunctionId::UnstakeAllV1 => { - let weight = Weight::from_parts(28_830_000, 0) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(0)); + env.charge_weight(weight)?; - env.charge_weight(weight)?; + let call_result = pallet_subtensor::Pallet::::unstake_all_alpha(origin.into(), hotkey); - let hotkey: T::AccountId = env - .read_as() - .map_err(|_| DispatchError::Other("Failed to decode input parameters"))?; + match call_result { + Ok(_) => Ok(RetVal::Converging(Output::Success as u32)), + Err(e) => { + let error_code = Output::from(e) as u32; + Ok(RetVal::Converging(error_code)) + } + } + } - let call_result = pallet_subtensor::Pallet::::unstake_all( - RawOrigin::Signed(env.caller()).into(), - hotkey, - ); + fn dispatch_move_stake_v1( + env: &mut Env, + origin: RawOrigin, + ) -> Result + where + Env: SubtensorExtensionEnv, + <::Lookup as StaticLookup>::Source: From<::AccountId>, + { + let (origin_hotkey, destination_hotkey, origin_netuid, destination_netuid, alpha_amount): ( + T::AccountId, + T::AccountId, + NetUid, + NetUid, + AlphaBalance, + ) = env + .read_as() + .map_err(|_| DispatchError::Other("Failed to decode input parameters"))?; + + let weight = + <::WeightInfo as SubtensorWeightInfo>::move_stake(); + + env.charge_weight(weight)?; + + let call_result = pallet_subtensor::Pallet::::move_stake( + origin.into(), + origin_hotkey, + destination_hotkey, + origin_netuid, + destination_netuid, + alpha_amount, + ); + + match call_result { + Ok(_) => Ok(RetVal::Converging(Output::Success as u32)), + Err(e) => { + let error_code = Output::from(e) as u32; + Ok(RetVal::Converging(error_code)) + } + } + } - match call_result { - Ok(_) => Ok(RetVal::Converging(Output::Success as u32)), - Err(e) => { - let error_code = Output::from(e) as u32; - Ok(RetVal::Converging(error_code)) - } - } + fn dispatch_transfer_stake_v1( + env: &mut Env, + origin: RawOrigin, + ) -> Result + where + Env: SubtensorExtensionEnv, + <::Lookup as StaticLookup>::Source: From<::AccountId>, + { + let (destination_coldkey, hotkey, origin_netuid, destination_netuid, alpha_amount): ( + T::AccountId, + T::AccountId, + NetUid, + NetUid, + AlphaBalance, + ) = env + .read_as() + .map_err(|_| DispatchError::Other("Failed to decode input parameters"))?; + + let weight = + <::WeightInfo as SubtensorWeightInfo>::transfer_stake(); + + env.charge_weight(weight)?; + + let call_result = pallet_subtensor::Pallet::::transfer_stake( + origin.into(), + destination_coldkey, + hotkey, + origin_netuid, + destination_netuid, + alpha_amount, + ); + + match call_result { + Ok(_) => Ok(RetVal::Converging(Output::Success as u32)), + Err(e) => { + let error_code = Output::from(e) as u32; + Ok(RetVal::Converging(error_code)) } - FunctionId::UnstakeAllAlphaV1 => { - let weight = Weight::from_parts(358_500_000, 0) - .saturating_add(T::DbWeight::get().reads(36_u64)) - .saturating_add(T::DbWeight::get().writes(21_u64)); + } + } - env.charge_weight(weight)?; + fn dispatch_swap_stake_v1( + env: &mut Env, + origin: RawOrigin, + ) -> Result + where + Env: SubtensorExtensionEnv, + <::Lookup as StaticLookup>::Source: From<::AccountId>, + { + let (hotkey, origin_netuid, destination_netuid, alpha_amount): ( + T::AccountId, + NetUid, + NetUid, + AlphaBalance, + ) = env + .read_as() + .map_err(|_| DispatchError::Other("Failed to decode input parameters"))?; + + let weight = + <::WeightInfo as SubtensorWeightInfo>::swap_stake(); + + env.charge_weight(weight)?; + + let call_result = pallet_subtensor::Pallet::::swap_stake( + origin.into(), + hotkey, + origin_netuid, + destination_netuid, + alpha_amount, + ); + + match call_result { + Ok(_) => Ok(RetVal::Converging(Output::Success as u32)), + Err(e) => { + let error_code = Output::from(e) as u32; + Ok(RetVal::Converging(error_code)) + } + } + } - let hotkey: T::AccountId = env - .read_as() - .map_err(|_| DispatchError::Other("Failed to decode input parameters"))?; + fn dispatch_add_stake_limit_v1( + env: &mut Env, + origin: RawOrigin, + ) -> Result + where + Env: SubtensorExtensionEnv, + <::Lookup as StaticLookup>::Source: From<::AccountId>, + { + let (hotkey, netuid, amount_staked, limit_price, allow_partial): ( + T::AccountId, + NetUid, + TaoBalance, + TaoBalance, + bool, + ) = env + .read_as() + .map_err(|_| DispatchError::Other("Failed to decode input parameters"))?; + + let weight = + <::WeightInfo as SubtensorWeightInfo>::add_stake_limit(); + + env.charge_weight(weight)?; + + let call_result = pallet_subtensor::Pallet::::add_stake_limit( + origin.into(), + hotkey, + netuid, + amount_staked, + limit_price, + allow_partial, + ); + + match call_result { + Ok(_) => Ok(RetVal::Converging(Output::Success as u32)), + Err(e) => { + let error_code = Output::from(e) as u32; + Ok(RetVal::Converging(error_code)) + } + } + } - let call_result = pallet_subtensor::Pallet::::unstake_all_alpha( - RawOrigin::Signed(env.caller()).into(), - hotkey, - ); + fn dispatch_remove_stake_limit_v1( + env: &mut Env, + origin: RawOrigin, + ) -> Result + where + Env: SubtensorExtensionEnv, + <::Lookup as StaticLookup>::Source: From<::AccountId>, + { + let (hotkey, netuid, amount_unstaked, limit_price, allow_partial): ( + T::AccountId, + NetUid, + AlphaBalance, + TaoBalance, + bool, + ) = env + .read_as() + .map_err(|_| DispatchError::Other("Failed to decode input parameters"))?; + + let weight = + <::WeightInfo as SubtensorWeightInfo>::remove_stake_limit(); + + env.charge_weight(weight)?; + + let call_result = pallet_subtensor::Pallet::::remove_stake_limit( + origin.into(), + hotkey, + netuid, + amount_unstaked, + limit_price, + allow_partial, + ); + + match call_result { + Ok(_) => Ok(RetVal::Converging(Output::Success as u32)), + Err(e) => { + let error_code = Output::from(e) as u32; + Ok(RetVal::Converging(error_code)) + } + } + } - match call_result { - Ok(_) => Ok(RetVal::Converging(Output::Success as u32)), - Err(e) => { - let error_code = Output::from(e) as u32; - Ok(RetVal::Converging(error_code)) - } - } + fn dispatch_swap_stake_limit_v1( + env: &mut Env, + origin: RawOrigin, + ) -> Result + where + Env: SubtensorExtensionEnv, + <::Lookup as StaticLookup>::Source: From<::AccountId>, + { + let ( + hotkey, + origin_netuid, + destination_netuid, + alpha_amount, + limit_price, + allow_partial, + ): (T::AccountId, NetUid, NetUid, AlphaBalance, TaoBalance, bool) = + env.read_as() + .map_err(|_| DispatchError::Other("Failed to decode input parameters"))?; + + let weight = + <::WeightInfo as SubtensorWeightInfo>::swap_stake_limit( + ); + + env.charge_weight(weight)?; + + let call_result = pallet_subtensor::Pallet::::swap_stake_limit( + origin.into(), + hotkey, + origin_netuid, + destination_netuid, + alpha_amount, + limit_price, + allow_partial, + ); + + match call_result { + Ok(_) => Ok(RetVal::Converging(Output::Success as u32)), + Err(e) => { + let error_code = Output::from(e) as u32; + Ok(RetVal::Converging(error_code)) } - FunctionId::MoveStakeV1 => { - let weight = Weight::from_parts(164_300_000, 0) - .saturating_add(T::DbWeight::get().reads(15_u64)) - .saturating_add(T::DbWeight::get().writes(7_u64)); + } + } - env.charge_weight(weight)?; + fn dispatch_remove_stake_full_limit_v1( + env: &mut Env, + origin: RawOrigin, + ) -> Result + where + Env: SubtensorExtensionEnv, + <::Lookup as StaticLookup>::Source: From<::AccountId>, + { + let (hotkey, netuid, limit_price): (T::AccountId, NetUid, Option) = env + .read_as() + .map_err(|_| DispatchError::Other("Failed to decode input parameters"))?; + + let weight = <::WeightInfo as SubtensorWeightInfo>::remove_stake_full_limit(); + + env.charge_weight(weight)?; + + let call_result = pallet_subtensor::Pallet::::remove_stake_full_limit( + origin.into(), + hotkey, + netuid, + limit_price, + ); + + match call_result { + Ok(_) => Ok(RetVal::Converging(Output::Success as u32)), + Err(e) => { + let error_code = Output::from(e) as u32; + Ok(RetVal::Converging(error_code)) + } + } + } - let ( - origin_hotkey, - destination_hotkey, - origin_netuid, - destination_netuid, - alpha_amount, - ): (T::AccountId, T::AccountId, NetUid, NetUid, AlphaBalance) = env - .read_as() - .map_err(|_| DispatchError::Other("Failed to decode input parameters"))?; + fn dispatch_set_coldkey_auto_stake_hotkey_v1( + env: &mut Env, + origin: RawOrigin, + ) -> Result + where + Env: SubtensorExtensionEnv, + <::Lookup as StaticLookup>::Source: From<::AccountId>, + { + let (netuid, hotkey): (NetUid, T::AccountId) = env + .read_as() + .map_err(|_| DispatchError::Other("Failed to decode input parameters"))?; - let call_result = pallet_subtensor::Pallet::::move_stake( - RawOrigin::Signed(env.caller()).into(), - origin_hotkey, - destination_hotkey, - origin_netuid, - destination_netuid, - alpha_amount, - ); + let weight = <::WeightInfo as SubtensorWeightInfo>::set_coldkey_auto_stake_hotkey(); - match call_result { - Ok(_) => Ok(RetVal::Converging(Output::Success as u32)), - Err(e) => { - let error_code = Output::from(e) as u32; - Ok(RetVal::Converging(error_code)) - } - } - } - FunctionId::TransferStakeV1 => { - let weight = Weight::from_parts(160_300_000, 0) - .saturating_add(T::DbWeight::get().reads(13_u64)) - .saturating_add(T::DbWeight::get().writes(6_u64)); + env.charge_weight(weight)?; - env.charge_weight(weight)?; + let call_result = pallet_subtensor::Pallet::::set_coldkey_auto_stake_hotkey( + origin.into(), + netuid, + hotkey, + ); - let (destination_coldkey, hotkey, origin_netuid, destination_netuid, alpha_amount): ( - T::AccountId, - T::AccountId, - NetUid, - NetUid, - AlphaBalance, - ) = env - .read_as() - .map_err(|_| DispatchError::Other("Failed to decode input parameters"))?; + match call_result { + Ok(_) => Ok(RetVal::Converging(Output::Success as u32)), + Err(e) => { + let error_code = Output::from(e) as u32; + Ok(RetVal::Converging(error_code)) + } + } + } - let call_result = pallet_subtensor::Pallet::::transfer_stake( - RawOrigin::Signed(env.caller()).into(), - destination_coldkey, - hotkey, - origin_netuid, - destination_netuid, - alpha_amount, - ); + fn dispatch_add_proxy_v1( + env: &mut Env, + origin: RawOrigin, + ) -> Result + where + Env: SubtensorExtensionEnv, + <::Lookup as StaticLookup>::Source: From<::AccountId>, + { + let delegate: T::AccountId = env + .read_as() + .map_err(|_| DispatchError::Other("Failed to decode input parameters"))?; + + let weight = ::WeightInfo::add_proxy( + ::MaxProxies::get(), + ); + + env.charge_weight(weight)?; + + let delegate_lookup = + <::Lookup as StaticLookup>::Source::from(delegate); + + let call_result = pallet_proxy::Pallet::::add_proxy( + origin.into(), + delegate_lookup, + ProxyType::Staking, + 0u32.into(), + ); + + match call_result { + Ok(_) => Ok(RetVal::Converging(Output::Success as u32)), + Err(e) => { + let error_code = Output::from(e) as u32; + Ok(RetVal::Converging(error_code)) + } + } + } - match call_result { - Ok(_) => Ok(RetVal::Converging(Output::Success as u32)), - Err(e) => { - let error_code = Output::from(e) as u32; - Ok(RetVal::Converging(error_code)) - } - } + fn dispatch_remove_proxy_v1( + env: &mut Env, + origin: RawOrigin, + ) -> Result + where + Env: SubtensorExtensionEnv, + <::Lookup as StaticLookup>::Source: From<::AccountId>, + { + let delegate: T::AccountId = env + .read_as() + .map_err(|_| DispatchError::Other("Failed to decode input parameters"))?; + + let weight = ::WeightInfo::remove_proxy( + ::MaxProxies::get(), + ); + + env.charge_weight(weight)?; + + let delegate_lookup = + <::Lookup as StaticLookup>::Source::from(delegate); + + let call_result = pallet_proxy::Pallet::::remove_proxy( + origin.into(), + delegate_lookup, + ProxyType::Staking, + 0u32.into(), + ); + + match call_result { + Ok(_) => Ok(RetVal::Converging(Output::Success as u32)), + Err(e) => { + let error_code = Output::from(e) as u32; + Ok(RetVal::Converging(error_code)) } - FunctionId::SwapStakeV1 => { - let weight = Weight::from_parts(351_300_000, 0) - .saturating_add(T::DbWeight::get().reads(35_u64)) - .saturating_add(T::DbWeight::get().writes(22_u64)); + } + } - env.charge_weight(weight)?; + fn dispatch(env: &mut Env) -> Result + where + Env: SubtensorExtensionEnv, + <::Lookup as StaticLookup>::Source: From<::AccountId>, + { + let func_id: FunctionId = env.func_id().try_into().map_err(|_| { + DispatchError::Other( + "Invalid function id - does not correspond to any registered function", + ) + })?; - let (hotkey, origin_netuid, destination_netuid, alpha_amount): ( - T::AccountId, - NetUid, - NetUid, - AlphaBalance, - ) = env + match func_id { + FunctionId::GetStakeInfoForHotkeyColdkeyNetuidV1 => { + let (hotkey, coldkey, netuid): (T::AccountId, T::AccountId, NetUid) = env .read_as() .map_err(|_| DispatchError::Other("Failed to decode input parameters"))?; - let call_result = pallet_subtensor::Pallet::::swap_stake( - RawOrigin::Signed(env.caller()).into(), - hotkey, - origin_netuid, - destination_netuid, - alpha_amount, - ); + let stake_info = + pallet_subtensor::Pallet::::get_stake_info_for_hotkey_coldkey_netuid( + hotkey, coldkey, netuid, + ); - match call_result { - Ok(_) => Ok(RetVal::Converging(Output::Success as u32)), - Err(e) => { - let error_code = Output::from(e) as u32; - Ok(RetVal::Converging(error_code)) - } - } - } - FunctionId::AddStakeLimitV1 => { - let weight = Weight::from_parts(402_900_000, 0) - .saturating_add(T::DbWeight::get().reads(24_u64)) - .saturating_add(T::DbWeight::get().writes(15)); + let encoded_result = stake_info.encode(); - env.charge_weight(weight)?; + env.write_output(&encoded_result) + .map_err(|_| DispatchError::Other("Failed to write output"))?; - let (hotkey, netuid, amount_staked, limit_price, allow_partial): ( - T::AccountId, - NetUid, - TaoBalance, - TaoBalance, - bool, - ) = env - .read_as() - .map_err(|_| DispatchError::Other("Failed to decode input parameters"))?; + Ok(RetVal::Converging(Output::Success as u32)) + } + FunctionId::AddStakeV1 => { + let origin = RawOrigin::Signed(env.caller()); + Self::dispatch_add_stake_v1(env, origin) + } - let call_result = pallet_subtensor::Pallet::::add_stake_limit( - RawOrigin::Signed(env.caller()).into(), - hotkey, - netuid, - amount_staked, - limit_price, - allow_partial, - ); + FunctionId::CallerAddStakeV1 => { + let origin = convert_origin(env.origin()); + Self::dispatch_add_stake_v1(env, origin) + } - match call_result { - Ok(_) => Ok(RetVal::Converging(Output::Success as u32)), - Err(e) => { - let error_code = Output::from(e) as u32; - Ok(RetVal::Converging(error_code)) - } - } + FunctionId::RemoveStakeV1 => { + let origin = RawOrigin::Signed(env.caller()); + Self::dispatch_remove_stake_v1(env, origin) + } + FunctionId::CallerRemoveStakeV1 => { + let origin = convert_origin(env.origin()); + Self::dispatch_remove_stake_v1(env, origin) + } + FunctionId::UnstakeAllV1 => { + let origin = RawOrigin::Signed(env.caller()); + Self::dispatch_unstake_all_v1(env, origin) + } + FunctionId::CallerUnstakeAllV1 => { + let origin = convert_origin(env.origin()); + Self::dispatch_unstake_all_v1(env, origin) + } + FunctionId::UnstakeAllAlphaV1 => { + let origin = RawOrigin::Signed(env.caller()); + Self::dispatch_unstake_all_alpha_v1(env, origin) + } + FunctionId::CallerUnstakeAllAlphaV1 => { + let origin = convert_origin(env.origin()); + Self::dispatch_unstake_all_alpha_v1(env, origin) + } + FunctionId::MoveStakeV1 => { + let origin = RawOrigin::Signed(env.caller()); + Self::dispatch_move_stake_v1(env, origin) + } + FunctionId::CallerMoveStakeV1 => { + let origin = convert_origin(env.origin()); + Self::dispatch_move_stake_v1(env, origin) + } + FunctionId::TransferStakeV1 => { + let origin = RawOrigin::Signed(env.caller()); + Self::dispatch_transfer_stake_v1(env, origin) + } + FunctionId::CallerTransferStakeV1 => { + let origin = convert_origin(env.origin()); + Self::dispatch_transfer_stake_v1(env, origin) + } + FunctionId::SwapStakeV1 => { + let origin = RawOrigin::Signed(env.caller()); + Self::dispatch_swap_stake_v1(env, origin) + } + FunctionId::CallerSwapStakeV1 => { + let origin = convert_origin(env.origin()); + Self::dispatch_swap_stake_v1(env, origin) + } + FunctionId::AddStakeLimitV1 => { + let origin = RawOrigin::Signed(env.caller()); + Self::dispatch_add_stake_limit_v1(env, origin) + } + FunctionId::CallerAddStakeLimitV1 => { + let origin = convert_origin(env.origin()); + Self::dispatch_add_stake_limit_v1(env, origin) } FunctionId::RemoveStakeLimitV1 => { - let weight = Weight::from_parts(377_400_000, 0) - .saturating_add(T::DbWeight::get().reads(28_u64)) - .saturating_add(T::DbWeight::get().writes(14)); - - env.charge_weight(weight)?; - - let (hotkey, netuid, amount_unstaked, limit_price, allow_partial): ( - T::AccountId, - NetUid, - AlphaBalance, - TaoBalance, - bool, - ) = env + let origin = RawOrigin::Signed(env.caller()); + Self::dispatch_remove_stake_limit_v1(env, origin) + } + FunctionId::CallerRemoveStakeLimitV1 => { + let origin = convert_origin(env.origin()); + Self::dispatch_remove_stake_limit_v1(env, origin) + } + FunctionId::SwapStakeLimitV1 => { + let origin = RawOrigin::Signed(env.caller()); + Self::dispatch_swap_stake_limit_v1(env, origin) + } + FunctionId::CallerSwapStakeLimitV1 => { + let origin = convert_origin(env.origin()); + Self::dispatch_swap_stake_limit_v1(env, origin) + } + FunctionId::RemoveStakeFullLimitV1 => { + let origin = RawOrigin::Signed(env.caller()); + Self::dispatch_remove_stake_full_limit_v1(env, origin) + } + FunctionId::CallerRemoveStakeFullLimitV1 => { + let origin = convert_origin(env.origin()); + Self::dispatch_remove_stake_full_limit_v1(env, origin) + } + FunctionId::SetColdkeyAutoStakeHotkeyV1 => { + let origin = RawOrigin::Signed(env.caller()); + Self::dispatch_set_coldkey_auto_stake_hotkey_v1(env, origin) + } + FunctionId::CallerSetColdkeyAutoStakeHotkeyV1 => { + let origin = convert_origin(env.origin()); + Self::dispatch_set_coldkey_auto_stake_hotkey_v1(env, origin) + } + FunctionId::AddProxyV1 => { + let origin = RawOrigin::Signed(env.caller()); + Self::dispatch_add_proxy_v1(env, origin) + } + FunctionId::CallerAddProxyV1 => { + let origin = convert_origin(env.origin()); + Self::dispatch_add_proxy_v1(env, origin) + } + FunctionId::RemoveProxyV1 => { + let origin = RawOrigin::Signed(env.caller()); + Self::dispatch_remove_proxy_v1(env, origin) + } + FunctionId::CallerRemoveProxyV1 => { + let origin = convert_origin(env.origin()); + Self::dispatch_remove_proxy_v1(env, origin) + } + FunctionId::GetAlphaPriceV1 => { + let netuid: NetUid = env .read_as() .map_err(|_| DispatchError::Other("Failed to decode input parameters"))?; - let call_result = pallet_subtensor::Pallet::::remove_stake_limit( - RawOrigin::Signed(env.caller()).into(), - hotkey, - netuid, - amount_unstaked, - limit_price, - allow_partial, - ); + let current_alpha_price = + as SwapHandler>::current_alpha_price( + netuid.into(), + ); - match call_result { - Ok(_) => Ok(RetVal::Converging(Output::Success as u32)), - Err(e) => { - let error_code = Output::from(e) as u32; - Ok(RetVal::Converging(error_code)) - } - } - } - FunctionId::SwapStakeLimitV1 => { - let weight = Weight::from_parts(411_500_000, 0) - .saturating_add(T::DbWeight::get().reads(35_u64)) - .saturating_add(T::DbWeight::get().writes(22_u64)); + let price = current_alpha_price.saturating_mul(U96F32::from_num(1_000_000_000)); + let price: u64 = price.saturating_to_num(); - env.charge_weight(weight)?; + let encoded_result = price.encode(); - let ( - hotkey, - origin_netuid, - destination_netuid, - alpha_amount, - limit_price, - allow_partial, - ): (T::AccountId, NetUid, NetUid, AlphaBalance, TaoBalance, bool) = - env.read_as() - .map_err(|_| DispatchError::Other("Failed to decode input parameters"))?; - - let call_result = pallet_subtensor::Pallet::::swap_stake_limit( - RawOrigin::Signed(env.caller()).into(), - hotkey, - origin_netuid, - destination_netuid, - alpha_amount, - limit_price, - allow_partial, - ); + env.write_output(&encoded_result) + .map_err(|_| DispatchError::Other("Failed to write output"))?; - match call_result { - Ok(_) => Ok(RetVal::Converging(Output::Success as u32)), - Err(e) => { - let error_code = Output::from(e) as u32; - Ok(RetVal::Converging(error_code)) - } - } + Ok(RetVal::Converging(Output::Success as u32)) } - FunctionId::RemoveStakeFullLimitV1 => { - let weight = Weight::from_parts(395_300_000, 0) - .saturating_add(T::DbWeight::get().reads(28_u64)) - .saturating_add(T::DbWeight::get().writes(14_u64)); + FunctionId::RecycleAlphaV1 => { + let weight = + <::WeightInfo as SubtensorWeightInfo>::recycle_alpha(); env.charge_weight(weight)?; - let (hotkey, netuid, limit_price): (T::AccountId, NetUid, Option) = env - .read_as() - .map_err(|_| DispatchError::Other("Failed to decode input parameters"))?; + let (hotkey, netuid, amount): (T::AccountId, NetUid, AlphaBalance) = + env.read_as()?; - let call_result = pallet_subtensor::Pallet::::remove_stake_full_limit( - RawOrigin::Signed(env.caller()).into(), + let caller = env.caller(); + + let call_result = pallet_subtensor::Pallet::::do_recycle_alpha( + RawOrigin::Signed(caller).into(), hotkey, + amount, netuid, - limit_price, ); match call_result { - Ok(_) => Ok(RetVal::Converging(Output::Success as u32)), + Ok(real_amount) => { + env.write_output(&real_amount.encode()) + .map_err(|_| DispatchError::Other("Failed to write output"))?; + Ok(RetVal::Converging(Output::Success as u32)) + } Err(e) => { let error_code = Output::from(e) as u32; Ok(RetVal::Converging(error_code)) } } } - FunctionId::SetColdkeyAutoStakeHotkeyV1 => { - let weight = Weight::from_parts(29_930_000, 0) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)); + FunctionId::BurnAlphaV1 => { + let weight = + <::WeightInfo as SubtensorWeightInfo>::burn_alpha(); env.charge_weight(weight)?; - let (netuid, hotkey): (NetUid, T::AccountId) = env - .read_as() - .map_err(|_| DispatchError::Other("Failed to decode input parameters"))?; + let (hotkey, netuid, amount): (T::AccountId, NetUid, AlphaBalance) = + env.read_as()?; - let call_result = pallet_subtensor::Pallet::::set_coldkey_auto_stake_hotkey( - RawOrigin::Signed(env.caller()).into(), - netuid, + let caller = env.caller(); + + let call_result = pallet_subtensor::Pallet::::do_burn_alpha( + RawOrigin::Signed(caller).into(), hotkey, + amount, + netuid, ); match call_result { - Ok(_) => Ok(RetVal::Converging(Output::Success as u32)), + Ok(real_amount) => { + env.write_output(&real_amount.encode()) + .map_err(|_| DispatchError::Other("Failed to write output"))?; + Ok(RetVal::Converging(Output::Success as u32)) + } Err(e) => { let error_code = Output::from(e) as u32; Ok(RetVal::Converging(error_code)) } } } - FunctionId::AddProxyV1 => { - let weight = ::WeightInfo::add_proxy( - ::MaxProxies::get(), - ); + FunctionId::AddStakeRecycleV1 => { + let weight = + <::WeightInfo as SubtensorWeightInfo>::add_stake() + .saturating_add( + <::WeightInfo as SubtensorWeightInfo>::recycle_alpha(), + ); env.charge_weight(weight)?; - let delegate: T::AccountId = env - .read_as() - .map_err(|_| DispatchError::Other("Failed to decode input parameters"))?; - - let delegate_lookup = - <::Lookup as StaticLookup>::Source::from(delegate); + let (hotkey, netuid, tao_amount): (T::AccountId, NetUid, TaoBalance) = + env.read_as()?; - let call_result = pallet_proxy::Pallet::::add_proxy( + let call_result = pallet_subtensor::Pallet::::do_add_stake_recycle( RawOrigin::Signed(env.caller()).into(), - delegate_lookup, - ProxyType::Staking, - 0u32.into(), + hotkey, + netuid, + tao_amount, ); match call_result { - Ok(_) => Ok(RetVal::Converging(Output::Success as u32)), + Ok(alpha) => { + env.write_output(&alpha.encode()) + .map_err(|_| DispatchError::Other("Failed to write output"))?; + Ok(RetVal::Converging(Output::Success as u32)) + } Err(e) => { let error_code = Output::from(e) as u32; Ok(RetVal::Converging(error_code)) } } } - FunctionId::RemoveProxyV1 => { - let weight = ::WeightInfo::remove_proxy( - ::MaxProxies::get(), - ); + FunctionId::AddStakeBurnV1 => { + let weight = + <::WeightInfo as SubtensorWeightInfo>::add_stake() + .saturating_add( + <::WeightInfo as SubtensorWeightInfo>::burn_alpha(), + ); env.charge_weight(weight)?; - let delegate: T::AccountId = env - .read_as() - .map_err(|_| DispatchError::Other("Failed to decode input parameters"))?; + let (hotkey, netuid, tao_amount): (T::AccountId, NetUid, TaoBalance) = + env.read_as()?; - let delegate_lookup = - <::Lookup as StaticLookup>::Source::from(delegate); - - let call_result = pallet_proxy::Pallet::::remove_proxy( + let call_result = pallet_subtensor::Pallet::::do_add_stake_burn_permissionless( RawOrigin::Signed(env.caller()).into(), - delegate_lookup, - ProxyType::Staking, - 0u32.into(), + hotkey, + netuid, + tao_amount, ); match call_result { - Ok(_) => Ok(RetVal::Converging(Output::Success as u32)), + Ok(alpha) => { + env.write_output(&alpha.encode()) + .map_err(|_| DispatchError::Other("Failed to write output"))?; + Ok(RetVal::Converging(Output::Success as u32)) + } Err(e) => { let error_code = Output::from(e) as u32; Ok(RetVal::Converging(error_code)) } } } - FunctionId::GetAlphaPriceV1 => { - let netuid: NetUid = env - .read_as() - .map_err(|_| DispatchError::Other("Failed to decode input parameters"))?; - - let current_alpha_price = - as SwapHandler>::current_alpha_price( - netuid.into(), - ); - - let price = current_alpha_price.saturating_mul(U96F32::from_num(1_000_000_000)); - let price: u64 = price.saturating_to_num(); - - let encoded_result = price.encode(); - - env.write_output(&encoded_result) - .map_err(|_| DispatchError::Other("Failed to write output"))?; - - Ok(RetVal::Converging(Output::Success as u32)) - } } } } -trait SubtensorExtensionEnv { +// Convert from the contract origin to the raw origin +fn convert_origin(origin: pallet_contracts::Origin) -> RawOrigin +where + T: pallet_contracts::Config, +{ + match origin { + pallet_contracts::Origin::Signed(caller) => RawOrigin::Signed(caller), + pallet_contracts::Origin::Root => RawOrigin::Root, + } +} + +trait SubtensorExtensionEnv +where + T: pallet_contracts::Config, +{ fn func_id(&self) -> u16; fn charge_weight(&mut self, weight: Weight) -> Result<(), DispatchError>; - fn read_as(&mut self) -> Result; + fn read_as(&mut self) -> Result; fn write_output(&mut self, data: &[u8]) -> Result<(), DispatchError>; - fn caller(&mut self) -> AccountId; + fn caller(&mut self) -> T::AccountId; + #[allow(dead_code)] + fn origin(&mut self) -> pallet_contracts::Origin; } struct ContractsEnvAdapter<'a, 'b, T, E> @@ -558,7 +902,7 @@ where } } -impl<'a, 'b, T, E> SubtensorExtensionEnv for ContractsEnvAdapter<'a, 'b, T, E> +impl<'a, 'b, T, E> SubtensorExtensionEnv for ContractsEnvAdapter<'a, 'b, T, E> where T: pallet_subtensor::Config + pallet_contracts::Config, T::AccountId: Clone, @@ -583,4 +927,8 @@ where fn caller(&mut self) -> T::AccountId { self.env.ext().address().clone() } + + fn origin(&mut self) -> pallet_contracts::Origin { + self.env.ext().caller() + } } diff --git a/chain-extensions/src/mock.rs b/chain-extensions/src/mock.rs index e3b7eb2e94..9c4b3bd4a6 100644 --- a/chain-extensions/src/mock.rs +++ b/chain-extensions/src/mock.rs @@ -37,6 +37,7 @@ frame_support::construct_runtime!( { System: frame_system::{Pallet, Call, Config, Storage, Event} = 1, Balances: pallet_balances::{Pallet, Call, Config, Storage, Event} = 2, + AlphaAssets: pallet_alpha_assets = 3, SubtensorModule: pallet_subtensor::{Pallet, Call, Storage, Event} = 7, Utility: pallet_utility::{Pallet, Call, Storage, Event} = 8, Scheduler: pallet_scheduler::{Pallet, Call, Storage, Event} = 9, @@ -98,6 +99,8 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); } +impl pallet_alpha_assets::Config for Test {} + #[derive_impl(pallet_timestamp::config_preludes::TestDefaultConfig)] impl pallet_timestamp::Config for Test { type MinimumPeriod = ConstU64<1>; @@ -348,6 +351,8 @@ parameter_types! { pub const LeaseDividendsDistributionInterval: u32 = 100; pub const MaxImmuneUidsPercentage: Percent = Percent::from_percent(80); pub const EvmKeyAssociateRateLimit: u64 = 10; + pub const SubtensorPalletId: PalletId = PalletId(*b"subtensr"); + pub const BurnAccountId: PalletId = PalletId(*b"burntnsr"); } impl pallet_subtensor::Config for Test { @@ -407,6 +412,7 @@ impl pallet_subtensor::Config for Test { type LiquidAlphaOn = InitialLiquidAlphaOn; type Yuma3On = InitialYuma3On; type Preimages = Preimage; + type AlphaAssets = AlphaAssets; type InitialColdkeySwapAnnouncementDelay = InitialColdkeySwapAnnouncementDelay; type InitialColdkeySwapReannouncementDelay = InitialColdkeySwapReannouncementDelay; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; @@ -423,6 +429,8 @@ impl pallet_subtensor::Config for Test { type CommitmentsInterface = CommitmentsI; type EvmKeyAssociateRateLimit = EvmKeyAssociateRateLimit; type AuthorshipProvider = MockAuthorshipProvider; + type SubtensorPalletId = SubtensorPalletId; + type BurnAccountId = BurnAccountId; type WeightInfo = (); } @@ -690,7 +698,7 @@ pub fn register_ok_neuron( let bal: TaoBalance = SubtensorModule::get_coldkey_balance(&cold); if bal < min_balance_needed { - SubtensorModule::add_balance_to_coldkey_account(&cold, min_balance_needed - bal); + add_balance_to_coldkey_account(&cold, min_balance_needed - bal); } }; @@ -727,11 +735,22 @@ pub fn register_ok_neuron( ); } +#[allow(dead_code)] +pub fn add_balance_to_coldkey_account(coldkey: &U256, tao: TaoBalance) { + let credit = SubtensorModule::mint_tao(tao); + let _ = SubtensorModule::spend_tao(coldkey, credit, tao).unwrap(); +} + +#[allow(dead_code)] +pub fn remove_balance_from_coldkey_account(coldkey: &U256, tao: TaoBalance) { + let _ = SubtensorModule::burn_tao(coldkey, tao); +} + #[allow(dead_code)] 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()); + add_balance_to_coldkey_account(coldkey, lock_cost.into()); assert_ok!(SubtensorModule::register_network( RawOrigin::Signed(*coldkey).into(), diff --git a/chain-extensions/src/tests.rs b/chain-extensions/src/tests.rs index b8956e8659..08a9e17f77 100644 --- a/chain-extensions/src/tests.rs +++ b/chain-extensions/src/tests.rs @@ -8,6 +8,7 @@ use frame_support::{assert_ok, weights::Weight}; use frame_system::RawOrigin; use pallet_contracts::chain_extension::RetVal; use pallet_subtensor::DefaultMinStake; +use pallet_subtensor::weights::WeightInfo as SubtensorWeightInfo; use sp_core::Get; use sp_core::U256; use sp_runtime::DispatchError; @@ -27,6 +28,12 @@ struct MockEnv { expected_weight: Option, } +#[allow(dead_code)] +pub fn add_balance_to_coldkey_account(coldkey: &U256, tao: TaoBalance) { + let credit = pallet_subtensor::Pallet::::mint_tao(tao); + let _ = pallet_subtensor::Pallet::::spend_tao(coldkey, credit, tao).unwrap(); +} + #[test] fn set_coldkey_auto_stake_hotkey_success_sets_destination() { mock::new_test_ext(1).execute_with(|| { @@ -46,9 +53,7 @@ fn set_coldkey_auto_stake_hotkey_success_sets_destination() { None ); - let expected_weight = Weight::from_parts(29_930_000, 0) - .saturating_add(::DbWeight::get().reads(4)) - .saturating_add(::DbWeight::get().writes(2)); + let expected_weight = <::WeightInfo as SubtensorWeightInfo>::set_coldkey_auto_stake_hotkey(); let mut env = MockEnv::new( FunctionId::SetColdkeyAutoStakeHotkeyV1, @@ -89,7 +94,7 @@ fn remove_stake_full_limit_success_with_limit_price() { mock::register_ok_neuron(netuid, hotkey, coldkey, 0); - pallet_subtensor::Pallet::::add_balance_to_coldkey_account( + add_balance_to_coldkey_account( &coldkey, TaoBalance::from(stake_amount_raw + 1_000_000_000), ); @@ -103,9 +108,7 @@ fn remove_stake_full_limit_success_with_limit_price() { mock::remove_stake_rate_limit_for_tests(&hotkey, &coldkey, netuid); - let expected_weight = Weight::from_parts(395_300_000, 0) - .saturating_add(::DbWeight::get().reads(28)) - .saturating_add(::DbWeight::get().writes(14)); + let expected_weight = <::WeightInfo as SubtensorWeightInfo>::remove_stake_full_limit(); let balance_before = pallet_subtensor::Pallet::::get_coldkey_balance(&coldkey); @@ -181,9 +184,7 @@ fn swap_stake_limit_with_tight_price_returns_slippage_error() { let alpha_to_swap: AlphaBalance = (alpha_origin_before.to_u64() / 8).into(); let limit_price: TaoBalance = 100u64.into(); - let expected_weight = Weight::from_parts(411_500_000, 0) - .saturating_add(::DbWeight::get().reads(35)) - .saturating_add(::DbWeight::get().writes(22)); + let expected_weight = <::WeightInfo as SubtensorWeightInfo>::swap_stake_limit(); let mut env = MockEnv::new( FunctionId::SwapStakeLimitV1, @@ -228,7 +229,7 @@ fn remove_stake_limit_success_respects_price_limit() { mock::register_ok_neuron(netuid, hotkey, coldkey, 0); - pallet_subtensor::Pallet::::add_balance_to_coldkey_account( + add_balance_to_coldkey_account( &coldkey, TaoBalance::from(stake_amount_raw + 1_000_000_000), ); @@ -256,9 +257,7 @@ fn remove_stake_limit_success_respects_price_limit() { let alpha_to_unstake: AlphaBalance = (alpha_before.to_u64() / 2).into(); - let expected_weight = Weight::from_parts(377_400_000, 0) - .saturating_add(::DbWeight::get().reads(28)) - .saturating_add(::DbWeight::get().writes(14)); + let expected_weight = <::WeightInfo as SubtensorWeightInfo>::remove_stake_limit(); let balance_before = pallet_subtensor::Pallet::::get_coldkey_balance(&coldkey); @@ -304,10 +303,7 @@ fn add_stake_limit_success_executes_within_price_guard() { mock::register_ok_neuron(netuid, hotkey, coldkey, 0); - pallet_subtensor::Pallet::::add_balance_to_coldkey_account( - &coldkey, - (amount_raw + 1_000_000_000).into(), - ); + add_balance_to_coldkey_account(&coldkey, (amount_raw + 1_000_000_000).into()); let stake_before = pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -315,9 +311,7 @@ fn add_stake_limit_success_executes_within_price_guard() { ); let balance_before = pallet_subtensor::Pallet::::get_coldkey_balance(&coldkey); - let expected_weight = Weight::from_parts(402_900_000, 0) - .saturating_add(::DbWeight::get().reads(24)) - .saturating_add(::DbWeight::get().writes(15)); + let expected_weight = <::WeightInfo as SubtensorWeightInfo>::add_stake_limit(); let mut env = MockEnv::new( FunctionId::AddStakeLimitV1, @@ -379,10 +373,7 @@ fn swap_stake_success_moves_between_subnets() { mock::register_ok_neuron(netuid_a, hotkey, coldkey, 0); mock::register_ok_neuron(netuid_b, hotkey, coldkey, 1); - pallet_subtensor::Pallet::::add_balance_to_coldkey_account( - &coldkey, - (stake_amount_raw + 1_000_000_000).into(), - ); + add_balance_to_coldkey_account(&coldkey, (stake_amount_raw + 1_000_000_000).into()); assert_ok!(pallet_subtensor::Pallet::::add_stake( RawOrigin::Signed(coldkey).into(), @@ -403,9 +394,7 @@ fn swap_stake_success_moves_between_subnets() { ); let alpha_to_swap: AlphaBalance = (alpha_origin_before.to_u64() / 3).into(); - let expected_weight = Weight::from_parts(351_300_000, 0) - .saturating_add(::DbWeight::get().reads(35)) - .saturating_add(::DbWeight::get().writes(22)); + let expected_weight = <::WeightInfo as SubtensorWeightInfo>::swap_stake(); let mut env = MockEnv::new( FunctionId::SwapStakeV1, @@ -456,10 +445,7 @@ fn transfer_stake_success_moves_between_coldkeys() { mock::register_ok_neuron(netuid, hotkey, origin_coldkey, 0); - pallet_subtensor::Pallet::::add_balance_to_coldkey_account( - &origin_coldkey, - (stake_amount_raw + 1_000_000_000).into(), - ); + add_balance_to_coldkey_account(&origin_coldkey, (stake_amount_raw + 1_000_000_000).into()); assert_ok!(pallet_subtensor::Pallet::::add_stake( RawOrigin::Signed(origin_coldkey).into(), @@ -478,9 +464,7 @@ fn transfer_stake_success_moves_between_coldkeys() { ); let alpha_to_transfer: AlphaBalance = (alpha_before.to_u64() / 3).into(); - let expected_weight = Weight::from_parts(160_300_000, 0) - .saturating_add(::DbWeight::get().reads(13)) - .saturating_add(::DbWeight::get().writes(6)); + let expected_weight = <::WeightInfo as SubtensorWeightInfo>::transfer_stake(); let mut env = MockEnv::new( FunctionId::TransferStakeV1, @@ -540,10 +524,7 @@ fn move_stake_success_moves_alpha_between_hotkeys() { mock::register_ok_neuron(netuid, origin_hotkey, coldkey, 0); mock::register_ok_neuron(netuid, destination_hotkey, coldkey, 1); - pallet_subtensor::Pallet::::add_balance_to_coldkey_account( - &coldkey, - (stake_amount_raw + 1_000_000_000).into(), - ); + add_balance_to_coldkey_account(&coldkey, (stake_amount_raw + 1_000_000_000).into()); assert_ok!(pallet_subtensor::Pallet::::add_stake( RawOrigin::Signed(coldkey).into(), @@ -562,9 +543,7 @@ fn move_stake_success_moves_alpha_between_hotkeys() { ); let alpha_to_move: AlphaBalance = (alpha_before.to_u64() / 2).into(); - let expected_weight = Weight::from_parts(164_300_000, 0) - .saturating_add(::DbWeight::get().reads(15)) - .saturating_add(::DbWeight::get().writes(7)); + let expected_weight = <::WeightInfo as SubtensorWeightInfo>::move_stake(); let mut env = MockEnv::new( FunctionId::MoveStakeV1, @@ -620,10 +599,7 @@ fn unstake_all_alpha_success_moves_stake_to_root() { ); mock::register_ok_neuron(netuid, hotkey, coldkey, 0); - pallet_subtensor::Pallet::::add_balance_to_coldkey_account( - &coldkey, - (stake_amount_raw + 1_000_000_000).into(), - ); + add_balance_to_coldkey_account(&coldkey, (stake_amount_raw + 1_000_000_000).into()); assert_ok!(pallet_subtensor::Pallet::::add_stake( RawOrigin::Signed(coldkey).into(), @@ -634,9 +610,7 @@ fn unstake_all_alpha_success_moves_stake_to_root() { mock::remove_stake_rate_limit_for_tests(&hotkey, &coldkey, netuid); - let expected_weight = Weight::from_parts(358_500_000, 0) - .saturating_add(::DbWeight::get().reads(36)) - .saturating_add(::DbWeight::get().writes(21)); + let expected_weight = <::WeightInfo as SubtensorWeightInfo>::unstake_all_alpha(); let mut env = MockEnv::new(FunctionId::UnstakeAllAlphaV1, coldkey, hotkey.encode()) .with_expected_weight(expected_weight); @@ -667,10 +641,7 @@ fn add_proxy_success_creates_proxy_relationship() { let delegator = U256::from(6001); let delegate = U256::from(6002); - pallet_subtensor::Pallet::::add_balance_to_coldkey_account( - &delegator, - 1_000_000_000.into(), - ); + add_balance_to_coldkey_account(&delegator, 1_000_000_000.into()); assert_eq!( pallet_subtensor_proxy::Proxies::::get(delegator) @@ -705,10 +676,7 @@ fn remove_proxy_success_removes_proxy_relationship() { let delegator = U256::from(7001); let delegate = U256::from(7002); - pallet_subtensor::Pallet::::add_balance_to_coldkey_account( - &delegator, - 1_000_000_000.into(), - ); + add_balance_to_coldkey_account(&delegator, 1_000_000_000.into()); let mut add_env = MockEnv::new(FunctionId::AddProxyV1, delegator, delegate.encode()); let ret = SubtensorChainExtension::::dispatch(&mut add_env).unwrap(); @@ -726,275 +694,942 @@ fn remove_proxy_success_removes_proxy_relationship() { }); } -impl MockEnv { - fn new(func_id: FunctionId, caller: AccountId, input: Vec) -> Self { - Self { - func_id: func_id as u16, - caller, - input, - output: Vec::new(), - charged_weight: None, - expected_weight: None, - } - } +#[test] +fn recycle_alpha_success_reduces_stake_and_returns_actual_amount() { + mock::new_test_ext(1).execute_with(|| { + let owner_hotkey = U256::from(9001); + let owner_coldkey = U256::from(9002); + let coldkey = U256::from(9101); + let hotkey = U256::from(9102); + let min_stake = DefaultMinStake::::get(); + let stake_amount_raw = min_stake.to_u64().saturating_mul(200); - fn with_expected_weight(mut self, weight: Weight) -> Self { - self.expected_weight = Some(weight); - self - } + let netuid = mock::add_dynamic_network(&owner_hotkey, &owner_coldkey); + mock::setup_reserves( + netuid, + TaoBalance::from(130_000_000_000_u64), + AlphaBalance::from(110_000_000_000_u64), + ); - fn charged_weight(&self) -> Option { - self.charged_weight - } + mock::register_ok_neuron(netuid, hotkey, coldkey, 0); - fn output(&self) -> &[u8] { - &self.output - } -} + add_balance_to_coldkey_account( + &coldkey, + TaoBalance::from(stake_amount_raw.saturating_add(1_000_000_000)), + ); -impl SubtensorExtensionEnv for MockEnv { - fn func_id(&self) -> u16 { - self.func_id - } + assert_ok!(pallet_subtensor::Pallet::::add_stake( + RawOrigin::Signed(coldkey).into(), + hotkey, + netuid, + stake_amount_raw.into(), + )); - fn charge_weight(&mut self, weight: Weight) -> Result<(), DispatchError> { - if let Some(expected) = self.expected_weight - && weight != expected - { - return Err(DispatchError::Other( - "unexpected weight charged by mock env", - )); - } - self.charged_weight = Some(weight); - Ok(()) - } + let alpha_before = + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid, + ); + assert!(alpha_before > AlphaBalance::ZERO); - fn read_as(&mut self) -> Result { - T::decode(&mut &self.input[..]).map_err(|_| DispatchError::Other("mock env decode failure")) - } + let alpha_out_before = pallet_subtensor::SubnetAlphaOut::::get(netuid); - fn write_output(&mut self, data: &[u8]) -> Result<(), DispatchError> { - self.output.clear(); - self.output.extend_from_slice(data); - Ok(()) - } + let recycle_amount: AlphaBalance = (alpha_before.to_u64() / 2).into(); - fn caller(&mut self) -> AccountId { - self.caller - } -} + let expected_weight = + <::WeightInfo as SubtensorWeightInfo>::recycle_alpha(); -fn assert_success(ret: RetVal) { - match ret { - RetVal::Converging(code) => { - assert_eq!(code, Output::Success as u32, "expected success code") - } - _ => panic!("unexpected return value"), - } + let mut env = MockEnv::new( + FunctionId::RecycleAlphaV1, + coldkey, + (hotkey, netuid, recycle_amount).encode(), + ) + .with_expected_weight(expected_weight); + + let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); + assert_success(ret); + assert_eq!(env.charged_weight(), Some(expected_weight)); + + let returned_amount = AlphaBalance::decode(&mut env.output()).unwrap(); + assert_eq!(returned_amount, recycle_amount); + + let alpha_after = + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid, + ); + assert!(alpha_after < alpha_before); + + let alpha_out_after = pallet_subtensor::SubnetAlphaOut::::get(netuid); + assert!(alpha_out_after < alpha_out_before); + }); } #[test] -fn get_stake_info_returns_encoded_runtime_value() { +fn recycle_alpha_on_root_subnet_returns_error() { mock::new_test_ext(1).execute_with(|| { - let owner_hotkey = U256::from(1); - let owner_coldkey = U256::from(2); - let hotkey = U256::from(11); - let coldkey = U256::from(22); - let netuid = mock::add_dynamic_network(&owner_hotkey, &owner_coldkey); - mock::register_ok_neuron(netuid, hotkey, coldkey, 0); + let coldkey = U256::from(9201); + let hotkey = U256::from(9202); - let expected = - pallet_subtensor::Pallet::::get_stake_info_for_hotkey_coldkey_netuid( - hotkey, coldkey, netuid, - ) - .encode(); + pallet_subtensor::Owner::::insert(hotkey, coldkey); + + let expected_weight = + <::WeightInfo as SubtensorWeightInfo>::recycle_alpha(); let mut env = MockEnv::new( - FunctionId::GetStakeInfoForHotkeyColdkeyNetuidV1, + FunctionId::RecycleAlphaV1, coldkey, - (hotkey, coldkey, netuid).encode(), - ); + (hotkey, NetUid::ROOT, AlphaBalance::from(1_000u64)).encode(), + ) + .with_expected_weight(expected_weight); let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); - - assert_success(ret); - assert_eq!(env.output(), expected.as_slice()); - assert!(env.charged_weight().is_none()); + match ret { + RetVal::Converging(code) => { + assert_ne!( + code, + Output::Success as u32, + "should not succeed on root subnet" + ) + } + _ => panic!("unexpected return value"), + } }); } #[test] -fn add_stake_success_updates_stake_and_returns_success_code() { +fn burn_alpha_success_reduces_stake_and_returns_actual_amount() { mock::new_test_ext(1).execute_with(|| { - let owner_hotkey = U256::from(1); - let owner_coldkey = U256::from(2); - let coldkey = U256::from(101); - let hotkey = U256::from(202); + let owner_hotkey = U256::from(9301); + let owner_coldkey = U256::from(9302); + let coldkey = U256::from(9401); + let hotkey = U256::from(9402); let min_stake = DefaultMinStake::::get(); - let amount_raw = min_stake.to_u64().saturating_mul(10); - let amount: TaoBalance = amount_raw.into(); + let stake_amount_raw = min_stake.to_u64().saturating_mul(200); let netuid = mock::add_dynamic_network(&owner_hotkey, &owner_coldkey); mock::setup_reserves( netuid, - (amount_raw * 1_000_000).into(), - AlphaBalance::from(amount_raw * 10_000_000), + TaoBalance::from(130_000_000_000_u64), + AlphaBalance::from(110_000_000_000_u64), ); + mock::register_ok_neuron(netuid, hotkey, coldkey, 0); - pallet_subtensor::Pallet::::add_balance_to_coldkey_account( + add_balance_to_coldkey_account( &coldkey, - amount_raw.into(), + TaoBalance::from(stake_amount_raw.saturating_add(1_000_000_000)), ); - assert!( - pallet_subtensor::Pallet::::get_total_stake_for_hotkey(&hotkey).is_zero() - ); + assert_ok!(pallet_subtensor::Pallet::::add_stake( + RawOrigin::Signed(coldkey).into(), + hotkey, + netuid, + stake_amount_raw.into(), + )); + + let alpha_before = + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid, + ); + assert!(alpha_before > AlphaBalance::ZERO); + + let alpha_out_before = pallet_subtensor::SubnetAlphaOut::::get(netuid); - let expected_weight = Weight::from_parts(340_800_000, 0) - .saturating_add(::DbWeight::get().reads(24)) - .saturating_add(::DbWeight::get().writes(15)); + let burn_amount: AlphaBalance = (alpha_before.to_u64() / 2).into(); + + let expected_weight = + <::WeightInfo as SubtensorWeightInfo>::burn_alpha(); let mut env = MockEnv::new( - FunctionId::AddStakeV1, + FunctionId::BurnAlphaV1, coldkey, - (hotkey, netuid, amount).encode(), + (hotkey, netuid, burn_amount).encode(), ) .with_expected_weight(expected_weight); let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); - assert_success(ret); assert_eq!(env.charged_weight(), Some(expected_weight)); - let total_stake = - pallet_subtensor::Pallet::::get_total_stake_for_hotkey(&hotkey); - assert!(total_stake > TaoBalance::ZERO); + let returned_amount = AlphaBalance::decode(&mut env.output()).unwrap(); + assert_eq!(returned_amount, burn_amount); + + let alpha_after = + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid, + ); + assert!(alpha_after < alpha_before); + + // Burn should NOT decrease SubnetAlphaOut (unlike recycle) + let alpha_out_after = pallet_subtensor::SubnetAlphaOut::::get(netuid); + assert_eq!(alpha_out_after, alpha_out_before); }); } #[test] -fn remove_stake_with_no_stake_returns_amount_too_low() { +fn burn_alpha_on_nonexistent_subnet_returns_error() { mock::new_test_ext(1).execute_with(|| { - let owner_hotkey = U256::from(1); - let owner_coldkey = U256::from(2); - let coldkey = U256::from(301); - let hotkey = U256::from(302); - let netuid = mock::add_dynamic_network(&owner_hotkey, &owner_coldkey); - mock::register_ok_neuron(netuid, hotkey, coldkey, 0); - - let min_stake = DefaultMinStake::::get(); - let amount: AlphaBalance = AlphaBalance::from(min_stake.to_u64()); + let coldkey = U256::from(9501); + let hotkey = U256::from(9502); - let expected_weight = Weight::from_parts(196_800_000, 0) - .saturating_add(::DbWeight::get().reads(19)) - .saturating_add(::DbWeight::get().writes(10)); + let expected_weight = + <::WeightInfo as SubtensorWeightInfo>::burn_alpha(); let mut env = MockEnv::new( - FunctionId::RemoveStakeV1, + FunctionId::BurnAlphaV1, coldkey, - (hotkey, netuid, amount).encode(), + (hotkey, NetUid::from(999u16), AlphaBalance::from(1_000u64)).encode(), ) .with_expected_weight(expected_weight); let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); - match ret { RetVal::Converging(code) => { - assert_eq!(code, Output::AmountTooLow as u32, "mismatched error output") + assert_eq!( + code, + Output::SubnetNotExists as u32, + "expected subnet not exists error" + ) } _ => panic!("unexpected return value"), } - assert_eq!(env.charged_weight(), Some(expected_weight)); - assert!( - pallet_subtensor::Pallet::::get_total_stake_for_hotkey(&hotkey).is_zero() - ); }); } #[test] -fn unstake_all_success_unstakes_balance() { +fn add_stake_recycle_success_atomically_stakes_and_recycles() { mock::new_test_ext(1).execute_with(|| { - let owner_hotkey = U256::from(4001); - let owner_coldkey = U256::from(4002); - let coldkey = U256::from(5001); - let hotkey = U256::from(5002); + let owner_hotkey = U256::from(9601); + let owner_coldkey = U256::from(9602); + let coldkey = U256::from(9701); + let hotkey = U256::from(9702); let min_stake = DefaultMinStake::::get(); - let stake_amount_raw = min_stake.to_u64().saturating_mul(200); - let netuid = mock::add_dynamic_network(&owner_hotkey, &owner_coldkey); + let tao_amount_raw = min_stake.to_u64().saturating_mul(200); + let netuid = mock::add_dynamic_network(&owner_hotkey, &owner_coldkey); mock::setup_reserves( netuid, - stake_amount_raw.saturating_mul(10).into(), - AlphaBalance::from(stake_amount_raw.saturating_mul(20)), + TaoBalance::from(130_000_000_000_u64), + AlphaBalance::from(110_000_000_000_u64), ); mock::register_ok_neuron(netuid, hotkey, coldkey, 0); - pallet_subtensor::Pallet::::add_balance_to_coldkey_account( + + add_balance_to_coldkey_account( &coldkey, - (stake_amount_raw + 1_000_000_000).into(), + TaoBalance::from(tao_amount_raw.saturating_add(1_000_000_000)), ); - assert_ok!(pallet_subtensor::Pallet::::add_stake( - RawOrigin::Signed(coldkey).into(), - hotkey, - netuid, - stake_amount_raw.into(), - )); - - mock::remove_stake_rate_limit_for_tests(&hotkey, &coldkey, netuid); - - let expected_weight = Weight::from_parts(28_830_000, 0) - .saturating_add(::DbWeight::get().reads(6)) - .saturating_add(::DbWeight::get().writes(0)); + let alpha_out_before = pallet_subtensor::SubnetAlphaOut::::get(netuid); - let pre_balance = pallet_subtensor::Pallet::::get_coldkey_balance(&coldkey); + let expected_weight = + <::WeightInfo as SubtensorWeightInfo>::add_stake() + .saturating_add( + <::WeightInfo as SubtensorWeightInfo>::recycle_alpha(), + ); - let mut env = MockEnv::new(FunctionId::UnstakeAllV1, coldkey, hotkey.encode()) - .with_expected_weight(expected_weight); + let mut env = MockEnv::new( + FunctionId::AddStakeRecycleV1, + coldkey, + (hotkey, netuid, TaoBalance::from(tao_amount_raw)).encode(), + ) + .with_expected_weight(expected_weight); let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); assert_success(ret); assert_eq!(env.charged_weight(), Some(expected_weight)); - let remaining_alpha = + let returned_alpha = AlphaBalance::decode(&mut env.output()).unwrap(); + assert!(returned_alpha > AlphaBalance::ZERO); + + // After atomic add+recycle, the stake should be zero (we recycled everything we added) + let alpha_after = pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey, netuid, ); - assert!(remaining_alpha <= AlphaBalance::from(1_000)); + assert!(alpha_after.is_zero()); - let post_balance = pallet_subtensor::Pallet::::get_coldkey_balance(&coldkey); - assert!(post_balance > pre_balance); + // SubnetAlphaOut should not have increased (recycle cancels out the add) + let alpha_out_after = pallet_subtensor::SubnetAlphaOut::::get(netuid); + assert!(alpha_out_after <= alpha_out_before); }); } #[test] -fn get_alpha_price_returns_encoded_price() { +fn add_stake_burn_success_atomically_stakes_and_burns() { mock::new_test_ext(1).execute_with(|| { - let owner_hotkey = U256::from(8001); - let owner_coldkey = U256::from(8002); - let caller = U256::from(8003); + let owner_hotkey = U256::from(9801); + let owner_coldkey = U256::from(9802); + let coldkey = U256::from(9901); + let hotkey = U256::from(9902); + let min_stake = DefaultMinStake::::get(); + let tao_amount_raw = min_stake.to_u64().saturating_mul(200); let netuid = mock::add_dynamic_network(&owner_hotkey, &owner_coldkey); + mock::setup_reserves( + netuid, + TaoBalance::from(130_000_000_000_u64), + AlphaBalance::from(110_000_000_000_u64), + ); - // Set up reserves to establish a price - let tao_reserve = TaoBalance::from(150_000_000_000u64); - let alpha_reserve = AlphaBalance::from(100_000_000_000u64); - mock::setup_reserves(netuid, tao_reserve, alpha_reserve); + mock::register_ok_neuron(netuid, hotkey, coldkey, 0); - // Get expected price from swap handler - let expected_price = - as SwapHandler>::current_alpha_price( - netuid.into(), - ); - let expected_price_scaled = expected_price.saturating_mul(U96F32::from_num(1_000_000_000)); - let expected_price_u64: u64 = expected_price_scaled.saturating_to_num(); + add_balance_to_coldkey_account( + &coldkey, + TaoBalance::from(tao_amount_raw.saturating_add(1_000_000_000)), + ); - let mut env = MockEnv::new(FunctionId::GetAlphaPriceV1, caller, netuid.encode()); + let alpha_out_before = pallet_subtensor::SubnetAlphaOut::::get(netuid); - let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); - assert_success(ret); - assert!(env.charged_weight().is_none()); + let expected_weight = + <::WeightInfo as SubtensorWeightInfo>::add_stake() + .saturating_add( + <::WeightInfo as SubtensorWeightInfo>::burn_alpha(), + ); + + let mut env = MockEnv::new( + FunctionId::AddStakeBurnV1, + coldkey, + (hotkey, netuid, TaoBalance::from(tao_amount_raw)).encode(), + ) + .with_expected_weight(expected_weight); + + let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); + assert_success(ret); + assert_eq!(env.charged_weight(), Some(expected_weight)); + + let returned_alpha = AlphaBalance::decode(&mut env.output()).unwrap(); + assert!(returned_alpha > AlphaBalance::ZERO); + + // After atomic add+burn, the stake should be zero + let alpha_after = + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid, + ); + assert!(alpha_after.is_zero()); + + // SubnetAlphaOut should have increased (burn does NOT reduce AlphaOut) + let alpha_out_after = pallet_subtensor::SubnetAlphaOut::::get(netuid); + assert!(alpha_out_after > alpha_out_before); + }); +} + +#[test] +fn add_stake_recycle_with_insufficient_balance_returns_error() { + mock::new_test_ext(1).execute_with(|| { + let owner_hotkey = U256::from(10001); + let owner_coldkey = U256::from(10002); + let coldkey = U256::from(10101); + let hotkey = U256::from(10102); + + let netuid = mock::add_dynamic_network(&owner_hotkey, &owner_coldkey); + mock::setup_reserves( + netuid, + TaoBalance::from(130_000_000_000_u64), + AlphaBalance::from(110_000_000_000_u64), + ); + + mock::register_ok_neuron(netuid, hotkey, coldkey, 0); + + // Don't fund the coldkey - should fail with balance error + + let expected_weight = + <::WeightInfo as SubtensorWeightInfo>::add_stake() + .saturating_add( + <::WeightInfo as SubtensorWeightInfo>::recycle_alpha(), + ); + + let mut env = MockEnv::new( + FunctionId::AddStakeRecycleV1, + coldkey, + (hotkey, netuid, TaoBalance::from(100_000_000_000_u64)).encode(), + ) + .with_expected_weight(expected_weight); + + let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); + match ret { + RetVal::Converging(code) => { + assert_ne!(code, Output::Success as u32, "should not succeed") + } + _ => panic!("unexpected return value"), + } + assert_eq!(env.charged_weight(), Some(expected_weight)); + }); +} + +#[test] +fn recycle_alpha_clamps_to_available_when_amount_exceeds_stake() { + mock::new_test_ext(1).execute_with(|| { + let owner_hotkey = U256::from(11001); + let owner_coldkey = U256::from(11002); + let coldkey = U256::from(11101); + let hotkey = U256::from(11102); + let min_stake = DefaultMinStake::::get(); + let stake_amount_raw = min_stake.to_u64().saturating_mul(200); + + let netuid = mock::add_dynamic_network(&owner_hotkey, &owner_coldkey); + mock::setup_reserves( + netuid, + TaoBalance::from(130_000_000_000_u64), + AlphaBalance::from(110_000_000_000_u64), + ); + + mock::register_ok_neuron(netuid, hotkey, coldkey, 0); + + add_balance_to_coldkey_account( + &coldkey, + TaoBalance::from(stake_amount_raw.saturating_add(1_000_000_000)), + ); + + assert_ok!(pallet_subtensor::Pallet::::add_stake( + RawOrigin::Signed(coldkey).into(), + hotkey, + netuid, + stake_amount_raw.into(), + )); + + let alpha_before = + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid, + ); + assert!(alpha_before > AlphaBalance::ZERO); + + // Request way more than available — should clamp to alpha_before + let huge_amount = AlphaBalance::from(u64::MAX); + + let expected_weight = + <::WeightInfo as SubtensorWeightInfo>::recycle_alpha(); + + let mut env = MockEnv::new( + FunctionId::RecycleAlphaV1, + coldkey, + (hotkey, netuid, huge_amount).encode(), + ) + .with_expected_weight(expected_weight); + + let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); + assert_success(ret); + + let returned_amount = AlphaBalance::decode(&mut env.output()).unwrap(); + assert_eq!( + returned_amount, alpha_before, + "should return actual clamped amount, not requested amount" + ); + + let alpha_after = + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid, + ); + assert!(alpha_after.is_zero(), "all alpha should be recycled"); + }); +} + +#[test] +fn burn_alpha_on_root_subnet_returns_error() { + mock::new_test_ext(1).execute_with(|| { + let coldkey = U256::from(11201); + let hotkey = U256::from(11202); + + pallet_subtensor::Owner::::insert(hotkey, coldkey); + + let expected_weight = + <::WeightInfo as SubtensorWeightInfo>::burn_alpha(); + + let mut env = MockEnv::new( + FunctionId::BurnAlphaV1, + coldkey, + (hotkey, NetUid::ROOT, AlphaBalance::from(1_000u64)).encode(), + ) + .with_expected_weight(expected_weight); + + let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); + match ret { + RetVal::Converging(code) => { + assert_ne!( + code, + Output::Success as u32, + "should not succeed on root subnet" + ) + } + _ => panic!("unexpected return value"), + } + }); +} + +#[test] +fn burn_alpha_clamps_to_available_when_amount_exceeds_stake() { + mock::new_test_ext(1).execute_with(|| { + let owner_hotkey = U256::from(11301); + let owner_coldkey = U256::from(11302); + let coldkey = U256::from(11401); + let hotkey = U256::from(11402); + let min_stake = DefaultMinStake::::get(); + let stake_amount_raw = min_stake.to_u64().saturating_mul(200); + + let netuid = mock::add_dynamic_network(&owner_hotkey, &owner_coldkey); + mock::setup_reserves( + netuid, + TaoBalance::from(130_000_000_000_u64), + AlphaBalance::from(110_000_000_000_u64), + ); + + mock::register_ok_neuron(netuid, hotkey, coldkey, 0); + + add_balance_to_coldkey_account( + &coldkey, + TaoBalance::from(stake_amount_raw.saturating_add(1_000_000_000)), + ); + + assert_ok!(pallet_subtensor::Pallet::::add_stake( + RawOrigin::Signed(coldkey).into(), + hotkey, + netuid, + stake_amount_raw.into(), + )); + + let alpha_before = + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid, + ); + assert!(alpha_before > AlphaBalance::ZERO); + + // Request way more than available — should clamp to alpha_before + let huge_amount = AlphaBalance::from(u64::MAX); + + let expected_weight = + <::WeightInfo as SubtensorWeightInfo>::burn_alpha(); + + let mut env = MockEnv::new( + FunctionId::BurnAlphaV1, + coldkey, + (hotkey, netuid, huge_amount).encode(), + ) + .with_expected_weight(expected_weight); + + let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); + assert_success(ret); + + let returned_amount = AlphaBalance::decode(&mut env.output()).unwrap(); + assert_eq!( + returned_amount, alpha_before, + "should return actual clamped amount, not requested amount" + ); + + let alpha_after = + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid, + ); + assert!(alpha_after.is_zero(), "all alpha should be burned"); + }); +} + +impl MockEnv { + fn new(func_id: FunctionId, caller: AccountId, input: Vec) -> Self { + Self { + func_id: func_id as u16, + caller, + input, + output: Vec::new(), + charged_weight: None, + expected_weight: None, + } + } + + fn with_expected_weight(mut self, weight: Weight) -> Self { + self.expected_weight = Some(weight); + self + } + + fn charged_weight(&self) -> Option { + self.charged_weight + } + + fn output(&self) -> &[u8] { + &self.output + } +} + +impl SubtensorExtensionEnv for MockEnv { + fn func_id(&self) -> u16 { + self.func_id + } + + fn charge_weight(&mut self, weight: Weight) -> Result<(), DispatchError> { + let prev = self.charged_weight.unwrap_or_default(); + let cumulative = Weight::from_parts( + prev.ref_time().checked_add(weight.ref_time()).unwrap(), + prev.proof_size().checked_add(weight.proof_size()).unwrap(), + ); + if let Some(expected) = self.expected_weight + && (cumulative.ref_time() > expected.ref_time() + || cumulative.proof_size() > expected.proof_size()) + { + return Err(DispatchError::Other( + "unexpected weight charged by mock env", + )); + } + self.charged_weight = Some(cumulative); + Ok(()) + } + + fn read_as(&mut self) -> Result { + U::decode(&mut &self.input[..]).map_err(|_| DispatchError::Other("mock env decode failure")) + } + + fn write_output(&mut self, data: &[u8]) -> Result<(), DispatchError> { + self.output.clear(); + self.output.extend_from_slice(data); + Ok(()) + } + + fn caller(&mut self) -> AccountId { + self.caller + } + + fn origin(&mut self) -> pallet_contracts::Origin { + pallet_contracts::Origin::Signed(self.caller) + } +} + +fn assert_success(ret: RetVal) { + match ret { + RetVal::Converging(code) => { + assert_eq!(code, Output::Success as u32, "expected success code") + } + _ => panic!("unexpected return value"), + } +} + +#[test] +fn add_stake_recycle_rollback_on_recycle_failure() { + mock::new_test_ext(1).execute_with(|| { + let owner_hotkey = U256::from(12001); + let owner_coldkey = U256::from(12002); + let coldkey = U256::from(12101); + let hotkey = U256::from(12102); + let min_stake = DefaultMinStake::::get(); + let tao_amount_raw = min_stake.to_u64().saturating_mul(200); + + let netuid = mock::add_dynamic_network(&owner_hotkey, &owner_coldkey); + + // Set up very low reserves so recycle will fail with InsufficientLiquidity + mock::setup_reserves( + netuid, + TaoBalance::from(1_000_u64), + AlphaBalance::from(1_000_u64), + ); + + mock::register_ok_neuron(netuid, hotkey, coldkey, 0); + + add_balance_to_coldkey_account( + &coldkey, + TaoBalance::from(tao_amount_raw.saturating_add(1_000_000_000)), + ); + + let balance_before = pallet_subtensor::Pallet::::get_coldkey_balance(&coldkey); + let alpha_before = + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid, + ); + + let expected_weight = + <::WeightInfo as SubtensorWeightInfo>::add_stake() + .saturating_add( + <::WeightInfo as SubtensorWeightInfo>::recycle_alpha(), + ); + + let mut env = MockEnv::new( + FunctionId::AddStakeRecycleV1, + coldkey, + (hotkey, netuid, TaoBalance::from(tao_amount_raw)).encode(), + ) + .with_expected_weight(expected_weight); + + let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); + match ret { + RetVal::Converging(code) => { + assert_ne!(code, Output::Success as u32, "should not succeed") + } + _ => panic!("unexpected return value"), + } + + // Verify full rollback: balance and stake unchanged + let balance_after = pallet_subtensor::Pallet::::get_coldkey_balance(&coldkey); + let alpha_after = + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid, + ); + + assert_eq!( + balance_before, balance_after, + "balance should be unchanged after rollback" + ); + assert_eq!( + alpha_before, alpha_after, + "stake should be unchanged after rollback" + ); + }); +} + +#[test] +fn add_stake_burn_rollback_on_burn_failure() { + mock::new_test_ext(1).execute_with(|| { + let owner_hotkey = U256::from(12201); + let owner_coldkey = U256::from(12202); + let coldkey = U256::from(12301); + let hotkey = U256::from(12302); + let min_stake = DefaultMinStake::::get(); + let tao_amount_raw = min_stake.to_u64().saturating_mul(200); + + let netuid = mock::add_dynamic_network(&owner_hotkey, &owner_coldkey); + + // Set up very low reserves so burn will fail with InsufficientLiquidity + mock::setup_reserves( + netuid, + TaoBalance::from(1_000_u64), + AlphaBalance::from(1_000_u64), + ); + + mock::register_ok_neuron(netuid, hotkey, coldkey, 0); + + add_balance_to_coldkey_account( + &coldkey, + TaoBalance::from(tao_amount_raw.saturating_add(1_000_000_000)), + ); + + let balance_before = pallet_subtensor::Pallet::::get_coldkey_balance(&coldkey); + let alpha_before = + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid, + ); + + let expected_weight = + <::WeightInfo as SubtensorWeightInfo>::add_stake() + .saturating_add( + <::WeightInfo as SubtensorWeightInfo>::burn_alpha(), + ); + + let mut env = MockEnv::new( + FunctionId::AddStakeBurnV1, + coldkey, + (hotkey, netuid, TaoBalance::from(tao_amount_raw)).encode(), + ) + .with_expected_weight(expected_weight); + + let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); + match ret { + RetVal::Converging(code) => { + assert_ne!(code, Output::Success as u32, "should not succeed") + } + _ => panic!("unexpected return value"), + } + + // Verify full rollback: balance and stake unchanged + let balance_after = pallet_subtensor::Pallet::::get_coldkey_balance(&coldkey); + let alpha_after = + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid, + ); + + assert_eq!( + balance_before, balance_after, + "balance should be unchanged after rollback" + ); + assert_eq!( + alpha_before, alpha_after, + "stake should be unchanged after rollback" + ); + }); +} + +#[test] +fn get_stake_info_returns_encoded_runtime_value() { + mock::new_test_ext(1).execute_with(|| { + let owner_hotkey = U256::from(1); + let owner_coldkey = U256::from(2); + let hotkey = U256::from(11); + let coldkey = U256::from(22); + let netuid = mock::add_dynamic_network(&owner_hotkey, &owner_coldkey); + mock::register_ok_neuron(netuid, hotkey, coldkey, 0); + + let expected = + pallet_subtensor::Pallet::::get_stake_info_for_hotkey_coldkey_netuid( + hotkey, coldkey, netuid, + ) + .encode(); + + let mut env = MockEnv::new( + FunctionId::GetStakeInfoForHotkeyColdkeyNetuidV1, + coldkey, + (hotkey, coldkey, netuid).encode(), + ); + + let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); + + assert_success(ret); + assert_eq!(env.output(), expected.as_slice()); + assert!(env.charged_weight().is_none()); + }); +} + +#[test] +fn add_stake_success_updates_stake_and_returns_success_code() { + mock::new_test_ext(1).execute_with(|| { + let owner_hotkey = U256::from(1); + let owner_coldkey = U256::from(2); + let coldkey = U256::from(101); + let hotkey = U256::from(202); + let min_stake = DefaultMinStake::::get(); + let amount_raw = min_stake.to_u64().saturating_mul(10); + let amount: TaoBalance = amount_raw.into(); + + let netuid = mock::add_dynamic_network(&owner_hotkey, &owner_coldkey); + mock::setup_reserves( + netuid, + (amount_raw * 1_000_000).into(), + AlphaBalance::from(amount_raw * 10_000_000), + ); + mock::register_ok_neuron(netuid, hotkey, coldkey, 0); + + add_balance_to_coldkey_account(&coldkey, amount_raw.into()); + + assert!( + pallet_subtensor::Pallet::::get_total_stake_for_hotkey(&hotkey).is_zero() + ); + + let expected_weight = <::WeightInfo as SubtensorWeightInfo>::add_stake(); + + let mut env = MockEnv::new( + FunctionId::AddStakeV1, + coldkey, + (hotkey, netuid, amount).encode(), + ) + .with_expected_weight(expected_weight); + + let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); + + assert_success(ret); + assert_eq!(env.charged_weight(), Some(expected_weight)); + + let total_stake = + pallet_subtensor::Pallet::::get_total_stake_for_hotkey(&hotkey); + assert!(total_stake > TaoBalance::ZERO); + }); +} + +#[test] +fn remove_stake_with_no_stake_returns_amount_too_low() { + mock::new_test_ext(1).execute_with(|| { + let owner_hotkey = U256::from(1); + let owner_coldkey = U256::from(2); + let coldkey = U256::from(301); + let hotkey = U256::from(302); + let netuid = mock::add_dynamic_network(&owner_hotkey, &owner_coldkey); + mock::register_ok_neuron(netuid, hotkey, coldkey, 0); + + let min_stake = DefaultMinStake::::get(); + let amount: AlphaBalance = AlphaBalance::from(min_stake.to_u64()); + + let expected_weight = <::WeightInfo as SubtensorWeightInfo>::remove_stake(); + let mut env = MockEnv::new( + FunctionId::RemoveStakeV1, + coldkey, + (hotkey, netuid, amount).encode(), + ) + .with_expected_weight(expected_weight); + + let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); + + match ret { + RetVal::Converging(code) => { + assert_eq!(code, Output::AmountTooLow as u32, "mismatched error output") + } + _ => panic!("unexpected return value"), + } + assert_eq!(env.charged_weight(), Some(expected_weight)); + assert!( + pallet_subtensor::Pallet::::get_total_stake_for_hotkey(&hotkey).is_zero() + ); + }); +} + +#[test] +fn unstake_all_success_unstakes_balance() { + mock::new_test_ext(1).execute_with(|| { + let owner_hotkey = U256::from(4001); + let owner_coldkey = U256::from(4002); + let coldkey = U256::from(5001); + let hotkey = U256::from(5002); + let min_stake = DefaultMinStake::::get(); + let stake_amount_raw = min_stake.to_u64().saturating_mul(200); + let netuid = mock::add_dynamic_network(&owner_hotkey, &owner_coldkey); + + mock::setup_reserves( + netuid, + stake_amount_raw.saturating_mul(10).into(), + AlphaBalance::from(stake_amount_raw.saturating_mul(20)), + ); + + mock::register_ok_neuron(netuid, hotkey, coldkey, 0); + add_balance_to_coldkey_account(&coldkey, (stake_amount_raw + 1_000_000_000).into()); + + assert_ok!(pallet_subtensor::Pallet::::add_stake( + RawOrigin::Signed(coldkey).into(), + hotkey, + netuid, + stake_amount_raw.into(), + )); + + mock::remove_stake_rate_limit_for_tests(&hotkey, &coldkey, netuid); + + let expected_weight = <::WeightInfo as SubtensorWeightInfo>::unstake_all(); + + let pre_balance = pallet_subtensor::Pallet::::get_coldkey_balance(&coldkey); + + let mut env = MockEnv::new(FunctionId::UnstakeAllV1, coldkey, hotkey.encode()) + .with_expected_weight(expected_weight); + + let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); + assert_success(ret); + assert_eq!(env.charged_weight(), Some(expected_weight)); + + let remaining_alpha = + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid, + ); + assert!(remaining_alpha <= AlphaBalance::from(1_000)); + + let post_balance = pallet_subtensor::Pallet::::get_coldkey_balance(&coldkey); + assert!(post_balance > pre_balance); + }); +} + +#[test] +fn get_alpha_price_returns_encoded_price() { + mock::new_test_ext(1).execute_with(|| { + let owner_hotkey = U256::from(8001); + let owner_coldkey = U256::from(8002); + let caller = U256::from(8003); + + let netuid = mock::add_dynamic_network(&owner_hotkey, &owner_coldkey); + + // Set up reserves to establish a price + let tao_reserve = TaoBalance::from(150_000_000_000u64); + let alpha_reserve = AlphaBalance::from(100_000_000_000u64); + mock::setup_reserves(netuid, tao_reserve, alpha_reserve); + + // Get expected price from swap handler + let expected_price = + as SwapHandler>::current_alpha_price( + netuid.into(), + ); + let expected_price_scaled = expected_price.saturating_mul(U96F32::from_num(1_000_000_000)); + let expected_price_u64: u64 = expected_price_scaled.saturating_to_num(); + + let mut env = MockEnv::new(FunctionId::GetAlphaPriceV1, caller, netuid.encode()); + + let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); + assert_success(ret); + assert!(env.charged_weight().is_none()); // Decode the output let output_price: u64 = Decode::decode(&mut &env.output()[..]).unwrap(); @@ -1005,3 +1640,796 @@ fn get_alpha_price_returns_encoded_price() { ); }); } + +/// `Caller*` dispatch uses `env.origin()` via `convert_origin`; with [`MockEnv`] both match +/// `Signed(caller)`, so outcomes align with non-`Caller` arms. Weight expectations match the shared +/// `dispatch_*_v1` helpers used by each pair. +mod caller_dispatch_tests { + use super::*; + + #[test] + fn caller_add_stake_success_updates_stake_and_returns_success_code() { + mock::new_test_ext(1).execute_with(|| { + let owner_hotkey = U256::from(1); + let owner_coldkey = U256::from(2); + let coldkey = U256::from(10101); + let hotkey = U256::from(10202); + let min_stake = DefaultMinStake::::get(); + let amount_raw = min_stake.to_u64().saturating_mul(10); + let amount: TaoBalance = amount_raw.into(); + + let netuid = mock::add_dynamic_network(&owner_hotkey, &owner_coldkey); + mock::setup_reserves( + netuid, + (amount_raw * 1_000_000).into(), + AlphaBalance::from(amount_raw * 10_000_000), + ); + mock::register_ok_neuron(netuid, hotkey, coldkey, 0); + + mock::add_balance_to_coldkey_account( + &coldkey, + amount_raw.into(), + ); + + let expected_weight = <::WeightInfo as SubtensorWeightInfo>::add_stake(); + + let mut env = MockEnv::new( + FunctionId::CallerAddStakeV1, + coldkey, + (hotkey, netuid, amount).encode(), + ) + .with_expected_weight(expected_weight); + + let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); + assert_success(ret); + assert_eq!(env.charged_weight(), Some(expected_weight)); + + let total_stake = + pallet_subtensor::Pallet::::get_total_stake_for_hotkey(&hotkey); + assert!(total_stake > TaoBalance::ZERO); + }); + } + + #[test] + fn caller_remove_stake_with_no_stake_returns_amount_too_low() { + mock::new_test_ext(1).execute_with(|| { + let owner_hotkey = U256::from(1); + let owner_coldkey = U256::from(2); + let coldkey = U256::from(30301); + let hotkey = U256::from(30302); + let netuid = mock::add_dynamic_network(&owner_hotkey, &owner_coldkey); + mock::register_ok_neuron(netuid, hotkey, coldkey, 0); + + let min_stake = DefaultMinStake::::get(); + let amount: AlphaBalance = AlphaBalance::from(min_stake.to_u64()); + + let expected_weight = <::WeightInfo as SubtensorWeightInfo>::remove_stake(); + let mut env = MockEnv::new( + FunctionId::CallerRemoveStakeV1, + coldkey, + (hotkey, netuid, amount).encode(), + ) + .with_expected_weight(expected_weight); + + let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); + match ret { + RetVal::Converging(code) => { + assert_eq!(code, Output::AmountTooLow as u32, "mismatched error output") + } + _ => panic!("unexpected return value"), + } + assert_eq!(env.charged_weight(), Some(expected_weight)); + }); + } + + #[test] + fn caller_unstake_all_success_unstakes_balance() { + mock::new_test_ext(1).execute_with(|| { + let owner_hotkey = U256::from(40001); + let owner_coldkey = U256::from(40002); + let coldkey = U256::from(50001); + let hotkey = U256::from(50002); + let min_stake = DefaultMinStake::::get(); + let stake_amount_raw = min_stake.to_u64().saturating_mul(200); + let netuid = mock::add_dynamic_network(&owner_hotkey, &owner_coldkey); + + mock::setup_reserves( + netuid, + stake_amount_raw.saturating_mul(10).into(), + AlphaBalance::from(stake_amount_raw.saturating_mul(20)), + ); + + mock::register_ok_neuron(netuid, hotkey, coldkey, 0); + mock::add_balance_to_coldkey_account( + &coldkey, + (stake_amount_raw + 1_000_000_000).into(), + ); + + assert_ok!(pallet_subtensor::Pallet::::add_stake( + RawOrigin::Signed(coldkey).into(), + hotkey, + netuid, + stake_amount_raw.into(), + )); + + mock::remove_stake_rate_limit_for_tests(&hotkey, &coldkey, netuid); + + let expected_weight = <::WeightInfo as SubtensorWeightInfo>::unstake_all(); + + let pre_balance = pallet_subtensor::Pallet::::get_coldkey_balance(&coldkey); + + let mut env = MockEnv::new(FunctionId::CallerUnstakeAllV1, coldkey, hotkey.encode()) + .with_expected_weight(expected_weight); + + let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); + assert_success(ret); + + let remaining_alpha = + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid, + ); + assert!(remaining_alpha <= AlphaBalance::from(1_000)); + + let post_balance = + pallet_subtensor::Pallet::::get_coldkey_balance(&coldkey); + assert!(post_balance > pre_balance); + }); + } + + #[test] + fn caller_unstake_all_alpha_success_moves_stake_to_root() { + mock::new_test_ext(1).execute_with(|| { + let owner_hotkey = U256::from(41001); + let owner_coldkey = U256::from(41002); + let coldkey = U256::from(51001); + let hotkey = U256::from(51002); + let min_stake = DefaultMinStake::::get(); + let stake_amount_raw = min_stake.to_u64().saturating_mul(220); + let netuid = mock::add_dynamic_network(&owner_hotkey, &owner_coldkey); + + mock::setup_reserves( + netuid, + stake_amount_raw.saturating_mul(20).into(), + AlphaBalance::from(stake_amount_raw.saturating_mul(30)), + ); + + mock::register_ok_neuron(netuid, hotkey, coldkey, 0); + mock::add_balance_to_coldkey_account( + &coldkey, + (stake_amount_raw + 1_000_000_000).into(), + ); + + assert_ok!(pallet_subtensor::Pallet::::add_stake( + RawOrigin::Signed(coldkey).into(), + hotkey, + netuid, + stake_amount_raw.into(), + )); + + mock::remove_stake_rate_limit_for_tests(&hotkey, &coldkey, netuid); + + let expected_weight = <::WeightInfo as SubtensorWeightInfo>::unstake_all_alpha(); + + let mut env = MockEnv::new( + FunctionId::CallerUnstakeAllAlphaV1, + coldkey, + hotkey.encode(), + ) + .with_expected_weight(expected_weight); + + let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); + assert_success(ret); + + let subnet_alpha = + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid, + ); + assert!(subnet_alpha <= AlphaBalance::from(1_000)); + + let root_alpha = + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &coldkey, + NetUid::ROOT, + ); + assert!(root_alpha > AlphaBalance::ZERO); + }); + } + + #[test] + fn caller_move_stake_success_moves_alpha_between_hotkeys() { + mock::new_test_ext(1).execute_with(|| { + let owner_hotkey = U256::from(42001); + let owner_coldkey = U256::from(42002); + let coldkey = U256::from(52001); + let origin_hotkey = U256::from(52002); + let destination_hotkey = U256::from(52003); + + let min_stake = DefaultMinStake::::get(); + let stake_amount_raw = min_stake.to_u64().saturating_mul(240); + + let netuid = mock::add_dynamic_network(&owner_hotkey, &owner_coldkey); + mock::setup_reserves( + netuid, + stake_amount_raw.saturating_mul(15).into(), + AlphaBalance::from(stake_amount_raw.saturating_mul(25)), + ); + + mock::register_ok_neuron(netuid, origin_hotkey, coldkey, 0); + mock::register_ok_neuron(netuid, destination_hotkey, coldkey, 1); + + mock::add_balance_to_coldkey_account( + &coldkey, + (stake_amount_raw + 1_000_000_000).into(), + ); + + assert_ok!(pallet_subtensor::Pallet::::add_stake( + RawOrigin::Signed(coldkey).into(), + origin_hotkey, + netuid, + stake_amount_raw.into(), + )); + + mock::remove_stake_rate_limit_for_tests(&origin_hotkey, &coldkey, netuid); + + let alpha_before = + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + netuid, + ); + let alpha_to_move: AlphaBalance = (alpha_before.to_u64() / 2).into(); + + let expected_weight = <::WeightInfo as SubtensorWeightInfo>::move_stake(); + + let mut env = MockEnv::new( + FunctionId::CallerMoveStakeV1, + coldkey, + ( + origin_hotkey, + destination_hotkey, + netuid, + netuid, + alpha_to_move, + ) + .encode(), + ) + .with_expected_weight(expected_weight); + + let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); + assert_success(ret); + + let origin_alpha_after = + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + netuid, + ); + let destination_alpha_after = + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + &destination_hotkey, + &coldkey, + netuid, + ); + + assert_eq!(origin_alpha_after, alpha_before - alpha_to_move); + assert_eq!(destination_alpha_after, alpha_to_move); + }); + } + + #[test] + fn caller_transfer_stake_success_moves_between_coldkeys() { + mock::new_test_ext(1).execute_with(|| { + let owner_hotkey = U256::from(43001); + let owner_coldkey = U256::from(43002); + let origin_coldkey = U256::from(53001); + let destination_coldkey = U256::from(53002); + let hotkey = U256::from(53003); + + let min_stake = DefaultMinStake::::get(); + let stake_amount_raw = min_stake.to_u64().saturating_mul(250); + + let netuid = mock::add_dynamic_network(&owner_hotkey, &owner_coldkey); + mock::setup_reserves( + netuid, + stake_amount_raw.saturating_mul(15).into(), + AlphaBalance::from(stake_amount_raw.saturating_mul(25)), + ); + + mock::register_ok_neuron(netuid, hotkey, origin_coldkey, 0); + + mock::add_balance_to_coldkey_account( + &origin_coldkey, + (stake_amount_raw + 1_000_000_000).into(), + ); + + assert_ok!(pallet_subtensor::Pallet::::add_stake( + RawOrigin::Signed(origin_coldkey).into(), + hotkey, + netuid, + stake_amount_raw.into(), + )); + + mock::remove_stake_rate_limit_for_tests(&hotkey, &origin_coldkey, netuid); + + let alpha_before = + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &origin_coldkey, + netuid, + ); + let alpha_to_transfer: AlphaBalance = (alpha_before.to_u64() / 3).into(); + + let expected_weight = <::WeightInfo as SubtensorWeightInfo>::transfer_stake(); + + let mut env = MockEnv::new( + FunctionId::CallerTransferStakeV1, + origin_coldkey, + ( + destination_coldkey, + hotkey, + netuid, + netuid, + alpha_to_transfer, + ) + .encode(), + ) + .with_expected_weight(expected_weight); + + let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); + assert_success(ret); + + let origin_alpha_after = + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &origin_coldkey, + netuid, + ); + let destination_alpha_after = + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &destination_coldkey, + netuid, + ); + + assert_eq!(origin_alpha_after, alpha_before - alpha_to_transfer); + assert_eq!(destination_alpha_after, alpha_to_transfer); + }); + } + + #[test] + fn caller_swap_stake_success_moves_between_subnets() { + mock::new_test_ext(1).execute_with(|| { + let owner_hotkey_a = U256::from(44001); + let owner_coldkey_a = U256::from(44002); + let owner_hotkey_b = U256::from(44003); + let owner_coldkey_b = U256::from(44004); + let coldkey = U256::from(54001); + let hotkey = U256::from(54002); + + let min_stake = DefaultMinStake::::get(); + let stake_amount_raw = min_stake.to_u64().saturating_mul(260); + + let netuid_a = mock::add_dynamic_network(&owner_hotkey_a, &owner_coldkey_a); + let netuid_b = mock::add_dynamic_network(&owner_hotkey_b, &owner_coldkey_b); + + mock::setup_reserves( + netuid_a, + stake_amount_raw.saturating_mul(18).into(), + AlphaBalance::from(stake_amount_raw.saturating_mul(30)), + ); + mock::setup_reserves( + netuid_b, + stake_amount_raw.saturating_mul(20).into(), + AlphaBalance::from(stake_amount_raw.saturating_mul(28)), + ); + + mock::register_ok_neuron(netuid_a, hotkey, coldkey, 0); + mock::register_ok_neuron(netuid_b, hotkey, coldkey, 1); + + mock::add_balance_to_coldkey_account( + &coldkey, + (stake_amount_raw + 1_000_000_000).into(), + ); + + assert_ok!(pallet_subtensor::Pallet::::add_stake( + RawOrigin::Signed(coldkey).into(), + hotkey, + netuid_a, + stake_amount_raw.into(), + )); + + mock::remove_stake_rate_limit_for_tests(&hotkey, &coldkey, netuid_a); + + let alpha_origin_before = + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid_a, + ); + let alpha_destination_before = + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid_b, + ); + let alpha_to_swap: AlphaBalance = (alpha_origin_before.to_u64() / 3).into(); + + let expected_weight = <::WeightInfo as SubtensorWeightInfo>::swap_stake(); + + let mut env = MockEnv::new( + FunctionId::CallerSwapStakeV1, + coldkey, + (hotkey, netuid_a, netuid_b, alpha_to_swap).encode(), + ) + .with_expected_weight(expected_weight); + + let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); + assert_success(ret); + + let alpha_origin_after = + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid_a, + ); + let alpha_destination_after = + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid_b, + ); + + assert!(alpha_origin_after < alpha_origin_before); + assert!(alpha_destination_after > alpha_destination_before); + }); + } + + #[test] + fn caller_add_stake_limit_success_executes_within_price_guard() { + mock::new_test_ext(1).execute_with(|| { + let owner_hotkey = U256::from(45001); + let owner_coldkey = U256::from(45002); + let coldkey = U256::from(55001); + let hotkey = U256::from(55002); + let amount_raw: u64 = 900_000_000_000; + let limit_price: TaoBalance = 24_000_000_000u64.into(); + + let netuid = mock::add_dynamic_network(&owner_hotkey, &owner_coldkey); + + mock::setup_reserves( + netuid, + TaoBalance::from(150_000_000_000_u64), + AlphaBalance::from(100_000_000_000_u64), + ); + + mock::register_ok_neuron(netuid, hotkey, coldkey, 0); + + mock::add_balance_to_coldkey_account( + &coldkey, + (amount_raw + 1_000_000_000).into(), + ); + + let stake_before = + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid, + ); + let balance_before = + pallet_subtensor::Pallet::::get_coldkey_balance(&coldkey); + + let expected_weight = <::WeightInfo as SubtensorWeightInfo>::add_stake_limit(); + + let mut env = MockEnv::new( + FunctionId::CallerAddStakeLimitV1, + coldkey, + ( + hotkey, + netuid, + TaoBalance::from(amount_raw), + limit_price, + true, + ) + .encode(), + ) + .with_expected_weight(expected_weight); + + let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); + assert_success(ret); + + let stake_after = + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid, + ); + let balance_after = + pallet_subtensor::Pallet::::get_coldkey_balance(&coldkey); + + assert!(stake_after > stake_before); + assert!(stake_after > AlphaBalance::ZERO); + assert!(balance_after < balance_before); + }); + } + + #[test] + fn caller_remove_stake_limit_success_respects_price_limit() { + mock::new_test_ext(1).execute_with(|| { + let owner_hotkey = U256::from(46001); + let owner_coldkey = U256::from(46002); + let coldkey = U256::from(56001); + let hotkey = U256::from(56002); + let stake_amount_raw: u64 = 320_000_000_000; + + let netuid = mock::add_dynamic_network(&owner_hotkey, &owner_coldkey); + mock::setup_reserves( + netuid, + TaoBalance::from(120_000_000_000_u64), + AlphaBalance::from(100_000_000_000_u64), + ); + + mock::register_ok_neuron(netuid, hotkey, coldkey, 0); + + mock::add_balance_to_coldkey_account( + &coldkey, + TaoBalance::from(stake_amount_raw + 1_000_000_000), + ); + + assert_ok!(pallet_subtensor::Pallet::::add_stake( + RawOrigin::Signed(coldkey).into(), + hotkey, + netuid, + stake_amount_raw.into(), + )); + + mock::remove_stake_rate_limit_for_tests(&hotkey, &coldkey, netuid); + + let alpha_before = + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid, + ); + + let current_price = + ::SwapInterface::current_alpha_price( + netuid.into(), + ); + let limit_price_value = (current_price.to_num::() * 990_000_000f64).round() as u64; + let limit_price: TaoBalance = limit_price_value.into(); + + let alpha_to_unstake: AlphaBalance = (alpha_before.to_u64() / 2).into(); + + let expected_weight = <::WeightInfo as SubtensorWeightInfo>::remove_stake_limit(); + + let balance_before = + pallet_subtensor::Pallet::::get_coldkey_balance(&coldkey); + + let mut env = MockEnv::new( + FunctionId::CallerRemoveStakeLimitV1, + coldkey, + (hotkey, netuid, alpha_to_unstake, limit_price, true).encode(), + ) + .with_expected_weight(expected_weight); + + let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); + assert_success(ret); + assert_eq!(env.charged_weight(), Some(expected_weight)); + + let alpha_after = + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid, + ); + let balance_after = + pallet_subtensor::Pallet::::get_coldkey_balance(&coldkey); + + assert!(alpha_after < alpha_before); + assert!(balance_after > balance_before); + }); + } + + #[test] + fn caller_swap_stake_limit_matches_standard_slippage_path() { + mock::new_test_ext(1).execute_with(|| { + let owner_hotkey_a = U256::from(47001); + let owner_coldkey_a = U256::from(47002); + let owner_hotkey_b = U256::from(47003); + let owner_coldkey_b = U256::from(47004); + let coldkey = U256::from(57001); + let hotkey = U256::from(57002); + + let stake_alpha = AlphaBalance::from(150_000_000_000u64); + + let netuid_a = mock::add_dynamic_network(&owner_hotkey_a, &owner_coldkey_a); + let netuid_b = mock::add_dynamic_network(&owner_hotkey_b, &owner_coldkey_b); + + mock::setup_reserves( + netuid_a, + TaoBalance::from(150_000_000_000_u64), + AlphaBalance::from(110_000_000_000_u64), + ); + mock::setup_reserves( + netuid_b, + TaoBalance::from(120_000_000_000_u64), + AlphaBalance::from(90_000_000_000_u64), + ); + + mock::register_ok_neuron(netuid_a, hotkey, coldkey, 0); + mock::register_ok_neuron(netuid_b, hotkey, coldkey, 1); + + pallet_subtensor::Pallet::::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &coldkey, + netuid_a, + stake_alpha, + ); + + mock::remove_stake_rate_limit_for_tests(&hotkey, &coldkey, netuid_a); + + let alpha_origin_before = + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid_a, + ); + let alpha_destination_before = + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid_b, + ); + + let alpha_to_swap: AlphaBalance = (alpha_origin_before.to_u64() / 8).into(); + let limit_price: TaoBalance = 100u64.into(); + + let expected_weight = <::WeightInfo as SubtensorWeightInfo>::swap_stake_limit(); + + let mut env = MockEnv::new( + FunctionId::CallerSwapStakeLimitV1, + coldkey, + (hotkey, netuid_a, netuid_b, alpha_to_swap, limit_price, true).encode(), + ) + .with_expected_weight(expected_weight); + + let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); + assert_success(ret); + + let alpha_origin_after = + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid_a, + ); + let alpha_destination_after = + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid_b, + ); + + assert!(alpha_origin_after <= alpha_origin_before); + assert!(alpha_destination_after >= alpha_destination_before); + }); + } + + #[test] + fn caller_remove_stake_full_limit_success_with_limit_price() { + mock::new_test_ext(1).execute_with(|| { + let owner_hotkey = U256::from(48001); + let owner_coldkey = U256::from(48002); + let coldkey = U256::from(58001); + let hotkey = U256::from(58002); + let stake_amount_raw: u64 = 340_000_000_000; + + let netuid = mock::add_dynamic_network(&owner_hotkey, &owner_coldkey); + mock::setup_reserves( + netuid, + TaoBalance::from(130_000_000_000_u64), + AlphaBalance::from(110_000_000_000_u64), + ); + + mock::register_ok_neuron(netuid, hotkey, coldkey, 0); + + mock::add_balance_to_coldkey_account( + &coldkey, + TaoBalance::from(stake_amount_raw + 1_000_000_000), + ); + + assert_ok!(pallet_subtensor::Pallet::::add_stake( + RawOrigin::Signed(coldkey).into(), + hotkey, + netuid, + stake_amount_raw.into(), + )); + + mock::remove_stake_rate_limit_for_tests(&hotkey, &coldkey, netuid); + + let expected_weight = <::WeightInfo as SubtensorWeightInfo>::remove_stake_full_limit(); + + let balance_before = + pallet_subtensor::Pallet::::get_coldkey_balance(&coldkey); + + let mut env = MockEnv::new( + FunctionId::CallerRemoveStakeFullLimitV1, + coldkey, + (hotkey, netuid, Option::::None).encode(), + ) + .with_expected_weight(expected_weight); + + let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); + assert_success(ret); + assert_eq!(env.charged_weight(), Some(expected_weight)); + + let alpha_after = + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid, + ); + let balance_after = + pallet_subtensor::Pallet::::get_coldkey_balance(&coldkey); + + assert!(alpha_after.is_zero()); + assert!(balance_after > balance_before); + }); + } + + #[test] + fn caller_set_coldkey_auto_stake_hotkey_success_sets_destination() { + mock::new_test_ext(1).execute_with(|| { + let owner_hotkey = U256::from(49001); + let owner_coldkey = U256::from(49002); + let coldkey = U256::from(59001); + let hotkey = U256::from(59002); + + let netuid = mock::add_dynamic_network(&owner_hotkey, &owner_coldkey); + + pallet_subtensor::Owner::::insert(hotkey, coldkey); + pallet_subtensor::OwnedHotkeys::::insert(coldkey, vec![hotkey]); + pallet_subtensor::Uids::::insert(netuid, hotkey, 0u16); + + assert_eq!( + pallet_subtensor::AutoStakeDestination::::get(coldkey, netuid), + None + ); + + let expected_weight = <::WeightInfo as SubtensorWeightInfo>::set_coldkey_auto_stake_hotkey(); + + let mut env = MockEnv::new( + FunctionId::CallerSetColdkeyAutoStakeHotkeyV1, + coldkey, + (netuid, hotkey).encode(), + ) + .with_expected_weight(expected_weight); + + let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); + assert_success(ret); + assert_eq!(env.charged_weight(), Some(expected_weight)); + + assert_eq!( + pallet_subtensor::AutoStakeDestination::::get(coldkey, netuid), + Some(hotkey) + ); + }); + } + + #[test] + fn caller_add_proxy_success_creates_proxy_relationship() { + mock::new_test_ext(1).execute_with(|| { + let delegator = U256::from(60001); + let delegate = U256::from(60002); + + mock::add_balance_to_coldkey_account(&delegator, 1_000_000_000.into()); + + let mut env = MockEnv::new(FunctionId::CallerAddProxyV1, delegator, delegate.encode()); + + let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); + assert_success(ret); + + let proxies = pallet_subtensor_proxy::Proxies::::get(delegator).0; + assert_eq!(proxies.len(), 1); + }); + } + + #[test] + fn caller_remove_proxy_success_removes_proxy_relationship() { + mock::new_test_ext(1).execute_with(|| { + let delegator = U256::from(70001); + let delegate = U256::from(70002); + + mock::add_balance_to_coldkey_account(&delegator, 1_000_000_000.into()); + + let mut add_env = + MockEnv::new(FunctionId::CallerAddProxyV1, delegator, delegate.encode()); + assert_success(SubtensorChainExtension::::dispatch(&mut add_env).unwrap()); + + let mut remove_env = MockEnv::new( + FunctionId::CallerRemoveProxyV1, + delegator, + delegate.encode(), + ); + let ret = SubtensorChainExtension::::dispatch(&mut remove_env).unwrap(); + assert_success(ret); + + let proxies_after = pallet_subtensor_proxy::Proxies::::get(delegator).0; + assert_eq!(proxies_after.len(), 0); + }); + } +} diff --git a/chain-extensions/src/types.rs b/chain-extensions/src/types.rs index ee6298ad5b..5c799d71e2 100644 --- a/chain-extensions/src/types.rs +++ b/chain-extensions/src/types.rs @@ -21,6 +21,24 @@ pub enum FunctionId { AddProxyV1 = 13, RemoveProxyV1 = 14, GetAlphaPriceV1 = 15, + RecycleAlphaV1 = 16, + BurnAlphaV1 = 17, + AddStakeRecycleV1 = 18, + AddStakeBurnV1 = 19, + CallerAddStakeV1 = 20, + CallerRemoveStakeV1 = 21, + CallerUnstakeAllV1 = 22, + CallerUnstakeAllAlphaV1 = 23, + CallerMoveStakeV1 = 24, + CallerTransferStakeV1 = 25, + CallerSwapStakeV1 = 26, + CallerAddStakeLimitV1 = 27, + CallerRemoveStakeLimitV1 = 28, + CallerSwapStakeLimitV1 = 29, + CallerRemoveStakeFullLimitV1 = 30, + CallerSetColdkeyAutoStakeHotkeyV1 = 31, + CallerAddProxyV1 = 32, + CallerRemoveProxyV1 = 33, } #[derive(PartialEq, Eq, Copy, Clone, Encode, Decode, Debug)] @@ -66,6 +84,12 @@ pub enum Output { ProxyNoSelfProxy = 18, /// Proxy relationship not found ProxyNotFound = 19, + /// A system account cannot be used in this operation + CannotUseSystemAccount = 20, + /// Cannot burn or recycle on root subnet + CannotBurnOrRecycleOnRootSubnet = 21, + /// Subtoken is disabled for this subnet + SubtokenDisabled = 22, } impl From for Output { @@ -77,6 +101,7 @@ impl From for Output { match error_text { Some("NotEnoughBalanceToStake") => Output::NotEnoughBalanceToStake, Some("NonAssociatedColdKey") => Output::NonAssociatedColdKey, + Some("CannotUseSystemAccount") => Output::CannotUseSystemAccount, Some("BalanceWithdrawalError") => Output::BalanceWithdrawalError, Some("HotKeyNotRegisteredInSubNet") => Output::NotRegistered, Some("HotKeyAccountNotExists") => Output::NotRegistered, @@ -93,7 +118,47 @@ impl From for Output { Some("Duplicate") => Output::ProxyDuplicate, Some("NoSelfProxy") => Output::ProxyNoSelfProxy, Some("NotFound") => Output::ProxyNotFound, + Some("CannotBurnOrRecycleOnRootSubnet") => Output::CannotBurnOrRecycleOnRootSubnet, + Some("SubtokenDisabled") => Output::SubtokenDisabled, _ => Output::RuntimeError, } } } + +#[cfg(test)] +mod function_id_tests { + use super::FunctionId; + use num_enum::TryFromPrimitive; + + #[test] + fn caller_variants_have_stable_discriminants() { + assert_eq!(FunctionId::GetAlphaPriceV1 as u16, 15); + assert_eq!(FunctionId::RecycleAlphaV1 as u16, 16); + assert_eq!(FunctionId::BurnAlphaV1 as u16, 17); + assert_eq!(FunctionId::AddStakeRecycleV1 as u16, 18); + assert_eq!(FunctionId::AddStakeBurnV1 as u16, 19); + assert_eq!(FunctionId::CallerAddStakeV1 as u16, 20); + assert_eq!(FunctionId::CallerRemoveStakeV1 as u16, 21); + assert_eq!(FunctionId::CallerUnstakeAllV1 as u16, 22); + assert_eq!(FunctionId::CallerUnstakeAllAlphaV1 as u16, 23); + assert_eq!(FunctionId::CallerMoveStakeV1 as u16, 24); + assert_eq!(FunctionId::CallerTransferStakeV1 as u16, 25); + assert_eq!(FunctionId::CallerSwapStakeV1 as u16, 26); + assert_eq!(FunctionId::CallerAddStakeLimitV1 as u16, 27); + assert_eq!(FunctionId::CallerRemoveStakeLimitV1 as u16, 28); + assert_eq!(FunctionId::CallerSwapStakeLimitV1 as u16, 29); + assert_eq!(FunctionId::CallerRemoveStakeFullLimitV1 as u16, 30); + assert_eq!(FunctionId::CallerSetColdkeyAutoStakeHotkeyV1 as u16, 31); + assert_eq!(FunctionId::CallerAddProxyV1 as u16, 32); + assert_eq!(FunctionId::CallerRemoveProxyV1 as u16, 33); + } + + #[test] + fn caller_ids_roundtrip_try_from_primitive() { + for id in 16u16..=33u16 { + let v = FunctionId::try_from_primitive(id) + .unwrap_or_else(|_| panic!("try_from_primitive failed for {id}")); + assert_eq!(v as u16, id); + } + } +} diff --git a/common/src/lib.rs b/common/src/lib.rs index 70fa42c32b..a606dca71d 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -246,8 +246,6 @@ pub trait TokenReserve { pub trait BalanceOps { fn tao_balance(account_id: &AccountId) -> TaoBalance; fn alpha_balance(netuid: NetUid, coldkey: &AccountId, hotkey: &AccountId) -> AlphaBalance; - fn increase_balance(coldkey: &AccountId, tao: TaoBalance); - fn decrease_balance(coldkey: &AccountId, tao: TaoBalance) -> Result; fn increase_stake( coldkey: &AccountId, hotkey: &AccountId, diff --git a/contract-tests/bittensor/lib.rs b/contract-tests/bittensor/lib.rs index 8867d017d8..f3dd8c8ff2 100755 --- a/contract-tests/bittensor/lib.rs +++ b/contract-tests/bittensor/lib.rs @@ -22,6 +22,24 @@ pub enum FunctionId { AddProxyV1 = 13, RemoveProxyV1 = 14, GetAlphaPriceV1 = 15, + RecycleAlphaV1 = 16, + BurnAlphaV1 = 17, + AddStakeRecycleV1 = 18, + AddStakeBurnV1 = 19, + CallerAddStakeV1 = 20, + CallerRemoveStakeV1 = 21, + CallerUnstakeAllV1 = 22, + CallerUnstakeAllAlphaV1 = 23, + CallerMoveStakeV1 = 24, + CallerTransferStakeV1 = 25, + CallerSwapStakeV1 = 26, + CallerAddStakeLimitV1 = 27, + CallerRemoveStakeLimitV1 = 28, + CallerSwapStakeLimitV1 = 29, + CallerRemoveStakeFullLimitV1 = 30, + CallerSetColdkeyAutoStakeHotkeyV1 = 31, + CallerAddProxyV1 = 32, + CallerRemoveProxyV1 = 33, } #[ink::chain_extension(extension = 0x1000)] @@ -130,6 +148,127 @@ pub trait RuntimeReadWrite { #[ink(function = 15)] fn get_alpha_price(netuid: u16) -> u64; + + #[ink(function = 16)] + fn recycle_alpha( + hotkey: ::AccountId, + amount: u64, + netuid: u16, + ) -> u64; + + #[ink(function = 17)] + fn burn_alpha( + hotkey: ::AccountId, + amount: u64, + netuid: u16, + ) -> u64; + + #[ink(function = 18)] + fn add_stake_recycle( + hotkey: ::AccountId, + netuid: u16, + amount: u64, + ) -> u64; + + #[ink(function = 19)] + fn add_stake_burn( + hotkey: ::AccountId, + netuid: u16, + amount: u64, + ) -> u64; + + #[ink(function = 20)] + fn caller_add_stake( + hotkey: ::AccountId, + netuid: u16, + amount: u64, + ); + + #[ink(function = 21)] + fn caller_remove_stake( + hotkey: ::AccountId, + netuid: u16, + amount: u64, + ); + + #[ink(function = 22)] + fn caller_unstake_all(hotkey: ::AccountId); + + #[ink(function = 23)] + fn caller_unstake_all_alpha(hotkey: ::AccountId); + + #[ink(function = 24)] + fn caller_move_stake( + origin_hotkey: ::AccountId, + destination_hotkey: ::AccountId, + origin_netuid: u16, + destination_netuid: u16, + amount: u64, + ); + + #[ink(function = 25)] + fn caller_transfer_stake( + destination_coldkey: ::AccountId, + hotkey: ::AccountId, + origin_netuid: u16, + destination_netuid: u16, + amount: u64, + ); + + #[ink(function = 26)] + fn caller_swap_stake( + hotkey: ::AccountId, + origin_netuid: u16, + destination_netuid: u16, + amount: u64, + ); + + #[ink(function = 27)] + fn caller_add_stake_limit( + hotkey: ::AccountId, + netuid: u16, + amount: u64, + limit_price: u64, + allow_partial: bool, + ); + + #[ink(function = 28)] + fn caller_remove_stake_limit( + hotkey: ::AccountId, + netuid: u16, + amount: u64, + limit_price: u64, + allow_partial: bool, + ); + + #[ink(function = 29)] + fn caller_swap_stake_limit( + hotkey: ::AccountId, + origin_netuid: u16, + destination_netuid: u16, + amount: u64, + limit_price: u64, + allow_partial: bool, + ); + + #[ink(function = 30)] + fn caller_remove_stake_full_limit( + hotkey: ::AccountId, + netuid: u16, + limit_price: u64, + ); + + #[ink(function = 31)] + fn caller_set_coldkey_auto_stake_hotkey( + netuid: u16, + hotkey: ::AccountId, + ); + + #[ink(function = 32)] + fn caller_add_proxy(delegate: ::AccountId); + + #[ink(function = 33)] + fn caller_remove_proxy(delegate: ::AccountId); } #[ink::scale_derive(Encode, Decode, TypeInfo)] @@ -412,5 +551,255 @@ mod bittensor { .get_alpha_price(netuid) .map_err(|_e| ReadWriteErrorCode::ReadFailed) } + + #[ink(message)] + pub fn recycle_alpha( + &self, + hotkey: [u8; 32], + amount: u64, + netuid: u16, + ) -> Result { + self.env() + .extension() + .recycle_alpha(hotkey.into(), amount, netuid) + .map_err(|_e| ReadWriteErrorCode::WriteFailed) + } + + #[ink(message)] + pub fn burn_alpha( + &self, + hotkey: [u8; 32], + amount: u64, + netuid: u16, + ) -> Result { + self.env() + .extension() + .burn_alpha(hotkey.into(), amount, netuid) + .map_err(|_e| ReadWriteErrorCode::WriteFailed) + } + + #[ink(message)] + pub fn add_stake_recycle( + &self, + hotkey: [u8; 32], + netuid: u16, + amount: u64, + ) -> Result { + self.env() + .extension() + .add_stake_recycle(hotkey.into(), netuid, amount) + .map_err(|_e| ReadWriteErrorCode::WriteFailed) + } + + #[ink(message)] + pub fn add_stake_burn( + &self, + hotkey: [u8; 32], + netuid: u16, + amount: u64, + ) -> Result { + self.env() + .extension() + .add_stake_burn(hotkey.into(), netuid, amount) + .map_err(|_e| ReadWriteErrorCode::WriteFailed) + } + + #[ink(message)] + pub fn caller_add_stake( + &self, + hotkey: [u8; 32], + netuid: u16, + amount: u64, + ) -> Result<(), ReadWriteErrorCode> { + self.env() + .extension() + .caller_add_stake(hotkey.into(), netuid, amount) + .map_err(|_e| ReadWriteErrorCode::WriteFailed) + } + + #[ink(message)] + pub fn caller_remove_stake( + &self, + hotkey: [u8; 32], + netuid: u16, + amount: u64, + ) -> Result<(), ReadWriteErrorCode> { + self.env() + .extension() + .caller_remove_stake(hotkey.into(), netuid, amount) + .map_err(|_e| ReadWriteErrorCode::WriteFailed) + } + + #[ink(message)] + pub fn caller_unstake_all(&self, hotkey: [u8; 32]) -> Result<(), ReadWriteErrorCode> { + self.env() + .extension() + .caller_unstake_all(hotkey.into()) + .map_err(|_e| ReadWriteErrorCode::WriteFailed) + } + + #[ink(message)] + pub fn caller_unstake_all_alpha(&self, hotkey: [u8; 32]) -> Result<(), ReadWriteErrorCode> { + self.env() + .extension() + .caller_unstake_all_alpha(hotkey.into()) + .map_err(|_e| ReadWriteErrorCode::WriteFailed) + } + + #[ink(message)] + pub fn caller_move_stake( + &self, + origin_hotkey: [u8; 32], + destination_hotkey: [u8; 32], + origin_netuid: u16, + destination_netuid: u16, + amount: u64, + ) -> Result<(), ReadWriteErrorCode> { + self.env() + .extension() + .caller_move_stake( + origin_hotkey.into(), + destination_hotkey.into(), + origin_netuid, + destination_netuid, + amount, + ) + .map_err(|_e| ReadWriteErrorCode::WriteFailed) + } + + #[ink(message)] + pub fn caller_transfer_stake( + &self, + destination_coldkey: [u8; 32], + hotkey: [u8; 32], + origin_netuid: u16, + destination_netuid: u16, + amount: u64, + ) -> Result<(), ReadWriteErrorCode> { + self.env() + .extension() + .caller_transfer_stake( + destination_coldkey.into(), + hotkey.into(), + origin_netuid, + destination_netuid, + amount, + ) + .map_err(|_e| ReadWriteErrorCode::WriteFailed) + } + + #[ink(message)] + pub fn caller_swap_stake( + &self, + hotkey: [u8; 32], + origin_netuid: u16, + destination_netuid: u16, + amount: u64, + ) -> Result<(), ReadWriteErrorCode> { + self.env() + .extension() + .caller_swap_stake(hotkey.into(), origin_netuid, destination_netuid, amount) + .map_err(|_e| ReadWriteErrorCode::WriteFailed) + } + + #[ink(message)] + pub fn caller_add_stake_limit( + &self, + hotkey: [u8; 32], + netuid: u16, + amount: u64, + limit_price: u64, + allow_partial: bool, + ) -> Result<(), ReadWriteErrorCode> { + self.env() + .extension() + .caller_add_stake_limit(hotkey.into(), netuid, amount, limit_price, allow_partial) + .map_err(|_e| ReadWriteErrorCode::WriteFailed) + } + + #[ink(message)] + pub fn caller_remove_stake_limit( + &self, + hotkey: [u8; 32], + netuid: u16, + amount: u64, + limit_price: u64, + allow_partial: bool, + ) -> Result<(), ReadWriteErrorCode> { + self.env() + .extension() + .caller_remove_stake_limit( + hotkey.into(), + netuid, + amount, + limit_price, + allow_partial, + ) + .map_err(|_e| ReadWriteErrorCode::WriteFailed) + } + + #[ink(message)] + pub fn caller_swap_stake_limit( + &self, + hotkey: [u8; 32], + origin_netuid: u16, + destination_netuid: u16, + amount: u64, + limit_price: u64, + allow_partial: bool, + ) -> Result<(), ReadWriteErrorCode> { + self.env() + .extension() + .caller_swap_stake_limit( + hotkey.into(), + origin_netuid, + destination_netuid, + amount, + limit_price, + allow_partial, + ) + .map_err(|_e| ReadWriteErrorCode::WriteFailed) + } + + #[ink(message)] + pub fn caller_remove_stake_full_limit( + &self, + hotkey: [u8; 32], + netuid: u16, + limit_price: u64, + ) -> Result<(), ReadWriteErrorCode> { + self.env() + .extension() + .caller_remove_stake_full_limit(hotkey.into(), netuid, limit_price) + .map_err(|_e| ReadWriteErrorCode::WriteFailed) + } + + #[ink(message)] + pub fn caller_set_coldkey_auto_stake_hotkey( + &self, + netuid: u16, + hotkey: [u8; 32], + ) -> Result<(), ReadWriteErrorCode> { + self.env() + .extension() + .caller_set_coldkey_auto_stake_hotkey(netuid, hotkey.into()) + .map_err(|_e| ReadWriteErrorCode::WriteFailed) + } + + #[ink(message)] + pub fn caller_add_proxy(&self, delegate: [u8; 32]) -> Result<(), ReadWriteErrorCode> { + self.env() + .extension() + .caller_add_proxy(delegate.into()) + .map_err(|_e| ReadWriteErrorCode::WriteFailed) + } + + #[ink(message)] + pub fn caller_remove_proxy(&self, delegate: [u8; 32]) -> Result<(), ReadWriteErrorCode> { + self.env() + .extension() + .caller_remove_proxy(delegate.into()) + .map_err(|_e| ReadWriteErrorCode::WriteFailed) + } } } diff --git a/contract-tests/src/subtensor.ts b/contract-tests/src/subtensor.ts index 2b3b5d8be1..478148909d 100644 --- a/contract-tests/src/subtensor.ts +++ b/contract-tests/src/subtensor.ts @@ -365,6 +365,9 @@ export async function setTargetRegistrationsPerInterval( call: internal_tx.decodedCall, }); await waitForTransactionWithRetry(api, tx, alice); + + const value = await api.query.SubtensorModule.TargetRegistrationsPerInterval.getValue(netuid) + assert.equal(1000, value) } // Disable admin freeze window and owner hyperparam rate limiting for tests @@ -421,6 +424,142 @@ export async function setNetworkLastLockCost(api: TypedApi, defau assert.equal(defaultNetworkLastLockCost, valueOnChain) } +export function getSubnetAccountId(netuid: number): string { + // Hardcode to speed up tests + const NETUID_TO_ACCOUNT_ID: Record = { + 0: "5EYCAe5jLQhn6ofDSvqF6iY53erXNkwhyE1aCEgvi1NNs91F", + 1: "5EYCAe5jLQhn6ofDSvqWqk5fA9XiqK3ahtx5kBNmAqF78mqL", + 2: "5EYCAe5jLQhn6ofDSvqnamdFGeCvHs9TSZtbJ84bdf7qQRc6", + 3: "5EYCAe5jLQhn6ofDSvr4KoAqP8t7kRFLBEq6r4kS6UzZgCb5", + 4: "5EYCAe5jLQhn6ofDSvrL4piRVdZKCyMCuumcQ1SGZJsHwmeE", + 5: "5EYCAe5jLQhn6ofDSvrborG1c8EWfXT5eai7wx8728k2DHK7", + 6: "5EYCAe5jLQhn6ofDSvrsYsobicui85YxPFedVtowUxckUuF8", + 7: "5EYCAe5jLQhn6ofDSvs9HuMBq7auadeq7vb93qVmwnVUkg5A", + 8: "5EYCAe5jLQhn6ofDSvsR2vtmwcG73BkhrbXebnBcQcND2Bdh", + 9: "5EYCAe5jLQhn6ofDSvsgmxSN46wJVjrabGUA9isSsSEwHnFy", + 10: "5EYCAe5jLQhn6ofDSvsxWyyxAbcVxHxTKwQfhfZHLG7fZUJG", + 11: "5EYCAe5jLQhn6ofDSvtEG1XYH6HhQr4L4cMBFcF7o5zPpyA3", + 12: "5EYCAe5jLQhn6ofDSvtW1358PaxtsQACoHHgoYvxFus86kJK", + 13: "5EYCAe5jLQhn6ofDSvtmk4ciW5e6KxG5XxECMVcnijjrN8rz", + 14: "5EYCAe5jLQhn6ofDSvu3V6AJcaKHnWMxGdAhuSJdBZcadwDn", + 15: "5EYCAe5jLQhn6ofDSvuKE7htj4zVF4Tq1J7DTNzTePVJucfX", + 16: "5EYCAe5jLQhn6ofDSvuay9FUqZfghcZhjy3j1KgJ7DN3BDc2", + 17: "5EYCAe5jLQhn6ofDSvuriAo4x4LtAAfaUdzEZGN8a3EmSncG", + 18: "5EYCAe5jLQhn6ofDSvv8TCLf4Z25cimTDJvk7D3y2s7ViZEm", + 19: "5EYCAe5jLQhn6ofDSvvQCDtFB3hH5GsKwysFf9joVgzDytnb", + 20: "5EYCAe5jLQhn6ofDSvvfwFRqHYNUXpyCgeomD6RdxWrxFpQR", + 21: "5EYCAe5jLQhn6ofDSvvwgGyRQ33fzP55RKkGm37URLjgXG7M", + 22: "5EYCAe5jLQhn6ofDSvwDRJX1WXisSwAx9zgnJyoJtAcQo59Y", + 23: "5EYCAe5jLQhn6ofDSvwVAL4bd2Q4uVGptfdHrvV9LzV94VBb", + 24: "5EYCAe5jLQhn6ofDSvwkuMcBjX5GN3NhdLZoQsAyopMsL7A7", + 25: "5EYCAe5jLQhn6ofDSvx2eP9mr1kTpbUaN1WJxorpGeEbbfgG", + 26: "5EYCAe5jLQhn6ofDSvxJPQhMxWRfH9aT6gSpWkYejU7KsbGp", + 27: "5EYCAe5jLQhn6ofDSvxa8SEx516rjhgKqMPL4hEVCHz49DPw", + 28: "5EYCAe5jLQhn6ofDSvxqsTnYBVn4CFnCa2KqcdvKf7rnQo7f", + 29: "5EYCAe5jLQhn6ofDSvy7cVL8HzTFeot5JhGMAacA7wjWgPix", + 30: "5EYCAe5jLQhn6ofDSvyPMWsiQV8T7Myx3NCriXHzamcEwyqa", + 31: "5EYCAe5jLQhn6ofDSvyf6YRJWyoeZv5pn39NGTyq3bUyDc8k", + 32: "5EYCAe5jLQhn6ofDSvyvqZxtdUUr2UBhWi5spQffWRMhV5hU", + 33: "5EYCAe5jLQhn6ofDSvzCabWUjyA3V2HaFP2PNMMVyFERkxPm", + 34: "5EYCAe5jLQhn6ofDSvzUKd44rTqEwaPSz3xtvJ3LS57A2Td3", + 35: "5EYCAe5jLQhn6ofDSvzk4ebexxWSQ8VKiiuQUEjAttytJ8Nx", + 36: "5EYCAe5jLQhn6ofDSw11og9F5TBdrgbCTPqv2BR1MircZp68", + 37: "5EYCAe5jLQhn6ofDSw1HYhgqBwrqKEh5C4nRa86qpYjLqCQd", + 38: "5EYCAe5jLQhn6ofDSw1ZHjERJSY2mnnwvjiw84ngHNc56t9n", + 39: "5EYCAe5jLQhn6ofDSw1q2kn1QwDEELtpfQfSg1UWkCUoNVFh", + 40: "5EYCAe5jLQhn6ofDSw26mnKbXRtRgtzhQ5bxDxAMD2MXeL6A", + 41: "5EYCAe5jLQhn6ofDSw2NWosBdvZd9T6a8kYTmtrBfrEFusmX", + 42: "5EYCAe5jLQhn6ofDSw2eFqQmkREpc1CSsRUyKqY28g6zBbxD", + 43: "5EYCAe5jLQhn6ofDSw2uzrxMruv24ZJKc6RUsnDrbVyiT4uZ", + 44: "5EYCAe5jLQhn6ofDSw3BjtVwyQbDX7QCLmMzRiuh4KrSienC", + 45: "5EYCAe5jLQhn6ofDSw3TUv3Y5uGQyfW55SJVyfbXX9jAzHBc", + 46: "5EYCAe5jLQhn6ofDSw3jDwb8CPwcSDbwp7F1XcHMyybuFsV6", + 47: "5EYCAe5jLQhn6ofDSw3zxy8iJtcotmhpYnBX5YyCSoUdXS9C", + 48: "5EYCAe5jLQhn6ofDSw4GhzgJRPJ1MKohHT82dVf2udMMo854", + 49: "5EYCAe5jLQhn6ofDSw4YT2DtXsyCosua284YBSLsNTE64sjn", + 50: "5EYCAe5jLQhn6ofDSw4pC3mUeNeQGS1Sko13jP2hqH6pLJc1", + 51: "5EYCAe5jLQhn6ofDSw55w5K4ksKbiz7KVTwZHKiYJ6yYc62p", + 52: "5EYCAe5jLQhn6ofDSw5Mg6resMzoBYDCE8t4qGQNkvrGsYLi", + 53: "5EYCAe5jLQhn6ofDSw5dR8QEyrfze6K4xopaPD6DDkj19BcH", + 54: "5EYCAe5jLQhn6ofDSw5uA9wq6MMC6eQwhUm5w9n3gabjR243", + 55: "5EYCAe5jLQhn6ofDSw6AuBVRCr2PZCWpS9hbV6Tt9QUTghER", + 56: "5EYCAe5jLQhn6ofDSw6SeD31KLhb1kchApe7339icEMBxKr7", + 57: "5EYCAe5jLQhn6ofDSw6iPEabRqNnUJiZuVacayqZ54DvDhGB", + 58: "5EYCAe5jLQhn6ofDSw6z8G8BYL3yvrpSeAX88vXPXt6eVRoY", + 59: "5EYCAe5jLQhn6ofDSw7FsHfmepjBPQvKNqTdgsDDzhyNky3e", + 60: "5EYCAe5jLQhn6ofDSw7XcKDMmKQNqy2C7WQ9Eou4TXr72f1B", + 61: "5EYCAe5jLQhn6ofDSw7oMLkwsp5aJX84rBLenkatvMiqJC2W", + 62: "5EYCAe5jLQhn6ofDSw856NJXzJkmm5DwarHALhGjPBbZa5wW", + 63: "5EYCAe5jLQhn6ofDSw8LqPr86oRyDdKpKXDftdxZr1UHqWzq", + 64: "5EYCAe5jLQhn6ofDSw8caRPiDJ7AgBRh4CABSaeQJqM278gq", + 65: "5EYCAe5jLQhn6ofDSw8tKSwJKnnN8jXZns6gzXLEmfDkNtXu", + 66: "5EYCAe5jLQhn6ofDSw9A4UUtSHTZbHdSXY3CYU25EV6UeLH2", + 67: "5EYCAe5jLQhn6ofDSw9RoW2UYn8m3qjKGCyi6QhuhJyCv9nu", + 68: "5EYCAe5jLQhn6ofDSw9hYXa4fGoxWPqBzsvDeMPkA8qwBecQ", + 69: "5EYCAe5jLQhn6ofDSw9yHZ7emmV9xww4jYrjCJ5acxifTH7b", + 70: "5EYCAe5jLQhn6ofDSwAF2afEtGAMRW2wUDoEkEmR5nbPiuFf", + 71: "5EYCAe5jLQhn6ofDSwAWmcCpzkqYt48pCtjkJBTFYcU7ziWG", + 72: "5EYCAe5jLQhn6ofDSwAnWdkR7FWkLcEgwZgFr8961SLrGPJp", + 73: "5EYCAe5jLQhn6ofDSwB4FfJ1DkBwoALZgEcmQ4pvUGDaXxGw", + 74: "5EYCAe5jLQhn6ofDSwBKzgqbLEs9FiSSQuZGx1Wkw66JoNQY", + 75: "5EYCAe5jLQhn6ofDSwBbjiPBSjYLiGYK9aVnVxCbPuy357eQ", + 76: "5EYCAe5jLQhn6ofDSwBsUjvmZEDYApeBtFSJ3ttRrjqmLmRP", + 77: "5EYCAe5jLQhn6ofDSwC9DmUMfitjdNk4cvNobqaGKZiVcSd4", + 78: "5EYCAe5jLQhn6ofDSwCQxo1wnDZw5vqwMbKK9nG6nPbDsr3v", + 79: "5EYCAe5jLQhn6ofDSwCghpZXtiF8YUwp6GFphiwwFDTx9ZXw", + 80: "5EYCAe5jLQhn6ofDSwCxSr781CvL133gpwCLFfdmi3LgRGUs", + 81: "5EYCAe5jLQhn6ofDSwDEBsei7hbXTb9ZZc8qocKcAsDQgmDH", + 82: "5EYCAe5jLQhn6ofDSwDVvuCJECGiv9FSJH5MMZ1Sdh68xe6G", + 83: "5EYCAe5jLQhn6ofDSwDmfvjtLgwvNhMK2x1ruVhH6WxsE2Rh", + 84: "5EYCAe5jLQhn6ofDSwE3QxHUTBd7qFTBmcxNTSP7ZLqbVqHX", + 85: "5EYCAe5jLQhn6ofDSwEK9yq4ZgJKHoZ4WHtt1P4x2AiKmP2V", + 86: "5EYCAe5jLQhn6ofDSwEau1NegAyWkMewExqPZKknUzb42r36", + 87: "5EYCAe5jLQhn6ofDSwEre2vEnfeiCukoydmu7GScwpTnJa5d", + 88: "5EYCAe5jLQhn6ofDSwF8P4TpuAKufTrgiJiQfD8TQeLWaGop", + 89: "5EYCAe5jLQhn6ofDSwFQ861R1f1781xZSyevD9pHsUDEqiBR", + 90: "5EYCAe5jLQhn6ofDSwFfs7Z189gJaa4SBebRm6W8LJ5y7dfH", + 91: "5EYCAe5jLQhn6ofDSwFwc96bEeMW38AJvKXwK3Bxo7xhP3yn", + 92: "5EYCAe5jLQhn6ofDSwGDMAeBM92hVgGBezUSrysoFwqReqrS", + 93: "5EYCAe5jLQhn6ofDSwGV6CBmTdhtxEN4PfQxQvZdimi9vW9r", + 94: "5EYCAe5jLQhn6ofDSwGkqDjMa8P6QnTw8LMTxsFUBbatC8C5", + 95: "5EYCAe5jLQhn6ofDSwH2aFGwgd4HsLZos1HyWowJeRTcTVsg", + 96: "5EYCAe5jLQhn6ofDSwHJKGpXo7jVKtfgbgEV4kd97FLLjBeJ", + 97: "5EYCAe5jLQhn6ofDSwHa4JN7ucQgnSmZLMAzchJya5D4zq8v", + 98: "5EYCAe5jLQhn6ofDSwHqoKui275tEzsS527WAdzp2u5oGNSd", + 99: "5EYCAe5jLQhn6ofDSwJ7YMTJ8bm5hYyJoh41iageVixXYH59", + 100: "5EYCAe5jLQhn6ofDSwJPHNztF6SHA75BYMzXGXNUxYqFoj9g", + 101: "5EYCAe5jLQhn6ofDSwJf2QYUMb7UcfB4H2w2pU4KRNhz5GP5", + 102: "5EYCAe5jLQhn6ofDSwJvmS64U5ng5DGw1hsYNQk9tCaiLvoS", + 103: "5EYCAe5jLQhn6ofDSwKCWTdeaaTsXmNokNp3vMRzM2TScknA", + 104: "5EYCAe5jLQhn6ofDSwKUFVBEh594zKUgV3kZUJ7porLAtE76", + 105: "5EYCAe5jLQhn6ofDSwKjzWipoZpGSsaZDih52EofGgCu9mbP", + 106: "5EYCAe5jLQhn6ofDSwL1jYGQv4VTuRgRxPdaaBVVjW5dRU9u", + 107: "5EYCAe5jLQhn6ofDSwLHUZp12ZAfMynJh4a688BLCKxMhEMq", + 108: "5EYCAe5jLQhn6ofDSwLZDbMb93qrpXtBRjWbg4sAf9q5xtB8", + 109: "5EYCAe5jLQhn6ofDSwLpxcuBFYX4H5z4AQT7E1Z17yhpELLK", + 110: "5EYCAe5jLQhn6ofDSwM6heSmN3CFje5vu5PcmxEqaoaYW1KP", + 111: "5EYCAe5jLQhn6ofDSwMNSfzMUXsTCCBodkL8Ktvg3dTGmYbX", + 112: "5EYCAe5jLQhn6ofDSwMeBhXwb2YeekHgNRGdsqcWWTL13NLP", + 113: "5EYCAe5jLQhn6ofDSwMuvj5XhXDr7JPZ76D9RnJLyHCjK2Zy", + 114: "5EYCAe5jLQhn6ofDSwNBfkd7p1u3ZrVRqm9eyizBS75TaPgK", + 115: "5EYCAe5jLQhn6ofDSwNTQnAhvWaF2QbJaS6AXfg1tvxBrDUN", + 116: "5EYCAe5jLQhn6ofDSwNj9oiJ31FSUxhBK72g5cMrMkpv7iJx", + 117: "5EYCAe5jLQhn6ofDSwNztqFt9VvdwWo43myBdZ3gpahePQpf", + 118: "5EYCAe5jLQhn6ofDSwPGdroUFzbqQ4tvnSuhBVjXHQaNet2o", + 119: "5EYCAe5jLQhn6ofDSwPYNtM4NVH2rczoX7rCjSRMkET6vioH", + 120: "5EYCAe5jLQhn6ofDSwPp7uteUyxEKB6gFnniHP7CD4KqCQDN", + 121: "5EYCAe5jLQhn6ofDSwQ5rwSEbUdRmjCYzTjDqKo2ftCZTubr", + 122: "5EYCAe5jLQhn6ofDSwQMbxyphyJdEHJRj8fjPGUs8i5HjcA3", + 123: "5EYCAe5jLQhn6ofDSwQdLzXQpTypgqQJTocEwDAhbXx21Awy", + 124: "5EYCAe5jLQhn6ofDSwQu624zvxf29PWBCUYkV9rY4MpkGu1f", + 125: "5EYCAe5jLQhn6ofDSwRAq3cb3TLDbwc3w9VG36YNXBhUYKDi", + 126: "5EYCAe5jLQhn6ofDSwRSa5AB9x1R4VhvfpRmb3ECz1aCp2ze", + 127: "5EYCAe5jLQhn6ofDSwRiK6hmGSgcX3ooQVNH8yv3SqSw5mpH", + 128: "5EYCAe5jLQhn6ofDSwRz48FMNwMoybug9AJngvbsufKfME2t", + } + + return NETUID_TO_ACCOUNT_ID[netuid]; +} export async function getStake(api: TypedApi, hotkey: string, coldkey: string, netuid: number): Promise { const value = (await api.query.SubtensorModule.AlphaV2.getValue(hotkey, coldkey, netuid)); @@ -437,4 +576,13 @@ export async function getStake(api: TypedApi, hotkey: string, col } return result; +} + +export async function setAdminFreezeWindow(api: TypedApi) { + const alice = getAliceSigner() + const window = 0; + const internalCall = api.tx.AdminUtils.sudo_set_admin_freeze_window({ window: window }) + const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }) + await waitForTransactionWithRetry(api, tx, alice) + assert.equal(window, await api.query.SubtensorModule.AdminFreezeWindow.getValue()) } \ No newline at end of file diff --git a/contract-tests/test/crowdloan.precompile.test.ts b/contract-tests/test/crowdloan.precompile.test.ts index 70c93ca5f4..2a0655bc63 100644 --- a/contract-tests/test/crowdloan.precompile.test.ts +++ b/contract-tests/test/crowdloan.precompile.test.ts @@ -1,458 +1,204 @@ import * as assert from "assert"; -import { PublicClient } from "viem"; -import { ETH_LOCAL_URL } from "../src/config"; -import { generateRandomEthersWallet, getPublicClient } from "../src/utils"; import { ethers } from "ethers"; -import { ICROWDLOAN_ADDRESS, ICrowdloanABI } from "../src/contracts/crowdloan"; import { Binary, TypedApi } from "polkadot-api"; import { devnet } from "@polkadot-api/descriptors"; -import { getAliceSigner, getDevnetApi, waitForFinalizedBlock } from "../src/substrate"; -import { forceSetBalanceToEthAddress } from "../src/subtensor"; -import { decodeAddress } from "@polkadot/util-crypto"; -import { u8aToHex } from "@polkadot/util"; +import { ICROWDLOAN_ADDRESS, ICrowdloanABI } from "../src/contracts/crowdloan"; import { convertH160ToSS58 } from "../src/address-utils"; +import { generateRandomEthersWallet } from "../src/utils"; +import { + getAliceSigner, + getDevnetApi, + waitForFinalizedBlock, +} from "../src/substrate"; +import { forceSetBalanceToEthAddress } from "../src/subtensor"; -describe("Test Crowdloan precompile", () => { - let publicClient: PublicClient; - let api: TypedApi - - const alice = getAliceSigner(); - const wallet1 = generateRandomEthersWallet(); - const wallet2 = generateRandomEthersWallet(); - const wallet3 = generateRandomEthersWallet(); - const wallet4 = generateRandomEthersWallet(); - - const crowdloanContract = new ethers.Contract(ICROWDLOAN_ADDRESS, ICrowdloanABI, wallet1); - - before(async () => { - publicClient = await getPublicClient(ETH_LOCAL_URL) - api = await getDevnetApi() - - await forceSetBalanceToEthAddress(api, wallet1.address) - await forceSetBalanceToEthAddress(api, wallet2.address) - await forceSetBalanceToEthAddress(api, wallet3.address) - await forceSetBalanceToEthAddress(api, wallet4.address) - }) - - it("gets an existing crowdloan created on substrate side", async () => { - const nextId = await api.query.Crowdloan.NextCrowdloanId.getValue(); - const end = await api.query.System.Number.getValue() + 100; - - await api.tx.Crowdloan.create({ - deposit: BigInt(15_000_000_000), // 15 TAO - min_contribution: BigInt(1_000_000_000), // 1 TAO - cap: BigInt(100_000_000_000), // 100 TAO - end, - target_address: undefined, - call: api.tx.System.remark({ remark: Binary.fromText("foo") }).decodedCall - }).signAndSubmit(alice); - - const crowdloan = await api.query.Crowdloan.Crowdloans.getValue(nextId); - const crowdloanInfo = await crowdloanContract.getCrowdloan(nextId); - - assert.ok(crowdloan); - assert.equal(crowdloanInfo[0], u8aToHex(decodeAddress(crowdloan.creator))); - assert.equal(crowdloanInfo[1], crowdloan.deposit); - assert.equal(crowdloanInfo[2], crowdloan.min_contribution); - assert.equal(crowdloanInfo[3], crowdloan.end); - assert.equal(crowdloanInfo[4], crowdloan.cap); - assert.equal(crowdloanInfo[5], u8aToHex(decodeAddress(crowdloan.funds_account))); - assert.equal(crowdloanInfo[6], crowdloan.raised); - assert.equal(crowdloanInfo[7], false); // has_target_address - assert.equal(crowdloanInfo[8], u8aToHex(Uint8Array.from(Array(32).fill(0)))); // target_address - assert.equal(crowdloanInfo[9], false); // finalized - assert.equal(crowdloanInfo[10], 1); // contributors_count - }); - - it("creates a new crowdloan and retrieves it", async () => { - const deposit = BigInt(20_000_000_000); // 20 TAO - const minContribution = BigInt(2_000_000_000); // 2 TAO - const cap = BigInt(200_000_000_000); // 200 TAO - const end = await api.query.System.Number.getValue() + 100; - const targetAddress = generateRandomEthersWallet(); - - const nextId = await api.query.Crowdloan.NextCrowdloanId.getValue(); - - const tx = await crowdloanContract.create( - deposit, - minContribution, - cap, - end, - targetAddress - ); - await tx.wait(); - - const crowdloan = await api.query.Crowdloan.Crowdloans.getValue(nextId); - assert.ok(crowdloan); - assert.equal(crowdloan.creator, convertH160ToSS58(wallet1.address)); - assert.equal(crowdloan.deposit, deposit); - assert.equal(crowdloan.min_contribution, minContribution); - assert.equal(crowdloan.cap, cap); - assert.equal(crowdloan.end, end); - assert.equal(crowdloan.raised, deposit); - assert.equal(crowdloan.target_address, convertH160ToSS58(targetAddress.address)); - assert.equal(crowdloan.finalized, false); - assert.equal(crowdloan.contributors_count, 1); - - const crowdloanInfo = await crowdloanContract.getCrowdloan(nextId); - assert.equal(crowdloanInfo[0], u8aToHex(decodeAddress(crowdloan.creator))); - assert.equal(crowdloanInfo[1], crowdloan.deposit); - assert.equal(crowdloanInfo[2], crowdloan.min_contribution); - assert.equal(crowdloanInfo[3], crowdloan.end); - assert.equal(crowdloanInfo[4], crowdloan.cap); - assert.equal(crowdloanInfo[5], u8aToHex(decodeAddress(crowdloan.funds_account))); - assert.equal(crowdloanInfo[6], crowdloan.raised); - assert.equal(crowdloanInfo[7], true); // has_target_address - assert.equal(crowdloanInfo[8], u8aToHex(decodeAddress(crowdloan.target_address))); // target_address - assert.equal(crowdloanInfo[9], crowdloan.finalized); - assert.equal(crowdloanInfo[10], crowdloan.contributors_count); - }); - - it("contributes/withdraws to a crowdloan created on substrate side", async () => { - const nextId = await api.query.Crowdloan.NextCrowdloanId.getValue(); - const deposit = BigInt(15_000_000_000); // 15 TAO - const end = await api.query.System.Number.getValue() + 100; - - await api.tx.Crowdloan.create({ - deposit, - min_contribution: BigInt(1_000_000_000), // 1 TAO - cap: BigInt(100_000_000_000), // 100 TAO - end, - target_address: undefined, - call: api.tx.System.remark({ remark: Binary.fromText("foo") }).decodedCall - }).signAndSubmit(alice); - - let crowdloan = await api.query.Crowdloan.Crowdloans.getValue(nextId); - assert.ok(crowdloan); - assert.equal(crowdloan.raised, deposit); - assert.equal(crowdloan.contributors_count, 1); - - let crowdloanInfo = await crowdloanContract.getCrowdloan(nextId); - assert.equal(crowdloanInfo[6], deposit); - assert.equal(crowdloanInfo[10], 1); - - let balanceBefore = await api.query.System.Account.getValue(convertH160ToSS58(wallet1.address)); - - const contribution = BigInt(5_000_000_000); - const tx = await crowdloanContract.contribute(nextId, contribution); - await tx.wait(); - - let balanceAfter = await api.query.System.Account.getValue(convertH160ToSS58(wallet1.address)); - assert.ok(Number(balanceBefore.data.free - balanceAfter.data.free) - Number(contribution) < 1_000_000); - - crowdloan = await api.query.Crowdloan.Crowdloans.getValue(nextId); - assert.ok(crowdloan); - assert.equal(crowdloan.raised, deposit + contribution); - assert.equal(crowdloan.contributors_count, 2); - - crowdloanInfo = await crowdloanContract.getCrowdloan(nextId); - assert.equal(crowdloanInfo[6], deposit + contribution); - assert.equal(crowdloanInfo[10], 2); - - balanceBefore = await api.query.System.Account.getValue(convertH160ToSS58(wallet1.address)); - - const tx2 = await crowdloanContract.withdraw(nextId); - await tx2.wait(); - - balanceAfter = await api.query.System.Account.getValue(convertH160ToSS58(wallet1.address)); - assert.ok(Number(balanceAfter.data.free) - Number(balanceBefore.data.free + contribution) < 1_000_000); - - crowdloan = await api.query.Crowdloan.Crowdloans.getValue(nextId); - assert.ok(crowdloan); - assert.equal(crowdloan.raised, deposit); - assert.equal(crowdloan.contributors_count, 1); - - crowdloanInfo = await crowdloanContract.getCrowdloan(nextId); - assert.equal(crowdloanInfo[6], deposit); - assert.equal(crowdloanInfo[10], 1); - }); - - it("contributes/withdraws to a crowdloan", async () => { - const deposit = BigInt(20_000_000_000); // 20 TAO - const minContribution = BigInt(2_000_000_000); // 2 TAO - const cap = BigInt(200_000_000_000); // 200 TAO - const end = await api.query.System.Number.getValue() + 100; - const targetAddress = generateRandomEthersWallet(); - - const nextId = await api.query.Crowdloan.NextCrowdloanId.getValue(); - - let balanceBefore = await api.query.System.Account.getValue(convertH160ToSS58(wallet1.address)); - - let tx = await crowdloanContract.create( - deposit, - minContribution, - cap, - end, - targetAddress - ); - await tx.wait(); - - let balanceAfter = await api.query.System.Account.getValue(convertH160ToSS58(wallet1.address)); - assert.ok(Number(balanceBefore.data.free - balanceAfter.data.free) - Number(deposit) < 1_000_000); - - let crowdloan = await api.query.Crowdloan.Crowdloans.getValue(nextId); - assert.ok(crowdloan); - assert.equal(crowdloan.raised, deposit); - assert.equal(crowdloan.contributors_count, 1); - - let crowdloanInfo = await crowdloanContract.getCrowdloan(nextId); - assert.equal(crowdloanInfo[6], deposit); - assert.equal(crowdloanInfo[10], 1); - - balanceBefore = await api.query.System.Account.getValue(convertH160ToSS58(wallet2.address)); - - const contribution = BigInt(3_000_000_000); - const crowdloanContract2 = new ethers.Contract(ICROWDLOAN_ADDRESS, ICrowdloanABI, wallet2); - tx = await crowdloanContract2.contribute(nextId, contribution); - await tx.wait(); - - balanceAfter = await api.query.System.Account.getValue(convertH160ToSS58(wallet2.address)); - assert.ok(Number(balanceBefore.data.free - balanceAfter.data.free) - Number(contribution) < 1_000_000); - - crowdloan = await api.query.Crowdloan.Crowdloans.getValue(nextId); - assert.ok(crowdloan); - assert.equal(crowdloan.raised, deposit + contribution); - assert.equal(crowdloan.contributors_count, 2); - - crowdloanInfo = await crowdloanContract.getCrowdloan(nextId); - assert.equal(crowdloanInfo[6], deposit + contribution); - assert.equal(crowdloanInfo[10], 2); - - balanceBefore = await api.query.System.Account.getValue(convertH160ToSS58(wallet2.address)); - - const tx2 = await crowdloanContract2.withdraw(nextId); - await tx2.wait(); - - balanceAfter = await api.query.System.Account.getValue(convertH160ToSS58(wallet2.address)); - assert.ok(Number(balanceAfter.data.free) - Number(balanceBefore.data.free + contribution) < 1_000_000); - - crowdloan = await api.query.Crowdloan.Crowdloans.getValue(nextId); - assert.ok(crowdloan); - assert.equal(crowdloan.raised, deposit); - assert.equal(crowdloan.contributors_count, 1); - - crowdloanInfo = await crowdloanContract.getCrowdloan(nextId); - assert.equal(crowdloanInfo[6], deposit); - assert.equal(crowdloanInfo[10], 1); - }); - - it("finalizes a crowdloan", async () => { - const deposit = BigInt(20_000_000_000); // 20 TAO - const minContribution = BigInt(2_000_000_000); // 2 TAO - const cap = BigInt(100_000_000_000); // 200 TAO - const end = await api.query.System.Number.getValue() + 100; - const targetAddress = generateRandomEthersWallet(); - - const balanceBefore = await api.query.System.Account.getValue(convertH160ToSS58(targetAddress.address)); - assert.equal(balanceBefore.data.free, BigInt(0)); - - const nextId = await api.query.Crowdloan.NextCrowdloanId.getValue(); - - let tx = await crowdloanContract.create( - deposit, - minContribution, - cap, - end, - targetAddress - ); - await tx.wait() - - const contribution = cap - deposit; - const crowdloanContract2 = new ethers.Contract(ICROWDLOAN_ADDRESS, ICrowdloanABI, wallet2); - tx = await crowdloanContract2.contribute(nextId, contribution); - await tx.wait(); - - await waitForFinalizedBlock(api, end); - - tx = await crowdloanContract.finalize(nextId); - await tx.wait(); - - const crowdloan = await api.query.Crowdloan.Crowdloans.getValue(nextId); - assert.ok(crowdloan); - assert.equal(crowdloan.finalized, true); - - const crowdloanInfo = await crowdloanContract.getCrowdloan(nextId); - assert.equal(crowdloanInfo[9], true); - - const balanceAfter = await api.query.System.Account.getValue(convertH160ToSS58(targetAddress.address)); - assert.equal(balanceAfter.data.free, cap); - }); - - it("refunds/dissolves a crowdloan", async () => { - const deposit = BigInt(20_000_000_000); // 20 TAO - const minContribution = BigInt(2_000_000_000); // 2 TAO - const cap = BigInt(100_000_000_000); // 100 TAO - const end = await api.query.System.Number.getValue() + 100; - const targetAddress = generateRandomEthersWallet(); - - const nextId = await api.query.Crowdloan.NextCrowdloanId.getValue(); - - const balanceBefore1 = await api.query.System.Account.getValue(convertH160ToSS58(wallet1.address)); - let tx = await crowdloanContract.create( - deposit, - minContribution, - cap, - end, - targetAddress - ); - await tx.wait() - - const balanceBefore2 = await api.query.System.Account.getValue(convertH160ToSS58(wallet2.address)); - const contribution = BigInt(20_000_000_000); // 20 TAO - const crowdloanContract2 = new ethers.Contract(ICROWDLOAN_ADDRESS, ICrowdloanABI, wallet2); - tx = await crowdloanContract2.contribute(nextId, contribution); - await tx.wait(); - - const balanceBefore3 = await api.query.System.Account.getValue(convertH160ToSS58(wallet3.address)); - const crowdloanContract3 = new ethers.Contract(ICROWDLOAN_ADDRESS, ICrowdloanABI, wallet3); - tx = await crowdloanContract3.contribute(nextId, contribution); - await tx.wait(); - - const balanceBefore4 = await api.query.System.Account.getValue(convertH160ToSS58(wallet4.address)); - const crowdloanContract4 = new ethers.Contract(ICROWDLOAN_ADDRESS, ICrowdloanABI, wallet4); - tx = await crowdloanContract4.contribute(nextId, contribution); - await tx.wait(); - - await waitForFinalizedBlock(api, end); - - let crowdloan = await api.query.Crowdloan.Crowdloans.getValue(nextId); - assert.ok(crowdloan); - assert.equal(crowdloan.raised, deposit + contribution * BigInt(3)); - assert.equal(crowdloan.contributors_count, 4); - - let crowdloanInfo = await crowdloanContract.getCrowdloan(nextId); - assert.equal(crowdloanInfo[6], deposit + contribution * BigInt(3)); - assert.equal(crowdloanInfo[10], 4); - - tx = await crowdloanContract.refund(nextId); - await tx.wait(); - - const balanceAfter2 = await api.query.System.Account.getValue(convertH160ToSS58(wallet2.address)); - assert.ok(Number(balanceAfter2.data.free) - Number(balanceBefore2.data.free) < 1_000_000); - const balanceAfter3 = await api.query.System.Account.getValue(convertH160ToSS58(wallet3.address)); - assert.ok(Number(balanceAfter3.data.free) - Number(balanceBefore3.data.free) < 1_000_000); - const balanceAfter4 = await api.query.System.Account.getValue(convertH160ToSS58(wallet4.address)); - assert.ok(Number(balanceAfter4.data.free) - Number(balanceBefore4.data.free) < 1_000_000); - - crowdloan = await api.query.Crowdloan.Crowdloans.getValue(nextId); - assert.ok(crowdloan); - assert.equal(crowdloan.raised, deposit); - assert.equal(crowdloan.contributors_count, 1); - - crowdloanInfo = await crowdloanContract.getCrowdloan(nextId); - assert.equal(crowdloanInfo[6], deposit); - assert.equal(crowdloanInfo[10], 1); - - tx = await crowdloanContract.dissolve(nextId); - await tx.wait(); - - crowdloan = await api.query.Crowdloan.Crowdloans.getValue(nextId); - assert.equal(crowdloan, undefined); - - const balanceAfter1 = await api.query.System.Account.getValue(convertH160ToSS58(wallet1.address)); - assert.ok(Number(balanceAfter1.data.free) - Number(balanceBefore1.data.free) < 2_000_000); - }); - - it("updates the min contribution", async () => { - const deposit = BigInt(20_000_000_000); // 20 TAO - const minContribution = BigInt(1_000_000_000); // 1 TAO - const cap = BigInt(200_000_000_000); // 200 TAO - const end = await api.query.System.Number.getValue() + 100; - const targetAddress = generateRandomEthersWallet(); - - const nextId = await api.query.Crowdloan.NextCrowdloanId.getValue(); - - let tx = await crowdloanContract.create( - deposit, - minContribution, - cap, - end, - targetAddress - ); - await tx.wait(); - - const crowdloan = await api.query.Crowdloan.Crowdloans.getValue(nextId); - assert.ok(crowdloan); - assert.equal(crowdloan.min_contribution, BigInt(1_000_000_000)); - - const newMinContribution = BigInt(2_000_000_000); - tx = await crowdloanContract.updateMinContribution(nextId, newMinContribution); - await tx.wait(); - - const updatedCrowdloan = await api.query.Crowdloan.Crowdloans.getValue(nextId); - assert.ok(updatedCrowdloan); - assert.equal(updatedCrowdloan.min_contribution, newMinContribution); - - const updatedCrowdloanInfo = await crowdloanContract.getCrowdloan(nextId); - assert.equal(updatedCrowdloanInfo[2], newMinContribution); - }); - - it("updates the end", async () => { - const deposit = BigInt(20_000_000_000); // 20 TAO - const minContribution = BigInt(1_000_000_000); // 1 TAO - const cap = BigInt(200_000_000_000); // 200 TAO - const end = await api.query.System.Number.getValue() + 100; - const targetAddress = generateRandomEthersWallet(); - - const nextId = await api.query.Crowdloan.NextCrowdloanId.getValue(); - - const tx = await crowdloanContract.create( - deposit, - minContribution, - cap, - end, - targetAddress - ); - await tx.wait(); - - const crowdloan = await api.query.Crowdloan.Crowdloans.getValue(nextId); - assert.ok(crowdloan); - assert.equal(crowdloan.end, end); - - const newEnd = end + 200; - const tx2 = await crowdloanContract.updateEnd(nextId, newEnd); - await tx2.wait(); - - const updatedCrowdloan = await api.query.Crowdloan.Crowdloans.getValue(nextId); - assert.ok(updatedCrowdloan); - assert.equal(updatedCrowdloan.end, newEnd); - - const updatedCrowdloanInfo = await crowdloanContract.getCrowdloan(nextId); - assert.equal(updatedCrowdloanInfo[3], newEnd); - }); - - it("updates the cap", async () => { - const deposit = BigInt(20_000_000_000); // 20 TAO - const minContribution = BigInt(1_000_000_000); // 1 TAO - const cap = BigInt(200_000_000_000); // 200 TAO - const end = await api.query.System.Number.getValue() + 100; - const targetAddress = generateRandomEthersWallet(); - - const nextId = await api.query.Crowdloan.NextCrowdloanId.getValue(); - - const tx = await crowdloanContract.create( - deposit, - minContribution, - cap, - end, - targetAddress - ); - await tx.wait(); - - const crowdloan = await api.query.Crowdloan.Crowdloans.getValue(nextId); - assert.ok(crowdloan); - assert.equal(crowdloan.cap, BigInt(200_000_000_000)); - - const newCap = BigInt(300_000_000_000); - const tx2 = await crowdloanContract.updateCap(nextId, newCap); - await tx2.wait(); - - const updatedCrowdloan = await api.query.Crowdloan.Crowdloans.getValue(nextId); - assert.ok(updatedCrowdloan); - assert.equal(updatedCrowdloan.cap, newCap); - - const updatedCrowdloanInfo = await crowdloanContract.getCrowdloan(nextId); - assert.equal(updatedCrowdloanInfo[4], newCap); - }); -}); \ No newline at end of file +describe("Crowdloan precompile E2E balance smoke", () => { + let api: TypedApi; + + const alice = getAliceSigner(); + const wallet1 = generateRandomEthersWallet(); + const wallet2 = generateRandomEthersWallet(); + const wallet3 = generateRandomEthersWallet(); + const wallet4 = generateRandomEthersWallet(); + + const crowdloanContract = new ethers.Contract( + ICROWDLOAN_ADDRESS, + ICrowdloanABI, + wallet1, + ); + + before(async () => { + api = await getDevnetApi(); + + await forceSetBalanceToEthAddress(api, wallet1.address); + await forceSetBalanceToEthAddress(api, wallet2.address); + await forceSetBalanceToEthAddress(api, wallet3.address); + await forceSetBalanceToEthAddress(api, wallet4.address); + }); + + it("charges and refunds balances through create, contribute, withdraw, refund, and dissolve", async () => { + const deposit = BigInt(20_000_000_000); + const minContribution = BigInt(2_000_000_000); + const cap = BigInt(100_000_000_000); + const end = (await api.query.System.Number.getValue()) + 100; + const targetAddress = generateRandomEthersWallet(); + const nextId = await api.query.Crowdloan.NextCrowdloanId.getValue(); + + const creatorBalanceBeforeCreate = await api.query.System.Account.getValue( + convertH160ToSS58(wallet1.address), + ); + let tx = await crowdloanContract.create( + deposit, + minContribution, + cap, + end, + targetAddress, + ); + await tx.wait(); + + const creatorBalanceAfterCreate = await api.query.System.Account.getValue( + convertH160ToSS58(wallet1.address), + ); + assert.ok( + Number( + creatorBalanceBeforeCreate.data.free - + creatorBalanceAfterCreate.data.free, + ) - + Number(deposit) < + 1_000_000, + ); + + const contribution = BigInt(20_000_000_000); + const crowdloanContract2 = new ethers.Contract( + ICROWDLOAN_ADDRESS, + ICrowdloanABI, + wallet2, + ); + const contributorBalanceBefore = await api.query.System.Account.getValue( + convertH160ToSS58(wallet2.address), + ); + tx = await crowdloanContract2.contribute(nextId, contribution); + await tx.wait(); + + let contributorBalanceAfter = await api.query.System.Account.getValue( + convertH160ToSS58(wallet2.address), + ); + assert.ok( + Number( + contributorBalanceBefore.data.free - contributorBalanceAfter.data.free, + ) - + Number(contribution) < + 1_000_000, + ); + + tx = await crowdloanContract2.withdraw(nextId); + await tx.wait(); + + contributorBalanceAfter = await api.query.System.Account.getValue( + convertH160ToSS58(wallet2.address), + ); + assert.ok( + Number( + contributorBalanceBefore.data.free - contributorBalanceAfter.data.free, + ) < 1_000_000, + ); + + const crowdloanContract3 = new ethers.Contract( + ICROWDLOAN_ADDRESS, + ICrowdloanABI, + wallet3, + ); + const crowdloanContract4 = new ethers.Contract( + ICROWDLOAN_ADDRESS, + ICrowdloanABI, + wallet4, + ); + const refundBalanceBefore3 = await api.query.System.Account.getValue( + convertH160ToSS58(wallet3.address), + ); + const refundBalanceBefore4 = await api.query.System.Account.getValue( + convertH160ToSS58(wallet4.address), + ); + tx = await crowdloanContract3.contribute(nextId, contribution); + await tx.wait(); + tx = await crowdloanContract4.contribute(nextId, contribution); + await tx.wait(); + + await waitForFinalizedBlock(api, end); + + tx = await crowdloanContract.refund(nextId); + await tx.wait(); + + const refundBalanceAfter3 = await api.query.System.Account.getValue( + convertH160ToSS58(wallet3.address), + ); + const refundBalanceAfter4 = await api.query.System.Account.getValue( + convertH160ToSS58(wallet4.address), + ); + assert.ok( + Number(refundBalanceBefore3.data.free - refundBalanceAfter3.data.free) < + 1_000_000, + ); + assert.ok( + Number(refundBalanceBefore4.data.free - refundBalanceAfter4.data.free) < + 1_000_000, + ); + + tx = await crowdloanContract.dissolve(nextId); + await tx.wait(); + + const creatorBalanceAfterDissolve = await api.query.System.Account.getValue( + convertH160ToSS58(wallet1.address), + ); + assert.ok( + Number( + creatorBalanceBeforeCreate.data.free - + creatorBalanceAfterDissolve.data.free, + ) < 2_000_000, + ); + }); + + it("contributes and withdraws against a crowdloan created on substrate side", async () => { + const nextId = await api.query.Crowdloan.NextCrowdloanId.getValue(); + const deposit = BigInt(15_000_000_000); + const end = (await api.query.System.Number.getValue()) + 100; + + await api.tx.Crowdloan.create({ + deposit, + min_contribution: BigInt(1_000_000_000), + cap: BigInt(100_000_000_000), + end, + target_address: undefined, + call: api.tx.System.remark({ remark: Binary.fromText("foo") }) + .decodedCall, + }).signAndSubmit(alice); + + const balanceBefore = await api.query.System.Account.getValue( + convertH160ToSS58(wallet1.address), + ); + + const contribution = BigInt(5_000_000_000); + const tx = await crowdloanContract.contribute(nextId, contribution); + await tx.wait(); + + let balanceAfter = await api.query.System.Account.getValue( + convertH160ToSS58(wallet1.address), + ); + assert.ok( + Number(balanceBefore.data.free - balanceAfter.data.free) - + Number(contribution) < + 1_000_000, + ); + + const tx2 = await crowdloanContract.withdraw(nextId); + await tx2.wait(); + + balanceAfter = await api.query.System.Account.getValue( + convertH160ToSS58(wallet1.address), + ); + assert.ok( + Number(balanceBefore.data.free - balanceAfter.data.free) < 1_000_000, + ); + }); +}); diff --git a/contract-tests/test/leasing.precompile.test.ts b/contract-tests/test/leasing.precompile.test.ts index 7ea45c0509..7205a8ed4a 100644 --- a/contract-tests/test/leasing.precompile.test.ts +++ b/contract-tests/test/leasing.precompile.test.ts @@ -1,208 +1,139 @@ import * as assert from "assert"; -import { PublicClient } from "viem"; -import { ETH_LOCAL_URL } from "../src/config"; -import { generateRandomEthersWallet, getPublicClient } from "../src/utils"; import { ethers } from "ethers"; import { TypedApi } from "polkadot-api"; import { devnet } from "@polkadot-api/descriptors"; -import { getAliceSigner, getBobSigner, getDevnetApi, waitForFinalizedBlock } from "../src/substrate"; -import { forceSetBalanceToEthAddress } from "../src/subtensor"; -import { decodeAddress } from "@polkadot/util-crypto"; -import { u8aToHex } from "@polkadot/util"; -import { ILEASING_ADDRESS, ILeasingABI } from "../src/contracts/leasing"; import { ICROWDLOAN_ADDRESS, ICrowdloanABI } from "../src/contracts/crowdloan"; +import { ILEASING_ADDRESS, ILeasingABI } from "../src/contracts/leasing"; import { INEURON_ADDRESS, INeuronABI } from "../src/contracts/neuron"; -import { convertH160ToPublicKey, convertH160ToSS58 } from "../src/address-utils"; - -describe("Test Leasing precompile", () => { - let publicClient: PublicClient; - let api: TypedApi; - - let wallet1: ethers.Wallet; - let wallet2: ethers.Wallet; - let leaseContract: ethers.Contract; - let crowdloanContract: ethers.Contract; - let neuronContract: ethers.Contract; - - const alice = getAliceSigner(); - const bob = getBobSigner(); - - beforeEach(async () => { - publicClient = await getPublicClient(ETH_LOCAL_URL); - api = await getDevnetApi(); - - wallet1 = generateRandomEthersWallet(); - wallet2 = generateRandomEthersWallet(); - leaseContract = new ethers.Contract(ILEASING_ADDRESS, ILeasingABI, wallet1); - crowdloanContract = new ethers.Contract(ICROWDLOAN_ADDRESS, ICrowdloanABI, wallet1); - neuronContract = new ethers.Contract(INEURON_ADDRESS, INeuronABI, wallet1); - - await forceSetBalanceToEthAddress(api, wallet1.address); - await forceSetBalanceToEthAddress(api, wallet2.address); - }); - - it("gets an existing lease created on substrate side, its subnet id and its contributor shares", async () => { - const nextCrowdloanId = await api.query.Crowdloan.NextCrowdloanId.getValue(); - const crowdloanDeposit = BigInt(100_000_000_000); // 100 TAO - const crowdloanCap = await api.query.SubtensorModule.NetworkLastLockCost.getValue() * BigInt(2); - const crowdloanEnd = await api.query.System.Number.getValue() + 100; - const leaseEmissionsShare = 15; - const leaseEnd = await api.query.System.Number.getValue() + 300; - - await api.tx.Crowdloan.create({ - deposit: crowdloanDeposit, - min_contribution: BigInt(1_000_000_000), // 1 TAO - cap: crowdloanCap, - end: crowdloanEnd, - target_address: undefined, - call: api.tx.SubtensorModule.register_leased_network({ - emissions_share: leaseEmissionsShare, - end_block: leaseEnd, - }).decodedCall - }).signAndSubmit(alice); - - await api.tx.Crowdloan.contribute({ - crowdloan_id: nextCrowdloanId, - amount: crowdloanCap - crowdloanDeposit, - }).signAndSubmit(bob); - - await waitForFinalizedBlock(api, crowdloanEnd); - - const nextLeaseId = await api.query.SubtensorModule.NextSubnetLeaseId.getValue(); - await api.tx.Crowdloan.finalize({ crowdloan_id: nextCrowdloanId }).signAndSubmit(alice); - - const lease = await api.query.SubtensorModule.SubnetLeases.getValue(nextLeaseId); - const leaseInfo = await leaseContract.getLease(nextLeaseId); - - assert.ok(lease); - assert.equal(leaseInfo[0], u8aToHex(decodeAddress(lease.beneficiary))); - assert.equal(leaseInfo[1], u8aToHex(decodeAddress(lease.coldkey))); - assert.equal(leaseInfo[2], u8aToHex(decodeAddress(lease.hotkey))); - assert.equal(leaseInfo[3], lease.emissions_share); - assert.equal(leaseInfo[4], true); //has_end_block - assert.equal(leaseInfo[5], lease.end_block); - assert.equal(leaseInfo[6], lease.netuid); - assert.equal(leaseInfo[7], lease.cost); - - const leaseId = await leaseContract.getLeaseIdForSubnet(lease.netuid); - assert.equal(leaseId, nextLeaseId); - - // Bob has some share and alice share is 0 because she is the beneficiary - // and beneficiary share is dynamic based on other contributors shares - const aliceShare = await leaseContract.getContributorShare(nextLeaseId, alice.publicKey) - assert.deepEqual(aliceShare, [BigInt(0), BigInt(0)]); - const bobShare = await leaseContract.getContributorShare(nextLeaseId, bob.publicKey) - assert.notDeepEqual(bobShare, [BigInt(0), BigInt(0)]); - }); - - it("registers a new leased network through a crowdloan and retrieves the lease", async () => { - const nextCrowdloanId = await api.query.Crowdloan.NextCrowdloanId.getValue(); - const crowdloanDeposit = BigInt(100_000_000_000); // 100 TAO - const crowdloanMinContribution = BigInt(1_000_000_000); // 1 TAO - const crowdloanCap = await api.query.SubtensorModule.NetworkLastLockCost.getValue() * BigInt(2); - const crowdloanEnd = await api.query.System.Number.getValue() + 100; - const leasingEmissionsShare = 15; - const leasingEndBlock = await api.query.System.Number.getValue() + 300; - - let tx = await leaseContract.createLeaseCrowdloan( - crowdloanDeposit, - crowdloanMinContribution, - crowdloanCap, - crowdloanEnd, - leasingEmissionsShare, - true, // has_leasing_end_block - leasingEndBlock - ); - await tx.wait(); - - const crowdloanContract2 = new ethers.Contract(ICROWDLOAN_ADDRESS, ICrowdloanABI, wallet2); - tx = await crowdloanContract2.contribute(nextCrowdloanId, crowdloanCap - crowdloanDeposit); - await tx.wait(); - - await waitForFinalizedBlock(api, crowdloanEnd); - - const nextLeaseId = await api.query.SubtensorModule.NextSubnetLeaseId.getValue(); - tx = await crowdloanContract.finalize(nextCrowdloanId); - await tx.wait(); - - const lease = await api.query.SubtensorModule.SubnetLeases.getValue(nextLeaseId); - assert.ok(lease); - assert.equal(lease.beneficiary, convertH160ToSS58(wallet1.address)); - assert.equal(lease.emissions_share, leasingEmissionsShare); - assert.equal(lease.end_block, leasingEndBlock); - - const leaseInfo = await leaseContract.getLease(nextLeaseId); - assert.equal(leaseInfo[0], u8aToHex(decodeAddress(lease.beneficiary))); - assert.equal(leaseInfo[1], u8aToHex(decodeAddress(lease.coldkey))); - assert.equal(leaseInfo[2], u8aToHex(decodeAddress(lease.hotkey))); - assert.equal(leaseInfo[3], lease.emissions_share); - assert.equal(leaseInfo[4], true); // has_end_block - assert.equal(leaseInfo[5], lease.end_block); - assert.equal(leaseInfo[6], lease.netuid); - assert.equal(leaseInfo[7], lease.cost); - - const leaseId = await leaseContract.getLeaseIdForSubnet(lease.netuid); - assert.equal(leaseId, nextLeaseId); - - // Bob has some share and alice share is 0 because she is the beneficiary - // and beneficiary share is dynamic based on other contributors shares - const contributor1 = await leaseContract.getContributorShare(nextLeaseId, convertH160ToPublicKey(wallet1.address)) - assert.deepEqual(contributor1, [BigInt(0), BigInt(0)]); - const contributor2 = await leaseContract.getContributorShare(nextLeaseId, convertH160ToPublicKey(wallet2.address)) - assert.notDeepEqual(contributor2, [BigInt(0), BigInt(0)]); - }); - - it("terminates a lease", async () => { - const hotkey = generateRandomEthersWallet(); - let tx = await neuronContract.burnedRegister(1, convertH160ToPublicKey(hotkey.address)); - await tx.wait(); - - const nextCrowdloanId = await api.query.Crowdloan.NextCrowdloanId.getValue(); - const crowdloanDeposit = BigInt(100_000_000_000); // 100 TAO - const crowdloanMinContribution = BigInt(1_000_000_000); // 1 TAO - const crowdloanCap = await api.query.SubtensorModule.NetworkLastLockCost.getValue() * BigInt(2); - const crowdloanEnd = await api.query.System.Number.getValue() + 100; - const leasingEmissionsShare = 15; - const leasingEndBlock = await api.query.System.Number.getValue() + 200; - - tx = await leaseContract.createLeaseCrowdloan( - crowdloanDeposit, - crowdloanMinContribution, - crowdloanCap, - crowdloanEnd, - leasingEmissionsShare, - true, // has_leasing_end_block - leasingEndBlock - ); - await tx.wait(); - - const crowdloanContract2 = new ethers.Contract(ICROWDLOAN_ADDRESS, ICrowdloanABI, wallet2); - tx = await crowdloanContract2.contribute(nextCrowdloanId, crowdloanCap - crowdloanDeposit); - await tx.wait(); - - await waitForFinalizedBlock(api, crowdloanEnd); - - const nextLeaseId = await api.query.SubtensorModule.NextSubnetLeaseId.getValue(); - tx = await crowdloanContract.finalize(nextCrowdloanId); - await tx.wait(); - - await waitForFinalizedBlock(api, leasingEndBlock); - - let lease = await api.query.SubtensorModule.SubnetLeases.getValue(nextLeaseId); - assert.ok(lease); - const netuid = lease.netuid; - - tx = await leaseContract.terminateLease(nextLeaseId, convertH160ToPublicKey(hotkey.address)); - await tx.wait(); - - lease = await api.query.SubtensorModule.SubnetLeases.getValue(nextLeaseId); - assert.strictEqual(lease, undefined); - - // Ensure that the subnet ownership has been transferred - const ownerColdkey = await api.query.SubtensorModule.SubnetOwner.getValue(netuid); - const ownerHotkey = await api.query.SubtensorModule.SubnetOwnerHotkey.getValue(netuid); - assert.equal(ownerColdkey, convertH160ToSS58(wallet1.address)); - assert.equal(ownerHotkey, convertH160ToSS58(hotkey.address)); - }); -}) \ No newline at end of file +import { + convertH160ToPublicKey, + convertH160ToSS58, +} from "../src/address-utils"; +import { generateRandomEthersWallet } from "../src/utils"; +import { getDevnetApi, waitForFinalizedBlock } from "../src/substrate"; +import { forceSetBalanceToEthAddress } from "../src/subtensor"; + +describe("Leasing precompile E2E smoke", () => { + let api: TypedApi; + let wallet1: ethers.Wallet; + let wallet2: ethers.Wallet; + let leaseContract: ethers.Contract; + let crowdloanContract: ethers.Contract; + let neuronContract: ethers.Contract; + + beforeEach(async () => { + api = await getDevnetApi(); + + wallet1 = generateRandomEthersWallet(); + wallet2 = generateRandomEthersWallet(); + leaseContract = new ethers.Contract(ILEASING_ADDRESS, ILeasingABI, wallet1); + crowdloanContract = new ethers.Contract( + ICROWDLOAN_ADDRESS, + ICrowdloanABI, + wallet1, + ); + neuronContract = new ethers.Contract(INEURON_ADDRESS, INeuronABI, wallet1); + + await forceSetBalanceToEthAddress(api, wallet1.address); + await forceSetBalanceToEthAddress(api, wallet2.address); + }); + + it("creates, reads, and terminates a lease through RPC", async () => { + const hotkey = generateRandomEthersWallet(); + let tx = await neuronContract.burnedRegister( + 1, + convertH160ToPublicKey(hotkey.address), + ); + await tx.wait(); + + const nextCrowdloanId = + await api.query.Crowdloan.NextCrowdloanId.getValue(); + const crowdloanDeposit = BigInt(100_000_000_000); + const crowdloanMinContribution = BigInt(1_000_000_000); + const crowdloanCap = + (await api.query.SubtensorModule.NetworkLastLockCost.getValue()) * + BigInt(2); + const crowdloanEnd = (await api.query.System.Number.getValue()) + 100; + const leasingEmissionsShare = 15; + const leasingEndBlock = (await api.query.System.Number.getValue()) + 200; + + tx = await leaseContract.createLeaseCrowdloan( + crowdloanDeposit, + crowdloanMinContribution, + crowdloanCap, + crowdloanEnd, + leasingEmissionsShare, + true, + leasingEndBlock, + ); + await tx.wait(); + + const crowdloanContract2 = new ethers.Contract( + ICROWDLOAN_ADDRESS, + ICrowdloanABI, + wallet2, + ); + tx = await crowdloanContract2.contribute( + nextCrowdloanId, + crowdloanCap - crowdloanDeposit, + ); + await tx.wait(); + + await waitForFinalizedBlock(api, crowdloanEnd); + + const nextLeaseId = + await api.query.SubtensorModule.NextSubnetLeaseId.getValue(); + tx = await crowdloanContract.finalize(nextCrowdloanId); + await tx.wait(); + + const lease = + await api.query.SubtensorModule.SubnetLeases.getValue(nextLeaseId); + assert.ok(lease); + assert.equal(lease.beneficiary, convertH160ToSS58(wallet1.address)); + assert.equal(lease.emissions_share, leasingEmissionsShare); + assert.equal(lease.end_block, leasingEndBlock); + + const leaseInfo = await leaseContract.getLease(nextLeaseId); + assert.equal(leaseInfo[3], lease.emissions_share); + assert.equal(leaseInfo[4], true); + assert.equal(leaseInfo[5], lease.end_block); + assert.equal(leaseInfo[6], lease.netuid); + assert.equal(leaseInfo[7], lease.cost); + + const leaseId = await leaseContract.getLeaseIdForSubnet(lease.netuid); + assert.equal(leaseId, nextLeaseId); + + const beneficiaryShare = await leaseContract.getContributorShare( + nextLeaseId, + convertH160ToPublicKey(wallet1.address), + ); + assert.deepEqual(beneficiaryShare, [BigInt(0), BigInt(0)]); + + const contributorShare = await leaseContract.getContributorShare( + nextLeaseId, + convertH160ToPublicKey(wallet2.address), + ); + assert.notDeepEqual(contributorShare, [BigInt(0), BigInt(0)]); + + await waitForFinalizedBlock(api, leasingEndBlock); + + tx = await leaseContract.terminateLease( + nextLeaseId, + convertH160ToPublicKey(hotkey.address), + ); + await tx.wait(); + + const terminatedLease = + await api.query.SubtensorModule.SubnetLeases.getValue(nextLeaseId); + assert.equal(terminatedLease, undefined); + + const ownerColdkey = await api.query.SubtensorModule.SubnetOwner.getValue( + lease.netuid, + ); + const ownerHotkey = + await api.query.SubtensorModule.SubnetOwnerHotkey.getValue(lease.netuid); + assert.equal(ownerColdkey, convertH160ToSS58(wallet1.address)); + assert.equal(ownerHotkey, convertH160ToSS58(hotkey.address)); + }); +}); diff --git a/contract-tests/test/runtime.call.precompile.test.ts b/contract-tests/test/runtime.call.precompile.test.ts index 7bacc947fd..40a05827f8 100644 --- a/contract-tests/test/runtime.call.precompile.test.ts +++ b/contract-tests/test/runtime.call.precompile.test.ts @@ -6,7 +6,7 @@ import { devnet, MultiAddress } from "@polkadot-api/descriptors" import { PublicClient } from "viem"; import { PolkadotSigner, TypedApi, getTypedCodecs } from "polkadot-api"; import { convertPublicKeyToSs58 } from "../src/address-utils" -import { forceSetBalanceToEthAddress, setMaxChildkeyTake, burnedRegister, forceSetBalanceToSs58Address, addStake, setTxRateLimit, addNewSubnetwork, startCall, setTempo } from "../src/subtensor"; +import { forceSetBalanceToEthAddress, setMaxChildkeyTake, burnedRegister, forceSetBalanceToSs58Address, addStake, setTxRateLimit, addNewSubnetwork, startCall, setTempo, disableAdminFreezeWindowAndOwnerHyperparamRateLimit } from "../src/subtensor"; import { xxhashAsHex } from "@polkadot/util-crypto"; describe("Test the dispatch precompile", () => { @@ -27,6 +27,7 @@ describe("Test the dispatch precompile", () => { await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(hotkey.publicKey)) await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(coldkey.publicKey)) + await disableAdminFreezeWindowAndOwnerHyperparamRateLimit(api) netuid = await addNewSubnetwork(api, hotkey, coldkey) // set tempo big enough to avoid stake value updated with fast block feature @@ -76,7 +77,7 @@ describe("Test the dispatch precompile", () => { await api.query.Multisig.Multisigs.getKey(), await api.query.Timestamp.Now.getKey(), ]; - + for (const key of authorizedKeys) { await assert.doesNotReject( publicClient.call({ @@ -89,7 +90,7 @@ describe("Test the dispatch precompile", () => { const unauthorizedKeys = [ await api.query.System.Events.getKey(), await api.query.Grandpa.CurrentSetId.getKey(), - xxhashAsHex(":code" , 128), + xxhashAsHex(":code", 128), ]; for (const key of unauthorizedKeys) { diff --git a/contract-tests/test/sr25519.precompile.verify.test.ts b/contract-tests/test/sr25519.precompile.verify.test.ts deleted file mode 100644 index 234638f195..0000000000 --- a/contract-tests/test/sr25519.precompile.verify.test.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { ISr25519VERIFY_ADDRESS, ISr25519VerifyABI, ETH_LOCAL_URL } from '../src/config' -import { getPublicClient } from "../src/utils"; -import { toHex, toBytes, keccak256, PublicClient } from 'viem' -import { Keyring } from "@polkadot/keyring"; -import * as assert from "assert"; - -describe("Verfication of sr25519 signature", () => { - // init eth part - let ethClient: PublicClient; - - before(async () => { - ethClient = await getPublicClient(ETH_LOCAL_URL); - }); - - it("Verification of sr25519 works", async () => { - const keyring = new Keyring({ type: "sr25519" }); - const alice = keyring.addFromUri("//Alice"); - - ////////////////////////////////////////////////////////////////////// - // Generate a signature - - // Your message to sign - const message = "Sign this message"; - const messageU8a = new TextEncoder().encode(message); - const messageHex = toHex(messageU8a); // Convert message to hex string - const messageHash = keccak256(messageHex); // Hash the message to fit into bytes32 - console.log(`messageHash = ${messageHash}`); - const hashedMessageBytes = toBytes(messageHash); - console.log(`hashedMessageBytes = ${hashedMessageBytes}`); - - // Sign the message - const signature = await alice.sign(hashedMessageBytes); - console.log(`Signature: ${toHex(signature)}`); - - // Verify the signature locally - const isValid = alice.verify( - hashedMessageBytes, - signature, - alice.publicKey - ); - console.log(`Is the signature valid? ${isValid}`); - - ////////////////////////////////////////////////////////////////////// - // Verify the signature using the precompile contract - - const publicKeyBytes = toHex(alice.publicKey); - console.log(`publicKeyBytes = ${publicKeyBytes}`); - - // Split signture into Commitment (R) and response (s) - let r = signature.slice(0, 32); // Commitment, a.k.a. "r" - first 32 bytes - let s = signature.slice(32, 64); // Response, a.k.a. "s" - second 32 bytes - let rBytes = toHex(r); - let sBytes = toHex(s); - - const isPrecompileValid = await ethClient.readContract({ - address: ISr25519VERIFY_ADDRESS, - abi: ISr25519VerifyABI, - functionName: "verify", - args: [messageHash, - publicKeyBytes, - rBytes, - sBytes] - - }); - - console.log( - `Is the signature valid according to the smart contract? ${isPrecompileValid}` - ); - assert.equal(isPrecompileValid, true) - - ////////////////////////////////////////////////////////////////////// - // Verify the signature for bad data using the precompile contract - - let brokenHashedMessageBytes = hashedMessageBytes; - brokenHashedMessageBytes[0] = (brokenHashedMessageBytes[0] + 1) % 0xff; - const brokenMessageHash = toHex(brokenHashedMessageBytes); - console.log(`brokenMessageHash = ${brokenMessageHash}`); - - const isPrecompileValidBadData = await ethClient.readContract({ - address: ISr25519VERIFY_ADDRESS, - abi: ISr25519VerifyABI, - functionName: "verify", - args: [brokenMessageHash, - publicKeyBytes, - rBytes, - sBytes] - - }); - - console.log( - `Is the signature valid according to the smart contract for broken data? ${isPrecompileValidBadData}` - ); - assert.equal(isPrecompileValidBadData, false) - - ////////////////////////////////////////////////////////////////////// - // Verify the bad signature for good data using the precompile contract - - let brokenR = r; - brokenR[0] = (brokenR[0] + 1) % 0xff; - rBytes = toHex(r); - const isPrecompileValidBadSignature = await ethClient.readContract({ - address: ISr25519VERIFY_ADDRESS, - abi: ISr25519VerifyABI, - functionName: "verify", - args: [messageHash, - publicKeyBytes, - rBytes, - sBytes] - - }); - - console.log( - `Is the signature valid according to the smart contract for broken signature? ${isPrecompileValidBadSignature}` - ); - assert.equal(isPrecompileValidBadSignature, false) - - }); -}); \ No newline at end of file diff --git a/contract-tests/test/staking.precompile.add-remove.test.ts b/contract-tests/test/staking.precompile.add-remove.test.ts deleted file mode 100644 index 9eef7d4dbf..0000000000 --- a/contract-tests/test/staking.precompile.add-remove.test.ts +++ /dev/null @@ -1,342 +0,0 @@ -import * as assert from "assert"; -import { getDevnetApi, getRandomSubstrateKeypair } from "../src/substrate" -import { devnet } from "@polkadot-api/descriptors" -import { PolkadotSigner, TypedApi } from "polkadot-api"; -import { convertPublicKeyToSs58, convertH160ToSS58 } from "../src/address-utils" -import { raoToEth, tao } from "../src/balance-math" -import { ethers } from "ethers" -import { generateRandomEthersWallet, getPublicClient } from "../src/utils" -import { convertH160ToPublicKey } from "../src/address-utils" -import { - forceSetBalanceToEthAddress, forceSetBalanceToSs58Address, addNewSubnetwork, burnedRegister, - sendProxyCall, - startCall, - getStake, -} from "../src/subtensor" -import { ETH_LOCAL_URL } from "../src/config"; -import { ISTAKING_ADDRESS, ISTAKING_V2_ADDRESS, IStakingABI, IStakingV2ABI } from "../src/contracts/staking" -import { PublicClient } from "viem"; - -describe("Test neuron precompile add remove stake", () => { - // init eth part - const wallet1 = generateRandomEthersWallet(); - const wallet2 = generateRandomEthersWallet(); - let publicClient: PublicClient; - // init substrate part - const hotkey = getRandomSubstrateKeypair(); - const coldkey = getRandomSubstrateKeypair(); - const proxy = getRandomSubstrateKeypair(); - - let api: TypedApi - - // sudo account alice as signer - let alice: PolkadotSigner; - before(async () => { - publicClient = await getPublicClient(ETH_LOCAL_URL) - // init variables got from await and async - api = await getDevnetApi() - - // await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(alice.publicKey)) - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(hotkey.publicKey)) - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(coldkey.publicKey)) - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(proxy.publicKey)) - await forceSetBalanceToEthAddress(api, wallet1.address) - await forceSetBalanceToEthAddress(api, wallet2.address) - let netuid = await addNewSubnetwork(api, hotkey, coldkey) - await startCall(api, netuid, coldkey) - - console.log("test the case on subnet ", netuid) - - await burnedRegister(api, netuid, convertH160ToSS58(wallet1.address), coldkey) - await burnedRegister(api, netuid, convertH160ToSS58(wallet2.address), coldkey) - }) - - it("Can add stake", async () => { - let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1 - // ETH unit - let stakeBalance = raoToEth(tao(20)) - const stakeBefore = await getStake(api, convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet1.address), netuid) - const contract = new ethers.Contract(ISTAKING_ADDRESS, IStakingABI, wallet1); - const tx = await contract.addStake(hotkey.publicKey, netuid, { value: stakeBalance.toString() }) - await tx.wait() - - const stakeFromContract = BigInt( - await contract.getStake(hotkey.publicKey, convertH160ToPublicKey(wallet1.address), netuid) - ); - - assert.ok(stakeFromContract > stakeBefore) - const stakeAfter = await getStake(api, convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet1.address), netuid) - assert.ok(stakeAfter > stakeBefore) - }) - - it("Can add stake V2", async () => { - let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1 - // the unit in V2 is RAO, not ETH - let stakeBalance = tao(20) - const stakeBefore = await getStake(api, convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet2.address), netuid) - const contract = new ethers.Contract(ISTAKING_V2_ADDRESS, IStakingV2ABI, wallet2); - const tx = await contract.addStake(hotkey.publicKey, stakeBalance.toString(), netuid) - await tx.wait() - - const stakeFromContract = BigInt( - await contract.getStake(hotkey.publicKey, convertH160ToPublicKey(wallet2.address), netuid) - ); - - assert.ok(stakeFromContract > stakeBefore) - const stakeAfter = await getStake(api, convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet2.address), netuid) - assert.ok(stakeAfter > stakeBefore) - }) - - it("Can not add stake if subnet doesn't exist", async () => { - // wrong netuid - let netuid = 12345; - let stakeBalance = raoToEth(tao(20)) - const stakeBefore = await getStake(api, convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet1.address), netuid) - const contract = new ethers.Contract(ISTAKING_ADDRESS, IStakingABI, wallet1); - try { - const tx = await contract.addStake(hotkey.publicKey, netuid, { value: stakeBalance.toString() }) - await tx.wait() - assert.fail("Transaction should have failed"); - } catch (error) { - // Transaction failed as expected - } - - const stakeFromContract = BigInt( - await contract.getStake(hotkey.publicKey, convertH160ToPublicKey(wallet1.address), netuid) - ); - assert.equal(stakeFromContract, stakeBefore) - const stakeAfter = await getStake(api, convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet1.address), netuid) - assert.equal(stakeAfter, stakeBefore) - }); - - it("Can not add stake V2 if subnet doesn't exist", async () => { - // wrong netuid - let netuid = 12345; - // the unit in V2 is RAO, not ETH - let stakeBalance = tao(20) - const stakeBefore = await getStake(api, convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet2.address), netuid) - const contract = new ethers.Contract(ISTAKING_V2_ADDRESS, IStakingV2ABI, wallet2); - - try { - const tx = await contract.addStake(hotkey.publicKey, stakeBalance.toString(), netuid); - await tx.wait(); - assert.fail("Transaction should have failed"); - } catch (error) { - // Transaction failed as expected - } - - const stakeFromContract = BigInt( - await contract.getStake(hotkey.publicKey, convertH160ToPublicKey(wallet2.address), netuid) - ); - assert.equal(stakeFromContract, stakeBefore) - const stakeAfter = await getStake(api, convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet2.address), netuid) - assert.equal(stakeAfter, stakeBefore) - }) - - it("Can get stake via contract read method", async () => { - let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1 - - // TODO need check how to pass bytes32 as parameter of readContract - // const value = await publicClient.readContract({ - // address: ISTAKING_ADDRESS, - // abi: IStakingABI, - // functionName: "getStake", - // args: [hotkey.publicKey, // Convert to bytes32 format - // convertH160ToPublicKey(wallet1.address), - // netuid] - // }) - // if (value === undefined || value === null) { - // throw new Error("value of getStake from contract is undefined") - // } - // const intValue = BigInt(value.toString()) - - const contractV1 = new ethers.Contract(ISTAKING_ADDRESS, IStakingABI, wallet1); - const stakeFromContractV1 = BigInt( - await contractV1.getStake(hotkey.publicKey, convertH160ToPublicKey(wallet1.address), netuid) - ); - - const contractV2 = new ethers.Contract(ISTAKING_V2_ADDRESS, IStakingV2ABI, wallet1); - // unit from contract V2 is RAO, not ETH - const stakeFromContractV2 = Number( - await contractV2.getStake(hotkey.publicKey, convertH160ToPublicKey(wallet1.address), netuid) - ); - - assert.equal(stakeFromContractV1, tao(stakeFromContractV2)) - - const totalColdkeyStakeOnSubnet = Number( - await contractV2.getTotalColdkeyStakeOnSubnet(convertH160ToPublicKey(wallet1.address), netuid) - ); - - // check the value is not undefined and is greater than or equal to the stake from contract V2 - assert.ok(totalColdkeyStakeOnSubnet != undefined) - // is greater than or equal to the stake from contract V2 because of emission - assert.ok(totalColdkeyStakeOnSubnet >= stakeFromContractV2) - - }) - - it("Can remove stake", async () => { - let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1 - const contract = new ethers.Contract( - ISTAKING_ADDRESS, - IStakingABI, - wallet1 - ); - - const stakeBeforeRemove = BigInt( - await contract.getStake(hotkey.publicKey, convertH160ToPublicKey(wallet1.address), netuid) - ); - - let stakeBalance = raoToEth(tao(10)) - const tx = await contract.removeStake(hotkey.publicKey, stakeBalance, netuid) - await tx.wait() - - const stakeAfterRemove = BigInt( - await contract.getStake(hotkey.publicKey, convertH160ToPublicKey(wallet1.address), netuid) - ); - assert.ok(stakeAfterRemove < stakeBeforeRemove) - - }) - - it("Can remove stake V2", async () => { - let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1 - const contract = new ethers.Contract( - ISTAKING_V2_ADDRESS, - IStakingV2ABI, - wallet2 - ); - - const stakeBeforeRemove = BigInt( - await contract.getStake(hotkey.publicKey, convertH160ToPublicKey(wallet2.address), netuid) - ); - - let stakeBalance = tao(10) - const tx = await contract.removeStake(hotkey.publicKey, stakeBalance, netuid) - await tx.wait() - - const stakeAfterRemove = BigInt( - await contract.getStake(hotkey.publicKey, convertH160ToPublicKey(wallet2.address), netuid) - ); - - assert.ok(stakeAfterRemove < stakeBeforeRemove) - }) - - it("Can add/remove proxy", async () => { - let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1 - // add/remove are done in a single test case, because we can't use the same private/public key - // between substrate and EVM, but to test the remove part, we must predefine the proxy first. - // it makes `remove` being dependent on `add`, because we should use `addProxy` from contract - // to prepare the proxy for `removeProxy` testing - the proxy is specified for the - // caller/origin. - - // first, check we don't have proxies - const ss58Address = convertH160ToSS58(wallet1.address); - // the result include two items array, first one is delegate info, second one is balance - const initProxies = await api.query.Proxy.Proxies.getValue(ss58Address); - assert.equal(initProxies[0].length, 0); - - // intialize the contract - const contract = new ethers.Contract( - ISTAKING_ADDRESS, - IStakingABI, - wallet1 - ); - - // test "add" - let tx = await contract.addProxy(proxy.publicKey); - await tx.wait(); - - const proxiesAfterAdd = await api.query.Proxy.Proxies.getValue(ss58Address); - - assert.equal(proxiesAfterAdd[0][0].delegate, convertPublicKeyToSs58(proxy.publicKey)) - - let stakeBefore = await getStake( - api, - convertPublicKeyToSs58(hotkey.publicKey), - ss58Address, - netuid - ) - - const call = api.tx.SubtensorModule.add_stake({ - hotkey: convertPublicKeyToSs58(hotkey.publicKey), - netuid: netuid, - amount_staked: tao(1) - }) - await sendProxyCall(api, call.decodedCall, ss58Address, proxy) - - let stakeAfter = await getStake( - api, - convertPublicKeyToSs58(hotkey.publicKey), - ss58Address, - netuid - ) - - assert.ok(stakeAfter > stakeBefore) - // test "remove" - tx = await contract.removeProxy(proxy.publicKey); - await tx.wait(); - - const proxiesAfterRemove = await api.query.Proxy.Proxies.getValue(ss58Address); - assert.equal(proxiesAfterRemove[0].length, 0) - }); - - it("Can add/remove proxy V2", async () => { - let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1 - // add/remove are done in a single test case, because we can't use the same private/public key - // between substrate and EVM, but to test the remove part, we must predefine the proxy first. - // it makes `remove` being dependent on `add`, because we should use `addProxy` from contract - // to prepare the proxy for `removeProxy` testing - the proxy is specified for the - // caller/origin. - - // first, check we don't have proxies - const ss58Address = convertH160ToSS58(wallet1.address); - // the result include two items array, first one is delegate info, second one is balance - const initProxies = await api.query.Proxy.Proxies.getValue(ss58Address); - assert.equal(initProxies[0].length, 0); - - // intialize the contract - // const signer = new ethers.Wallet(fundedEthWallet.privateKey, provider); - const contract = new ethers.Contract( - ISTAKING_V2_ADDRESS, - IStakingV2ABI, - wallet1 - ); - - // test "add" - let tx = await contract.addProxy(proxy.publicKey); - await tx.wait(); - - const proxiesAfterAdd = await api.query.Proxy.Proxies.getValue(ss58Address); - - assert.equal(proxiesAfterAdd[0][0].delegate, convertPublicKeyToSs58(proxy.publicKey)) - - let stakeBefore = await getStake( - api, - convertPublicKeyToSs58(hotkey.publicKey), - ss58Address, - netuid - ) - - const call = api.tx.SubtensorModule.add_stake({ - hotkey: convertPublicKeyToSs58(hotkey.publicKey), - netuid: netuid, - amount_staked: tao(1) - }) - - await sendProxyCall(api, call.decodedCall, ss58Address, proxy) - - let stakeAfter = await getStake( - api, - convertPublicKeyToSs58(hotkey.publicKey), - ss58Address, - netuid - ) - - assert.ok(stakeAfter > stakeBefore) - // test "remove" - tx = await contract.removeProxy(proxy.publicKey); - await tx.wait(); - - const proxiesAfterRemove = await api.query.Proxy.Proxies.getValue(ss58Address); - assert.equal(proxiesAfterRemove[0].length, 0) - }); -}); diff --git a/contract-tests/test/staking.precompile.approval.test.ts b/contract-tests/test/staking.precompile.approval.test.ts deleted file mode 100644 index 94161696cd..0000000000 --- a/contract-tests/test/staking.precompile.approval.test.ts +++ /dev/null @@ -1,242 +0,0 @@ -import * as assert from "assert"; -import { getDevnetApi, getRandomSubstrateKeypair } from "../src/substrate" -import { devnet } from "@polkadot-api/descriptors" -import { PolkadotSigner, TypedApi } from "polkadot-api"; -import { convertPublicKeyToSs58, convertH160ToSS58 } from "../src/address-utils" -import { raoToEth, tao } from "../src/balance-math" -import { ethers } from "ethers" -import { generateRandomEthersWallet, getPublicClient } from "../src/utils" -import { convertH160ToPublicKey } from "../src/address-utils" -import { - forceSetBalanceToEthAddress, forceSetBalanceToSs58Address, addNewSubnetwork, burnedRegister, - sendProxyCall, - startCall, - getStake, -} from "../src/subtensor" -import { ETH_LOCAL_URL } from "../src/config"; -import { ISTAKING_ADDRESS, ISTAKING_V2_ADDRESS, IStakingABI, IStakingV2ABI } from "../src/contracts/staking" -import { PublicClient } from "viem"; - -describe("Test approval in staking precompile", () => { - // init eth part - const wallet1 = generateRandomEthersWallet(); - const wallet2 = generateRandomEthersWallet(); - let publicClient: PublicClient; - // init substrate part - const hotkey = getRandomSubstrateKeypair(); - const coldkey = getRandomSubstrateKeypair(); - const proxy = getRandomSubstrateKeypair(); - - let api: TypedApi - let stakeNetuid: number; - - let expectedAllowance = BigInt(0); - - // sudo account alice as signer - let alice: PolkadotSigner; - before(async () => { - publicClient = await getPublicClient(ETH_LOCAL_URL) - // init variables got from await and async - api = await getDevnetApi() - - // await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(alice.publicKey)) - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(hotkey.publicKey)) - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(coldkey.publicKey)) - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(proxy.publicKey)) - await forceSetBalanceToEthAddress(api, wallet1.address) - await forceSetBalanceToEthAddress(api, wallet2.address) - let netuid = await addNewSubnetwork(api, hotkey, coldkey) - await startCall(api, netuid, coldkey) - - console.log("test the case on subnet ", netuid) - - await burnedRegister(api, netuid, convertH160ToSS58(wallet1.address), coldkey) - await burnedRegister(api, netuid, convertH160ToSS58(wallet2.address), coldkey) - - // add stake as wallet1 - { - stakeNetuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1 - // the unit in V2 is RAO, not ETH - let stakeBalance = tao(20) - const stakeBefore = await getStake(api, convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet1.address), stakeNetuid) - const contract = new ethers.Contract(ISTAKING_V2_ADDRESS, IStakingV2ABI, wallet1); - const tx = await contract.addStake(hotkey.publicKey, stakeBalance.toString(), stakeNetuid) - await tx.wait() - - const stakeFromContract = BigInt( - await contract.getStake(hotkey.publicKey, convertH160ToPublicKey(wallet1.address), stakeNetuid) - ); - - assert.ok(stakeFromContract > stakeBefore) - const stakeAfter = await getStake(api, convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet1.address), stakeNetuid) - assert.ok(stakeAfter > stakeBefore) - } - }) - - it("Can't transfer from account without approval", async () => { - try { - // wallet2 tries to transfer from wallet1 - const contract = new ethers.Contract(ISTAKING_V2_ADDRESS, IStakingV2ABI, wallet2); - const tx = await contract.transferStakeFrom( - wallet1.address, // source - wallet2.address, // destination - hotkey.publicKey, - stakeNetuid, - stakeNetuid, - 1 - ) - await tx.wait(); - - assert.fail("should have reverted due to missing allowance"); - } catch (e) { - assert.equal(e.reason, "trying to spend more than allowed", "wrong revert message"); - } - }) - - it("Can approve some amount", async () => { - const contract = new ethers.Contract(ISTAKING_V2_ADDRESS, IStakingV2ABI, wallet1); - - { - let allowance = BigInt( - await contract.allowance( - wallet1.address, // source - wallet2.address, // spender - stakeNetuid, - ) - ); - assert.equal(allowance, expectedAllowance, "default allowance should be 0"); - } - - { - const tx = await contract.approve( - wallet2.address, // spender - stakeNetuid, - tao(10) - ) - await tx.wait(); - - expectedAllowance += BigInt(tao(10)); - - let allowance = BigInt( - await contract.allowance( - wallet1.address, // source - wallet2.address, // spender - stakeNetuid, - ) - ); - assert.equal(allowance, expectedAllowance, "should have set allowance"); - } - }) - - it("Can now use transfer from", async () => { - const contract = new ethers.Contract(ISTAKING_V2_ADDRESS, IStakingV2ABI, wallet2); - - // wallet2 transfer from wallet1 - const tx = await contract.transferStakeFrom( - wallet1.address, // source - wallet2.address, // destination - hotkey.publicKey, - stakeNetuid, - stakeNetuid, - tao(5) - ) - await tx.wait(); - - expectedAllowance -= BigInt(tao(5)); - - { - let allowance = BigInt( - await contract.allowance( - wallet1.address, // source - wallet2.address, // spender - stakeNetuid, - ) - ); - assert.equal(allowance, expectedAllowance, "allowance should now be 500"); - } - }) - - it("Can't use transfer from with amount too high", async () => { - try { - // wallet2 tries to transfer from wallet1 - const contract = new ethers.Contract(ISTAKING_V2_ADDRESS, IStakingV2ABI, wallet2); - const tx = await contract.transferStakeFrom( - wallet1.address, // source - wallet2.address, // destination - hotkey.publicKey, - stakeNetuid, - stakeNetuid, - expectedAllowance + BigInt(1) - ) - await tx.wait(); - - assert.fail("should have reverted due to missing allowance"); - } catch (e) { - assert.equal(e.reason, "trying to spend more than allowed", "wrong revert message"); - } - }) - - it("Approval functions works as expected", async () => { - const contract = new ethers.Contract(ISTAKING_V2_ADDRESS, IStakingV2ABI, wallet1); - - { - const tx = await contract.increaseAllowance( - wallet2.address, // spender - stakeNetuid, - tao(10) - ) - await tx.wait(); - - expectedAllowance += BigInt(tao(10)); - - let allowance = BigInt( - await contract.allowance( - wallet1.address, // source - wallet2.address, // spender - stakeNetuid, - ) - ); - assert.equal(allowance, expectedAllowance, "allowance have been increased correctly"); - } - - { - const tx = await contract.decreaseAllowance( - wallet2.address, // spender - stakeNetuid, - tao(2) - ) - await tx.wait(); - - expectedAllowance -= BigInt(tao(2)); - - let allowance = BigInt( - await contract.allowance( - wallet1.address, // source - wallet2.address, // spender - stakeNetuid, - ) - ); - assert.equal(allowance, expectedAllowance, "allowance have been decreased correctly"); - } - - { - const tx = await contract.approve( - wallet2.address, // spender - stakeNetuid, - 0 - ) - await tx.wait(); - - expectedAllowance = BigInt(0); - - let allowance = BigInt( - await contract.allowance( - wallet1.address, // source - wallet2.address, // spender - stakeNetuid, - ) - ); - assert.equal(allowance, expectedAllowance, "allowance have been overwritten correctly"); - } - }) -}) diff --git a/contract-tests/test/staking.precompile.burn-alpha.test.ts b/contract-tests/test/staking.precompile.burn-alpha.test.ts deleted file mode 100644 index f98c988b52..0000000000 --- a/contract-tests/test/staking.precompile.burn-alpha.test.ts +++ /dev/null @@ -1,138 +0,0 @@ -import * as assert from "assert"; -import { getDevnetApi, getRandomSubstrateKeypair } from "../src/substrate" -import { devnet } from "@polkadot-api/descriptors" -import { TypedApi } from "polkadot-api"; -import { convertPublicKeyToSs58, convertH160ToSS58 } from "../src/address-utils" -import { tao } from "../src/balance-math" -import { ethers } from "ethers" -import { generateRandomEthersWallet } from "../src/utils" -import { convertH160ToPublicKey } from "../src/address-utils" -import { - forceSetBalanceToEthAddress, forceSetBalanceToSs58Address, addNewSubnetwork, burnedRegister, - startCall, - getStake, -} from "../src/subtensor" -import { ISTAKING_V2_ADDRESS, IStakingV2ABI } from "../src/contracts/staking" - -describe("Test staking precompile burn alpha", () => { - // init eth part - const wallet1 = generateRandomEthersWallet(); - // init substrate part - const hotkey = getRandomSubstrateKeypair(); - const coldkey = getRandomSubstrateKeypair(); - - let api: TypedApi - - before(async () => { - // init variables got from await and async - api = await getDevnetApi() - - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(hotkey.publicKey)) - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(coldkey.publicKey)) - await forceSetBalanceToEthAddress(api, wallet1.address) - - let netuid = await addNewSubnetwork(api, hotkey, coldkey) - await startCall(api, netuid, coldkey) - - console.log("test the case on subnet ", netuid) - - await burnedRegister(api, netuid, convertH160ToSS58(wallet1.address), coldkey) - }) - - it("Can burn alpha after adding stake", async () => { - let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1 - - // First add some stake - let stakeBalance = tao(50) - const contract = new ethers.Contract(ISTAKING_V2_ADDRESS, IStakingV2ABI, wallet1); - const addStakeTx = await contract.addStake(hotkey.publicKey, stakeBalance.toString(), netuid) - await addStakeTx.wait() - - // Get stake before burning - const stakeBefore = BigInt(await contract.getStake(hotkey.publicKey, convertH160ToPublicKey(wallet1.address), netuid)) - - console.log("Stake before burn:", stakeBefore) - assert.ok(stakeBefore > BigInt(0), "Should have stake before burning") - - // Burn some alpha (burn 20 TAO worth) - let burnAmount = tao(20) - const burnTx = await contract.burnAlpha(hotkey.publicKey, burnAmount.toString(), netuid) - await burnTx.wait() - - // Get stake after burning - const stakeAfter = BigInt(await contract.getStake(hotkey.publicKey, convertH160ToPublicKey(wallet1.address), netuid)) - - console.log("Stake after burn:", stakeAfter) - - // Verify that stake decreased by burn amount - assert.ok(stakeAfter < stakeBefore, "Stake should decrease after burning") - // assert.strictEqual(stakeBefore - stakeAfter, burnAmount, "Stake should decrease by exactly burn amount") - }) - - it("Cannot burn more alpha than staked", async () => { - let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1 - - // Get current stake - const currentStake = await getStake( - api, - convertPublicKeyToSs58(hotkey.publicKey), - convertH160ToSS58(wallet1.address), - netuid - ) - - // Try to burn more than staked - let burnAmount = currentStake + tao(10000) - const contract = new ethers.Contract(ISTAKING_V2_ADDRESS, IStakingV2ABI, wallet1); - - try { - const burnTx = await contract.burnAlpha(hotkey.publicKey, burnAmount.toString(), netuid) - await burnTx.wait() - assert.fail("Transaction should have failed - cannot burn more than staked"); - } catch (error) { - // Transaction failed as expected - console.log("Correctly failed to burn more than staked amount") - assert.ok(true, "Burning more than staked should fail"); - } - }) - - it("Cannot burn alpha from non-existent subnet", async () => { - // wrong netuid - let netuid = 12345; - let burnAmount = tao(10) - const contract = new ethers.Contract(ISTAKING_V2_ADDRESS, IStakingV2ABI, wallet1); - - try { - const burnTx = await contract.burnAlpha(hotkey.publicKey, burnAmount.toString(), netuid) - await burnTx.wait() - assert.fail("Transaction should have failed - subnet doesn't exist"); - } catch (error) { - // Transaction failed as expected - console.log("Correctly failed to burn from non-existent subnet") - assert.ok(true, "Burning from non-existent subnet should fail"); - } - }) - - it("Cannot burn zero alpha", async () => { - let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1 - - // First add some stake for this test - let stakeBalance = tao(10) - const contract = new ethers.Contract(ISTAKING_V2_ADDRESS, IStakingV2ABI, wallet1); - const addStakeTx = await contract.addStake(hotkey.publicKey, stakeBalance.toString(), netuid) - await addStakeTx.wait() - - // Try to burn zero amount - let burnAmount = BigInt(0) - - try { - const burnTx = await contract.burnAlpha(hotkey.publicKey, burnAmount.toString(), netuid) - await burnTx.wait() - assert.fail("Transaction should have failed - cannot burn zero amount"); - } catch (error) { - // Transaction failed as expected - console.log("Correctly failed to burn zero amount") - assert.ok(true, "Burning zero amount should fail"); - } - }) -}) - diff --git a/contract-tests/test/staking.precompile.full-limit.test.ts b/contract-tests/test/staking.precompile.full-limit.test.ts deleted file mode 100644 index faf09d65fd..0000000000 --- a/contract-tests/test/staking.precompile.full-limit.test.ts +++ /dev/null @@ -1,193 +0,0 @@ -import * as assert from "assert"; -import { getDevnetApi, getRandomSubstrateKeypair } from "../src/substrate"; -import { devnet } from "@polkadot-api/descriptors"; -import { TypedApi } from "polkadot-api"; -import { - convertH160ToSS58, - convertPublicKeyToSs58, -} from "../src/address-utils"; -import { tao, raoToEth } from "../src/balance-math"; -import { - addNewSubnetwork, - addStake, - forceSetBalanceToEthAddress, - forceSetBalanceToSs58Address, - getStake, - startCall, -} from "../src/subtensor"; -import { ethers } from "ethers"; -import { generateRandomEthersWallet } from "../src/utils"; -import { ISTAKING_V2_ADDRESS, IStakingV2ABI } from "../src/contracts/staking"; -import { log } from "console"; - -describe("Test staking precompile add remove limit methods", () => { - const hotkey = getRandomSubstrateKeypair(); - const coldkey = getRandomSubstrateKeypair(); - const wallet1 = generateRandomEthersWallet(); - const wallet2 = generateRandomEthersWallet(); - - let api: TypedApi; - - before(async () => { - api = await getDevnetApi(); - await forceSetBalanceToSs58Address( - api, - convertPublicKeyToSs58(hotkey.publicKey), - ); - await forceSetBalanceToSs58Address( - api, - convertPublicKeyToSs58(coldkey.publicKey), - ); - await forceSetBalanceToEthAddress(api, wallet1.address); - await forceSetBalanceToEthAddress(api, wallet2.address); - - await addNewSubnetwork(api, hotkey, coldkey); - let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1; - await startCall(api, netuid, coldkey); - console.log("will test in subnet: ", netuid); - }); - - describe("Add limit then remove stake with limit price", () => { - it("Staker add limit for wallet 1", async () => { - let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1; - let ss58Address = convertH160ToSS58(wallet1.address); - - const alpha = await getStake( - api, - convertPublicKeyToSs58(hotkey.publicKey), - ss58Address, - netuid, - ); - - const contract = new ethers.Contract( - ISTAKING_V2_ADDRESS, - IStakingV2ABI, - wallet1, - ); - - const tx = await contract.addStakeLimit( - hotkey.publicKey, - tao(2000), - tao(1000), - true, - netuid, - ); - await tx.wait(); - - const alphaAfterAddStake = await getStake( - api, - convertPublicKeyToSs58(hotkey.publicKey), - ss58Address, - netuid, - ); - - assert.ok(alphaAfterAddStake > alpha); - }); - - it("Staker remove stake with limit price", async () => { - let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1; - let ss58Address = convertH160ToSS58(wallet1.address); - - const alpha = await getStake( - api, - convertPublicKeyToSs58(hotkey.publicKey), - ss58Address, - netuid, - ); - - const contract = new ethers.Contract( - ISTAKING_V2_ADDRESS, - IStakingV2ABI, - wallet1, - ); - - const tx = await contract.removeStakeFullLimit( - hotkey.publicKey, - netuid, - 90_000_000, - ); - await tx.wait(); - - const alphaAfterRemoveStake = await getStake( - api, - convertPublicKeyToSs58(hotkey.publicKey), - ss58Address, - netuid, - ); - - assert.ok(alphaAfterRemoveStake < alpha); - }); - }); - - describe("Add limit then remove stake full", () => { - - it("Staker add limit for wallet 2", async () => { - let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1; - let ss58Address = convertH160ToSS58(wallet2.address); - - const alpha = await getStake( - api, - convertPublicKeyToSs58(hotkey.publicKey), - ss58Address, - netuid, - ); - - const contract = new ethers.Contract( - ISTAKING_V2_ADDRESS, - IStakingV2ABI, - wallet2, - ); - - const tx = await contract.addStakeLimit( - hotkey.publicKey, - tao(2000), - tao(1000), - true, - netuid, - ); - await tx.wait(); - - const alphaAfterAddStake = await getStake( - api, - convertPublicKeyToSs58(hotkey.publicKey), - ss58Address, - netuid, - ); - - assert.ok(alphaAfterAddStake > alpha); - }); - - it("Staker remove stake with full", async () => { - let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1; - let ss58Address = convertH160ToSS58(wallet2.address); - - const alpha = await getStake( - api, - convertPublicKeyToSs58(hotkey.publicKey), - ss58Address, - netuid, - ); - - const contract = new ethers.Contract( - ISTAKING_V2_ADDRESS, - IStakingV2ABI, - wallet2, - ); - - const tx = await contract.removeStakeFull( - hotkey.publicKey, - netuid, - ); - await tx.wait(); - - const alphaAfterRemoveStake = await getStake( - api, - convertPublicKeyToSs58(hotkey.publicKey), - ss58Address, - netuid, - ); - - assert.ok(alphaAfterRemoveStake < alpha); - }); - }); -}); diff --git a/contract-tests/test/staking.precompile.limit.test.ts b/contract-tests/test/staking.precompile.limit.test.ts deleted file mode 100644 index eff1394911..0000000000 --- a/contract-tests/test/staking.precompile.limit.test.ts +++ /dev/null @@ -1,118 +0,0 @@ -import * as assert from "assert"; -import { getDevnetApi, getRandomSubstrateKeypair } from "../src/substrate"; -import { devnet } from "@polkadot-api/descriptors"; -import { TypedApi } from "polkadot-api"; -import { - convertH160ToSS58, - convertPublicKeyToSs58, -} from "../src/address-utils"; -import { tao, raoToEth } from "../src/balance-math"; -import { - addNewSubnetwork, - addStake, - forceSetBalanceToEthAddress, - forceSetBalanceToSs58Address, - getStake, - startCall, -} from "../src/subtensor"; -import { ethers } from "ethers"; -import { generateRandomEthersWallet } from "../src/utils"; -import { ISTAKING_V2_ADDRESS, IStakingV2ABI } from "../src/contracts/staking"; -import { log } from "console"; - -describe("Test staking precompile add remove limit methods", () => { - const hotkey = getRandomSubstrateKeypair(); - const coldkey = getRandomSubstrateKeypair(); - const wallet1 = generateRandomEthersWallet(); - - let api: TypedApi; - - before(async () => { - api = await getDevnetApi(); - await forceSetBalanceToSs58Address( - api, - convertPublicKeyToSs58(hotkey.publicKey), - ); - await forceSetBalanceToSs58Address( - api, - convertPublicKeyToSs58(coldkey.publicKey), - ); - await forceSetBalanceToEthAddress(api, wallet1.address); - await addNewSubnetwork(api, hotkey, coldkey); - let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1; - await startCall(api, netuid, coldkey); - console.log("will test in subnet: ", netuid); - }); - - it("Staker add limit", async () => { - let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1; - let ss58Address = convertH160ToSS58(wallet1.address); - - const alpha = await getStake( - api, - convertPublicKeyToSs58(hotkey.publicKey), - ss58Address, - netuid, - ); - - const contract = new ethers.Contract( - ISTAKING_V2_ADDRESS, - IStakingV2ABI, - wallet1, - ); - - const tx = await contract.addStakeLimit( - hotkey.publicKey, - tao(2000), - tao(1000), - true, - netuid, - ); - await tx.wait(); - - const alphaAfterAddStake = await getStake( - api, - convertPublicKeyToSs58(hotkey.publicKey), - ss58Address, - netuid, - ); - - assert.ok(alphaAfterAddStake > alpha); - }); - - it("Staker remove limit", async () => { - let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1; - let ss58Address = convertH160ToSS58(wallet1.address); - - const alpha = await getStake( - api, - convertPublicKeyToSs58(hotkey.publicKey), - ss58Address, - netuid, - ); - - const contract = new ethers.Contract( - ISTAKING_V2_ADDRESS, - IStakingV2ABI, - wallet1, - ); - - const tx = await contract.removeStakeLimit( - hotkey.publicKey, - tao(100), - tao(1), - true, - netuid, - ); - await tx.wait(); - - const alphaAfterRemoveStake = await getStake( - api, - convertPublicKeyToSs58(hotkey.publicKey), - ss58Address, - netuid, - ); - - assert.ok(alphaAfterRemoveStake < alpha); - }); -}); diff --git a/contract-tests/test/staking.precompile.stake-get.test.ts b/contract-tests/test/staking.precompile.stake-get.test.ts deleted file mode 100644 index 4730e310d9..0000000000 --- a/contract-tests/test/staking.precompile.stake-get.test.ts +++ /dev/null @@ -1,75 +0,0 @@ -import * as assert from "assert"; -import { getDevnetApi, getRandomSubstrateKeypair } from "../src/substrate" -import { devnet } from "@polkadot-api/descriptors" -import { TypedApi } from "polkadot-api"; -import { convertPublicKeyToSs58 } from "../src/address-utils" -import { tao } from "../src/balance-math" -import { - forceSetBalanceToSs58Address, addNewSubnetwork, addStake, - startCall -} from "../src/subtensor" -import { ethers } from "ethers"; -import { generateRandomEthersWallet } from "../src/utils" -import { ISTAKING_V2_ADDRESS, IStakingV2ABI } from "../src/contracts/staking" -import { log } from "console"; - -describe("Test staking precompile get methods", () => { - const hotkey = getRandomSubstrateKeypair(); - const coldkey = getRandomSubstrateKeypair(); - const wallet1 = generateRandomEthersWallet(); - - let api: TypedApi - - before(async () => { - api = await getDevnetApi() - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(hotkey.publicKey)) - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(coldkey.publicKey)) - await addNewSubnetwork(api, hotkey, coldkey) - let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1 - await startCall(api, netuid, coldkey) - console.log("will test in subnet: ", netuid) - }) - - it("Staker receives rewards", async () => { - let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1 - - await addStake(api, netuid, convertPublicKeyToSs58(hotkey.publicKey), tao(1), coldkey) - - const contract = new ethers.Contract( - ISTAKING_V2_ADDRESS, - IStakingV2ABI, - wallet1 - ); - - const stake = BigInt( - await contract.getStake(hotkey.publicKey, coldkey.publicKey, netuid) - ); - - // validator returned as bigint now. - const validators = - await contract.getAlphaStakedValidators(hotkey.publicKey, netuid) - - const alpha = BigInt( - await contract.getTotalAlphaStaked(hotkey.publicKey, netuid) - ); - assert.ok(stake > 0) - assert.equal(validators.length, 1) - assert.ok(alpha > 0) - - }) - - it("Get nominator min required stake", async () => { - const contract = new ethers.Contract( - ISTAKING_V2_ADDRESS, - IStakingV2ABI, - wallet1 - ); - - const stake = await contract.getNominatorMinRequiredStake() - const stakeOnChain = await api.query.SubtensorModule.NominatorMinRequiredStake.getValue() - - assert.ok(stake !== undefined) - assert.equal(stake.toString(), stakeOnChain.toString()) - - }) -}) diff --git a/contract-tests/test/subnet.precompile.hyperparameter.test.ts b/contract-tests/test/subnet.precompile.hyperparameter.test.ts deleted file mode 100644 index 347a9639eb..0000000000 --- a/contract-tests/test/subnet.precompile.hyperparameter.test.ts +++ /dev/null @@ -1,583 +0,0 @@ -import * as assert from "assert"; - -import { getAliceSigner, getDevnetApi, getRandomSubstrateKeypair, waitForTransactionWithRetry } from "../src/substrate" -import { devnet } from "@polkadot-api/descriptors" -import { Binary, FixedSizeBinary, TypedApi, getTypedCodecs } from "polkadot-api"; -import { convertH160ToSS58, convertPublicKeyToSs58 } from "../src/address-utils" -import { generateRandomEthersWallet } from "../src/utils"; -import { ISubnetABI, ISUBNET_ADDRESS } from "../src/contracts/subnet" -import { ethers } from "ethers" -import { disableAdminFreezeWindowAndOwnerHyperparamRateLimit, forceSetBalanceToEthAddress, forceSetBalanceToSs58Address } from "../src/subtensor" -import { blake2AsU8a } from "@polkadot/util-crypto" - -describe("Test the Subnet precompile contract", () => { - // init eth part - const wallet = generateRandomEthersWallet(); - // init substrate part - - const hotkey1 = getRandomSubstrateKeypair(); - const hotkey2 = getRandomSubstrateKeypair(); - let api: TypedApi - - before(async () => { - // init variables got from await and async - api = await getDevnetApi() - - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(hotkey1.publicKey)) - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(hotkey2.publicKey)) - await forceSetBalanceToEthAddress(api, wallet.address) - - await disableAdminFreezeWindowAndOwnerHyperparamRateLimit(api) - }) - - it("Can register network without identity info", async () => { - const totalNetwork = await api.query.SubtensorModule.TotalNetworks.getValue() - - const contract = new ethers.Contract(ISUBNET_ADDRESS, ISubnetABI, wallet); - const tx = await contract.registerNetwork(hotkey1.publicKey); - await tx.wait(); - - const totalNetworkAfterAdd = await api.query.SubtensorModule.TotalNetworks.getValue() - assert.ok(totalNetwork + 1 === totalNetworkAfterAdd) - }); - - it("Can register network with identity info", async () => { - const totalNetwork = await api.query.SubtensorModule.TotalNetworks.getValue() - - const contract = new ethers.Contract(ISUBNET_ADDRESS, ISubnetABI, wallet); - const tx = await contract.registerNetwork(hotkey2.publicKey, - "name", - "repo", - "contact", - "subnetUrl", - "discord", - "description", - "additional" - ); - await tx.wait(); - - const totalNetworkAfterAdd = await api.query.SubtensorModule.TotalNetworks.getValue() - assert.ok(totalNetwork + 1 === totalNetworkAfterAdd) - }); - - // it.only("Can register network with identity info and logo url", async () => { - // const totalNetwork = await api.query.SubtensorModule.TotalNetworks.getValue() - - // const contract = new ethers.Contract(ISUBNET_ADDRESS, ISubnetABI, wallet); - - // const tx = await contract["registerNetwork(bytes32,string,string,string,string,string,string,string,string)"]( - // hotkey2.publicKey, - // "name", - // "repo", - // "contact", - // "subnetUrl", - // "discord", - // "description", - // "logoUrl", - // "additional" - // ); - // await tx.wait(); - - // const totalNetworkAfterAdd = await api.query.SubtensorModule.TotalNetworks.getValue() - // assert.ok(totalNetwork + 1 === totalNetworkAfterAdd) - // }); - - it("Can set servingRateLimit parameter", async () => { - - const totalNetwork = await api.query.SubtensorModule.TotalNetworks.getValue() - const contract = new ethers.Contract(ISUBNET_ADDRESS, ISubnetABI, wallet); - const netuid = totalNetwork - 1; - - const newValue = 100; - const tx = await contract.setServingRateLimit(netuid, newValue); - await tx.wait(); - - let onchainValue = await api.query.SubtensorModule.ServingRateLimit.getValue(netuid) - - - let valueFromContract = Number( - await contract.getServingRateLimit(netuid) - ); - - assert.equal(valueFromContract, newValue) - assert.equal(valueFromContract, onchainValue); - }) - - - // minDifficulty hyperparameter - // - // disabled: only by sudo - // - // newValue = 101; - // tx = await contract.setMinDifficulty(netuid, newValue); - // await tx.wait(); - - // await usingApi(async (api) => { - // onchainValue = Number( - // await api.query.subtensorModule.minDifficulty(netuid) - // ); - // }); - - // valueFromContract = Number(await contract.getMinDifficulty(netuid)); - - // expect(valueFromContract).to.eq(newValue); - // expect(valueFromContract).to.eq(onchainValue); - - it("Can set maxDifficulty parameter", async () => { - - const totalNetwork = await api.query.SubtensorModule.TotalNetworks.getValue() - const contract = new ethers.Contract(ISUBNET_ADDRESS, ISubnetABI, wallet); - const netuid = totalNetwork - 1; - - const newValue = 102; - const tx = await contract.setMaxDifficulty(netuid, newValue); - await tx.wait(); - - let onchainValue = await api.query.SubtensorModule.MaxDifficulty.getValue(netuid) - - - let valueFromContract = Number( - await contract.getMaxDifficulty(netuid) - ); - - assert.equal(valueFromContract, newValue) - assert.equal(valueFromContract, onchainValue); - }) - - - it("Can set weightsVersionKey parameter", async () => { - - const totalNetwork = await api.query.SubtensorModule.TotalNetworks.getValue() - const contract = new ethers.Contract(ISUBNET_ADDRESS, ISubnetABI, wallet); - const netuid = totalNetwork - 1; - - const newValue = 103; - const tx = await contract.setWeightsVersionKey(netuid, newValue); - await tx.wait(); - - let onchainValue = await api.query.SubtensorModule.WeightsVersionKey.getValue(netuid) - - - let valueFromContract = Number( - await contract.getWeightsVersionKey(netuid) - ); - - assert.equal(valueFromContract, newValue) - assert.equal(valueFromContract, onchainValue); - }) - - // need sudo as origin now - // it("Can set weightsSetRateLimit parameter", async () => { - - // const totalNetwork = await api.query.SubtensorModule.TotalNetworks.getValue() - // const contract = new ethers.Contract(ISUBNET_ADDRESS, ISubnetABI, wallet); - // const netuid = totalNetwork - 1; - - // const newValue = 104; - // const tx = await contract.setWeightsSetRateLimit(netuid, newValue); - // await tx.wait(); - - // let onchainValue = await api.query.SubtensorModule.WeightsSetRateLimit.getValue(netuid) - - - // let valueFromContract = Number( - // await contract.getWeightsSetRateLimit(netuid) - // ); - - // assert.equal(valueFromContract, newValue) - // assert.equal(valueFromContract, onchainValue); - // }) - - it("Can set adjustmentAlpha parameter", async () => { - - const totalNetwork = await api.query.SubtensorModule.TotalNetworks.getValue() - const contract = new ethers.Contract(ISUBNET_ADDRESS, ISubnetABI, wallet); - const netuid = totalNetwork - 1; - - const newValue = 105; - const tx = await contract.setAdjustmentAlpha(netuid, newValue); - await tx.wait(); - - let onchainValue = await api.query.SubtensorModule.AdjustmentAlpha.getValue(netuid) - - - let valueFromContract = Number( - await contract.getAdjustmentAlpha(netuid) - ); - - assert.equal(valueFromContract, newValue) - assert.equal(valueFromContract, onchainValue); - }) - - it("Returns constant maxWeightLimit", async () => { - - const totalNetwork = await api.query.SubtensorModule.TotalNetworks.getValue() - const contract = new ethers.Contract(ISUBNET_ADDRESS, ISubnetABI, wallet); - const netuid = totalNetwork - 1; - - const valueFromContract = Number( - await contract.getMaxWeightLimit(netuid) - ); - - assert.equal(valueFromContract, 0xFFFF) - }) - - it("Can set immunityPeriod parameter", async () => { - - const totalNetwork = await api.query.SubtensorModule.TotalNetworks.getValue() - const contract = new ethers.Contract(ISUBNET_ADDRESS, ISubnetABI, wallet); - const netuid = totalNetwork - 1; - - const newValue = 107; - const tx = await contract.setImmunityPeriod(netuid, newValue); - await tx.wait(); - - let onchainValue = await api.query.SubtensorModule.ImmunityPeriod.getValue(netuid) - - - let valueFromContract = Number( - await contract.getImmunityPeriod(netuid) - ); - - assert.equal(valueFromContract, newValue) - assert.equal(valueFromContract, onchainValue); - }) - - it("Can set minAllowedWeights parameter", async () => { - - const totalNetwork = await api.query.SubtensorModule.TotalNetworks.getValue() - const contract = new ethers.Contract(ISUBNET_ADDRESS, ISubnetABI, wallet); - const netuid = totalNetwork - 1; - - const newValue = 108; - const tx = await contract.setMinAllowedWeights(netuid, newValue); - await tx.wait(); - - let onchainValue = await api.query.SubtensorModule.MinAllowedWeights.getValue(netuid) - - - let valueFromContract = Number( - await contract.getMinAllowedWeights(netuid) - ); - - assert.equal(valueFromContract, newValue) - assert.equal(valueFromContract, onchainValue); - }) - - // disable the set kappa parameter test, because it is only callable by sudo now - // it("Can set kappa parameter", async () => { - - // const totalNetwork = await api.query.SubtensorModule.TotalNetworks.getValue() - // const contract = new ethers.Contract(ISUBNET_ADDRESS, ISubnetABI, wallet); - // const netuid = totalNetwork - 1; - - // const newValue = 109; - // const tx = await contract.setKappa(netuid, newValue); - // await tx.wait(); - - // let onchainValue = await api.query.SubtensorModule.Kappa.getValue(netuid) - - - // let valueFromContract = Number( - // await contract.getKappa(netuid) - // ); - - // assert.equal(valueFromContract, newValue) - // assert.equal(valueFromContract, onchainValue); - // }) - - it("Can set rho parameter", async () => { - - const totalNetwork = await api.query.SubtensorModule.TotalNetworks.getValue() - const contract = new ethers.Contract(ISUBNET_ADDRESS, ISubnetABI, wallet); - const netuid = totalNetwork - 1; - - const newValue = 110; - const tx = await contract.setRho(netuid, newValue); - await tx.wait(); - - let onchainValue = await api.query.SubtensorModule.Rho.getValue(netuid) - - - let valueFromContract = Number( - await contract.getRho(netuid) - ); - - assert.equal(valueFromContract, newValue) - assert.equal(valueFromContract, onchainValue); - }) - - it("Can set activityCutoff parameter", async () => { - - const totalNetwork = await api.query.SubtensorModule.TotalNetworks.getValue() - const contract = new ethers.Contract(ISUBNET_ADDRESS, ISubnetABI, wallet); - const netuid = totalNetwork - 1; - const newValue = await api.query.SubtensorModule.MinActivityCutoff.getValue() + 1; - const tx = await contract.setActivityCutoff(netuid, newValue); - await tx.wait(); - - let onchainValue = await api.query.SubtensorModule.ActivityCutoff.getValue(netuid) - - - let valueFromContract = Number( - await contract.getActivityCutoff(netuid) - ); - - assert.equal(valueFromContract, newValue) - assert.equal(valueFromContract, onchainValue); - }) - - // it("Can set networkRegistrationAllowed parameter", async () => { - - // const totalNetwork = await api.query.SubtensorModule.TotalNetworks.getValue() - // const contract = new ethers.Contract(ISUBNET_ADDRESS, ISubnetABI, wallet); - // const netuid = totalNetwork - 1; - - // const newValue = true; - // const tx = await contract.setNetworkRegistrationAllowed(netuid, newValue); - // await tx.wait(); - - // let onchainValue = await api.query.SubtensorModule.NetworkRegistrationAllowed.getValue(netuid) - - - // let valueFromContract = Boolean( - // await contract.getNetworkRegistrationAllowed(netuid) - // ); - - // assert.equal(valueFromContract, newValue) - // assert.equal(valueFromContract, onchainValue); - // }) - - // it("Can set networkPowRegistrationAllowed parameter", async () => { - - // const totalNetwork = await api.query.SubtensorModule.TotalNetworks.getValue() - // const contract = new ethers.Contract(ISUBNET_ADDRESS, ISubnetABI, wallet); - // const netuid = totalNetwork - 1; - - // const newValue = true; - // const tx = await contract.setNetworkPowRegistrationAllowed(netuid, newValue); - // await tx.wait(); - - // let onchainValue = await api.query.SubtensorModule.NetworkPowRegistrationAllowed.getValue(netuid) - - - // let valueFromContract = Boolean( - // await contract.getNetworkPowRegistrationAllowed(netuid) - // ); - - // assert.equal(valueFromContract, newValue) - // assert.equal(valueFromContract, onchainValue); - // }) - - // minBurn hyperparameter. only sudo can set it now - // newValue = 112; - - // tx = await contract.setMinBurn(netuid, newValue); - // await tx.wait(); - - // await usingApi(async (api) => { - // onchainValue = Number( - // await api.query.subtensorModule.minBurn(netuid) - // ); - // }); - - // valueFromContract = Number(await contract.getMinBurn(netuid)); - - // expect(valueFromContract).to.eq(newValue); - // expect(valueFromContract).to.eq(onchainValue); - - // maxBurn hyperparameter. only sudo can set it now - // it("Can set maxBurn parameter", async () => { - - // const totalNetwork = await api.query.SubtensorModule.TotalNetworks.getValue() - // const contract = new ethers.Contract(ISUBNET_ADDRESS, ISubnetABI, wallet); - // const netuid = totalNetwork - 1; - - // const newValue = 113; - // const tx = await contract.setMaxBurn(netuid, newValue); - // await tx.wait(); - - // let onchainValue = await api.query.SubtensorModule.MaxBurn.getValue(netuid) - - - // let valueFromContract = Number( - // await contract.getMaxBurn(netuid) - // ); - - // assert.equal(valueFromContract, newValue) - // assert.equal(valueFromContract, onchainValue); - // }) - - - // difficulty hyperparameter (disabled: sudo only) - // newValue = 114; - - // tx = await contract.setDifficulty(netuid, newValue); - // await tx.wait(); - - // await usingApi(async (api) => { - // onchainValue = Number( - // await api.query.subtensorModule.difficulty(netuid) - // ); - // }); - - // valueFromContract = Number(await contract.getDifficulty(netuid)); - - // expect(valueFromContract).to.eq(newValue); - // expect(valueFromContract).to.eq(onchainValue); - - it("Can set bondsMovingAverage parameter", async () => { - - const totalNetwork = await api.query.SubtensorModule.TotalNetworks.getValue() - const contract = new ethers.Contract(ISUBNET_ADDRESS, ISubnetABI, wallet); - const netuid = totalNetwork - 1; - - const newValue = 115; - const tx = await contract.setBondsMovingAverage(netuid, newValue); - await tx.wait(); - - let onchainValue = await api.query.SubtensorModule.BondsMovingAverage.getValue(netuid) - - - let valueFromContract = Number( - await contract.getBondsMovingAverage(netuid) - ); - - assert.equal(valueFromContract, newValue) - assert.equal(valueFromContract, onchainValue); - }) - - it("Can set commitRevealWeightsEnabled parameter", async () => { - - const totalNetwork = await api.query.SubtensorModule.TotalNetworks.getValue() - const contract = new ethers.Contract(ISUBNET_ADDRESS, ISubnetABI, wallet); - const netuid = totalNetwork - 1; - - const newValue = true; - const tx = await contract.setCommitRevealWeightsEnabled(netuid, newValue); - await tx.wait(); - - let onchainValue = await api.query.SubtensorModule.CommitRevealWeightsEnabled.getValue(netuid) - - - let valueFromContract = Boolean( - await contract.getCommitRevealWeightsEnabled(netuid) - ); - - assert.equal(valueFromContract, newValue) - assert.equal(valueFromContract, onchainValue); - }) - - it("Can set liquidAlphaEnabled parameter", async () => { - - const totalNetwork = await api.query.SubtensorModule.TotalNetworks.getValue() - const contract = new ethers.Contract(ISUBNET_ADDRESS, ISubnetABI, wallet); - const netuid = totalNetwork - 1; - - const newValue = true; - const tx = await contract.setLiquidAlphaEnabled(netuid, newValue); - await tx.wait(); - - let onchainValue = await api.query.SubtensorModule.LiquidAlphaOn.getValue(netuid) - - - let valueFromContract = Boolean( - await contract.getLiquidAlphaEnabled(netuid) - ); - - assert.equal(valueFromContract, newValue) - assert.equal(valueFromContract, onchainValue); - }) - - it("Can set yuma3Enabled hyperparameter", async () => { - const totalNetwork = await api.query.SubtensorModule.TotalNetworks.getValue() - const contract = new ethers.Contract(ISUBNET_ADDRESS, ISubnetABI, wallet); - const netuid = totalNetwork - 1; - - const newValue = true; - const tx = await contract.setYuma3Enabled(netuid, newValue); - await tx.wait(); - - let onchainValue = await api.query.SubtensorModule.Yuma3On.getValue(netuid) - - let valueFromContract = Boolean( - await contract.getYuma3Enabled(netuid) - ); - assert.equal(valueFromContract, newValue) - assert.equal(valueFromContract, onchainValue); - }) - - - // it("Can set alphaValues parameter", async () => { - // const totalNetwork = await api.query.SubtensorModule.TotalNetworks.getValue() - // const contract = new ethers.Contract(ISUBNET_ADDRESS, ISubnetABI, wallet); - // const netuid = totalNetwork - 1; - - // const newValue = [118, 52429]; - // const tx = await contract.setAlphaValues(netuid, newValue[0], newValue[1]); - // await tx.wait(); - - // let onchainValue = await api.query.SubtensorModule.AlphaV2Values.getValue(netuid) - - // let value = await contract.getAlphaValues(netuid) - // let valueFromContract = [Number(value[0]), Number(value[1])] - - // assert.equal(valueFromContract[0], newValue[0]) - // assert.equal(valueFromContract[1], newValue[1]) - // assert.equal(valueFromContract[0], onchainValue[0]); - // assert.equal(valueFromContract[1], onchainValue[1]); - // }) - - it("Can set commitRevealWeightsInterval parameter", async () => { - const totalNetwork = await api.query.SubtensorModule.TotalNetworks.getValue() - const contract = new ethers.Contract(ISUBNET_ADDRESS, ISubnetABI, wallet); - const netuid = totalNetwork - 1; - - const newValue = 99; - const tx = await contract.setCommitRevealWeightsInterval(netuid, newValue); - await tx.wait(); - - let onchainValue = await api.query.SubtensorModule.RevealPeriodEpochs.getValue(netuid) - - let valueFromContract = Number( - await contract.getCommitRevealWeightsInterval(netuid) - ); - - assert.equal(valueFromContract, newValue) - assert.equal(valueFromContract, onchainValue); - }) - - it("Rejects subnet precompile calls when coldkey swap is scheduled (tx extension)", async () => { - const totalNetwork = await api.query.SubtensorModule.TotalNetworks.getValue() - const contract = new ethers.Contract(ISUBNET_ADDRESS, ISubnetABI, wallet); - const netuid = totalNetwork - 1; - - const coldkeySs58 = convertH160ToSS58(wallet.address) - const newColdkeyHash = FixedSizeBinary.fromBytes(blake2AsU8a(hotkey1.publicKey)) - const currentBlock = await api.query.System.Number.getValue() - const executionBlock = currentBlock + 10 - - const codec = await getTypedCodecs(devnet); - const valueBytes = codec.query.SubtensorModule.ColdkeySwapAnnouncements.value.enc([ - executionBlock, - newColdkeyHash - ]) - const key = await api.query.SubtensorModule.ColdkeySwapAnnouncements.getKey(coldkeySs58); - - // Use sudo + set_storage since the swap-scheduled check only exists in the tx extension. - const setStorageCall = api.tx.System.set_storage({ - items: [[Binary.fromHex(key), Binary.fromBytes(valueBytes)]], - }) - const sudoTx = api.tx.Sudo.sudo({ call: setStorageCall.decodedCall }) - await waitForTransactionWithRetry(api, sudoTx, getAliceSigner()) - - const storedValue = await api.query.SubtensorModule.ColdkeySwapAnnouncements.getValue(coldkeySs58) - assert.equal(storedValue?.[0], executionBlock) - assert.equal(storedValue?.[1].asHex(), newColdkeyHash.asHex()) - - await assert.rejects(async () => { - const tx = await contract.setServingRateLimit(netuid, 100); - await tx.wait(); - }) - }) -}) diff --git a/contract-tests/test/votingPower.precompile.test.ts b/contract-tests/test/votingPower.precompile.test.ts deleted file mode 100644 index f98edd5fc1..0000000000 --- a/contract-tests/test/votingPower.precompile.test.ts +++ /dev/null @@ -1,226 +0,0 @@ -import * as assert from "assert"; - -import { getDevnetApi, getRandomSubstrateKeypair, getAliceSigner, getSignerFromKeypair, waitForTransactionWithRetry } from "../src/substrate" -import { getPublicClient } from "../src/utils"; -import { ETH_LOCAL_URL } from "../src/config"; -import { devnet } from "@polkadot-api/descriptors" -import { PublicClient } from "viem"; -import { PolkadotSigner, TypedApi } from "polkadot-api"; -import { toViemAddress, convertPublicKeyToSs58 } from "../src/address-utils" -import { IVotingPowerABI, IVOTING_POWER_ADDRESS } from "../src/contracts/votingPower" -import { forceSetBalanceToSs58Address, addNewSubnetwork, startCall } from "../src/subtensor"; - -describe("Test VotingPower Precompile", () => { - // init substrate part - const hotkey = getRandomSubstrateKeypair(); - const coldkey = getRandomSubstrateKeypair(); - let publicClient: PublicClient; - - let api: TypedApi; - - // sudo account alice as signer - let alice: PolkadotSigner; - - // init other variable - let subnetId = 0; - - before(async () => { - // init variables got from await and async - publicClient = await getPublicClient(ETH_LOCAL_URL) - api = await getDevnetApi() - alice = await getAliceSigner(); - - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(hotkey.publicKey)) - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(coldkey.publicKey)) - - let netuid = await addNewSubnetwork(api, hotkey, coldkey) - await startCall(api, netuid, coldkey) - subnetId = netuid - }) - - describe("VotingPower Tracking Status Functions", () => { - it("isVotingPowerTrackingEnabled returns false by default", async () => { - const isEnabled = await publicClient.readContract({ - abi: IVotingPowerABI, - address: toViemAddress(IVOTING_POWER_ADDRESS), - functionName: "isVotingPowerTrackingEnabled", - args: [subnetId] - }) - - assert.ok(isEnabled !== undefined, "isVotingPowerTrackingEnabled should return a value"); - assert.strictEqual(typeof isEnabled, 'boolean', "isVotingPowerTrackingEnabled should return a boolean"); - // By default, voting power tracking is disabled - assert.strictEqual(isEnabled, false, "Voting power tracking should be disabled by default"); - }); - - it("getVotingPowerDisableAtBlock returns 0 when not scheduled", async () => { - const disableAtBlock = await publicClient.readContract({ - abi: IVotingPowerABI, - address: toViemAddress(IVOTING_POWER_ADDRESS), - functionName: "getVotingPowerDisableAtBlock", - args: [subnetId] - }) - - assert.ok(disableAtBlock !== undefined, "getVotingPowerDisableAtBlock should return a value"); - assert.strictEqual(typeof disableAtBlock, 'bigint', "getVotingPowerDisableAtBlock should return a bigint"); - assert.strictEqual(disableAtBlock, BigInt(0), "Disable at block should be 0 when not scheduled"); - }); - - it("getVotingPowerEmaAlpha returns default alpha value", async () => { - const alpha = await publicClient.readContract({ - abi: IVotingPowerABI, - address: toViemAddress(IVOTING_POWER_ADDRESS), - functionName: "getVotingPowerEmaAlpha", - args: [subnetId] - }) - - assert.ok(alpha !== undefined, "getVotingPowerEmaAlpha should return a value"); - assert.strictEqual(typeof alpha, 'bigint', "getVotingPowerEmaAlpha should return a bigint"); - // Default alpha is 0_003_570_000_000_000_000 // 0.00357 * 10^18 = 2 weeks e-folding (time-constant) @ 361 - assert.strictEqual(alpha, BigInt("3570000000000000"), "Default alpha should be 0.00357 * 10^18 (3570000000000000)"); - }); - }); - - describe("VotingPower Query Functions", () => { - it("getVotingPower returns 0 for hotkey without voting power", async () => { - // Convert hotkey public key to bytes32 format (0x prefixed hex string) - const hotkeyBytes32 = '0x' + Buffer.from(hotkey.publicKey).toString('hex'); - - const votingPower = await publicClient.readContract({ - abi: IVotingPowerABI, - address: toViemAddress(IVOTING_POWER_ADDRESS), - functionName: "getVotingPower", - args: [subnetId, hotkeyBytes32 as `0x${string}`] - }) - - assert.ok(votingPower !== undefined, "getVotingPower should return a value"); - assert.strictEqual(typeof votingPower, 'bigint', "getVotingPower should return a bigint"); - // Without voting power tracking enabled, voting power should be 0 - assert.strictEqual(votingPower, BigInt(0), "Voting power should be 0 when tracking is disabled"); - }); - - it("getVotingPower returns 0 for unknown hotkey", async () => { - // Generate a random hotkey that doesn't exist - const randomHotkey = getRandomSubstrateKeypair(); - const randomHotkeyBytes32 = '0x' + Buffer.from(randomHotkey.publicKey).toString('hex'); - - const votingPower = await publicClient.readContract({ - abi: IVotingPowerABI, - address: toViemAddress(IVOTING_POWER_ADDRESS), - functionName: "getVotingPower", - args: [subnetId, randomHotkeyBytes32 as `0x${string}`] - }) - - assert.ok(votingPower !== undefined, "getVotingPower should return a value"); - assert.strictEqual(votingPower, BigInt(0), "Voting power should be 0 for unknown hotkey"); - }); - - it("getTotalVotingPower returns 0 when no voting power exists", async () => { - const totalVotingPower = await publicClient.readContract({ - abi: IVotingPowerABI, - address: toViemAddress(IVOTING_POWER_ADDRESS), - functionName: "getTotalVotingPower", - args: [subnetId] - }) - - assert.ok(totalVotingPower !== undefined, "getTotalVotingPower should return a value"); - assert.strictEqual(typeof totalVotingPower, 'bigint', "getTotalVotingPower should return a bigint"); - assert.strictEqual(totalVotingPower, BigInt(0), "Total voting power should be 0 when tracking is disabled"); - }); - }); - - describe("VotingPower with Tracking Enabled", () => { - let enabledSubnetId: number; - - before(async () => { - // Create a new subnet for this test - const hotkey2 = getRandomSubstrateKeypair(); - const coldkey2 = getRandomSubstrateKeypair(); - - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(hotkey2.publicKey)) - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(coldkey2.publicKey)) - - enabledSubnetId = await addNewSubnetwork(api, hotkey2, coldkey2) - await startCall(api, enabledSubnetId, coldkey2) - - // Enable voting power tracking via sudo - const internalCall = api.tx.SubtensorModule.enable_voting_power_tracking({ netuid: enabledSubnetId }) - const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }) - await waitForTransactionWithRetry(api, tx, alice) - }); - - it("isVotingPowerTrackingEnabled returns true after enabling", async () => { - const isEnabled = await publicClient.readContract({ - abi: IVotingPowerABI, - address: toViemAddress(IVOTING_POWER_ADDRESS), - functionName: "isVotingPowerTrackingEnabled", - args: [enabledSubnetId] - }) - - assert.strictEqual(isEnabled, true, "Voting power tracking should be enabled"); - }); - - it("getVotingPowerDisableAtBlock still returns 0 when enabled but not scheduled for disable", async () => { - const disableAtBlock = await publicClient.readContract({ - abi: IVotingPowerABI, - address: toViemAddress(IVOTING_POWER_ADDRESS), - functionName: "getVotingPowerDisableAtBlock", - args: [enabledSubnetId] - }) - - assert.strictEqual(disableAtBlock, BigInt(0), "Disable at block should still be 0"); - }); - }); - - describe("All precompile functions are accessible", () => { - it("All VotingPower precompile functions can be called", async () => { - const hotkeyBytes32 = '0x' + Buffer.from(hotkey.publicKey).toString('hex'); - - // Test all five functions - const results = await Promise.all([ - publicClient.readContract({ - abi: IVotingPowerABI, - address: toViemAddress(IVOTING_POWER_ADDRESS), - functionName: "getVotingPower", - args: [subnetId, hotkeyBytes32 as `0x${string}`] - }), - publicClient.readContract({ - abi: IVotingPowerABI, - address: toViemAddress(IVOTING_POWER_ADDRESS), - functionName: "isVotingPowerTrackingEnabled", - args: [subnetId] - }), - publicClient.readContract({ - abi: IVotingPowerABI, - address: toViemAddress(IVOTING_POWER_ADDRESS), - functionName: "getVotingPowerDisableAtBlock", - args: [subnetId] - }), - publicClient.readContract({ - abi: IVotingPowerABI, - address: toViemAddress(IVOTING_POWER_ADDRESS), - functionName: "getVotingPowerEmaAlpha", - args: [subnetId] - }), - publicClient.readContract({ - abi: IVotingPowerABI, - address: toViemAddress(IVOTING_POWER_ADDRESS), - functionName: "getTotalVotingPower", - args: [subnetId] - }) - ]); - - // All functions should return defined values - results.forEach((result: unknown, index: number) => { - assert.ok(result !== undefined, `Function ${index} should return a value`); - }); - - // Verify types - assert.strictEqual(typeof results[0], 'bigint', "getVotingPower should return bigint"); - assert.strictEqual(typeof results[1], 'boolean', "isVotingPowerTrackingEnabled should return boolean"); - assert.strictEqual(typeof results[2], 'bigint', "getVotingPowerDisableAtBlock should return bigint"); - assert.strictEqual(typeof results[3], 'bigint', "getVotingPowerEmaAlpha should return bigint"); - assert.strictEqual(typeof results[4], 'bigint', "getTotalVotingPower should return bigint"); - }); - }); -}); diff --git a/contract-tests/test/wasm.contract.test.ts b/contract-tests/test/wasm.contract.test.ts index 26d5c87924..465b3214f8 100644 --- a/contract-tests/test/wasm.contract.test.ts +++ b/contract-tests/test/wasm.contract.test.ts @@ -4,11 +4,12 @@ import { Binary, TypedApi } from "polkadot-api"; import * as assert from "assert"; import { contracts } from "../.papi/descriptors"; import { getInkClient, InkClient, } from "@polkadot-api/ink-contracts" -import { forceSetBalanceToSs58Address, startCall, burnedRegister } from "../src/subtensor"; +import { forceSetBalanceToSs58Address, startCall, burnedRegister, setTargetRegistrationsPerInterval, setAdminFreezeWindow } from "../src/subtensor"; import fs from "fs" import { convertPublicKeyToSs58 } from "../src/address-utils"; import { addNewSubnetwork, sendWasmContractExtrinsic } from "../src/subtensor"; import { tao } from "../src/balance-math"; +import { KeyPair } from "@polkadot-labs/hdkd-helpers" const bittensorWasmPath = "./bittensor/target/ink/bittensor.wasm" const bittensorBytecode = fs.readFileSync(bittensorWasmPath) @@ -16,31 +17,33 @@ const bittensorBytecode = fs.readFileSync(bittensorWasmPath) describe("Test wasm contract", () => { let api: TypedApi - const hotkey = getRandomSubstrateKeypair(); - const coldkey = getRandomSubstrateKeypair(); + let hotkey: KeyPair; + let coldkey: KeyPair; - const hotkey2 = getRandomSubstrateKeypair(); - const coldkey2 = getRandomSubstrateKeypair(); + let hotkey2: KeyPair; + let coldkey2: KeyPair; // set initial netuid to 0 to avoid warning let netuid: number = 0; - let contractAddress: string; + let contractAddress = ""; let inkClient: InkClient; - async function addStakeWhenWithoutStake() { - const stakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - contractAddress, - netuid, - ))?.stake - - assert.ok(stakeBefore !== undefined) - if (stakeBefore > BigInt(0)) { + async function addStakeViaContract(addStakeToContract: boolean) { + if (contractAddress === "") { return; } const amount = tao(100) - const message = inkClient.message("add_stake") + let message + let dest + if (addStakeToContract) { + message = inkClient.message("add_stake") + dest = contractAddress; + } else { + message = inkClient.message("caller_add_stake") + dest = convertPublicKeyToSs58(coldkey.publicKey); + } + const data = message.encode({ hotkey: Binary.fromBytes(hotkey.publicKey), netuid: netuid, @@ -50,7 +53,7 @@ describe("Test wasm contract", () => { const stake = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( convertPublicKeyToSs58(hotkey.publicKey), - contractAddress, + dest, netuid, ))?.stake @@ -58,25 +61,42 @@ describe("Test wasm contract", () => { assert.ok(stake > BigInt(0)) } + async function initSecondColdAndHotkey() { + hotkey2 = getRandomSubstrateKeypair(); + coldkey2 = getRandomSubstrateKeypair(); + await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(coldkey2.publicKey)) + await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(hotkey2.publicKey)) + await burnedRegister(api, netuid, convertPublicKeyToSs58(hotkey2.publicKey), coldkey2) + } before(async () => { // init variables got from await and async api = await getDevnetApi() + await setAdminFreezeWindow(api); inkClient = getInkClient(contracts.bittensor) + hotkey = getRandomSubstrateKeypair(); + coldkey = getRandomSubstrateKeypair(); await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(coldkey.publicKey)) - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(coldkey2.publicKey)) await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(hotkey.publicKey)) - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(hotkey2.publicKey)) + netuid = await addNewSubnetwork(api, hotkey, coldkey) await startCall(api, netuid, coldkey) console.log("test the case on subnet ", netuid) - await burnedRegister(api, netuid, convertPublicKeyToSs58(hotkey2.publicKey), coldkey2) - await addNewSubnetwork(api, hotkey, coldkey) await startCall(api, netuid + 1, coldkey) + await setTargetRegistrationsPerInterval(api, netuid) }) + beforeEach(async () => { + hotkey = getRandomSubstrateKeypair(); + coldkey = getRandomSubstrateKeypair(); + await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(coldkey.publicKey)) + await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(hotkey.publicKey)) + await burnedRegister(api, netuid, convertPublicKeyToSs58(hotkey.publicKey), coldkey) + + }); + it("Can instantiate contract", async () => { const signer = getSignerFromKeypair(coldkey); const constructor = inkClient.constructor('new') @@ -105,12 +125,11 @@ describe("Test wasm contract", () => { value: tao(2000), }) await waitForTransactionWithRetry(api, transfer, signer) - - console.log("===== contractAddress", contractAddress) }) it("Can query stake info from contract", async () => { + const queryMessage = inkClient.message("get_stake_info_for_hotkey_coldkey_netuid") const data = queryMessage.encode({ @@ -143,11 +162,11 @@ describe("Test wasm contract", () => { }) it("Can add stake to contract", async () => { - await addStakeWhenWithoutStake() + await addStakeViaContract(true) }) it("Can remove stake to contract", async () => { - await addStakeWhenWithoutStake() + await addStakeViaContract(true) const stake = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( convertPublicKeyToSs58(hotkey.publicKey), contractAddress, @@ -178,8 +197,7 @@ describe("Test wasm contract", () => { }) it("Can unstake all from contract", async () => { - await addStakeWhenWithoutStake() - + await addStakeViaContract(true) // Get stake before unstake_all const stakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( convertPublicKeyToSs58(hotkey.publicKey), @@ -208,7 +226,7 @@ describe("Test wasm contract", () => { }) it("Can unstake all alpha from contract", async () => { - await addStakeWhenWithoutStake() + await addStakeViaContract(true) // Get stake before unstake_all_alpha const stakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( convertPublicKeyToSs58(hotkey.publicKey), @@ -237,8 +255,8 @@ describe("Test wasm contract", () => { }) it("Can move stake between hotkeys", async () => { - await addStakeWhenWithoutStake() - + await addStakeViaContract(true) + await initSecondColdAndHotkey() // Get initial stakes const originStakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( convertPublicKeyToSs58(hotkey.publicKey), @@ -286,8 +304,8 @@ describe("Test wasm contract", () => { }) it("Can transfer stake between coldkeys", async () => { - await addStakeWhenWithoutStake() - + await addStakeViaContract(true) + await initSecondColdAndHotkey() // Get initial stake const stakeBeforeOrigin = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( convertPublicKeyToSs58(hotkey.publicKey), @@ -336,8 +354,7 @@ describe("Test wasm contract", () => { }) it("Can swap stake between networks", async () => { - await addStakeWhenWithoutStake() - + await addStakeViaContract(true) // Get initial stakes const stakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( convertPublicKeyToSs58(hotkey.publicKey), @@ -384,7 +401,6 @@ describe("Test wasm contract", () => { }) it("Can add stake with limit", async () => { - await addStakeWhenWithoutStake() const stakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( convertPublicKeyToSs58(hotkey.publicKey), contractAddress, @@ -415,7 +431,7 @@ describe("Test wasm contract", () => { }) it("Can remove stake with limit", async () => { - await addStakeWhenWithoutStake() + await addStakeViaContract(true) const stakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( convertPublicKeyToSs58(hotkey.publicKey), contractAddress, @@ -445,7 +461,7 @@ describe("Test wasm contract", () => { }) it("Can swap stake with limit", async () => { - await addStakeWhenWithoutStake() + await addStakeViaContract(true) const stakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( convertPublicKeyToSs58(hotkey.publicKey), @@ -492,8 +508,7 @@ describe("Test wasm contract", () => { }) it("Can remove stake full limit", async () => { - await addStakeWhenWithoutStake() - + await addStakeViaContract(true) const stakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( convertPublicKeyToSs58(hotkey.publicKey), contractAddress, @@ -524,7 +539,7 @@ describe("Test wasm contract", () => { const message = inkClient.message("set_coldkey_auto_stake_hotkey") const data = message.encode({ netuid: netuid, - hotkey: Binary.fromBytes(hotkey2.publicKey), + hotkey: Binary.fromBytes(hotkey.publicKey), }) await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) @@ -534,13 +549,13 @@ describe("Test wasm contract", () => { ) assert.ok(autoStakeHotkey !== undefined) - assert.ok(autoStakeHotkey === convertPublicKeyToSs58(hotkey2.publicKey)) + assert.ok(autoStakeHotkey === convertPublicKeyToSs58(hotkey.publicKey)) }) it("Can add and remove proxy", async () => { const message = inkClient.message("add_proxy") const data = message.encode({ - delegate: Binary.fromBytes(hotkey2.publicKey), + delegate: Binary.fromBytes(hotkey.publicKey), }) await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) let proxies = await api.query.Proxy.Proxies.getValue( @@ -548,12 +563,12 @@ describe("Test wasm contract", () => { ) assert.ok(proxies !== undefined) assert.ok(proxies.length > 0 && proxies[0].length > 0) - assert.ok(proxies[0][0].delegate === convertPublicKeyToSs58(hotkey2.publicKey)) + assert.ok(proxies[0][0].delegate === convertPublicKeyToSs58(hotkey.publicKey)) const removeMessage = inkClient.message("remove_proxy") const removeData = removeMessage.encode({ - delegate: Binary.fromBytes(hotkey2.publicKey), + delegate: Binary.fromBytes(hotkey.publicKey), }) await sendWasmContractExtrinsic(api, coldkey, contractAddress, removeData) @@ -584,4 +599,335 @@ describe("Test wasm contract", () => { assert.ok(result !== undefined) }) + + it("Can caller add stake (fn 20)", async () => { + await addStakeViaContract(false) + }) + + it("Can caller remove stake (fn 21)", async () => { + await addStakeViaContract(false) + const stake = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + assert.ok(stake !== undefined) + const amount = stake / BigInt(2) + const message = inkClient.message("caller_remove_stake") + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + netuid, + amount, + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + const stakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + assert.ok(stakeAfter !== undefined && stakeAfter < stake!) + }) + + it("Can caller unstake_all (fn 22)", async () => { + await addStakeViaContract(false) + const stakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + assert.ok(stakeBefore !== undefined && stakeBefore > BigInt(0)) + const message = inkClient.message("caller_unstake_all") + const data = message.encode({ hotkey: Binary.fromBytes(hotkey.publicKey) }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + const stakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + assert.ok(stakeAfter !== undefined) + assert.ok(stakeAfter < stakeBefore!) + }) + + it("Can caller unstake_all_alpha (fn 23)", async () => { + await addStakeViaContract(false) + const stakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + assert.ok(stakeBefore !== undefined && stakeBefore > BigInt(0)) + const message = inkClient.message("caller_unstake_all_alpha") + const data = message.encode({ hotkey: Binary.fromBytes(hotkey.publicKey) }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + const stakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + assert.ok(stakeAfter !== undefined) + assert.ok(stakeAfter < stakeBefore!) + }) + + it("Can caller move_stake (fn 24)", async () => { + await addStakeViaContract(false) + await initSecondColdAndHotkey() + const originStakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + const destStakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey2.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake || BigInt(0) + assert.ok(originStakeBefore !== undefined && originStakeBefore > BigInt(0)) + const moveAmount = originStakeBefore / BigInt(2) + const message = inkClient.message("caller_move_stake") + const data = message.encode({ + origin_hotkey: Binary.fromBytes(hotkey.publicKey), + destination_hotkey: Binary.fromBytes(hotkey2.publicKey), + origin_netuid: netuid, + destination_netuid: netuid, + amount: moveAmount, + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + const originStakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + const destStakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey2.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + assert.ok(originStakeAfter !== undefined && destStakeAfter !== undefined) + assert.ok(originStakeAfter < originStakeBefore!) + assert.ok(destStakeAfter > destStakeBefore) + }) + + it("Can caller transfer_stake (fn 25)", async () => { + await addStakeViaContract(false) + await initSecondColdAndHotkey() + const stakeBeforeOrigin = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + const stakeBeforeDest = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey2.publicKey), + netuid, + ))?.stake + assert.ok(stakeBeforeOrigin !== undefined && stakeBeforeOrigin > BigInt(0)) + assert.ok(stakeBeforeDest !== undefined) + const transferAmount = stakeBeforeOrigin / BigInt(2) + const message = inkClient.message("caller_transfer_stake") + const data = message.encode({ + destination_coldkey: Binary.fromBytes(coldkey2.publicKey), + hotkey: Binary.fromBytes(hotkey.publicKey), + origin_netuid: netuid, + destination_netuid: netuid, + amount: transferAmount, + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + const stakeAfterOrigin = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + const stakeAfterDest = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey2.publicKey), + netuid, + ))?.stake + assert.ok(stakeAfterOrigin !== undefined && stakeAfterDest !== undefined) + assert.ok(stakeAfterOrigin < stakeBeforeOrigin!) + assert.ok(stakeAfterDest > stakeBeforeDest!) + }) + + it("Can caller swap_stake (fn 26)", async () => { + await addStakeViaContract(false) + await initSecondColdAndHotkey() + const stakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + const stakeBefore2 = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid + 1, + ))?.stake || BigInt(0) + assert.ok(stakeBefore !== undefined && stakeBefore > BigInt(0)) + const swapAmount = stakeBefore / BigInt(2) + const message = inkClient.message("caller_swap_stake") + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + origin_netuid: netuid, + destination_netuid: netuid + 1, + amount: swapAmount, + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + const stakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + const stakeAfter2 = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid + 1, + ))?.stake + assert.ok(stakeAfter !== undefined && stakeAfter2 !== undefined) + assert.ok(stakeAfter < stakeBefore) + assert.ok(stakeAfter2 > stakeBefore2) + }) + + it("Can caller add_stake_limit (fn 27)", async () => { + const stakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + assert.ok(stakeBefore !== undefined) + const message = inkClient.message("caller_add_stake_limit") + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + netuid, + amount: tao(200), + limit_price: tao(100), + allow_partial: false, + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + const stakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + assert.ok(stakeAfter !== undefined && stakeAfter > stakeBefore!) + }) + + it("Can caller remove_stake_limit (fn 28)", async () => { + await addStakeViaContract(false) + const stakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + assert.ok(stakeBefore !== undefined && stakeBefore > BigInt(0)) + const message = inkClient.message("caller_remove_stake_limit") + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + netuid, + amount: stakeBefore / BigInt(2), + limit_price: tao(1), + allow_partial: false, + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + const stakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + assert.ok(stakeAfter !== undefined && stakeAfter < stakeBefore!) + }) + + it("Can caller swap_stake_limit (fn 29)", async () => { + await addStakeViaContract(false) + const stakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + const stakeBefore2 = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid + 1, + ))?.stake + assert.ok(stakeBefore !== undefined && stakeBefore > BigInt(0)) + assert.ok(stakeBefore2 !== undefined) + const message = inkClient.message("caller_swap_stake_limit") + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + origin_netuid: netuid, + destination_netuid: netuid + 1, + amount: stakeBefore / BigInt(2), + limit_price: tao(1), + allow_partial: false, + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + const stakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + const stakeAfter2 = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid + 1, + ))?.stake + assert.ok(stakeAfter !== undefined && stakeAfter2 !== undefined) + assert.ok(stakeAfter < stakeBefore) + assert.ok(stakeAfter2 > stakeBefore2!) + }) + + it("Can caller remove_stake_full_limit (fn 30)", async () => { + await addStakeViaContract(false) + const stakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + assert.ok(stakeBefore !== undefined && stakeBefore > BigInt(0)) + const message = inkClient.message("caller_remove_stake_full_limit") + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + netuid, + limit_price: tao(60), + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + const stakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + assert.ok(stakeAfter !== undefined && stakeAfter < stakeBefore!) + }) + + it("Can caller set_coldkey_auto_stake_hotkey (fn 31)", async () => { + await addStakeViaContract(false) + await initSecondColdAndHotkey() + const message = inkClient.message("caller_set_coldkey_auto_stake_hotkey") + const data = message.encode({ + netuid, + hotkey: Binary.fromBytes(hotkey2.publicKey), + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + const autoStakeHotkey = await api.query.SubtensorModule.AutoStakeDestination.getValue( + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ) + assert.ok(autoStakeHotkey === convertPublicKeyToSs58(hotkey2.publicKey)) + }) + + it("Can caller add_proxy and remove_proxy (fn 32-33)", async () => { + const addMessage = inkClient.message("caller_add_proxy") + const addData = addMessage.encode({ + delegate: Binary.fromBytes(hotkey2.publicKey), + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, addData) + let proxies = await api.query.Proxy.Proxies.getValue(convertPublicKeyToSs58(coldkey.publicKey)) + assert.ok(proxies !== undefined && proxies[0].length > 0) + assert.ok(proxies[0][0].delegate === convertPublicKeyToSs58(hotkey2.publicKey)) + + const removeMessage = inkClient.message("caller_remove_proxy") + const removeData = removeMessage.encode({ + delegate: Binary.fromBytes(hotkey2.publicKey), + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, removeData) + proxies = await api.query.Proxy.Proxies.getValue(convertPublicKeyToSs58(coldkey.publicKey)) + assert.ok(proxies !== undefined && proxies[0].length === 0) + }) }); \ No newline at end of file diff --git a/docs/special-account-ids.md b/docs/special-account-ids.md new file mode 100644 index 0000000000..d7981baff5 --- /dev/null +++ b/docs/special-account-ids.md @@ -0,0 +1,1033 @@ +## Subnet account IDs + +**Format:** netuid: ss58 account ID + +0: 5EYCAe5jLQhn6ofDSvqF6iY53erXNkwhyE1aCEgvi1NNs91F +1: 5EYCAe5jLQhn6ofDSvqWqk5fA9XiqK3ahtx5kBNmAqF78mqL +2: 5EYCAe5jLQhn6ofDSvqnamdFGeCvHs9TSZtbJ84bdf7qQRc6 +3: 5EYCAe5jLQhn6ofDSvr4KoAqP8t7kRFLBEq6r4kS6UzZgCb5 +4: 5EYCAe5jLQhn6ofDSvrL4piRVdZKCyMCuumcQ1SGZJsHwmeE +5: 5EYCAe5jLQhn6ofDSvrborG1c8EWfXT5eai7wx8728k2DHK7 +6: 5EYCAe5jLQhn6ofDSvrsYsobicui85YxPFedVtowUxckUuF8 +7: 5EYCAe5jLQhn6ofDSvs9HuMBq7auadeq7vb93qVmwnVUkg5A +8: 5EYCAe5jLQhn6ofDSvsR2vtmwcG73BkhrbXebnBcQcND2Bdh +9: 5EYCAe5jLQhn6ofDSvsgmxSN46wJVjrabGUA9isSsSEwHnFy +10: 5EYCAe5jLQhn6ofDSvsxWyyxAbcVxHxTKwQfhfZHLG7fZUJG +11: 5EYCAe5jLQhn6ofDSvtEG1XYH6HhQr4L4cMBFcF7o5zPpyA3 +12: 5EYCAe5jLQhn6ofDSvtW1358PaxtsQACoHHgoYvxFus86kJK +13: 5EYCAe5jLQhn6ofDSvtmk4ciW5e6KxG5XxECMVcnijjrN8rz +14: 5EYCAe5jLQhn6ofDSvu3V6AJcaKHnWMxGdAhuSJdBZcadwDn +15: 5EYCAe5jLQhn6ofDSvuKE7htj4zVF4Tq1J7DTNzTePVJucfX +16: 5EYCAe5jLQhn6ofDSvuay9FUqZfghcZhjy3j1KgJ7DN3BDc2 +17: 5EYCAe5jLQhn6ofDSvuriAo4x4LtAAfaUdzEZGN8a3EmSncG +18: 5EYCAe5jLQhn6ofDSvv8TCLf4Z25cimTDJvk7D3y2s7ViZEm +19: 5EYCAe5jLQhn6ofDSvvQCDtFB3hH5GsKwysFf9joVgzDytnb +20: 5EYCAe5jLQhn6ofDSvvfwFRqHYNUXpyCgeomD6RdxWrxFpQR +21: 5EYCAe5jLQhn6ofDSvvwgGyRQ33fzP55RKkGm37URLjgXG7M +22: 5EYCAe5jLQhn6ofDSvwDRJX1WXisSwAx9zgnJyoJtAcQo59Y +23: 5EYCAe5jLQhn6ofDSvwVAL4bd2Q4uVGptfdHrvV9LzV94VBb +24: 5EYCAe5jLQhn6ofDSvwkuMcBjX5GN3NhdLZoQsAyopMsL7A7 +25: 5EYCAe5jLQhn6ofDSvx2eP9mr1kTpbUaN1WJxorpGeEbbfgG +26: 5EYCAe5jLQhn6ofDSvxJPQhMxWRfH9aT6gSpWkYejU7KsbGp +27: 5EYCAe5jLQhn6ofDSvxa8SEx516rjhgKqMPL4hEVCHz49DPw +28: 5EYCAe5jLQhn6ofDSvxqsTnYBVn4CFnCa2KqcdvKf7rnQo7f +29: 5EYCAe5jLQhn6ofDSvy7cVL8HzTFeot5JhGMAacA7wjWgPix +30: 5EYCAe5jLQhn6ofDSvyPMWsiQV8T7Myx3NCriXHzamcEwyqa +31: 5EYCAe5jLQhn6ofDSvyf6YRJWyoeZv5pn39NGTyq3bUyDc8k +32: 5EYCAe5jLQhn6ofDSvyvqZxtdUUr2UBhWi5spQffWRMhV5hU +33: 5EYCAe5jLQhn6ofDSvzCabWUjyA3V2HaFP2PNMMVyFERkxPm +34: 5EYCAe5jLQhn6ofDSvzUKd44rTqEwaPSz3xtvJ3LS57A2Td3 +35: 5EYCAe5jLQhn6ofDSvzk4ebexxWSQ8VKiiuQUEjAttytJ8Nx +36: 5EYCAe5jLQhn6ofDSw11og9F5TBdrgbCTPqv2BR1MircZp68 +37: 5EYCAe5jLQhn6ofDSw1HYhgqBwrqKEh5C4nRa86qpYjLqCQd +38: 5EYCAe5jLQhn6ofDSw1ZHjERJSY2mnnwvjiw84ngHNc56t9n +39: 5EYCAe5jLQhn6ofDSw1q2kn1QwDEELtpfQfSg1UWkCUoNVFh +40: 5EYCAe5jLQhn6ofDSw26mnKbXRtRgtzhQ5bxDxAMD2MXeL6A +41: 5EYCAe5jLQhn6ofDSw2NWosBdvZd9T6a8kYTmtrBfrEFusmX +42: 5EYCAe5jLQhn6ofDSw2eFqQmkREpc1CSsRUyKqY28g6zBbxD +43: 5EYCAe5jLQhn6ofDSw2uzrxMruv24ZJKc6RUsnDrbVyiT4uZ +44: 5EYCAe5jLQhn6ofDSw3BjtVwyQbDX7QCLmMzRiuh4KrSienC +45: 5EYCAe5jLQhn6ofDSw3TUv3Y5uGQyfW55SJVyfbXX9jAzHBc +46: 5EYCAe5jLQhn6ofDSw3jDwb8CPwcSDbwp7F1XcHMyybuFsV6 +47: 5EYCAe5jLQhn6ofDSw3zxy8iJtcotmhpYnBX5YyCSoUdXS9C +48: 5EYCAe5jLQhn6ofDSw4GhzgJRPJ1MKohHT82dVf2udMMo854 +49: 5EYCAe5jLQhn6ofDSw4YT2DtXsyCosua284YBSLsNTE64sjn +50: 5EYCAe5jLQhn6ofDSw4pC3mUeNeQGS1Sko13jP2hqH6pLJc1 +51: 5EYCAe5jLQhn6ofDSw55w5K4ksKbiz7KVTwZHKiYJ6yYc62p +52: 5EYCAe5jLQhn6ofDSw5Mg6resMzoBYDCE8t4qGQNkvrGsYLi +53: 5EYCAe5jLQhn6ofDSw5dR8QEyrfze6K4xopaPD6DDkj19BcH +54: 5EYCAe5jLQhn6ofDSw5uA9wq6MMC6eQwhUm5w9n3gabjR243 +55: 5EYCAe5jLQhn6ofDSw6AuBVRCr2PZCWpS9hbV6Tt9QUTghER +56: 5EYCAe5jLQhn6ofDSw6SeD31KLhb1kchApe7339icEMBxKr7 +57: 5EYCAe5jLQhn6ofDSw6iPEabRqNnUJiZuVacayqZ54DvDhGB +58: 5EYCAe5jLQhn6ofDSw6z8G8BYL3yvrpSeAX88vXPXt6eVRoY +59: 5EYCAe5jLQhn6ofDSw7FsHfmepjBPQvKNqTdgsDDzhyNky3e +60: 5EYCAe5jLQhn6ofDSw7XcKDMmKQNqy2C7WQ9Eou4TXr72f1B +61: 5EYCAe5jLQhn6ofDSw7oMLkwsp5aJX84rBLenkatvMiqJC2W +62: 5EYCAe5jLQhn6ofDSw856NJXzJkmm5DwarHALhGjPBbZa5wW +63: 5EYCAe5jLQhn6ofDSw8LqPr86oRyDdKpKXDftdxZr1UHqWzq +64: 5EYCAe5jLQhn6ofDSw8caRPiDJ7AgBRh4CABSaeQJqM278gq +65: 5EYCAe5jLQhn6ofDSw8tKSwJKnnN8jXZns6gzXLEmfDkNtXu +66: 5EYCAe5jLQhn6ofDSw9A4UUtSHTZbHdSXY3CYU25EV6UeLH2 +67: 5EYCAe5jLQhn6ofDSw9RoW2UYn8m3qjKGCyi6QhuhJyCv9nu +68: 5EYCAe5jLQhn6ofDSw9hYXa4fGoxWPqBzsvDeMPkA8qwBecQ +69: 5EYCAe5jLQhn6ofDSw9yHZ7emmV9xww4jYrjCJ5acxifTH7b +70: 5EYCAe5jLQhn6ofDSwAF2afEtGAMRW2wUDoEkEmR5nbPiuFf +71: 5EYCAe5jLQhn6ofDSwAWmcCpzkqYt48pCtjkJBTFYcU7ziWG +72: 5EYCAe5jLQhn6ofDSwAnWdkR7FWkLcEgwZgFr8961SLrGPJp +73: 5EYCAe5jLQhn6ofDSwB4FfJ1DkBwoALZgEcmQ4pvUGDaXxGw +74: 5EYCAe5jLQhn6ofDSwBKzgqbLEs9FiSSQuZGx1Wkw66JoNQY +75: 5EYCAe5jLQhn6ofDSwBbjiPBSjYLiGYK9aVnVxCbPuy357eQ +76: 5EYCAe5jLQhn6ofDSwBsUjvmZEDYApeBtFSJ3ttRrjqmLmRP +77: 5EYCAe5jLQhn6ofDSwC9DmUMfitjdNk4cvNobqaGKZiVcSd4 +78: 5EYCAe5jLQhn6ofDSwCQxo1wnDZw5vqwMbKK9nG6nPbDsr3v +79: 5EYCAe5jLQhn6ofDSwCghpZXtiF8YUwp6GFphiwwFDTx9ZXw +80: 5EYCAe5jLQhn6ofDSwCxSr781CvL133gpwCLFfdmi3LgRGUs +81: 5EYCAe5jLQhn6ofDSwDEBsei7hbXTb9ZZc8qocKcAsDQgmDH +82: 5EYCAe5jLQhn6ofDSwDVvuCJECGiv9FSJH5MMZ1Sdh68xe6G +83: 5EYCAe5jLQhn6ofDSwDmfvjtLgwvNhMK2x1ruVhH6WxsE2Rh +84: 5EYCAe5jLQhn6ofDSwE3QxHUTBd7qFTBmcxNTSP7ZLqbVqHX +85: 5EYCAe5jLQhn6ofDSwEK9yq4ZgJKHoZ4WHtt1P4x2AiKmP2V +86: 5EYCAe5jLQhn6ofDSwEau1NegAyWkMewExqPZKknUzb42r36 +87: 5EYCAe5jLQhn6ofDSwEre2vEnfeiCukoydmu7GScwpTnJa5d +88: 5EYCAe5jLQhn6ofDSwF8P4TpuAKufTrgiJiQfD8TQeLWaGop +89: 5EYCAe5jLQhn6ofDSwFQ861R1f1781xZSyevD9pHsUDEqiBR +90: 5EYCAe5jLQhn6ofDSwFfs7Z189gJaa4SBebRm6W8LJ5y7dfH +91: 5EYCAe5jLQhn6ofDSwFwc96bEeMW38AJvKXwK3Bxo7xhP3yn +92: 5EYCAe5jLQhn6ofDSwGDMAeBM92hVgGBezUSrysoFwqReqrS +93: 5EYCAe5jLQhn6ofDSwGV6CBmTdhtxEN4PfQxQvZdimi9vW9r +94: 5EYCAe5jLQhn6ofDSwGkqDjMa8P6QnTw8LMTxsFUBbatC8C5 +95: 5EYCAe5jLQhn6ofDSwH2aFGwgd4HsLZos1HyWowJeRTcTVsg +96: 5EYCAe5jLQhn6ofDSwHJKGpXo7jVKtfgbgEV4kd97FLLjBeJ +97: 5EYCAe5jLQhn6ofDSwHa4JN7ucQgnSmZLMAzchJya5D4zq8v +98: 5EYCAe5jLQhn6ofDSwHqoKui275tEzsS527WAdzp2u5oGNSd +99: 5EYCAe5jLQhn6ofDSwJ7YMTJ8bm5hYyJoh41iageVixXYH59 +100: 5EYCAe5jLQhn6ofDSwJPHNztF6SHA75BYMzXGXNUxYqFoj9g +101: 5EYCAe5jLQhn6ofDSwJf2QYUMb7UcfB4H2w2pU4KRNhz5GP5 +102: 5EYCAe5jLQhn6ofDSwJvmS64U5ng5DGw1hsYNQk9tCaiLvoS +103: 5EYCAe5jLQhn6ofDSwKCWTdeaaTsXmNokNp3vMRzM2TScknA +104: 5EYCAe5jLQhn6ofDSwKUFVBEh594zKUgV3kZUJ7porLAtE76 +105: 5EYCAe5jLQhn6ofDSwKjzWipoZpGSsaZDih52EofGgCu9mbP +106: 5EYCAe5jLQhn6ofDSwL1jYGQv4VTuRgRxPdaaBVVjW5dRU9u +107: 5EYCAe5jLQhn6ofDSwLHUZp12ZAfMynJh4a688BLCKxMhEMq +108: 5EYCAe5jLQhn6ofDSwLZDbMb93qrpXtBRjWbg4sAf9q5xtB8 +109: 5EYCAe5jLQhn6ofDSwLpxcuBFYX4H5z4AQT7E1Z17yhpELLK +110: 5EYCAe5jLQhn6ofDSwM6heSmN3CFje5vu5PcmxEqaoaYW1KP +111: 5EYCAe5jLQhn6ofDSwMNSfzMUXsTCCBodkL8Ktvg3dTGmYbX +112: 5EYCAe5jLQhn6ofDSwMeBhXwb2YeekHgNRGdsqcWWTL13NLP +113: 5EYCAe5jLQhn6ofDSwMuvj5XhXDr7JPZ76D9RnJLyHCjK2Zy +114: 5EYCAe5jLQhn6ofDSwNBfkd7p1u3ZrVRqm9eyizBS75TaPgK +115: 5EYCAe5jLQhn6ofDSwNTQnAhvWaF2QbJaS6AXfg1tvxBrDUN +116: 5EYCAe5jLQhn6ofDSwNj9oiJ31FSUxhBK72g5cMrMkpv7iJx +117: 5EYCAe5jLQhn6ofDSwNztqFt9VvdwWo43myBdZ3gpahePQpf +118: 5EYCAe5jLQhn6ofDSwPGdroUFzbqQ4tvnSuhBVjXHQaNet2o +119: 5EYCAe5jLQhn6ofDSwPYNtM4NVH2rczoX7rCjSRMkET6vioH +120: 5EYCAe5jLQhn6ofDSwPp7uteUyxEKB6gFnniHP7CD4KqCQDN +121: 5EYCAe5jLQhn6ofDSwQ5rwSEbUdRmjCYzTjDqKo2ftCZTubr +122: 5EYCAe5jLQhn6ofDSwQMbxyphyJdEHJRj8fjPGUs8i5HjcA3 +123: 5EYCAe5jLQhn6ofDSwQdLzXQpTypgqQJTocEwDAhbXx21Awy +124: 5EYCAe5jLQhn6ofDSwQu624zvxf29PWBCUYkV9rY4MpkGu1f +125: 5EYCAe5jLQhn6ofDSwRAq3cb3TLDbwc3w9VG36YNXBhUYKDi +126: 5EYCAe5jLQhn6ofDSwRSa5AB9x1R4VhvfpRmb3ECz1aCp2ze +127: 5EYCAe5jLQhn6ofDSwRiK6hmGSgcX3ooQVNH8yv3SqSw5mpH +128: 5EYCAe5jLQhn6ofDSwRz48FMNwMoybug9AJngvbsufKfME2t +129: 5EYCAe5jLQhn6ofDSwSFo9nwVS31SA1YsqFJEsHiNVCPcuZ9 +130: 5EYCAe5jLQhn6ofDSwSXYBLXbviCti7RcWBonoyYqK57tgCT +131: 5EYCAe5jLQhn6ofDSwSoHCt7iRPQMGDJMB8KLkfPJ8wrAGyP +132: 5EYCAe5jLQhn6ofDSwT52ERhpv4bopKB5r4pthMDkxpaRs97 +133: 5EYCAe5jLQhn6ofDSwTLmFyHwQjoGNR3pX1LSe34DnhJhU9A +134: 5EYCAe5jLQhn6ofDSwTcWHWt3uQzivWvZBwqzaitgca2xvCA +135: 5EYCAe5jLQhn6ofDSwTtFK4UAQ6CBUcoHrtMYXQj9SSmEgDM +136: 5EYCAe5jLQhn6ofDSwU9zLc4GtmPe2ig2Xps6U6ZcGKVWLNs +137: 5EYCAe5jLQhn6ofDSwURjN9ePPSb6apYmCmNeQnQ56CDn1SN +138: 5EYCAe5jLQhn6ofDSwUhUPhEVt7nZ8vRVshtCMUEXv4x3U6G +139: 5EYCAe5jLQhn6ofDSwUyDREpcNnz1h2JEYePkJA4zjwgK8dv +140: 5EYCAe5jLQhn6ofDSwVExSnQisUBUF8AyDauJEquTZpQaoue +141: 5EYCAe5jLQhn6ofDSwVWhUKzqN9NvoE3htXQrBXjvPh8rQgX +142: 5EYCAe5jLQhn6ofDSwVnSVsawrpaPMKvSZTvQ8DaPDZs8C7o +143: 5EYCAe5jLQhn6ofDSwW4BXRB4MVmquRoBEQRx4uQr3SbPfUD +144: 5EYCAe5jLQhn6ofDSwWKvYxmArAyJTXfuuLwW1bFJsKKf8Ax +145: 5EYCAe5jLQhn6ofDSwWbfaWMHLrAm1dYeaHT3xH5mhC3vmAe +146: 5EYCAe5jLQhn6ofDSwWsQc3wPqXNDZjRPFDxbtxvEX4nCgWj +147: 5EYCAe5jLQhn6ofDSwX99dbXWLCZg7qJ7vAU9qekhLwWUAUr +148: 5EYCAe5jLQhn6ofDSwXQtf97cpsm8fwArb6yhnLbAApEjeXz +149: 5EYCAe5jLQhn6ofDSwXgdgghjKYxbE33bG3VFj2Rczgy1Vmr +150: 5EYCAe5jLQhn6ofDSwXxNiEHqpEA3n8vKvyzofiG5pZhGuX1 +151: 5EYCAe5jLQhn6ofDSwYE7jmsxJuMWLEo4bvWMcQ6YeSRYYra +152: 5EYCAe5jLQhn6ofDSwYVrmKU4oaYxtLfoGs1uZ5w1UK9pR2x +153: 5EYCAe5jLQhn6ofDSwYmbns4BJFkRSSYXwoXTVmmUJBt5u26 +154: 5EYCAe5jLQhn6ofDSwZ3LpQeHnvwszYRGck31STbw84cMhaR +155: 5EYCAe5jLQhn6ofDSwZK5qxEQHc9LYeJ1HgYZP9SPwwLdHWw +156: 5EYCAe5jLQhn6ofDSwZapsVpWnHLo6kAjxd47KqGrmp4tuDS +157: 5EYCAe5jLQhn6ofDSwZrZu3QdGxYFer3UdZZfGX7KbgoAWJ3 +158: 5EYCAe5jLQhn6ofDSwa8JvazjmdjiCwvDJW5DDCwnRZXRvL1 +159: 5EYCAe5jLQhn6ofDSwaQ3x8arGJwAm3nwySam9tnFFSFhYDW +160: 5EYCAe5jLQhn6ofDSwafnygAxkz8dK9fgeP6K6aci5JyyTiu +161: 5EYCAe5jLQhn6ofDSwawY1Dm5FfL5sFYRKKbs3GTAuBiEuDo +162: 5EYCAe5jLQhn6ofDSwbDH2mMBkLXYRMR9zG7QyxHdj4SWa3R +163: 5EYCAe5jLQhn6ofDSwbV24JwJF1izyTHtfCcxve86YwAnHW1 +164: 5EYCAe5jLQhn6ofDSwbkm5rXQjgvTXZAdL98WsKxZNou3ejC +165: 5EYCAe5jLQhn6ofDSwc2W7Q7XEN7v5f3N15e4p1o2CgdKWRW +166: 5EYCAe5jLQhn6ofDSwcJF8whdj3KNdkv6g29ckhdV2ZMbAew +167: 5EYCAe5jLQhn6ofDSwcZzAVHkDiWqBrnqLxfAhPTwrS5rhzb +168: 5EYCAe5jLQhn6ofDSwcqjC2sriPiHjxfa1uAie5JQgJp8CWe +169: 5EYCAe5jLQhn6ofDSwd7UDaTyD4ukJ4YJgqgGam8sWBYQ2H8 +170: 5EYCAe5jLQhn6ofDSwdPDF845hk7CrAR3MnBpXSyLL4GfbVT +171: 5EYCAe5jLQhn6ofDSwdexGfeCCRJfQGHn2ihNU8oo9vzw6s9 +172: 5EYCAe5jLQhn6ofDSwdvhJDEJh6W7xNAWhfCvQpeFyojCkDK +173: 5EYCAe5jLQhn6ofDSweCSKkpRBmhaWU3FNbiUMWUiogTUUax +174: 5EYCAe5jLQhn6ofDSweUBMJQXgSu34Zuz3YE2JCKBdZBjxx9 +175: 5EYCAe5jLQhn6ofDSwejvNqzeB86VcfniiUjaEt9eTRv1hEQ +176: 5EYCAe5jLQhn6ofDSwf1fQPakfoHxAmfTPRF8BZz7HJeHEFa +177: 5EYCAe5jLQhn6ofDSwfHQRwAsAUVQisYC4Mkg8Fpa7BNZ735 +178: 5EYCAe5jLQhn6ofDSwfZ9TUkyf9gsGyQvjJGE4wf2w46pjEb +179: 5EYCAe5jLQhn6ofDSwfptV2M69ptKq5HfQEmn1dVVkvq6Jdm +180: 5EYCAe5jLQhn6ofDSwg6dWZwCeW5nPBAQ5BHKxKKxaoZMqLc +181: 5EYCAe5jLQhn6ofDSwgNNY7XK9BHEwH38k7nsu1ARQgHdMC3 +182: 5EYCAe5jLQhn6ofDSwge7Zf7RdrUhVNusR4JRqgztEZ1uEhR +183: 5EYCAe5jLQhn6ofDSwgurbChY8XgA3Unc5zoynNqM4RkAppF +184: 5EYCAe5jLQhn6ofDSwhBbckHedCscbafLkwKXj4fotJUSCzn +185: 5EYCAe5jLQhn6ofDSwhTLeHsm7t559gY5Rsq5fkWGiBChwy2 +186: 5EYCAe5jLQhn6ofDSwhj5fqTscZGXhnQp6pLdcSLjY3vyjeX +187: 5EYCAe5jLQhn6ofDSwhzphP3z7ETzFtHYmkrBZ8BCMvfFF73 +188: 5EYCAe5jLQhn6ofDSwiGZive6bufSozAHShMjVp1fBoPWsGG +189: 5EYCAe5jLQhn6ofDSwiYJkUED6aruN6327dsHSVr81g7nZdr +190: 5EYCAe5jLQhn6ofDSwip3n1pKbG4MvBuknaNqPBgaqYr4Epy +191: 5EYCAe5jLQhn6ofDSwj5noZQS5wFpUHnVTWtPKsX3fRaKfPX +192: 5EYCAe5jLQhn6ofDSwjMXq6zYacTH2PfE8TPwGZMWVJJbVUj +193: 5EYCAe5jLQhn6ofDSwjdGreaf5HejaVXxoPuVDFByKB2ryMD +194: 5EYCAe5jLQhn6ofDSwju1tCAmZxrC8bQhULR39w2S93m8YwD +195: 5EYCAe5jLQhn6ofDSwkAkujkt4e3eghHS9Gvb6crtxvVQ8TS +196: 5EYCAe5jLQhn6ofDSwkSVwHLzZKF7EoAApDS93JhMnoDfqYs +197: 5EYCAe5jLQhn6ofDSwkiExpw73zSZnu2uV9wgyzXpcfwwNtd +198: 5EYCAe5jLQhn6ofDSwkyyzNXDYfe2LzueA6TEvgNHSYgD9fh +199: 5EYCAe5jLQhn6ofDSwmFj1v7L3LqUu6nNq2xnsNCkGRQUdiJ +200: 5EYCAe5jLQhn6ofDSwmXU3ThSY22wTCf7VyULp43D6J8kSnP +201: 5EYCAe5jLQhn6ofDSwmoD51HZ2hEQ1JXrAuytkjsfvAs217d +202: 5EYCAe5jLQhn6ofDSwn4x6YsfXNRrZQQaqrVShRi8k3bHTHJ +203: 5EYCAe5jLQhn6ofDSwnLh86Tn23dK7WHKWnzze7YbZvKZ9W4 +204: 5EYCAe5jLQhn6ofDSwncS9e3tWipmfcA4BjWYaoP4Po3ptFu +205: 5EYCAe5jLQhn6ofDSwntBBBe11Q2EDi2nrg26XVDXDfn6UvC +206: 5EYCAe5jLQhn6ofDSwo9vCjE7W5DgmouXXcXeUB3z3YWN5rH +207: 5EYCAe5jLQhn6ofDSwoRfEGpDzkR9KunGCZ3CQrtSsREdeTi +208: 5EYCAe5jLQhn6ofDSwohQFpQLVRcbt1ezsVYkMYiuhHxuG9h +209: 5EYCAe5jLQhn6ofDSwoy9HMzSz6p4S7XjYS4JJEZNXAhB57Z +210: 5EYCAe5jLQhn6ofDSwpEtJuaZUn1WzDQUDNZrEvPqM3RShVi +211: 5EYCAe5jLQhn6ofDSwpWdLTAfyTCyYKHCtK5QBcEJAv9i5ua +212: 5EYCAe5jLQhn6ofDSwpnNMzknU8QS6R9wZFax8J4kznsyjCK +213: 5EYCAe5jLQhn6ofDSwq47PYLtxobteX2gEC6W4yuDpfcFKAe +214: 5EYCAe5jLQhn6ofDSwqKrR5w1TUoMCcuQu8c41fjgeYLXFWn +215: 5EYCAe5jLQhn6ofDSwqbbSdX7x9zokin9a57bxMa9UR4nnKt +216: 5EYCAe5jLQhn6ofDSwqsLUB7ESqCGJpetF1d9u3QcJHo4J89 +217: 5EYCAe5jLQhn6ofDSwr95VihLwWPirvXcux8hqjF58AXKq2x +218: 5EYCAe5jLQhn6ofDSwrQpXGHTSBbBR2QMateFnR5Xx3Fbkd6 +219: 5EYCAe5jLQhn6ofDSwrgZYosZvrndy8H6Fq9oj6uzmuys6og +220: 5EYCAe5jLQhn6ofDSwrxJaMTgRXz6XE9pvmfMfnkTbni8r8h +221: 5EYCAe5jLQhn6ofDSwsE3bu3nvDBZ5L2ZbiAucUavRfSQWNT +222: 5EYCAe5jLQhn6ofDSwsVndSduQtP1dRuJGegTZARPFYAg3e9 +223: 5EYCAe5jLQhn6ofDSwsmXezE1uZaUBXn2wbC1VrFr5QtwtdM +224: 5EYCAe5jLQhn6ofDSwt3GgXp8QEmvjdemcXhZSY6JuHdDNAJ +225: 5EYCAe5jLQhn6ofDSwtK1i5QEtuyPHjXWHUD7PDvmjAMUpH7 +226: 5EYCAe5jLQhn6ofDSwtakjczMPbAqqqQExQifKumEZ35kYLM +227: 5EYCAe5jLQhn6ofDSwtrVmAaTtGNJPwGydMEDGbbhNup2Aem +228: 5EYCAe5jLQhn6ofDSwu8EniAaNwZkx39iJHjmDHSACnYHqVA +229: 5EYCAe5jLQhn6ofDSwuPypFkgscmDW92SyEFK9yGd2fGZP4J +230: 5EYCAe5jLQhn6ofDSwufiqoLoNHxg4EuBeAks6f75rXzqFTr +231: 5EYCAe5jLQhn6ofDSwuwTsLvuryA8cLmvK7GR3LwYgQj6f8r +232: 5EYCAe5jLQhn6ofDSwvDCttX2MeMbASeez3mxz2n1WHTNE7n +233: 5EYCAe5jLQhn6ofDSwvUwvS78rKZ3iYXPezHWvicULABe9U8 +234: 5EYCAe5jLQhn6ofDSwvkgwyhFLzkWGeQ8Kvo4sQSwA2uudWf +235: 5EYCAe5jLQhn6ofDSww2RyXHMqfwxpkGrzsJcp6HPyueBKKS +236: 5EYCAe5jLQhn6ofDSwwJB14sULM9RNr9bfopAkn7ronNSouy +237: 5EYCAe5jLQhn6ofDSwwZv2cTaq2Lsvx2LLkKihTxKdf6iUQ9 +238: 5EYCAe5jLQhn6ofDSwwqf4A3hKhYLV3u51gqGe9nnTXpz5KG +239: 5EYCAe5jLQhn6ofDSwx7Q5hdopNjo39mogdLpaqdFHQZFjX1 +240: 5EYCAe5jLQhn6ofDSwxP97FDvK3wFbFeYMZrNXXTi7HHXSVQ +241: 5EYCAe5jLQhn6ofDSwxet8np2oj8i9MXH2WMvUDJAwA1nq7R +242: 5EYCAe5jLQhn6ofDSwxvdALQ9JQLAhTQ1hSsUQu8dm2k4jo3 +243: 5EYCAe5jLQhn6ofDSwyCNBszFo5XdFZGkNPP2May6auULR3m +244: 5EYCAe5jLQhn6ofDSwyU7DRaNHkj5of9V3KtaJGoZQnCbxRj +245: 5EYCAe5jLQhn6ofDSwyjrEyAUnRvYMm2DiGQ8Exe2Eevsbos +246: 5EYCAe5jLQhn6ofDSwz1bGWkbH77zurtxPCugBeUV4Xf9FEy +247: 5EYCAe5jLQhn6ofDSwzHLJ4LhmnKTTxmh49RE8LJwtQPQuPs +248: 5EYCAe5jLQhn6ofDSwzZ5KbvpGTWv24eRj5vn529QiH7gWns +249: 5EYCAe5jLQhn6ofDSwzppM9Wvm8iNaAXAQ2SL1hysY9qx3ao +250: 5EYCAe5jLQhn6ofDSx16ZNh73Fouq8GPu4xwsxPpLN2aDciL +251: 5EYCAe5jLQhn6ofDSx1NJQEh9kV7HgNGdjuTRu5eoBuJVHP9 +252: 5EYCAe5jLQhn6ofDSx1e3RnHGFAJkEU9NQqxyqmVG1n2kz46 +253: 5EYCAe5jLQhn6ofDSx1unTKsNjqWCna275nUXnTKiqem2beu +254: 5EYCAe5jLQhn6ofDSx2BXUsTVEWhfLftqkiz5j9ABfXVHzQ2 +255: 5EYCAe5jLQhn6ofDSx2TGWR3bjBu7tmmaRfVdfpzeVQDZscZ +256: 5EYCAe5jLQhn6ofDSvqFAHPozxdzP66nvfzRhb4qEUHpQXPv +257: 5EYCAe5jLQhn6ofDSvqWuJwQ7TKBqeCffLvwFXkfhJAYgD3P +258: 5EYCAe5jLQhn6ofDSvqneLUzDwzPJCJYQ1sSoUSWA83GwgQt +259: 5EYCAe5jLQhn6ofDSvr4PN2aLSfakkQR8goxMR8Lcwv1DSbq +260: 5EYCAe5jLQhn6ofDSvrL8PaASwLnDJWHsMkTuMpB5mnjUseM +261: 5EYCAe5jLQhn6ofDSvrbsR7kZS1yfrcAc2gyTJW1YbfTkXuS +262: 5EYCAe5jLQhn6ofDSvrscSfLfvhB8Qi3LhdV1FBr1RYC28tL +263: 5EYCAe5jLQhn6ofDSvs9MUCvnRNNaxov5NZzZBsgUFQvHj55 +264: 5EYCAe5jLQhn6ofDSvsR6VkWtv3a3Wunp3WW78ZWw5HeZQAP +265: 5EYCAe5jLQhn6ofDSvsgqXJ71QimW51fYiT1f5FMPuANqDNX +266: 5EYCAe5jLQhn6ofDSvsxaYqh7uPxxd7YHPPXD1wBrj376tWY +267: 5EYCAe5jLQhn6ofDSvtEKaPHEQ5ARBDR24L2kxd2KYuqNPar +268: 5EYCAe5jLQhn6ofDSvtW4bvsLtkMsjKHkjGYJuJrnNnZe4bY +269: 5EYCAe5jLQhn6ofDSvtmodUTTPRZLHRAVQD3rqzhFCfHuYa9 +270: 5EYCAe5jLQhn6ofDSvu3Yf23Zt6knqX3E59ZQngXi2Y2BNLJ +271: 5EYCAe5jLQhn6ofDSvuKHgZdgNmxFPcuxk64xjNNArQkSnAa +272: 5EYCAe5jLQhn6ofDSvub2i7DnsT9hwinhR2aWg4CdgHUiRdK +273: 5EYCAe5jLQhn6ofDSvurmjeouN8MAVpfS5y64ck36WACzBoE +274: 5EYCAe5jLQhn6ofDSvv8WmCQ1roYd3vYAkubcZRsZL2wFmUp +275: 5EYCAe5jLQhn6ofDSvvQFnjz8MUk5c2QuRr7AW7i29ufXLBs +276: 5EYCAe5jLQhn6ofDSvvfzpHaEr9wYA8He6nciSoYUynPo1wJ +277: 5EYCAe5jLQhn6ofDSvvwjqqAMLq8ziEANmj8GPVNwof84VTF +278: 5EYCAe5jLQhn6ofDSvwDUsNkTqWLTGL37SfdpLBDQdXrLLVP +279: 5EYCAe5jLQhn6ofDSvwVDtvLaLBXupRur7c9NGs3sTQabobG +280: 5EYCAe5jLQhn6ofDSvwkxvTvgprjNNXnanYevDYtLHHJsUWQ +281: 5EYCAe5jLQhn6ofDSvx2hx1WoKXvpvdfKTVAUAEio7A394VB +282: 5EYCAe5jLQhn6ofDSvxJSyZ6upD8HUjY48Rg26vZFw2mQtmG +283: 5EYCAe5jLQhn6ofDSvxaC16h2JtKk2qQnoNBa3cPikuVgH2a +284: 5EYCAe5jLQhn6ofDSvxqw2eH8oZXCawHXUJh7zJEBanDwyyQ +285: 5EYCAe5jLQhn6ofDSvy7g4BsFJEif93AG9FCfvz4eQexDirh +286: 5EYCAe5jLQhn6ofDSvyPR5jTMnuv7h92zpBiDsfu7EXgVLDf +287: 5EYCAe5jLQhn6ofDSvyfA7H3UHb7aFEujV8DmpMja4QQknoB +288: 5EYCAe5jLQhn6ofDSvyvu8pdanGK2oLnUA4jKm3a2tH92NjE +289: 5EYCAe5jLQhn6ofDSvzCeANDhGwWVMSfCq1EshjQVi9sJ6eA +290: 5EYCAe5jLQhn6ofDSvzUPBuoomchwuYXwVwkReRExY2bZe1S +291: 5EYCAe5jLQhn6ofDSvzk8DTPvGHuQTeQgAtFyb75RMuKqVPu +292: 5EYCAe5jLQhn6ofDSw11sEzz2ky6s1kHQqpmXXnutBn46shJ +293: 5EYCAe5jLQhn6ofDSw1HcGYa9FeJKZrA9WmH5UUkM1enNeCq +294: 5EYCAe5jLQhn6ofDSw1ZMJ6AFkKVn7x2tBhndRAaoqXWeDgU +295: 5EYCAe5jLQhn6ofDSw1q6KdkNEzhEg3ucreJBMrRGfQEuqcy +296: 5EYCAe5jLQhn6ofDSw26qMBLUjfthE9nMXaojJYFjVGyBbDv +297: 5EYCAe5jLQhn6ofDSw2NaNivbEM69nFf6CXKHFE6CK9hT1az +298: 5EYCAe5jLQhn6ofDSw2eKQGWhj2HcLMXpsTpqBuvf92Rie2g +299: 5EYCAe5jLQhn6ofDSw2v4Rp6pDhV4tTQZYQLP8bm7xu9zEfU +300: 5EYCAe5jLQhn6ofDSw3BoTMgviNgXSZHJDLqw5HbanmtFw81 +301: 5EYCAe5jLQhn6ofDSw3TYUuH3D3syzfA2tHMV1yS3cecXkzL +302: 5EYCAe5jLQhn6ofDSw3jHWSs9hj5SYm2mZDs2xfGWSXLoPFN +303: 5EYCAe5jLQhn6ofDSw412XzTGCQGu6ruWEANauM6yGQ54y3V +304: 5EYCAe5jLQhn6ofDSw4GmZY3Nh5UMexnEu6t8r2wS6GoLd6G +305: 5EYCAe5jLQhn6ofDSw4YWb5dVBkfpD4eya3Pgnimtv9XcAHv +306: 5EYCAe5jLQhn6ofDSw4pFcdDbgRsGmAXiEyuEjQcMk2Fso4J +307: 5EYCAe5jLQhn6ofDSw55zeAoiB74jKGQSuvQng6SpZtz9Vxy +308: 5EYCAe5jLQhn6ofDSw5MjfiPpfnGBsNHBarvLcnHHPmiQteL +309: 5EYCAe5jLQhn6ofDSw5dUhFywATTeRU9vFoRtZU7kDeSgbg2 +310: 5EYCAe5jLQhn6ofDSw5uDioa3f8f6ya2evjwSW9xD3XAxAx1 +311: 5EYCAe5jLQhn6ofDSw6AxkMAA9orZXfuPbgSzSqnfsPuDqwL +312: 5EYCAe5jLQhn6ofDSw6ShmtkGeV425mn8GcxYPXd8hGdVc6w +313: 5EYCAe5jLQhn6ofDSw6iSoSLP9AFUdserwZU6LDTbX9Mm6rm +314: 5EYCAe5jLQhn6ofDSw6zBpyvVdqSwByXbcVyeGuJ4M262q6Q +315: 5EYCAe5jLQhn6ofDSw7FvrXWc8WePk5QLHSVCDb8XAtpJXc2 +316: 5EYCAe5jLQhn6ofDSw7Xft56idBqrJBH4xNzkAGxyzmYa7Ff +317: 5EYCAe5jLQhn6ofDSw7oQucgq7s3JrH9odKWJ6xoSpeGqdqm +318: 5EYCAe5jLQhn6ofDSw859wAGwcYEmQP2YJG1r3edueX17Sh6 +319: 5EYCAe5jLQhn6ofDSw8Ltxhs47DSDxUuGyCXPzLUNUPjP1sE +320: 5EYCAe5jLQhn6ofDSw8cdzFTAbtdgWan1e92ww2JqJGTedjU +321: 5EYCAe5jLQhn6ofDSw8tP1o3H6Zq94gekK5YVsi9J89Bv64S +322: 5EYCAe5jLQhn6ofDSw9A83LdPbF2bcnXUz243pPykx1vBosi +323: 5EYCAe5jLQhn6ofDSw9Rs4tDW5vE4AtQDexZbm5pDmteTYXr +324: 5EYCAe5jLQhn6ofDSw9hc6RocabRWizGxKu59hmegbmNj178 +325: 5EYCAe5jLQhn6ofDSw9yM7yPj5GcyH69gzqaheTV9Re6zjMq +326: 5EYCAe5jLQhn6ofDSwAF69WyqZwpRqC2Rfn6Fb9KcFWqGBTn +327: 5EYCAe5jLQhn6ofDSwAWqB4Zx4d1tPHuALiboXqA55PZXq5E +328: 5EYCAe5jLQhn6ofDSwAnaCcA4ZJDLwPmu1f7MUWzXuGHoXBP +329: 5EYCAe5jLQhn6ofDSwB4KE9kB3yQoVVedgbcuRCpzj9254yw +330: 5EYCAe5jLQhn6ofDSwBL4FhLHYecG3bXNMY8TMtfTZ1kLpho +331: 5EYCAe5jLQhn6ofDSwBboHEvQ3KoibhQ72Ue1JaVvNtUcXUJ +332: 5EYCAe5jLQhn6ofDSwBsYJnWWY11B9oGqhR9ZFGLPCmCt89h +333: 5EYCAe5jLQhn6ofDSwC9HLL6d2gCdhu9aNMf7BxAr2dw9ppe +334: 5EYCAe5jLQhn6ofDSwCR2MsgjXMQ6G12K3JAf8e1JrWfRNDE +335: 5EYCAe5jLQhn6ofDSwCgmPRGr22bYp6u3iEgD5KqmgPPh3hX +336: 5EYCAe5jLQhn6ofDSwCxWQxrxWho1NCmnPBBm21gEWG7xc6b +337: 5EYCAe5jLQhn6ofDSwDEFSWT51NzTvJeX47hJxhWhL8rE71F +338: 5EYCAe5jLQhn6ofDSwDVzU43BW4BvUQXFj4CruPMAA1aVkSJ +339: 5EYCAe5jLQhn6ofDSwDmjVbdHzjPP2WPzPziQr5BcytJmWaH +340: 5EYCAe5jLQhn6ofDSwE3UX9DQVQaqacGj4wDxnm25om334HL +341: 5EYCAe5jLQhn6ofDSwEKDYgoWz5nJ8i9TjsjWjSrYddmJfht +342: 5EYCAe5jLQhn6ofDSwEaxaEPdUkykgp2CQpF4g8h1TWVaF57 +343: 5EYCAe5jLQhn6ofDSwErhbmyjySBDEutw5kkccpXUHPDqpD4 +344: 5EYCAe5jLQhn6ofDSwF8SdKZrU7Nfo1mfkhGAZWMw7Fx7Wbu +345: 5EYCAe5jLQhn6ofDSwFQBes9xxna8M7eQRdmiWCCPw8gPAnZ +346: 5EYCAe5jLQhn6ofDSwFfvgQk5TTmauDX96aHGSt2rm1QejQE +347: 5EYCAe5jLQhn6ofDSwFwfhxLBx8y3TKPsmWnpPZsKat8vLpQ +348: 5EYCAe5jLQhn6ofDSwGDQjVvJSpAW1RGcSTJNLFhnQksBuxZ +349: 5EYCAe5jLQhn6ofDSwGV9m3WQwVMxZX9M7PovGwYFEdbTqLQ +350: 5EYCAe5jLQhn6ofDSwGktnb6XSAZR7d25nLKUDdNi4WKjHVC +351: 5EYCAe5jLQhn6ofDSwH2dp8gdvqksfitpTGq2AKDAtP416pX +352: 5EYCAe5jLQhn6ofDSwHJNqgGkRWxLDpmZ8DLa713diFnGYZH +353: 5EYCAe5jLQhn6ofDSwHa7sDrrvC9nmveHo9r83gt6Y8WYAUE +354: 5EYCAe5jLQhn6ofDSwHqrtmSyQsMFL2X2U6MfzNiZN1EoxJG +355: 5EYCAe5jLQhn6ofDSwJ7bvK35uYYht8Pm92sDw4Z2Bsy5JYd +356: 5EYCAe5jLQhn6ofDSwJPLwrdCQDkASEGVoyNmskPV1khM3xf +357: 5EYCAe5jLQhn6ofDSwJf5yQDJttwczL9EUutKpSDwqdRcYKN +358: 5EYCAe5jLQhn6ofDSwJvpzwoRPa95YS1y9rPsm84QfW9tMQi +359: 5EYCAe5jLQhn6ofDSwKCa2VPXtFLY6XthpnuRhotsVNtA1nt +360: 5EYCAe5jLQhn6ofDSwKUK42yeNvXzedmSVjQyeVjLKFcRURc +361: 5EYCAe5jLQhn6ofDSwKk45aZksbjTCjeBAfvXbBZo98LhAFX +362: 5EYCAe5jLQhn6ofDSwL1o789sNGvukqWuqcS5XsQFy14xope +363: 5EYCAe5jLQhn6ofDSwLHY8fjyrx8NJwPeWYwdUZEinsoEZUW +364: 5EYCAe5jLQhn6ofDSwLZHADL6MdKps3GPBVTBRF5BckXW7BE +365: 5EYCAe5jLQhn6ofDSwLq2BkvCrJXHR997rRxjMvueSdFmhcW +366: 5EYCAe5jLQhn6ofDSwM6mDJWKLyijyF1rXNUHJck7GVz3PQs +367: 5EYCAe5jLQhn6ofDSwMNWEr6RqevCXLtbCJyqFJaa6NiJyR1 +368: 5EYCAe5jLQhn6ofDSwMeFGPgYLL7f5SmKsFVPBzR2vFSaVaJ +369: 5EYCAe5jLQhn6ofDSwMuzHwGeq1K7dYe4YBzw8gFVk8Ar7jR +370: 5EYCAe5jLQhn6ofDSwNBjKUrmKgWaBeWoD8WV5N5xZzu7st7 +371: 5EYCAe5jLQhn6ofDSwNTUM2SspMi2jkPXt52323vRPsdPTi7 +372: 5EYCAe5jLQhn6ofDSwNjDNa2zK2uVHrGGZ1XaxjktDkMezQq +373: 5EYCAe5jLQhn6ofDSwNzxQ7d6oi6wqx91Dx38uRbM3d5vkb5 +374: 5EYCAe5jLQhn6ofDSwPGhRfDDJPJQQ41jttYgr7RosVpCSnm +375: 5EYCAe5jLQhn6ofDSwPYSTCoKo4Vrx9tUZq4EnoGGhNYTsHL +376: 5EYCAe5jLQhn6ofDSwPpBUkPSHjhKWFmDEmZnjV6jXFGjavn +377: 5EYCAe5jLQhn6ofDSwQ5vWHyYnQtn4Mdwui5LgAwCM811FqV +378: 5EYCAe5jLQhn6ofDSwQMfXqZfH66EcTWgaeatcrmfAzjGwwQ +379: 5EYCAe5jLQhn6ofDSwQdQZP9mmmHhAZPRFb6SZYc7zsTYQ3S +380: 5EYCAe5jLQhn6ofDSwQu9avjtGSV9ifG9vXbzWESapkBpFWq +381: 5EYCAe5jLQhn6ofDSwRAtcUKzm7gcGm8tbU7YSvH3ecv5fQY +382: 5EYCAe5jLQhn6ofDSwRSde1v7Fnt4ps1dGQd6Pc7WUVeMKqG +383: 5EYCAe5jLQhn6ofDSwRiNfZWDkU5XNxtMwM8eLHwyJNNd1x9 +384: 5EYCAe5jLQhn6ofDSwRz7h76LF9Gyw4m6cHeCGynS8F6tUu4 +385: 5EYCAe5jLQhn6ofDSwSFriegSjpUSVAdqHE9kDfctx7qAGqb +386: 5EYCAe5jLQhn6ofDSwSXbkCGZEVfu3GWZxAfJAMTMmzZRjJj +387: 5EYCAe5jLQhn6ofDSwSoLmjrfjAsMbNPJd7Ar73HpbsHhKSv +388: 5EYCAe5jLQhn6ofDSwT55oHSnDr4p9UG3J3gQ3j8HRk1y3JJ +389: 5EYCAe5jLQhn6ofDSwTLppq2tiXGGha8mxzBwzQxkFckEpvZ +390: 5EYCAe5jLQhn6ofDSwTcZrNd1DCTjFg1WdvhVw6oD5VUWKxy +391: 5EYCAe5jLQhn6ofDSwTtJsvD7hsfBomtFJsD3sndfuNCmsJH +392: 5EYCAe5jLQhn6ofDSwUA3uToECYreMskyyoibpUU8jEw3T5N +393: 5EYCAe5jLQhn6ofDSwURnw1PLhE46uydiekE9mAJbZ7fKHH5 +394: 5EYCAe5jLQhn6ofDSwUhXxYyTBuFZU5WTKgjhhr94NzPakX9 +395: 5EYCAe5jLQhn6ofDSwUyGz6ZZgaT22BPBzdFFeXyXCs7rczx +396: 5EYCAe5jLQhn6ofDSwVF21e9gBFeUaHFvfZkobDoz2jr8F9t +397: 5EYCAe5jLQhn6ofDSwVWm3Bjnfvqw8P8fLWGMXueSrcaPq1Z +398: 5EYCAe5jLQhn6ofDSwVnW4jKuAc3PgV1Q1SmuUbUugVJfNVA +399: 5EYCAe5jLQhn6ofDSwW4F6Gv1fHErEat8gPHTRHKNWN2w5vR +400: 5EYCAe5jLQhn6ofDSwWKz7pW89xSJngksMKo1My9qLEmCh5V +401: 5EYCAe5jLQhn6ofDSwWbj9N6EeddmLndc2GJZJezJA7VUKPY +402: 5EYCAe5jLQhn6ofDSwWsUAugM9JqDttWLhCp7FLpkyzDjzxV +403: 5EYCAe5jLQhn6ofDSwX9DCTGTdz2gSzP5N9KfC2fDorx1RVV +404: 5EYCAe5jLQhn6ofDSwXQxDzra8fE916Fp35qD8iVgdjgHGPX +405: 5EYCAe5jLQhn6ofDSwXghFYSgdLRbZC8Yi2Lm5QL9TcQYr74 +406: 5EYCAe5jLQhn6ofDSwXxSH62o81d47J1HNxrK26AcHV8pFSS +407: 5EYCAe5jLQhn6ofDSwYEBJdcucgpWfPt23uMrxn157Ms5xvh +408: 5EYCAe5jLQhn6ofDSwYVvLBD27N1yDVkkiqsQuTqXwEbMX6v +409: 5EYCAe5jLQhn6ofDSwYmfMio8c3DRmbdVPnNxr9fzm7Kd75S +410: 5EYCAe5jLQhn6ofDSwZ3QPGPF6iQtKhWE4itWnqWTaz3tiZd +411: 5EYCAe5jLQhn6ofDSwZK9QoyMbPcLsoNxjfQ4jXLvQrnAT96 +412: 5EYCAe5jLQhn6ofDSwZatSMZU64ooRuFhQbucgDBPEjWS46h +413: 5EYCAe5jLQhn6ofDSwZrdTu9aak1Fz18S5YRAcu1r4cEhoLd +414: 5EYCAe5jLQhn6ofDSwa8NVSjh5RCiY71AkUviZarJtUxyQo6 +415: 5EYCAe5jLQhn6ofDSwaQ7WzKoa6QB6CsuRRSGWGgmiMhEyuu +416: 5EYCAe5jLQhn6ofDSwafrYXuv4mbdeJke6MwpSxXEYERWhTt +417: 5EYCAe5jLQhn6ofDSwawba5W2ZSo6CQdNmJTNPeMhN79n8Xw +418: 5EYCAe5jLQhn6ofDSwbDLbd6947zYkWW7SExvLLCAByt411A +419: 5EYCAe5jLQhn6ofDSwbV5dAgFYoC1JcNr7BUUH22d1rcKXhx +420: 5EYCAe5jLQhn6ofDSwbkpeiGN3UPTriFan7z2Dhs5qjLbAG1 +421: 5EYCAe5jLQhn6ofDSwc2ZgFrUY9avQp8KT4VaAPhYfc4rtSD +422: 5EYCAe5jLQhn6ofDSwcJJhoSb2pnNxv14811875Y1VUo8Udd +423: 5EYCAe5jLQhn6ofDSwca3jM2hXVyqX1snnwWg3mNUKMXQ7Lu +424: 5EYCAe5jLQhn6ofDSwcqnktcp2BBJ57kXTt2DzTCw9EFfjeB +425: 5EYCAe5jLQhn6ofDSwd7XnSCvWrNkdDdG8pXmw93Py6ywNA9 +426: 5EYCAe5jLQhn6ofDSwdPGoyo31XaDBKVzom3KspsrnyiCoBH +427: 5EYCAe5jLQhn6ofDSwdf1qXP9WCmfjRNjUhYspWiKcrSUZJy +428: 5EYCAe5jLQhn6ofDSwdvks4yFzsy8HXFU9e4RmCYnSjAkGv5 +429: 5EYCAe5jLQhn6ofDSweCVtcZNVZAaqd8CpaZyhtPFGbu1r89 +430: 5EYCAe5jLQhn6ofDSweUEvA9UzEN3PizwVX5XeaDi6UdHRW8 +431: 5EYCAe5jLQhn6ofDSwejywhjbUuZVwpsgATb5bG4AvMMYvzn +432: 5EYCAe5jLQhn6ofDSwf1iyFKhyakxVvkQqQ6dXwtdkE5pWEq +433: 5EYCAe5jLQhn6ofDSwfHTznupUFxR42d9WLcBUdj6a6p6RJP +434: 5EYCAe5jLQhn6ofDSwfZD2LVvxw9sc8VtBH7jRKZZPyYN3Qe +435: 5EYCAe5jLQhn6ofDSwfpx3t63TcMLAENcrDdHN1Q2DrGdXnV +436: 5EYCAe5jLQhn6ofDSwg6h5Rg9xHYniLFMXA8qJhEV3izuDDr +437: 5EYCAe5jLQhn6ofDSwgNS6yGGSxkFGS86C6ePFP4wsbjAhNK +438: 5EYCAe5jLQhn6ofDSwgeB8WrNwdwhpXzps39wC4uQhUTSNuL +439: 5EYCAe5jLQhn6ofDSwguvA4SVSK9ANdsZXyfV8kjsXMBi2rM +440: 5EYCAe5jLQhn6ofDSwhBfBc2bvzLcvjkJCvB35SaLMDuyoqD +441: 5EYCAe5jLQhn6ofDSwhTQD9ciRfY5Uqd2srgb28QoB6eFK1Z +442: 5EYCAe5jLQhn6ofDSwhj9EhCpvLjY2wVmYoC8xpFFzyNWvEr +443: 5EYCAe5jLQhn6ofDSwhztGEnwR1vzb3NWDjhguW5ipr6nXHN +444: 5EYCAe5jLQhn6ofDSwiGdHnP3uh8T99FEtgDErBvBeiq45cG +445: 5EYCAe5jLQhn6ofDSwiYNKKyAQNKuhF7yZcinnskeUbZKgnF +446: 5EYCAe5jLQhn6ofDSwip7LsZGu3XNFLziEZELjZb7JUHbZzu +447: 5EYCAe5jLQhn6ofDSwj5rNR9PPiipoSsSuVjtgFRa8M1s5jf +448: 5EYCAe5jLQhn6ofDSwjMbPxjVtPvHMYkBaSFScwG2xDk8eCi +449: 5EYCAe5jLQhn6ofDSwjdLRWKcP57juecvFNkzZd6Vn6UQFZC +450: 5EYCAe5jLQhn6ofDSwju5T3uiskKCTkVevKGYWJvxbyCg4Jv +451: 5EYCAe5jLQhn6ofDSwkApUbVqNRWf1rNPbFn6SzmRRqvwSdW +452: 5EYCAe5jLQhn6ofDSwkSZW95ws6i7ZxF8GCHePgbtFifDEja +453: 5EYCAe5jLQhn6ofDSwkiJXgg4Mmua847rw8oCLNSM5bPUoyW +454: 5EYCAe5jLQhn6ofDSwkz3ZEGArT72g9zbc5JkH4GouU7kW6c +455: 5EYCAe5jLQhn6ofDSwmFnamrHM8JVEFsLH1pJDk7GjLr27EN +456: 5EYCAe5jLQhn6ofDSwmXXcKSPqoVwnMk4wxKrARwjZDaHjGj +457: 5EYCAe5jLQhn6ofDSwmoGds2WLUhQLTcoctqQ77nCP6JZSt5 +458: 5EYCAe5jLQhn6ofDSwn51fQccq9trtZVYHqLx3ocfCy2pusw +459: 5EYCAe5jLQhn6ofDSwnLkgxCjKq6KSfNGxmrVzVT82qm6X4V +460: 5EYCAe5jLQhn6ofDSwncViVnqpWHmzmF1diN3wBHariVNDY4 +461: 5EYCAe5jLQhn6ofDSwntEk3NxKBVEYs7kJesbss83gbDdh5y +462: 5EYCAe5jLQhn6ofDSwo9ymay4orgh6xzUybP9pYxWWTwuZdk +463: 5EYCAe5jLQhn6ofDSwoRio8ZBJXt9f4sDeXthmEnyLLgAtRS +464: 5EYCAe5jLQhn6ofDSwohTpg9HoD5cDAjxKUQFhvdSADQSd7q +465: 5EYCAe5jLQhn6ofDSwoyCrDjQHtH4mGcgzQuoecTtz68iBNB +466: 5EYCAe5jLQhn6ofDSwpEwsmKWnZUXKNVRfMRMbJJMoxrz5ZA +467: 5EYCAe5jLQhn6ofDSwpWguJudHEfysUNALHvuXz8pdqbFU2C +468: 5EYCAe5jLQhn6ofDSwpnRvrVjmusSRaEu1ESTUfyHTiKX3yD +469: 5EYCAe5jLQhn6ofDSwq4AxQ5rGb4tyg7dgAx1RMokHb3nmAN +470: 5EYCAe5jLQhn6ofDSwqKuywfxmGGMXmzNM7TZN3eD7Tn4RDB +471: 5EYCAe5jLQhn6ofDSwqbf1VG5FwTp5ss723y7JjUfwLWL6mj +472: 5EYCAe5jLQhn6ofDSwqsQ32rBkcfGdyjqgzUfFRK8mDEbpVV +473: 5EYCAe5jLQhn6ofDSwr994aSJFHrjC5caMvzDC79bb5xsU5M +474: 5EYCAe5jLQhn6ofDSwrQt682Qjy4BkBVK2sVm8nz4Qxh8rdZ +475: 5EYCAe5jLQhn6ofDSwrgd7fcXEeFeJHN3hp1K5UpXEqRQQvd +476: 5EYCAe5jLQhn6ofDSwrxN9DCdjKT6rPEnNkWs2Aez4i9g7hy +477: 5EYCAe5jLQhn6ofDSwsE7AknkDzeZQV7X3h2QxrVStaswkrq +478: 5EYCAe5jLQhn6ofDSwsVrCJNrifr1xazFidXxuYKuiTcDVjH +479: 5EYCAe5jLQhn6ofDSwsmbDqxyDM3UWgrzPa3WrEANYLLUw1X +480: 5EYCAe5jLQhn6ofDSwt3LFPZ5i2Ew4njj4WZ4nuzqND4kbZN +481: 5EYCAe5jLQhn6ofDSwtK5Gw9CChSPctcTjT4cjbqJC5o2PJV +482: 5EYCAe5jLQhn6ofDSwtapJUjJhNdrAzVCQPaAgHfm1xXHtKK +483: 5EYCAe5jLQhn6ofDSwtrZL2KRC3qJj6Mw5L5icyWDqqFZadE +484: 5EYCAe5jLQhn6ofDSwu8JMZuXgj2mHCEfkGbGZfLgfhyqAhn +485: 5EYCAe5jLQhn6ofDSwuQ3P7VeBQEDqJ7QRD6pWMB9Vai6mth +486: 5EYCAe5jLQhn6ofDSwufnQf5kg5RgPPz969cNT31cKTSNSCS +487: 5EYCAe5jLQhn6ofDSwuwXSCfsAkd8wVrsm67vPir59LAe42A +488: 5EYCAe5jLQhn6ofDSwvDGTkFyfRpbVbjcS2dULQgXyCtubBX +489: 5EYCAe5jLQhn6ofDSwvV1VHr6A7243hcM6y92H6Wzo5dBP9F +490: 5EYCAe5jLQhn6ofDSwvkkWqSCenDWboV5mueaDnMTcxMT382 +491: 5EYCAe5jLQhn6ofDSww2VYP2K9TQy9uMpSrA8AUBvSq5iXaj +492: 5EYCAe5jLQhn6ofDSwwJEZvcRe8cRi1EZ7nfg7A2PGhoz9d6 +493: 5EYCAe5jLQhn6ofDSwwZybUCY8ootG77HnjBE3qrr6aYFvzB +494: 5EYCAe5jLQhn6ofDSwwqid1nedV1LpCz2TfgmzXhJvTGXUBY +495: 5EYCAe5jLQhn6ofDSwx7TeZNm8ACoNJrm8cCKwDXmkKznwLK +496: 5EYCAe5jLQhn6ofDSwxPCg6xscqQFvQjVoYhssuNEaCj4cV1 +497: 5EYCAe5jLQhn6ofDSwxewheYz7WbiUWcEUVDRpbChQ5TLKHR +498: 5EYCAe5jLQhn6ofDSwxvgjC96cBoB2cUy9RiymH3ADxBc4Lb +499: 5EYCAe5jLQhn6ofDSwyCRkjjD6rzdaiMhpNEXhxsd3pusccP +500: 5EYCAe5jLQhn6ofDSwyUAnHKKbYC68pESVJk5eei5she97qy +501: 5EYCAe5jLQhn6ofDSwyjuopuS6DPYgv7BAFFdbLYYhaNQuf9 +502: 5EYCAe5jLQhn6ofDSwz1eqNVYatb1F1yuqBmBY2P1XT6gLkd +503: 5EYCAe5jLQhn6ofDSwzHPrv5f5ZnTo7reW8GjUiDUMKpwzz6 +504: 5EYCAe5jLQhn6ofDSwzZ8tTfmaEyvMDjPB4nHRQ3wBCZDsZs +505: 5EYCAe5jLQhn6ofDSwzpsv1Ft4vBNuKc7r1HqN5tQ15HVCrz +506: 5EYCAe5jLQhn6ofDSx16cwYqzZbNqTRUrWwoPJmirpx1kybJ +507: 5EYCAe5jLQhn6ofDSx1NMy6S74GaJ1XMbBtJwFTZKepk2ghP +508: 5EYCAe5jLQhn6ofDSx1e6ze2DYwmkZdEKrppVC9PnUhUJ51w +509: 5EYCAe5jLQhn6ofDSx1ur2BcL3cyD7j74XmL38qEFJaCZfuR +510: 5EYCAe5jLQhn6ofDSx2Bb3jCSYJAffpyoChqb5X4i8SvqbpQ +511: 5EYCAe5jLQhn6ofDSx2TL5GnZ2yN8DvrXseM92CuAxKf7ELV +512: 5EYCAe5jLQhn6ofDSvqFDrFYxGRTPRFst7yHCwSjkwDFwear +513: 5EYCAe5jLQhn6ofDSvqWxso94m6eqyMkcnunkt8aDm5zDaY6 +514: 5EYCAe5jLQhn6ofDSvqnhuLjBFmrJXTdMTrJJppQgaxiV33E +515: 5EYCAe5jLQhn6ofDSvr4SvtKHkT3m5ZW68normWF9QqSkkTc +516: 5EYCAe5jLQhn6ofDSvrLBxRuQF8FDdfNpojKQiC5cEiB2Sk4 +517: 5EYCAe5jLQhn6ofDSvrbvyyVWjoSgBmFZUfpxesv54auJ2W6 +518: 5EYCAe5jLQhn6ofDSvrsg1X5dEUe8js8J9cLWbZkXtTdZh9r +519: 5EYCAe5jLQhn6ofDSvs9R34fjj9qbHy12pYr4YFaziLMqJTN +520: 5EYCAe5jLQhn6ofDSvsRA4cFrDq33r4smVVMcUwRTYD66mFA +521: 5EYCAe5jLQhn6ofDSvsgu69qxiWEWQAkWARsARdFvN5pNJkh +522: 5EYCAe5jLQhn6ofDSvsxe7hS5DBRxxGdEqNNiNK6PBxYdy7w +523: 5EYCAe5jLQhn6ofDSvtEP9F2BhrdRWNVyWJtGJzvr1qGuotj +524: 5EYCAe5jLQhn6ofDSvtW8AncJCXpt4UNiBFPpFgmJqi1BLVs +525: 5EYCAe5jLQhn6ofDSvtmsCLCQhD2LcaFSrBuNCNbmfajSpjE +526: 5EYCAe5jLQhn6ofDSvu3cDsnXBtDoAg8BX8Qv94SEVTTiiQ6 +527: 5EYCAe5jLQhn6ofDSvuKMFRNdgZRFimzvC4vU5kGhKLBzAn6 +528: 5EYCAe5jLQhn6ofDSvub6GxxkBEciGsses1S22S7A9CvFx62 +529: 5EYCAe5jLQhn6ofDSvurqJWYrfupApykPXwwZy7wcy5eXHgT +530: 5EYCAe5jLQhn6ofDSvv8aL48yAb1dP5d8CtT7uon5nxNoAHh +531: 5EYCAe5jLQhn6ofDSvvQKMbj5fGD5wBVrspxfrVcYcq74cff +532: 5EYCAe5jLQhn6ofDSvvg4P9KC9wQYVHNbYmUDoBT1ShqLTbC +533: 5EYCAe5jLQhn6ofDSvvwoQguJecc13PFLDhymjsHUGaZc2LL +534: 5EYCAe5jLQhn6ofDSvwDYSEVR9HoTbV84teVKgZ7w6THsXG4 +535: 5EYCAe5jLQhn6ofDSvwVHTn5Xdxzv9azoZazsdExPvL29Fyn +536: 5EYCAe5jLQhn6ofDSvwm2VKfe8eCNhgsYEXWRZvnrkCkQm3z +537: 5EYCAe5jLQhn6ofDSvx2mWsFkdKPqFnkGuU1yWcdKa5UgYgt +538: 5EYCAe5jLQhn6ofDSvxJWYQqs7zbHotd1aQXXTJTnPxCx4qw +539: 5EYCAe5jLQhn6ofDSvxaFZxRycfnkMzVkFM35PzJFDpwDdau +540: 5EYCAe5jLQhn6ofDSvxqzbW267LzCv6NUvHYdLg8i3hfVTFu +541: 5EYCAe5jLQhn6ofDSvy7jd3cCc2BfUCFDbE4BHMyAsaPm1sJ +542: 5EYCAe5jLQhn6ofDSvyPUebCK6hP82J7xGAZjE3odhT82USX +543: 5EYCAe5jLQhn6ofDSvyfDg8nRbNaaaPzgw75HAje6XKrJNeZ +544: 5EYCAe5jLQhn6ofDSvyvxhgNY63n38VsRc3aq7RUZMCaZzZ9 +545: 5EYCAe5jLQhn6ofDSvzChjDxeaiyVgbkAGz6P47K2B5JqJmC +546: 5EYCAe5jLQhn6ofDSvzUSkmYm5QAxEhctwvbvzo9Uzx375yn +547: 5EYCAe5jLQhn6ofDSvzkBnK8sa5NQnoVdcs7UwUywppmNkyL +548: 5EYCAe5jLQhn6ofDSw11voriz4kZsLuNNHod2tApQehVeFiH +549: 5EYCAe5jLQhn6ofDSw1HfqQK6ZRmKu1F6xk8apresUaDuoLZ +550: 5EYCAe5jLQhn6ofDSw1ZQrwuD46xnT77qdge8mYVLJSxBd2D +551: 5EYCAe5jLQhn6ofDSw1q9tVVKYnAF1CzaJd9giEKo8KgTFV2 +552: 5EYCAe5jLQhn6ofDSw26tv35S3TMhZJsJyZfEevAFxCQireQ +553: 5EYCAe5jLQhn6ofDSw2NdwafYY8ZA7Qk3eWAnbbzin58zS6X +554: 5EYCAe5jLQhn6ofDSw2eNy8Ff2okcfWcnKSgLYHqBbwsG4y5 +555: 5EYCAe5jLQhn6ofDSw2v7zfqmXUx5DcVWzPBtUyfeRpbXoLx +556: 5EYCAe5jLQhn6ofDSw3Bs2DRt2A9XmiNFfKhSRfW7FhKoSHv +557: 5EYCAe5jLQhn6ofDSw3Tc3m1zWqLzKpEzLGCzNMLa5a457dP +558: 5EYCAe5jLQhn6ofDSw3jM5Jc71WYSsv7j1CiYK3B2uSnLbvm +559: 5EYCAe5jLQhn6ofDSw4166rCDWBjuS1zTg9E6Fj1VjKWcGKu +560: 5EYCAe5jLQhn6ofDSw4Gq8PnKzrwMz7sCM5jeCQqxZCEsiEp +561: 5EYCAe5jLQhn6ofDSw4Ya9wNSVY8pYDjw22FC96gRP4y9dUg +562: 5EYCAe5jLQhn6ofDSw4pKBUxYzDLH6Kcfgxkk5nWtCwhR9aG +563: 5EYCAe5jLQhn6ofDSw564D2YfUtXjeRVQMuGJ2UMM2pRghUV +564: 5EYCAe5jLQhn6ofDSw5MoEa8myZjCCXN92qmqyABorh9xK4t +565: 5EYCAe5jLQhn6ofDSw5dYG7itUEvekdEshnHPur2GgZtE7i9 +566: 5EYCAe5jLQhn6ofDSw5uHHfJzxv87Jj7cNinwrXrjWScVjpQ +567: 5EYCAe5jLQhn6ofDSw6B2KCu7TbKZrpzM3fJVoDhCLKLmP5E +568: 5EYCAe5jLQhn6ofDSw6SmLkVDxGX2Qvs5ibp3juXfAC52pBg +569: 5EYCAe5jLQhn6ofDSw6iWNJ5LSwiUy2jpPYKbgbN7z4oJPDJ +570: 5EYCAe5jLQhn6ofDSw6zFPqfSwcuwX8cZ4Uq9dHCaowXZxCq +571: 5EYCAe5jLQhn6ofDSw7FzRPFZSJ7Q5EVHjRLhZy33dpFqdiY +572: 5EYCAe5jLQhn6ofDSw7XjSvqfvyJrdLN2QMrFWesWTgz7Wb2 +573: 5EYCAe5jLQhn6ofDSw7oUUURnReWKBSEm5JMoTLhyHZiNqXY +574: 5EYCAe5jLQhn6ofDSw85DW21tvKhmjY7VkEsMQ2YS7SSeWt4 +575: 5EYCAe5jLQhn6ofDSw8LxXZc1QzuEHdzERBNuLiNtwKAv6Pe +576: 5EYCAe5jLQhn6ofDSw8chZ7C7ug6gqjry67tTHQDMmBuC1de +577: 5EYCAe5jLQhn6ofDSw8tSaenEQMJ9Pqjhm4Q1E63pb4dTM7S +578: 5EYCAe5jLQhn6ofDSw9ABcCNLu2VbwwcSRzuZAmtHQwMj1Cb +579: 5EYCAe5jLQhn6ofDSw9RvdjxTPhh4W3VB6wR77TikEp5ze9D +580: 5EYCAe5jLQhn6ofDSw9hffHYZtNtX49Mumsvf49ZD4gpGUGb +581: 5EYCAe5jLQhn6ofDSw9yQgq8gP45ycFEeSpSCzqPftZYY8c6 +582: 5EYCAe5jLQhn6ofDSwAF9iNinsjHSAM7P7kwkwXE8iSGofY4 +583: 5EYCAe5jLQhn6ofDSwAWtjvJuNQUtiSz7nhTJtD4bYK15Mwa +584: 5EYCAe5jLQhn6ofDSwAndmTu1s5gMGYrrTdxrptu4NBjLm3i +585: 5EYCAe5jLQhn6ofDSwB4No1V8Mksopejb8aUQmajXC4TcdTi +586: 5EYCAe5jLQhn6ofDSwBL7pZ5ErS5GNkcKoWyxiGZz1wBt1hh +587: 5EYCAe5jLQhn6ofDSwBbrr6fMM7GivrV4UTVWexQSqov9udn +588: 5EYCAe5jLQhn6ofDSwBsbseFTqnUBUxMo9Q14beEufgeRF5g +589: 5EYCAe5jLQhn6ofDSwC9LuBqaLTfe34EXpLWcYL5NVZNguqD +590: 5EYCAe5jLQhn6ofDSwCR5vjRgq8s6bA7GVH2AV1uqKS6xivf +591: 5EYCAe5jLQhn6ofDSwCgpxH1oKp4Z9Fz1ADXiRhkJ9JqEH3f +592: 5EYCAe5jLQhn6ofDSwCxZypbupVG1hMrjqA3GNPakyBZW2hz +593: 5EYCAe5jLQhn6ofDSwDEK1NC2KATUFTjUW6YpK5RDo4Hmbw9 +594: 5EYCAe5jLQhn6ofDSwDW42un8oqevoZcDB34NFmFgcw23A4Q +595: 5EYCAe5jLQhn6ofDSwDmo4TNFJWrPMfUwqyZvCT69SokJrjA +596: 5EYCAe5jLQhn6ofDSwE3Y5zxMoC3qumMgWv5U98vcGgUaM83 +597: 5EYCAe5jLQhn6ofDSwEKH7YYUHsFJTsERBrb25pm56ZCr2CG +598: 5EYCAe5jLQhn6ofDSwEb2968anYSm1y79ro6a2WbXvRw7i4R +599: 5EYCAe5jLQhn6ofDSwErmAdihHDeDa4ytXjc7yCRzkJfPCoW +600: 5EYCAe5jLQhn6ofDSwF8WCBJomtqg8ArdCg7futGTaBPev5r +601: 5EYCAe5jLQhn6ofDSwFQFDitvGa38gGjMscdDra6vQ47vagE +602: 5EYCAe5jLQhn6ofDSwFfzFGV2mFEbENc6YZ8moFwPDvrCCj6 +603: 5EYCAe5jLQhn6ofDSwFwjGp59FvS3nUUqDVeKjwmr3oaTkqS +604: 5EYCAe5jLQhn6ofDSwGDUJMfFkbdWLaMZtS9sgdcJsgJjJF4 +605: 5EYCAe5jLQhn6ofDSwGVDKuFNFGpxtgEJZNfRdKSmhZ2zvJd +606: 5EYCAe5jLQhn6ofDSwGkxMSqUjx2RSn73EKAya1HEXRmGjcm +607: 5EYCAe5jLQhn6ofDSwH2hNzRbEdDszsymuFgXWh7hMJVY8Fk +608: 5EYCAe5jLQhn6ofDSwHJSQY1hjJRLYyrWaCC5TNxABBDonmo +609: 5EYCAe5jLQhn6ofDSwHaBS5bpDyco75jFF8hdQ4nd13x5VQN +610: 5EYCAe5jLQhn6ofDSwHqvTdBviepFfBbyv5DBLkd5pvgM2q6 +611: 5EYCAe5jLQhn6ofDSwJ7fVAn3DL1iDHUib1ijHSTYeoQcmDP +612: 5EYCAe5jLQhn6ofDSwJPQWiN9i1DAmPMTFxEHE8J1Ug8tTWG +613: 5EYCAe5jLQhn6ofDSwJf9YFxGCgQdKVEBvtjqAp8UJYs9xDb +614: 5EYCAe5jLQhn6ofDSwJvtZoYNhMc5sb6vbqFP7Vxw8RbReGD +615: 5EYCAe5jLQhn6ofDSwKCdbM8VC2oYRgyfGmkw4BoPxJKhPnV +616: 5EYCAe5jLQhn6ofDSwKUNctibghzzynrPwiGUzsdrnB3xs32 +617: 5EYCAe5jLQhn6ofDSwKk7eSJiBPCTXtj8cen2wZUKc3nESFM +618: 5EYCAe5jLQhn6ofDSwL1rfytpg4Pv5zbsHbHatFJnRvWW1je +619: 5EYCAe5jLQhn6ofDSwLHbhXUwAjbNe6UbxXo8pw9FFoEms63 +620: 5EYCAe5jLQhn6ofDSwLZLj553fQnqCCMLdUJgmcyi5fy3Gdi +621: 5EYCAe5jLQhn6ofDSwLq5kcfAA5zHkJE5JQpEiJpAuYhJzsm +622: 5EYCAe5jLQhn6ofDSwM6pnAFGemBkJQ6oyMKnezedjRRaevZ +623: 5EYCAe5jLQhn6ofDSwMNZohqP9SPCrVyYeHqLbgV6ZJ9rGoC +624: 5EYCAe5jLQhn6ofDSwMeJqFRVe7afQbrHKELtYNKZPAt7wnT +625: 5EYCAe5jLQhn6ofDSwMv3ro1c8nn7xhj1zArSV4A2D3cPfMu +626: 5EYCAe5jLQhn6ofDSwNBntLbidTyaWobkf7MzRjzV2vLfF6h +627: 5EYCAe5jLQhn6ofDSwNTXutBq89B34uUVL3sYNRpwro4vqqw +628: 5EYCAe5jLQhn6ofDSwNjGwRmwcpNVd1MDzzP6K7fQgfoCQtG +629: 5EYCAe5jLQhn6ofDSwP11xyN47VZxB7DxfvteFoVsWYXUAVF +630: 5EYCAe5jLQhn6ofDSwPGkzWxAcAmQjD6hLsQCCVLLLRFjZ9g +631: 5EYCAe5jLQhn6ofDSwPYW24YH6qxsHJyS1ouk9BAoAHz1CWW +632: 5EYCAe5jLQhn6ofDSwPpF3c8PbXAKqQrAgkRJ5s1FzAiGwZa +633: 5EYCAe5jLQhn6ofDSwQ5z59iW6CMnPWiuMgvr2Yqip3SYVED +634: 5EYCAe5jLQhn6ofDSwQMj6hJcasZEwcbe2dSPyEgBdvApCdf +635: 5EYCAe5jLQhn6ofDSwQdU8Etj5YkhViUNhZwwuvWeTnu5ev2 +636: 5EYCAe5jLQhn6ofDSwQuD9nUqaDxA3pM7NWTVrcM7HfdMWqo +637: 5EYCAe5jLQhn6ofDSwRAxBL4x4u9cbvDr3Sy3oJBa7YMd9mJ +638: 5EYCAe5jLQhn6ofDSwRShCsf4ZaM5A26aiPUbjz22wR5tne4 +639: 5EYCAe5jLQhn6ofDSwRiSERFB4FYXi7yKPKz9gfrVmHpALrK +640: 5EYCAe5jLQhn6ofDSwRzBFxqHYvjzGDr44GVhdMgxbAYS1GD +641: 5EYCAe5jLQhn6ofDSwSFvHWRQ3bwSpKinjD1Fa3XRR3GhTgF +642: 5EYCAe5jLQhn6ofDSwSXfK41WYH8uNRbXQ9WoWjMtEuzyHBD +643: 5EYCAe5jLQhn6ofDSwSoQLbbd2xLMvXUG562MTRCM4njEg2N +644: 5EYCAe5jLQhn6ofDSwT59N9BjXdXpUdLzk2XuQ72otfTWSny +645: 5EYCAe5jLQhn6ofDSwTLtPgmr2JjH2jDjQy3TLnsGiYBn5Bi +646: 5EYCAe5jLQhn6ofDSwTcdREMxWyvjaq6U5uZ1HUhjYQv3fCb +647: 5EYCAe5jLQhn6ofDSwTtNSmx51f8C8vyCkr4ZEAYCNHeKPyY +648: 5EYCAe5jLQhn6ofDSwUA7UKYBWLKeh2qwRna7ArNfCANb6AU +649: 5EYCAe5jLQhn6ofDSwURrVs8J11X7F8ig6j5f7YD8236rSyk +650: 5EYCAe5jLQhn6ofDSwUhbXQiQVgiZoEbQmfbD4E3aquq87RN +651: 5EYCAe5jLQhn6ofDSwUyLYxJWzMv2MLU9Sc6kzut3fnZPti5 +652: 5EYCAe5jLQhn6ofDSwVF5aVtdV37UuSLt7YcJwbiWVfHfaaG +653: 5EYCAe5jLQhn6ofDSwVWpc3UjyiJwTYDcnV7rtHYyKY1w8Yy +654: 5EYCAe5jLQhn6ofDSwVnZdb4rUPWQ1e6MTRdQpyPS9QkCqGi +655: 5EYCAe5jLQhn6ofDSwW4Jf8exy4hrZjy68N8xmfDtyHUUStT +656: 5EYCAe5jLQhn6ofDSwWL3ggF5TjuK7qqpoJeWiM4MoACjq89 +657: 5EYCAe5jLQhn6ofDSwWbniDqBxR6mfwiZUFA4f2tpd2w1WS2 +658: 5EYCAe5jLQhn6ofDSwWsXjmRJT6JEE3bJ9BfcbijHSufH4xW +659: 5EYCAe5jLQhn6ofDSwX9GmK1QwmVgn9U2p8BAYQZkGnPYjux +660: 5EYCAe5jLQhn6ofDSwXR1nrbXSSh9LFLmV4giV6QD6f7pJFn +661: 5EYCAe5jLQhn6ofDSwXgkpQBdw7tbtMDWA1CGRnEfvXr5vs6 +662: 5EYCAe5jLQhn6ofDSwXxVqwmkRo64ST6EpwhpNU58kQaMqxk +663: 5EYCAe5jLQhn6ofDSwYEEsVMrvUHWzYxyVtDNK9ubaHJdUrm +664: 5EYCAe5jLQhn6ofDSwYVyu2wyR9UyYeqiApivFqk4QA2tzVu +665: 5EYCAe5jLQhn6ofDSwYmivaY5upgS6kiSqmEUCXaXE2mAc6w +666: 5EYCAe5jLQhn6ofDSwZ3Tx88CQVsterbBWhk29DQz3uVS7i8 +667: 5EYCAe5jLQhn6ofDSwZKCyfiJuB5MCxTvBeFa5uFSsnDhgGK +668: 5EYCAe5jLQhn6ofDSwZax1DJRPrGom4Leram82b5uhewyNoP +669: 5EYCAe5jLQhn6ofDSwZrh2ktXtXUGKADPXXGfyGvNXXgEyZ3 +670: 5EYCAe5jLQhn6ofDSwa8S4JUePCfisG68CTnDuxkqMQQWXvt +671: 5EYCAe5jLQhn6ofDSwaQB5r4ksssBRMxrsQHmrebJBH8nAcv +672: 5EYCAe5jLQhn6ofDSwafv7PesNZ4dyTqbYLoKoLRm19s3qJz +673: 5EYCAe5jLQhn6ofDSwawf8wEysEG6XZiLDHJsk2GDq2bKTmw +674: 5EYCAe5jLQhn6ofDSwbDQAUq6MuTZ5fb4tDpRgi6geuKbLxK +675: 5EYCAe5jLQhn6ofDSwbV9C2RCraf1dmToZAKydPw9Un3rxbC +676: 5EYCAe5jLQhn6ofDSwbktDa1KMFrUBsLYE6qXa5mcJen8ckd +677: 5EYCAe5jLQhn6ofDSwc2dF7bRqw3vjyDGu3M5Wmc58XWQBuK +678: 5EYCAe5jLQhn6ofDSwcJNGfBYLcFPJ561ZyrdTTSXxQEfmEu +679: 5EYCAe5jLQhn6ofDSwca7JCmeqHSqrAxkEvNBQ9GznGxwBdp +680: 5EYCAe5jLQhn6ofDSwcqrKkMmKxeJQGqUursjLq7Tc9hCorw +681: 5EYCAe5jLQhn6ofDSwd7bMHwspdqkxNiDaoPHHWwvS2RUdXJ +682: 5EYCAe5jLQhn6ofDSwdPLNqXzKK3DWUaxFjtqECnPFu9kC7N +683: 5EYCAe5jLQhn6ofDSwdf5QP86ozEg4aTgvgQPAtcr5mt1rjf +684: 5EYCAe5jLQhn6ofDSwdvpRviDJfS8cgLRbcuw7aTJuecHTjg +685: 5EYCAe5jLQhn6ofDSweCZTUJKoLdbAnDAGZRV4GHmjXLZCAK +686: 5EYCAe5jLQhn6ofDSweUJV1tSJ1q3it5twVw2zx8EZQ4pj72 +687: 5EYCAe5jLQhn6ofDSwek3WZUYnh2WGyxdcSSawdxhPGo6JxS +688: 5EYCAe5jLQhn6ofDSwf1nY74fHNDxq5qNHNx8tKoAD9XMwtS +689: 5EYCAe5jLQhn6ofDSwfHXZeemn3RRPBi6xKTgq1dd32FdSM8 +690: 5EYCAe5jLQhn6ofDSwfZGbCEtGicswHaqdFyEmhU5rtyu5Z6 +691: 5EYCAe5jLQhn6ofDSwfq1cjpzmPpLVPTaJCUniPJYgmiAiMS +692: 5EYCAe5jLQhn6ofDSwg6keHR7G51o3VLJy8zLf591WeSSbhJ +693: 5EYCAe5jLQhn6ofDSwgNVfq1DkkDFbbD3e5VtbkyULXAhwZq +694: 5EYCAe5jLQhn6ofDSwgeEhNbLFRQi9h5nK21SYSowAPtycbw +695: 5EYCAe5jLQhn6ofDSwguyivBSk6cAhnxWyxWzV8ePzGdFWrE +696: 5EYCAe5jLQhn6ofDSwhBikTmZEmodFtqFeu2YRpUrp9MX6zu +697: 5EYCAe5jLQhn6ofDSwhTTn1MfjT15ozhzKqY6NWKKe25nf5e +698: 5EYCAe5jLQhn6ofDSwhjCoYwnE8CYN6aizn3eKC9nTtp4KzB +699: 5EYCAe5jLQhn6ofDSwhzwq6XtioPzvCTTfiZCFszFHmYL1bG +700: 5EYCAe5jLQhn6ofDSwiGgre81DUbTUJLCLf4kCZpi7eGbazB +701: 5EYCAe5jLQhn6ofDSwiYRtBi7i9nv2QCw1baJ9FfAwWzrzNK +702: 5EYCAe5jLQhn6ofDSwipAujJECpzNaW5fgY5r5wVdmPj8dgE +703: 5EYCAe5jLQhn6ofDSwj5uwGtLhWBq8bxQMUbQ2dL6bGTQRjz +704: 5EYCAe5jLQhn6ofDSwjMexpUTCBPHghq92R6wyKAZR9Bg2zd +705: 5EYCAe5jLQhn6ofDSwjdPzN4ZgrakEohshMcVv112F1uwSaB +706: 5EYCAe5jLQhn6ofDSwju91uegBXnCnuacNJ83rgqV4teDK4S +707: 5EYCAe5jLQhn6ofDSwkAt3TEngCyfM1TM3EdboNfwtmNUvX2 +708: 5EYCAe5jLQhn6ofDSwkSd4zpuAtB7u7L5iB99k4WQie6kRBz +709: 5EYCAe5jLQhn6ofDSwkiN6YR1fZNaTDCpP7ehgkLsYWq238p +710: 5EYCAe5jLQhn6ofDSwkz78618AEa31K5Z44AFdSBLNPZHotQ +711: 5EYCAe5jLQhn6ofDSwmFr9dbEeumVZQxHizfoa81oCGHZWCA +712: 5EYCAe5jLQhn6ofDSwmXbBBBM9axx7Wq2PwBMWorG291q82P +713: 5EYCAe5jLQhn6ofDSwmoLCimTeGAQfchm4sguTVgir1k6n9U +714: 5EYCAe5jLQhn6ofDSwn55EGMa8wMsDiaVjpCTQBXBftUNKJq +715: 5EYCAe5jLQhn6ofDSwnLpFowgdcZKmpTEQki1LsMeVmCdpCj +716: 5EYCAe5jLQhn6ofDSwncZHMXo8HknKvKy5hDZHZC7KdvuXrB +717: 5EYCAe5jLQhn6ofDSwntJJu7ucxxEt2Chkdj7EF2a9WfB5ec +718: 5EYCAe5jLQhn6ofDSwoA3LSi27e9hS85SRaEfAvs2yPPSqde +719: 5EYCAe5jLQhn6ofDSwoRnMzJ8cKM9zDxB6WkD7chVoG7iPFH +720: 5EYCAe5jLQhn6ofDSwohXPXtF6zYcYKpumTFm4JXxd8qyujN +721: 5EYCAe5jLQhn6ofDSwoyGR5UMbfk56RheSPmJzzNRT1aFek4 +722: 5EYCAe5jLQhn6ofDSwpF1Sd4U6LwXeXaP7LGrwgCtGtJXFVq +723: 5EYCAe5jLQhn6ofDSwpWkUAeab28zCdT7nGnQtN3M6m2nvKc +724: 5EYCAe5jLQhn6ofDSwpnVViEh5hLSkjKrTDHxq3sovdm4Nw6 +725: 5EYCAe5jLQhn6ofDSwq4EXFpoaNXuJqCb89oWmjiGkWVLAto +726: 5EYCAe5jLQhn6ofDSwqKyYoQv53jMrw5Ko6K4iRYjaPDbs68 +727: 5EYCAe5jLQhn6ofDSwqbiaM12ZivpR2x4U2pcf7PCQFwsHxu +728: 5EYCAe5jLQhn6ofDSwqsTbtb94Q8Gy8po8yLAboDfE8g8qfu +729: 5EYCAe5jLQhn6ofDSwr9CdSBFZ5KjXEhXouqiYV4841QQmeD +730: 5EYCAe5jLQhn6ofDSwrQweymN3kXC5LaGUrMGVAtast8g9P8 +731: 5EYCAe5jLQhn6ofDSwrgggXMUYRiedST19nrpRrj3hkrwr4P +732: 5EYCAe5jLQhn6ofDSwrxRi4wb36v7BYKjpjNNNYZWXdbDbT2 +733: 5EYCAe5jLQhn6ofDSwsEAjcXhXn7ZjeCUVfsvKEPyMWKUy64 +734: 5EYCAe5jLQhn6ofDSwsVumA7p2TK2Hk5DAcPUFvESBP3kgYh +735: 5EYCAe5jLQhn6ofDSwsmenhhvX8WUqqwwqYu2Cc4u1Fn2KSS +736: 5EYCAe5jLQhn6ofDSwt3PpFJ31ohwPwpgWVQa9HuMq8WHtcH +737: 5EYCAe5jLQhn6ofDSwtK8qnt9WUuPx3hRBRv85yjpf1EZadQ +738: 5EYCAe5jLQhn6ofDSwtassLUG1A6rW9a9rNRg2faHUsxqMFi +739: 5EYCAe5jLQhn6ofDSwtrctt4NVqJK4FStXJwDyMQkJkh6s1F +740: 5EYCAe5jLQhn6ofDSwu8MvReUzWVmcMKdCFSmv3FD8dRNY1G +741: 5EYCAe5jLQhn6ofDSwuQ6wyEbVBhEATCMsBxKrj5fxW9dz3E +742: 5EYCAe5jLQhn6ofDSwufqyWphyrtgiZ56Y8TsoQv8nNsuu3k +743: 5EYCAe5jLQhn6ofDSwuwb14QpUY69GewqD4yRk6kbcFcBLhB +744: 5EYCAe5jLQhn6ofDSwvDL2bzvyDHbpkpZt1Uygnb4S8LT5bh +745: 5EYCAe5jLQhn6ofDSwvV549b3TtV4NrhJYwzXdURXG14ikbp +746: 5EYCAe5jLQhn6ofDSwvkp5hB9xZgWvxa3DtW5aAFz5snz8Jm +747: 5EYCAe5jLQhn6ofDSww2Z7EmGTEsyV4Smtq1dWr6SukXFvLY +748: 5EYCAe5jLQhn6ofDSwwJJ8nMNwv5S3AKWZmXBTXvujdFXSdM +749: 5EYCAe5jLQhn6ofDSwwa3AKwVSbGtbGCFEi2jQDmNZVyo8bB +750: 5EYCAe5jLQhn6ofDSwwqnBsXbwGUM9N4yueYHLubqPNi4em6 +751: 5EYCAe5jLQhn6ofDSwx7XDR7iRwfohTwiab3qHbSJDFSLPWB +752: 5EYCAe5jLQhn6ofDSwxPGExhpvcsGFZpTFXZPEHGm38Ac8CZ +753: 5EYCAe5jLQhn6ofDSwxf1GWHwRJ4iofhBvU4wAy7Drztsd4d +754: 5EYCAe5jLQhn6ofDSwxvkJ3t3uyGBMmZvbQaV7ewggsd9LGE +755: 5EYCAe5jLQhn6ofDSwyCVKbUAQeTdusSfGM634Ln9WkMQkR1 +756: 5EYCAe5jLQhn6ofDSwyUEM94GuKf6TyKPwHbb12ccLd5gYNT +757: 5EYCAe5jLQhn6ofDSwyjyNgePPzrZ25C8cE78wiT5AVowzRJ +758: 5EYCAe5jLQhn6ofDSwz1iQEEVtg41aB4sHAcgtQHXzNYDfiq +759: 5EYCAe5jLQhn6ofDSwzHTRmpcPMFU8Gwbx78Eq67zpFGVKAV +760: 5EYCAe5jLQhn6ofDSwzZCTKQit2SvgNpLd3dnmmxTe7zm2FX +761: 5EYCAe5jLQhn6ofDSwzpwUrzqNhePEUh5Hz9LiTnvTzj2o2S +762: 5EYCAe5jLQhn6ofDSx16gWQawsNqqnaZoxvetf9dPHsTJGVj +763: 5EYCAe5jLQhn6ofDSx1NRXxB4N43JLgSYdsASbqTr7kBZkMV +764: 5EYCAe5jLQhn6ofDSx1eAZVmArjEktnKHJofzYXJJwcuqZNN +765: 5EYCAe5jLQhn6ofDSx1uub3MHMQSDStC1ykBYVD8mmVe72B9 +766: 5EYCAe5jLQhn6ofDSx2BecawPr5dfzz4kegh6RtyEbNNNvFe +767: 5EYCAe5jLQhn6ofDSx2TPe8XWLkq8Z5wVKdCeNaohRF6eYof +768: 5EYCAe5jLQhn6ofDSvqFHR7HuaCvPkQxqZx8iHpeHQ8hVBKK +769: 5EYCAe5jLQhn6ofDSvqX2Set24t7rJWqaEteGEWUkE1RkrkR +770: 5EYCAe5jLQhn6ofDSvqnmUCU8ZZKJrciJuq9pBCKD3tA2LLb +771: 5EYCAe5jLQhn6ofDSvr4WVk4F4EWmQib3amfN7t9fsktJ77C +772: 5EYCAe5jLQhn6ofDSvrLFXHeMYuiDxpTnFiAv4Zz8hdcZbNr +773: 5EYCAe5jLQhn6ofDSvrbzYqEU3augWvLWvegU1FpbXWLqFRa +774: 5EYCAe5jLQhn6ofDSvrsjaNpaYG7952DFbbC1wwf4MP56tjt +775: 5EYCAe5jLQhn6ofDSvs9UbvQh2wJbd85zGXhZtdVXBFoNVtD +776: 5EYCAe5jLQhn6ofDSvsRDdTzoXcW4BDxiwUD7qKKz18Xe12H +777: 5EYCAe5jLQhn6ofDSvsgxf1av2HhWjKqTcQifn1ASq1FucFi +778: 5EYCAe5jLQhn6ofDSvsxhgZB2WxtyHRiCHMEDigzueszBXBe +779: 5EYCAe5jLQhn6ofDSvtESi6m91e6RqXavxHjmfNqNUkiT7mZ +780: 5EYCAe5jLQhn6ofDSvtWBjeMFWKHtPdTfdEFKc4fqJdSidEs +781: 5EYCAe5jLQhn6ofDSvtmvmBwMzzVLwjLQJAksYkWJ8WAzBPh +782: 5EYCAe5jLQhn6ofDSvu3fnjXUVfgoVqD8y7GRVSLkxNuFnUr +783: 5EYCAe5jLQhn6ofDSvuKQpH7azLtG3w5se3myS8BDnFdXVDQ +784: 5EYCAe5jLQhn6ofDSvub9qphhV25ic2xcJzHXNp1gc8MoHRr +785: 5EYCAe5jLQhn6ofDSvurtsNHoyhHBA8qLyvo5KVr9S164gKm +786: 5EYCAe5jLQhn6ofDSvv8dtusvUNUdiEi5esJdGBgcFspLGuj +787: 5EYCAe5jLQhn6ofDSvvQNvTU2y3g6GLapKopBCsX55kYc6uX +788: 5EYCAe5jLQhn6ofDSvvg7x149TisYpSTYzkKj9ZMXudGsZQv +789: 5EYCAe5jLQhn6ofDSvvwryYeFxQ51NYLHfgqH6FBzjW19P2E +790: 5EYCAe5jLQhn6ofDSvwDc16ENT5GTveD2LdLq2w2TZNjQxh2 +791: 5EYCAe5jLQhn6ofDSvwVM2dpUwkTvUk5m1ZrNycrvPFTgNTx +792: 5EYCAe5jLQhn6ofDSvwm64BQbSRfP2qxVgWMvvJhPD8BxAmh +793: 5EYCAe5jLQhn6ofDSvx2q5izhw6rqawqEMSsUrzXr2zvDq9o +794: 5EYCAe5jLQhn6ofDSvxJa7GapRn4J93hy2PP2ogNJrseVQgH +795: 5EYCAe5jLQhn6ofDSvxaK8pAvvTFkh9ahhKtakNCmgkNm9iv +796: 5EYCAe5jLQhn6ofDSvxr4AMm3R8TDFFTSNGQ8h43EWd72aAb +797: 5EYCAe5jLQhn6ofDSvy7oBuM9uoefoMLB3CugdjshLVqJHyr +798: 5EYCAe5jLQhn6ofDSvyPYDSwGQUr8MTCui9REaRiAANZa2Nj +799: 5EYCAe5jLQhn6ofDSvyfHEzXNuA3auZ5eP5vnX7YczFHqVq1 +800: 5EYCAe5jLQhn6ofDSvyw2GY7VPqF3TexP42SLToP5p826zpV +801: 5EYCAe5jLQhn6ofDSvzCmJ5hbtWSW1kq7ixwtQVDYdzkNx8G +802: 5EYCAe5jLQhn6ofDSvzUWKdHiPBdxZrhrPuTSMB41TsUeMmn +803: 5EYCAe5jLQhn6ofDSvzkFMAspsrqR7xab4qxzHrtUHkCuxdQ +804: 5EYCAe5jLQhn6ofDSw11zNiTwNY2sg4TKjnUYEYiw7cwBaW5 +805: 5EYCAe5jLQhn6ofDSw1HjQG43sDELEAL4Qiz6BEZPwVfTFoS +806: 5EYCAe5jLQhn6ofDSw1ZURoeAMtRnnGCo5fVe7vPrmNPiyE5 +807: 5EYCAe5jLQhn6ofDSw1qDTMEGrZdFLN5Xkc1C4cEKbF7zTVD +808: 5EYCAe5jLQhn6ofDSw26xUtpPMEphtTxGRYWk1J4nR7rGFA2 +809: 5EYCAe5jLQhn6ofDSw2NhWSQVqv2ASZq16V2HwyuFEzaXgBa +810: 5EYCAe5jLQhn6ofDSw2eSXyzcLbDczfhjmRXqtfji4sJoHqH +811: 5EYCAe5jLQhn6ofDSw2vBZXaiqGR5YmaUSN3PqMaAtk34tDS +812: 5EYCAe5jLQhn6ofDSw3Bvb5AqKwcY6sTD7JYwn3QdicmLfNX +813: 5EYCAe5jLQhn6ofDSw3TfcckwpcozeyKwnF4VijF6YVVcNh2 +814: 5EYCAe5jLQhn6ofDSw3jQeAM4KJ1TD5CgTBa3fR5ZNNDspJU +815: 5EYCAe5jLQhn6ofDSw419fhwAoyCumB5R885bc6v2CEx9X8a +816: 5EYCAe5jLQhn6ofDSw4GthFXHJeQNKGx9o4b9YnkV27gRHoX +817: 5EYCAe5jLQhn6ofDSw4Ydio7PoKbpsNptU16hVUawqzQgipw +818: 5EYCAe5jLQhn6ofDSw4pNkLhWHzoHRUhd8wcFSARQfs8xMi8 +819: 5EYCAe5jLQhn6ofDSw567mtHcnfzjyaaMot7oNrFsVjsE6KH +820: 5EYCAe5jLQhn6ofDSw5MroRsjHMCCXgT6UpdMKY6LKcbViXv +821: 5EYCAe5jLQhn6ofDSw5dbpyTqn2Pf5nKq9m8uGDvo9VKmA4Z +822: 5EYCAe5jLQhn6ofDSw5uLrX3xGhb7dtCZpheTCumFyN42vWo +823: 5EYCAe5jLQhn6ofDSw6B5t4e4mNnaBz5JVeA19bbioEnJXJY +824: 5EYCAe5jLQhn6ofDSw6SpucEBG3z2k5x3AafZ6HSBd7Wa7SR +825: 5EYCAe5jLQhn6ofDSw6iZw9pHkjBVJBpmqXB72yGeSzEqniD +826: 5EYCAe5jLQhn6ofDSw6zJxhQQFQNwrHhWWTgeyf77Gry7Sno +827: 5EYCAe5jLQhn6ofDSw7G3zEzWk5aQQPaFBQCCvLwa6jhP465 +828: 5EYCAe5jLQhn6ofDSw7Xo1nadEkmrxVSyrLhks2n2vcRefhC +829: 5EYCAe5jLQhn6ofDSw7oY3LAjjRyKWbKiXHDJoicVkV9vBPY +830: 5EYCAe5jLQhn6ofDSw85H4skrE7An4hCTCDirkQSxaMtBsPh +831: 5EYCAe5jLQhn6ofDSw8M26RLxinNEco5BsAEQh6HRQEcTSy1 +832: 5EYCAe5jLQhn6ofDSw8cm7xw5DTZhAtwvY6jxdn7tE7Lj4CV +833: 5EYCAe5jLQhn6ofDSw8tW9WXBi8m9izpfD3FWaTxM3z4ztxu +834: 5EYCAe5jLQhn6ofDSw9AFB47JCoxcH6hPsym4X9nosroGaQ6 +835: 5EYCAe5jLQhn6ofDSw9RzCbhQhVA4qCa8YvGcTqdGhjXY5dN +836: 5EYCAe5jLQhn6ofDSw9hjE9HXCAMXPJSsDrnAQXTjXcFomyC +837: 5EYCAe5jLQhn6ofDSw9yUFgsdgqYywQKbtoHiMDJCMUz5KLc +838: 5EYCAe5jLQhn6ofDSwAFDHETkBWkSVWCLZjoGHu8fBMiLqyN +839: 5EYCAe5jLQhn6ofDSwAWxJn3rgBwu3c55EgJpEay81EScYDv +840: 5EYCAe5jLQhn6ofDSwAnhLKdyAs9MbhwoucpNBGoaq7AtBND +841: 5EYCAe5jLQhn6ofDSwB4SMsE5fYLp9opYaZKv7xe3eyu9pB6 +842: 5EYCAe5jLQhn6ofDSwBLBPQpCADYGhuhHFVqU4eUWUrdRM7v +843: 5EYCAe5jLQhn6ofDSwBbvQxQJetjjG1a1vSM21LJyJjMhBbF +844: 5EYCAe5jLQhn6ofDSwBsfSVzR9ZwBp7SkbNrZx29S8c5xc5e +845: 5EYCAe5jLQhn6ofDSwC9QU3aXeF8eNDKVGKN7thytxUpEQ99 +846: 5EYCAe5jLQhn6ofDSwCR9VbAe8vL6vKCDwFsfqPpMnMYVzgn +847: 5EYCAe5jLQhn6ofDSwCgtX8kkdbXZUR4xcCPDn5epcEGmiXF +848: 5EYCAe5jLQhn6ofDSwCxdYgLs8Gj22WwhH8tmimVHS713CwC +849: 5EYCAe5jLQhn6ofDSwDENaDvycwvUacpRx5QKfTKkFyjJjj9 +850: 5EYCAe5jLQhn6ofDSwDW7bmX67d7w8ihAd1usc9AD5rTacmN +851: 5EYCAe5jLQhn6ofDSwDmrdK7CcJKPgpZuHxRRYpzfujBr41L +852: 5EYCAe5jLQhn6ofDSwE3berhK6yWrEvSdxtvyVWq8jbv7aHQ +853: 5EYCAe5jLQhn6ofDSwEKLgQHRbeiJo2KNdqSXSCfbZUePR9i +854: 5EYCAe5jLQhn6ofDSwEb5hwsY6KumM8C7Jmx5NtW4PMNeshT +855: 5EYCAe5jLQhn6ofDSwErpjVTeb17DuE4qyiTdKaLXDE6vTCK +856: 5EYCAe5jLQhn6ofDSwF8Zm33m5gJgTKwaeeyBGGAz36qCDCc +857: 5EYCAe5jLQhn6ofDSwFQJnadsaMW91RpKKbUjCx1SryZTqHp +858: 5EYCAe5jLQhn6ofDSwFg3p8Dz52hbZXh3zXzH9dqugrHjWf4 +859: 5EYCAe5jLQhn6ofDSwFwnqfp6Zhu47dZnfUVq6KgNWj219Be +860: 5EYCAe5jLQhn6ofDSwGDXsDQD4P6WfjSXLR1P31WqLbkGqnY +861: 5EYCAe5jLQhn6ofDSwGVGtkzKZ4HyDqKG1MWvyhMJAUUYEpB +862: 5EYCAe5jLQhn6ofDSwGm1vJaS3jVRmwBzgJ2UvPBkzMCoxJh +863: 5EYCAe5jLQhn6ofDSwH2kwrAYYQgtL34jMEY2s52DpDw5VNy +864: 5EYCAe5jLQhn6ofDSwHJVyPkf35tLt8wU2B3aokrge6fMCZn +865: 5EYCAe5jLQhn6ofDSwHaEzwLmXm5oSEpCh7Z8kSh9TyPctVW +866: 5EYCAe5jLQhn6ofDSwHqz2Uvt2SHFzLgwN44gh8XcHr7tZat +867: 5EYCAe5jLQhn6ofDSwJ7j42WzX7UiYSZg2zaEdpN57irABgV +868: 5EYCAe5jLQhn6ofDSwJPU5a771ngB6YSQhw5naWCXwbaReyd +869: 5EYCAe5jLQhn6ofDSwJfD77hDWTsdeeK9NsbLXC2zmUJhHzL +870: 5EYCAe5jLQhn6ofDSwJvx8fHL1956CkBt3p6tTssTbM2xpSz +871: 5EYCAe5jLQhn6ofDSwKChACsSVpGYkr4cikcSQZhvRDmEWT2 +872: 5EYCAe5jLQhn6ofDSwKUSBkTYzVU1JwwMPh7zMFYPF6VWKwG +873: 5EYCAe5jLQhn6ofDSwKkBDJ3fVAfTs3p64ddYHwNr4yDmodk +874: 5EYCAe5jLQhn6ofDSwL1vEqdmyqrvR9gpja96EdDJtqx3NjK +875: 5EYCAe5jLQhn6ofDSwLHfGPDtUX4NyFZZQWeeBK3miigK7b2 +876: 5EYCAe5jLQhn6ofDSwLZQHvozyCFqXMSJ5TAC7ztEYbQaawr +877: 5EYCAe5jLQhn6ofDSwLq9KUQ7TsTJ5TK2kPfk4gihNU8rKKM +878: 5EYCAe5jLQhn6ofDSwM6tM1zDxYekdZBmRLBJ1NZACLs7za8 +879: 5EYCAe5jLQhn6ofDSwMNdNZaLTDrDBf4W6Ggqx4Pd2DbPm1Z +880: 5EYCAe5jLQhn6ofDSwMeNQ7ASwu3fjkwEmDCPtkE5r6KfCqS +881: 5EYCAe5jLQhn6ofDSwMv7RekZSaF8HroyS9hwqS4Yfy3vs1i +882: 5EYCAe5jLQhn6ofDSwNBrTCLfwFSaqxgi76DVn7u1VqnCKdZ +883: 5EYCAe5jLQhn6ofDSwNTbUjvnRve3Q4ZSn2j3iojUKiWTxuR +884: 5EYCAe5jLQhn6ofDSwNjLWHWtvbqVxASBSyEbfVZw9bEjuGF +885: 5EYCAe5jLQhn6ofDSwP15Xq71RH2xWGJv7uk9cBQPyTy1KK2 +886: 5EYCAe5jLQhn6ofDSwPGpZNh7uxER4NBenrFhYsEroLhGv4U +887: 5EYCAe5jLQhn6ofDSwPYZavHEQdRscU4PTnmFVZ5KdDRYbDs +888: 5EYCAe5jLQhn6ofDSwPpJcTsLuJdLAZw88jGoSEunT69pCcM +889: 5EYCAe5jLQhn6ofDSwQ63e1TTPypniforofnMNvkFGxt5i9U +890: 5EYCAe5jLQhn6ofDSwQMnfZ3Ztf2FGmgbUcHuKcai6qcMaYL +891: 5EYCAe5jLQhn6ofDSwQdXh6dgPLDhpsZL9YoTGJRAviLdEmZ +892: 5EYCAe5jLQhn6ofDSwQuGieDnt1RANyS4pVK1CzFdkb4tgYN +893: 5EYCAe5jLQhn6ofDSwRB1kBouNgccw5JoVRpZ9g66aToALaq +894: 5EYCAe5jLQhn6ofDSwRSkmjQ1sMp5VBBYANL76MvZQLXS4Vu +895: 5EYCAe5jLQhn6ofDSwRiVoGz8N31Y3H4GqJqf33m2EDFhj5S +896: 5EYCAe5jLQhn6ofDSwRzEppaEriCzbNw1WFMCyjbV45yy81m +897: 5EYCAe5jLQhn6ofDSwSFyrNAMMPQT9UokBBrkvRRwsxiErwp +898: 5EYCAe5jLQhn6ofDSwSXisukTr4buhagUr8NJs7GQhqSWRPe +899: 5EYCAe5jLQhn6ofDSwSoTuTLaLjoNFgZDX4sroo6sXiAmzRT +900: 5EYCAe5jLQhn6ofDSwT5CvzvgqQzponRxC1PQkUwLMau3ePs +901: 5EYCAe5jLQhn6ofDSwTLwxYWoL6CHMtJgrwtxhAmoBTdKXyR +902: 5EYCAe5jLQhn6ofDSwTcgz66upmPjuzBRXtQWdrcG1LMazy6 +903: 5EYCAe5jLQhn6ofDSwTtS1dh2KSbCU64ACpv4aYSiqD5rdpF +904: 5EYCAe5jLQhn6ofDSwUAB3BH8p7nf2BvtsmRcXEHBf5p874L +905: 5EYCAe5jLQhn6ofDSwURv4isFJnz7aHodYhwATv7eUxYPt9r +906: 5EYCAe5jLQhn6ofDSwUhf6GTMoUBa8PgNDeSiQbx7JqGfSEd +907: 5EYCAe5jLQhn6ofDSwUyQ7p3UJ9P2gVZ6taxGMHna8hzw5fY +908: 5EYCAe5jLQhn6ofDSwVF99MdanpaVEbRqZXTpHyd2xajCotp +909: 5EYCAe5jLQhn6ofDSwVWtAuDhHVmwnhJaETyNEfTVnTTUJ62 +910: 5EYCAe5jLQhn6ofDSwVndCSoonAyQLoBJuQUvBMHxcLBjrJi +911: 5EYCAe5jLQhn6ofDSwW4NDzPvGrArtu43aLzU838RSCv1ehN +912: 5EYCAe5jLQhn6ofDSwWL7FXz2mXNKSzvnFHW24ixtG5eHAkV +913: 5EYCAe5jLQhn6ofDSwWbrH5a9GCZn16oWvE1a1QoM5xNYqL8 +914: 5EYCAe5jLQhn6ofDSwWsbJdAFksmEZCgFbAX7x6douq6pSNe +915: 5EYCAe5jLQhn6ofDSwX9LLAkNFYxh7JYzG72ftnUGjhq6ByT +916: 5EYCAe5jLQhn6ofDSwXR5MiLUkEA9fQRiw3YDqUJjZaZMp43 +917: 5EYCAe5jLQhn6ofDSwXgpPFvbEuMcDWJTbz3mnA9CPTHdVsC +918: 5EYCAe5jLQhn6ofDSwXxZQoWhjaZ4mcBCGvZKiqyfDL1u7hx +919: 5EYCAe5jLQhn6ofDSwYEJSM6pEFkXKi3vws4sfXp83CkAivU +920: 5EYCAe5jLQhn6ofDSwYW3TtgvivwysovfcoaRcDeas5USMYT +921: 5EYCAe5jLQhn6ofDSwYmnVSH3Dc9SRuoQHk5yYuV3gxChyyF +922: 5EYCAe5jLQhn6ofDSwZ3XWys9iHLtz1g8xgbXVbKWWpvydok +923: 5EYCAe5jLQhn6ofDSwZKGYXTGCxYMY7Ysdd75SH9yLhfFKGv +924: 5EYCAe5jLQhn6ofDSwZb1a53Nhdjp6DRcJZcdNxzSAaPWrbz +925: 5EYCAe5jLQhn6ofDSwZrkbcdVCJwGeKJLyW8BKeptzT7nFc5 +926: 5EYCAe5jLQhn6ofDSwa8VdADbgz8jCRB5eSdjGLfMpKr3te4 +927: 5EYCAe5jLQhn6ofDSwaQEehoiBfLBkX3pKP9HD2VpeCaKhrx +928: 5EYCAe5jLQhn6ofDSwafygFPpgLXeJcvYzKeq9iLHU5JbD23 +929: 5EYCAe5jLQhn6ofDSwawihnywB1j6rioHfGAP6QAkHx2rjpq +930: 5EYCAe5jLQhn6ofDSwbDTjLa3fgvZQpg2LCfw361D7pm8UxM +931: 5EYCAe5jLQhn6ofDSwbVCktAAAN81xvYm19BUymqfwhVQ2QH +932: 5EYCAe5jLQhn6ofDSwbkwnRkGf3KUX2RVg5h2vTg8maDfkCV +933: 5EYCAe5jLQhn6ofDSwc2goyLP9iWw58JEM2Cas9WbbSwwF7u +934: 5EYCAe5jLQhn6ofDSwcJRqWvVePiPdEAy1xi8oqM4RKgCyrk +935: 5EYCAe5jLQhn6ofDSwcaAs4Wc94urBL3hguDgkXBXFCQUa1R +936: 5EYCAe5jLQhn6ofDSwcqutc6idk7JjRvSMqjEhD1z558kNSY +937: 5EYCAe5jLQhn6ofDSwd7ev9gq8RJmHXoB2nEndtrStws24Jk +938: 5EYCAe5jLQhn6ofDSwdPPwhGwd6WDqdfuhikLaaguipbHhTc +939: 5EYCAe5jLQhn6ofDSwdf8yEs47mhgPjYeNfFtXGXNYhKZGZk +940: 5EYCAe5jLQhn6ofDSwdvsznTAcSu8wqRP3bmSTxMqNa3puJ5 +941: 5EYCAe5jLQhn6ofDSweCd2L3H786bVwJ7iYGzQeCJCSn6H8L +942: 5EYCAe5jLQhn6ofDSweUN3sdPboJ443ArPUnYML2m2KWNBKc +943: 5EYCAe5jLQhn6ofDSwek75RDW6UVWc93b4RJ6J1sDrCEdfCm +944: 5EYCAe5jLQhn6ofDSwf1r6xocb9gyAEvKjMoeEhhgg4xuJbG +945: 5EYCAe5jLQhn6ofDSwfHb8WPj5ptRiLo4QJKCBPY9VwhAwVR +946: 5EYCAe5jLQhn6ofDSwfZLA3yqaW5tGSfo5Epk85NcKpRSg1o +947: 5EYCAe5jLQhn6ofDSwfq5BbZx5BHLpYYXkBLJ4mD59h9iEEa +948: 5EYCAe5jLQhn6ofDSwg6pD9A4ZrUoNeRGR7qr1T3XyZsyiw8 +949: 5EYCAe5jLQhn6ofDSwgNZEgkB4XgFvkJ164MPx8szoScFak2 +950: 5EYCAe5jLQhn6ofDSwgeJGELHZCsiUrAjkzrwtpiTdKLXB4X +951: 5EYCAe5jLQhn6ofDSwgv3HmvQ3t5B2x3URwNVqWYvTC4naER +952: 5EYCAe5jLQhn6ofDSwhBnKKWWYZGdb3vD6st3nCPPH4o4Mat +953: 5EYCAe5jLQhn6ofDSwhTXLs6d3EU699nwmpPbitDr6wXL32D +954: 5EYCAe5jLQhn6ofDSwhjGNQgjXufYhFfgSku9fa4JvpFbgt4 +955: 5EYCAe5jLQhn6ofDSwi11PxGr2as1FMYR7hQhcFtmkgysGkc +956: 5EYCAe5jLQhn6ofDSwiGkRVrxXG4ToTR9ndvFYwjEaZi8ngw +957: 5EYCAe5jLQhn6ofDSwiYVT3T51wFvMZHtTaRoVdZhQSSQP4g +958: 5EYCAe5jLQhn6ofDSwipEUb3BWcTNufAd8WwMSKQAEKAg1NA +959: 5EYCAe5jLQhn6ofDSwj5yW8dJ1HeqTm3MoTSuP1Ed4BtwabB +960: 5EYCAe5jLQhn6ofDSwjMiXgDQVxrJ1rv6UPxTKh55t4dDCHY +961: 5EYCAe5jLQhn6ofDSwjdTZDoWze3kZxnq9LU1GNuYhwMUvSK +962: 5EYCAe5jLQhn6ofDSwjuCamPdVKFD84fZpGyZD4k1Xp5kfJk +963: 5EYCAe5jLQhn6ofDSwkAwcJyjyzSfgAYJVDV79kaUMgp2EPZ +964: 5EYCAe5jLQhn6ofDSwkSgdrZrUfe8EGR3A9zf6SQwBZYHmgP +965: 5EYCAe5jLQhn6ofDSwkiRfQ9xyLqanNHmq6WD38FQ1SGZTTQ +966: 5EYCAe5jLQhn6ofDSwkzAgwk5U233LUAWW31kyp5rqJzqBXR +967: 5EYCAe5jLQhn6ofDSwmFuiVLBxhEVta3FAyXJvVvKfBj6fFs +968: 5EYCAe5jLQhn6ofDSwmXek2vJTNRxSfuyqv2rsBknV4TNUaZ +969: 5EYCAe5jLQhn6ofDSwmoPmaWQx3dQzmniWrYQosbFJwBdpHQ +970: 5EYCAe5jLQhn6ofDSwn58o86XSipsYsfTBo3xkZRi8ouujZb +971: 5EYCAe5jLQhn6ofDSwnLspfgdwQ2L6yYBrjZWhFGAxgeBF8E +972: 5EYCAe5jLQhn6ofDSwnccrDGkS5Dnf5QvXg54dw6dnZNSred +973: 5EYCAe5jLQhn6ofDSwntMskrrvkRFDBHfCcacacw6cS6iMUZ +974: 5EYCAe5jLQhn6ofDSwoA6uJSyRRchmHAPsZ6AXJmZSJpzCSq +975: 5EYCAe5jLQhn6ofDSwoRqvr35v6pAKP38YVbiTzc2GBZFfwh +976: 5EYCAe5jLQhn6ofDSwohaxPdCQn1csUusDS7GQgSV64HXCQ1 +977: 5EYCAe5jLQhn6ofDSwoyKywDJuTD5RanbtNcpMNGwuw1npdK +978: 5EYCAe5jLQhn6ofDSwpF51UoRQ8QXygfLZK8NJ47Qjok4jZT +979: 5EYCAe5jLQhn6ofDSwpWp32PXtobzXnY5EFdvEjwsZgULCga +980: 5EYCAe5jLQhn6ofDSwpnZ4ZyePUoT5tQouC9UBRnLPZCbmdk +981: 5EYCAe5jLQhn6ofDSwq4J67Zkt9zudzHYa8f287coDRvsRGc +982: 5EYCAe5jLQhn6ofDSwqL37f9sNqCNC6AHF5Aa4oTG3Jf8xpw +983: 5EYCAe5jLQhn6ofDSwqbn9CjysWPpkC31v1g81VHisBPQptU +984: 5EYCAe5jLQhn6ofDSwqsXAkL6NBbHJHukaxBfxB8Bh47gAbt +985: 5EYCAe5jLQhn6ofDSwr9GCHvCrrnjrPnVFthDtrxeWvqx2sP +986: 5EYCAe5jLQhn6ofDSwrR1DqWKMXzCQVfDvqCmqYo7LoaDX1B +987: 5EYCAe5jLQhn6ofDSwrgkFP6RrDBexbXxbmiKnEdaAgJVGJY +988: 5EYCAe5jLQhn6ofDSwrxVGvgYLtP7WhQhGiDsivU2zZ2kvZ6 +989: 5EYCAe5jLQhn6ofDSwsEEJUGeqZaa4oHRwejRfcJVpRm2TYT +990: 5EYCAe5jLQhn6ofDSwsVyL1rmLEn2cuAAcbEycJ8xeJVJAsG +991: 5EYCAe5jLQhn6ofDSwsmiMZSspuyVB12uHXkXYyyRUBDZmPU +992: 5EYCAe5jLQhn6ofDSwt3TP72zKbAwj6udxUG5VfotJ3wqCyJ +993: 5EYCAe5jLQhn6ofDSwtKCQed6pGNQHCnNdQmdSMeM7vg76HA +994: 5EYCAe5jLQhn6ofDSwtawSCDDJwZrqJf7JMHBP3UowoQNX1a +995: 5EYCAe5jLQhn6ofDSwtrgTjoKocmKPQXqyHnjKjKGmg8eN7M +996: 5EYCAe5jLQhn6ofDSwu8RVHPSJHxmwWQaeEJHGR9jbYruht5 +997: 5EYCAe5jLQhn6ofDSwuQAWpyYnyAEVcHKKAoqD6zCRRbBXTx +998: 5EYCAe5jLQhn6ofDSwufuYNZfHeMh3iA3z7KP9npfFJKSy6v +999: 5EYCAe5jLQhn6ofDSwuweZv9mnKZ9bp2nf3pw6Uf85B3ia6x +1000: 5EYCAe5jLQhn6ofDSwvDPbTjtGzkc9uuXKzLV3AVau3mzD3F +1001: 5EYCAe5jLQhn6ofDSwvV8d1Kzmfx4i1nFzvr2yrL3ivWG1Wg +1002: 5EYCAe5jLQhn6ofDSwvkseYv7GM9XG7ezfsMavYAWYoEXUrg +1003: 5EYCAe5jLQhn6ofDSww2cg6WDm2LypDXjLos8sDzyNfxoEqD +1004: 5EYCAe5jLQhn6ofDSwwJMhe6LFhYSNKQU1kNgouqSCYh4mRE +1005: 5EYCAe5jLQhn6ofDSwwa6jBgSkNjtvRHCggtEkbfu2RRLWcA +1006: 5EYCAe5jLQhn6ofDSwwqqkjGZF3wMUX9wMdPnhHWMrJ9bwiU +1007: 5EYCAe5jLQhn6ofDSwx7anGrfjj8p2d2g2ZuLdyLpgAssiC4 +1008: 5EYCAe5jLQhn6ofDSwxPKopSnEQLGaiuQhWQtafBHW3c9VSf +1009: 5EYCAe5jLQhn6ofDSwxf4qN2tj5Xj8pn9NSvSXM1kKvLQtJU +1010: 5EYCAe5jLQhn6ofDSwxvorud1DkjBgvet3PRzU2rD9o4gWNR +1011: 5EYCAe5jLQhn6ofDSwyCYtTD7iRveF2XciKwYQigfyfnxDDr +1012: 5EYCAe5jLQhn6ofDSwyUHuzoED786o8QMPGT6MQX8oYXDznu +1013: 5EYCAe5jLQhn6ofDSwyk2wYPLhnKZMEH64CxeJ6MbdRFVYiN +1014: 5EYCAe5jLQhn6ofDSwz1my5yTCTX1uL9pj9UCEnC4THymGqt +1015: 5EYCAe5jLQhn6ofDSwzHWzdZZh8iUTS2ZQ5ykBU2XHAi2c5d +1016: 5EYCAe5jLQhn6ofDSwzZG2B9gBouw1XuJ52VJ89rz73SJLQt +1017: 5EYCAe5jLQhn6ofDSwzq13ijngV7PZdn2jxzr4qhSvvAZqXq +1018: 5EYCAe5jLQhn6ofDSx16k5GKuBAJr7jemQuWQ1XXukntqcmK +1019: 5EYCAe5jLQhn6ofDSx1NV6ov1fqWJfqXW5r1wxDNNafd76hY +1020: 5EYCAe5jLQhn6ofDSx1eE8MW8AWhmDwQEknXVtuCqQYMNwWU +1021: 5EYCAe5jLQhn6ofDSx1uy9u6EfBuDn3GyRj33qb3JER5eVd4 +1022: 5EYCAe5jLQhn6ofDSx2BiBSgM9s6gL99i6fYbnGsm4HovEUC +1023: 5EYCAe5jLQhn6ofDSx2TTCzGTeYJ8tF2Smc49ixiDtAYBtUX +1024: 5EYCAe5jLQhn6ofDSvqFLyy2rszPQ5a3o1vzDeCYos492Xug + +## Burn account ID + +5EYCAe5fvqwE4eNE7ddxEfasPGZe11e6SWKvos7FUXP2LUrp diff --git a/docs/wasm-contracts.md b/docs/wasm-contracts.md index d3a6b5637f..3e39164a69 100644 --- a/docs/wasm-contracts.md +++ b/docs/wasm-contracts.md @@ -43,6 +43,11 @@ Subtensor provides a custom chain extension that allows smart contracts to inter | 12 | `set_coldkey_auto_stake_hotkey` | Configure automatic stake destination | `(NetUid, AccountId)` | Error code | | 13 | `add_proxy` | Add a staking proxy for the caller | `(AccountId)` | Error code | | 14 | `remove_proxy` | Remove a staking proxy for the caller | `(AccountId)` | Error code | +| 15 | `get_alpha_price` | Query the current alpha price for a subnet | `(NetUid)` | `u64` (price × 10⁹) | +| 16 | `recycle_alpha` | Recycle alpha stake, reducing SubnetAlphaOut (supply reduction) | `(AccountId, AlphaBalance, NetUid)` | `u64` (actual amount recycled) | +| 17 | `burn_alpha` | Burn alpha stake without reducing SubnetAlphaOut (supply neutral) | `(AccountId, AlphaBalance, NetUid)` | `u64` (actual amount burned) | +| 18 | `add_stake_recycle` | Atomically add stake then recycle the resulting alpha | `(AccountId, NetUid, TaoBalance)` | `u64` (alpha amount recycled) | +| 19 | `add_stake_burn` | Atomically add stake then burn the resulting alpha | `(AccountId, NetUid, TaoBalance)` | `u64` (alpha amount burned) | Example usage in your ink! contract: ```rust @@ -85,6 +90,9 @@ Chain extension functions that modify state return error codes as `u32` values. | 17 | `ProxyDuplicate` | Proxy already exists | | 18 | `ProxyNoSelfProxy` | Cannot add self as proxy | | 19 | `ProxyNotFound` | Proxy relationship not found | +| 20 | `CannotUseSystemAccount` | A system account cannot be used in this operation | +| 21 | `CannotBurnOrRecycleOnRootSubnet` | Cannot burn or recycle on the root subnet | +| 22 | `SubtokenDisabled` | Subtoken is not enabled for the specified subnet | ### Call Filter diff --git a/eco-tests/Cargo.toml b/eco-tests/Cargo.toml index 8884810fd6..f93c81386a 100644 --- a/eco-tests/Cargo.toml +++ b/eco-tests/Cargo.toml @@ -19,6 +19,7 @@ useless_conversion = "allow" [dependencies] pallet-subtensor = { path = "../pallets/subtensor", default-features = false, features = ["std"] } +pallet-alpha-assets = { path = "../pallets/alpha-assets", default-features = false, features = ["std"] } frame-support = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "7cc54bf2d50ae3921d718736dfeb0de9468539c7", default-features = false, features = ["std"] } frame-system = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "7cc54bf2d50ae3921d718736dfeb0de9468539c7", default-features = false, features = ["std"] } sp-core = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "7cc54bf2d50ae3921d718736dfeb0de9468539c7", default-features = false, features = ["std"] } diff --git a/eco-tests/src/helpers.rs b/eco-tests/src/helpers.rs index 5908590115..c6fa0ec72d 100644 --- a/eco-tests/src/helpers.rs +++ b/eco-tests/src/helpers.rs @@ -186,7 +186,7 @@ pub fn add_network_disable_subtoken(netuid: NetUid, tempo: u16, _modality: u16) 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()); + add_balance_to_coldkey_account(coldkey, lock_cost.into()); TotalIssuance::::mutate(|total_issuance| { *total_issuance = total_issuance.saturating_add(lock_cost); }); @@ -205,7 +205,7 @@ pub fn add_dynamic_network(hotkey: &U256, coldkey: &U256) -> NetUid { pub fn add_dynamic_network_without_emission_block(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()); + add_balance_to_coldkey_account(coldkey, lock_cost.into()); TotalIssuance::::mutate(|total_issuance| { *total_issuance = total_issuance.saturating_add(lock_cost); }); @@ -299,7 +299,7 @@ pub fn increase_stake_on_coldkey_hotkey_account( netuid: NetUid, ) { // Ensure the coldkey has enough balance - SubtensorModule::add_balance_to_coldkey_account(coldkey, tao_staked.into()); + add_balance_to_coldkey_account(coldkey, tao_staked.into()); assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(*coldkey), *hotkey, diff --git a/eco-tests/src/mock.rs b/eco-tests/src/mock.rs index 65cb6eac03..9ab48c12a7 100644 --- a/eco-tests/src/mock.rs +++ b/eco-tests/src/mock.rs @@ -45,6 +45,7 @@ frame_support::construct_runtime!( Swap: pallet_subtensor_swap = 9, Crowdloan: pallet_crowdloan = 10, Proxy: pallet_subtensor_proxy = 11, + AlphaAssets: pallet_alpha_assets = 12, } ); @@ -233,6 +234,8 @@ parameter_types! { pub const LeaseDividendsDistributionInterval: u32 = 100; pub const MaxImmuneUidsPercentage: Percent = Percent::from_percent(80); pub const EvmKeyAssociateRateLimit: u64 = 10; + pub const SubtensorPalletId: PalletId = PalletId(*b"subtensr"); + pub const BurnAccountId: PalletId = PalletId(*b"burntnsr"); } impl pallet_subtensor::Config for Test { @@ -308,7 +311,10 @@ impl pallet_subtensor::Config for Test { type CommitmentsInterface = CommitmentsI; type EvmKeyAssociateRateLimit = EvmKeyAssociateRateLimit; type AuthorshipProvider = MockAuthorshipProvider; + type SubtensorPalletId = SubtensorPalletId; + type BurnAccountId = BurnAccountId; type WeightInfo = (); + type AlphaAssets = AlphaAssets; } // Swap-related parameter types @@ -481,6 +487,8 @@ impl InstanceFilter for subtensor_runtime_common::ProxyType { } } +impl pallet_alpha_assets::Config for Test {} + mod test_crypto { use super::KEY_TYPE; use sp_core::{ @@ -590,3 +598,9 @@ pub fn init_logs_for_tests() { let _ = TEST_LOGS_INIT.set(()); } + +#[allow(dead_code)] +pub fn add_balance_to_coldkey_account(coldkey: &U256, tao: TaoBalance) { + let credit = SubtensorModule::mint_tao(tao); + let _ = SubtensorModule::spend_tao(coldkey, credit, tao).unwrap(); +} diff --git a/eco-tests/src/tests.rs b/eco-tests/src/tests.rs index 65a5a0cd0f..6c4dc4d10f 100644 --- a/eco-tests/src/tests.rs +++ b/eco-tests/src/tests.rs @@ -18,7 +18,7 @@ fn test_add_stake_ok_neuron_does_not_belong_to_coldkey() { let stake = DefaultMinStake::::get() * 10.into(); // Give it some $$$ in his coldkey balance - SubtensorModule::add_balance_to_coldkey_account(&other_cold_key, stake.into()); + add_balance_to_coldkey_account(&other_cold_key, stake.into()); // Perform the request which is signed by a different cold key assert_ok!(SubtensorModule::add_stake( diff --git a/node/src/cli.rs b/node/src/cli.rs index e7719b619c..a35ea86029 100644 --- a/node/src/cli.rs +++ b/node/src/cli.rs @@ -20,8 +20,8 @@ pub struct Cli { #[clap(flatten)] pub run: RunCmd, - /// Choose sealing method. - #[arg(long, value_enum, ignore_case = true)] + /// Choose sealing method: manual, instant, or interval=. + #[arg(long)] pub sealing: Option, /// Whether to try Aura or Babe consensus on first start. @@ -170,13 +170,32 @@ impl fmt::Display for HistoryBackfill { } /// Available Sealing methods. -#[derive(Copy, Clone, Debug, Default, clap::ValueEnum)] +#[derive(Copy, Clone, Debug, Default)] pub enum Sealing { /// Seal using rpc method. #[default] Manual, /// Seal when transaction is executed. Instant, + /// Seal on a fixed timer interval. Value is the period in milliseconds. + Interval(u64), +} + +impl std::str::FromStr for Sealing { + type Err = String; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "manual" => Ok(Sealing::Manual), + "instant" => Ok(Sealing::Instant), + s => s + .parse::() + .map(Sealing::Interval) + .map_err(|_| format!( + "unknown sealing mode '{s}': expected 'manual', 'instant', or a number of milliseconds" + )), + } + } } /// Supported consensus mechanisms. diff --git a/node/src/service.rs b/node/src/service.rs index 067fc3ff91..d07671f81f 100644 --- a/node/src/service.rs +++ b/node/src/service.rs @@ -1,7 +1,7 @@ //! Service and ServiceFactory implementation. Specialized wrapper over substrate service. use crate::consensus::ConsensusMechanism; -use futures::{FutureExt, channel::mpsc, future}; +use futures::{FutureExt, StreamExt as _, channel::mpsc}; use node_subtensor_runtime::{RuntimeApi, TransactionConverter, opaque::Block}; use sc_chain_spec::ChainType; use sc_client_api::{Backend as BackendT, BlockBackend}; @@ -16,6 +16,7 @@ use sc_service::{Configuration, PartialComponents, TaskManager, error::Error as use sc_telemetry::{Telemetry, TelemetryHandle, TelemetryWorker, log}; use sc_transaction_pool::TransactionPoolHandle; use sc_transaction_pool_api::OffchainTransactionPoolFactory; +use sc_transaction_pool_api::TransactionPool as _; use sp_core::H256; use sp_core::crypto::KeyTypeId; use sp_keystore::Keystore; @@ -719,6 +720,14 @@ pub fn new_chain_ops( Ok((client, backend, import_queue, task_manager, other.3)) } +type SealStream = std::pin::Pin< + Box< + dyn futures::Stream< + Item = sc_consensus_manual_seal::rpc::EngineCommand<::Hash>, + > + Send, + >, +>; + #[allow(clippy::too_many_arguments)] fn run_manual_seal_authorship( sealing: Sealing, @@ -778,32 +787,47 @@ fn run_manual_seal_authorship( let aura_data_provider = sc_consensus_manual_seal::consensus::aura::AuraConsensusDataProvider::new(client.clone()); - let manual_seal = match sealing { - Sealing::Manual => future::Either::Left(sc_consensus_manual_seal::run_manual_seal( - sc_consensus_manual_seal::ManualSealParams { - block_import, - env: proposer_factory, - client, - pool: transaction_pool, - commands_stream, - select_chain, - consensus_data_provider: Some(Box::new(aura_data_provider)), - create_inherent_data_providers, - }, - )), - Sealing::Instant => future::Either::Right(sc_consensus_manual_seal::run_instant_seal( - sc_consensus_manual_seal::InstantSealParams { - block_import, - env: proposer_factory, - client, - pool: transaction_pool, - select_chain, - consensus_data_provider: None, - create_inherent_data_providers, - }, - )), + let seal_stream: SealStream = match sealing { + Sealing::Manual => Box::pin(commands_stream), + Sealing::Instant => Box::pin(transaction_pool.import_notification_stream().map(|_| { + sc_consensus_manual_seal::rpc::EngineCommand::SealNewBlock { + create_empty: false, + finalize: false, + parent_hash: None, + sender: None, + } + })), + Sealing::Interval(millis) => Box::pin( + futures::stream::unfold( + tokio::time::interval(std::time::Duration::from_millis(millis)), + |mut interval| async move { + interval.tick().await; + Some(((), interval)) + }, + ) + .map( + |_| sc_consensus_manual_seal::rpc::EngineCommand::SealNewBlock { + create_empty: true, + finalize: true, + parent_hash: None, + sender: None, + }, + ), + ), }; + let manual_seal = + sc_consensus_manual_seal::run_manual_seal(sc_consensus_manual_seal::ManualSealParams { + block_import, + env: proposer_factory, + client, + pool: transaction_pool, + commands_stream: seal_stream, + select_chain, + consensus_data_provider: Some(Box::new(aura_data_provider)), + create_inherent_data_providers, + }); + // we spawn the future on a background thread managed by service. task_manager .spawn_essential_handle() diff --git a/pallets/admin-utils/Cargo.toml b/pallets/admin-utils/Cargo.toml index a97ef6fabc..85236a425a 100644 --- a/pallets/admin-utils/Cargo.toml +++ b/pallets/admin-utils/Cargo.toml @@ -38,6 +38,7 @@ sp-core.workspace = true sp-io.workspace = true sp-tracing.workspace = true sp-consensus-aura.workspace = true +pallet-alpha-assets.workspace = true pallet-balances = { workspace = true, features = ["std"] } pallet-scheduler.workspace = true pallet-grandpa.workspace = true @@ -54,6 +55,7 @@ std = [ "frame-support/std", "frame-system/std", "log/std", + "pallet-alpha-assets/std", "pallet-balances/std", "pallet-drand/std", "pallet-evm-chain-id/std", diff --git a/pallets/admin-utils/src/benchmarking.rs b/pallets/admin-utils/src/benchmarking.rs index 1a16c1f721..cfa72a9b6c 100644 --- a/pallets/admin-utils/src/benchmarking.rs +++ b/pallets/admin-utils/src/benchmarking.rs @@ -9,7 +9,8 @@ use alloc::vec::Vec; use crate::Pallet as AdminUtils; use frame_benchmarking::v1::account; use frame_benchmarking::v2::*; -use frame_support::BoundedVec; +use frame_support::dispatch::UnfilteredDispatchable; +use frame_support::{BoundedVec, assert_noop}; use frame_system::RawOrigin; use pallet_subtensor::SubnetworkN; @@ -417,8 +418,17 @@ mod benchmarks { #[benchmark] fn sudo_set_total_issuance() { - #[extrinsic_call] - _(RawOrigin::Root, 100u64.into()); + let call = Call::::sudo_set_total_issuance { + total_issuance: 100u64.into(), + }; + + #[block] + { + assert_noop!( + call.dispatch_bypass_filter(RawOrigin::Root.into()), + Error::::Deprecated + ); + } } #[benchmark] diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index d1bce9453c..4688b1f22f 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -141,6 +141,8 @@ pub mod pallet { NotPermittedOnRootSubnet, /// POW Registration has been deprecated POWRegistrationDisabled, + /// Call is deprecated + Deprecated, } /// Enum for specifying the type of precompile operation. #[derive( @@ -978,20 +980,14 @@ pub mod pallet { Ok(()) } - /// The extrinsic sets the total issuance for the network. - /// It is only callable by the root account. - /// The extrinsic will call the Subtensor pallet to set the issuance for the network. + /// DEPRECATED #[pallet::call_index(33)] #[pallet::weight(::WeightInfo::sudo_set_total_issuance())] pub fn sudo_set_total_issuance( - origin: OriginFor, - total_issuance: TaoBalance, + _origin: OriginFor, + _total_issuance: TaoBalance, ) -> DispatchResult { - ensure_root(origin)?; - - pallet_subtensor::Pallet::::set_total_issuance(total_issuance); - - Ok(()) + Err(Error::::Deprecated.into()) } /// The extrinsic sets the immunity period for the network. @@ -1967,6 +1963,23 @@ pub mod pallet { Ok(()) } + /// Enables or disables net TAO flow (protocol cost deduction from emission shares). + /// When enabled, emission shares use net flow = user flow - protocol cost. + /// When disabled, emission shares use gross user flow only (current behavior). + #[pallet::call_index(91)] + #[pallet::weight(Weight::from_parts(7_343_000, 0) + .saturating_add(::DbWeight::get().reads(0)) + .saturating_add(::DbWeight::get().writes(1)))] + pub fn sudo_set_net_tao_flow_enabled( + origin: OriginFor, + enabled: bool, + ) -> DispatchResult { + ensure_root(origin)?; + pallet_subtensor::Pallet::::set_net_tao_flow_enabled(enabled); + log::debug!("set_net_tao_flow_enabled( {enabled:?} ) "); + Ok(()) + } + /// Sets the global maximum number of mechanisms in a subnet #[pallet::call_index(88)] #[pallet::weight(Weight::from_parts(15_000_000, 0) diff --git a/pallets/admin-utils/src/tests/mock.rs b/pallets/admin-utils/src/tests/mock.rs index 2596d80069..9faf870cbe 100644 --- a/pallets/admin-utils/src/tests/mock.rs +++ b/pallets/admin-utils/src/tests/mock.rs @@ -29,6 +29,7 @@ frame_support::construct_runtime!( System: frame_system = 1, Balances: pallet_balances = 2, AdminUtils: crate = 3, + AlphaAssets: pallet_alpha_assets = 12, SubtensorModule: pallet_subtensor::{Pallet, Call, Storage, Event, Error} = 4, Scheduler: pallet_scheduler::{Pallet, Call, Storage, Event} = 5, Drand: pallet_drand::{Pallet, Call, Storage, Event} = 6, @@ -157,6 +158,8 @@ parameter_types! { pub const LeaseDividendsDistributionInterval: u32 = 100; // 100 blocks pub const MaxImmuneUidsPercentage: Percent = Percent::from_percent(80); pub const EvmKeyAssociateRateLimit: u64 = 0; + pub const SubtensorPalletId: PalletId = PalletId(*b"subtensr"); + pub const BurnAccountId: PalletId = PalletId(*b"burntnsr"); } impl pallet_subtensor::Config for Test { @@ -216,6 +219,7 @@ impl pallet_subtensor::Config for Test { type LiquidAlphaOn = InitialLiquidAlphaOn; type Yuma3On = InitialYuma3On; type Preimages = (); + type AlphaAssets = AlphaAssets; type InitialColdkeySwapAnnouncementDelay = InitialColdkeySwapAnnouncementDelay; type InitialColdkeySwapReannouncementDelay = InitialColdkeySwapReannouncementDelay; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; @@ -232,6 +236,8 @@ impl pallet_subtensor::Config for Test { type CommitmentsInterface = CommitmentsI; type EvmKeyAssociateRateLimit = EvmKeyAssociateRateLimit; type AuthorshipProvider = MockAuthorshipProvider; + type SubtensorPalletId = SubtensorPalletId; + type BurnAccountId = BurnAccountId; type WeightInfo = (); } @@ -329,6 +335,8 @@ impl pallet_balances::Config for Test { type RuntimeHoldReason = (); } +impl pallet_alpha_assets::Config for Test {} + // Swap-related parameter types parameter_types! { pub const SwapProtocolId: PalletId = PalletId(*b"ten/swap"); @@ -529,10 +537,7 @@ pub fn register_ok_neuron( let bal = SubtensorModule::get_coldkey_balance(&coldkey_account_id); if bal < burn_u64 { - SubtensorModule::add_balance_to_coldkey_account( - &coldkey_account_id, - burn_u64 - bal + 10.into(), - ); + add_balance_to_coldkey_account(&coldkey_account_id, burn_u64 - bal + 10.into()); } let result = SubtensorModule::burned_register( @@ -570,3 +575,14 @@ pub fn step_block(n: u64) { let current: u64 = frame_system::Pallet::::block_number().into(); run_to_block(current + n); } + +#[allow(dead_code)] +pub fn add_balance_to_coldkey_account(coldkey: &U256, tao: TaoBalance) { + let credit = SubtensorModule::mint_tao(tao); + let _ = SubtensorModule::spend_tao(coldkey, credit, tao).unwrap(); +} + +#[allow(dead_code)] +pub fn remove_balance_from_coldkey_account(coldkey: &U256, tao: TaoBalance) { + let _ = SubtensorModule::burn_tao(coldkey, tao); +} diff --git a/pallets/admin-utils/src/tests/mod.rs b/pallets/admin-utils/src/tests/mod.rs index 42ad19efd5..c94e1e96e8 100644 --- a/pallets/admin-utils/src/tests/mod.rs +++ b/pallets/admin-utils/src/tests/mod.rs @@ -387,25 +387,6 @@ fn test_sudo_subnet_owner_cut() { }); } -#[test] -fn test_sudo_set_issuance() { - new_test_ext().execute_with(|| { - let to_be_set = TaoBalance::from(10); - assert_eq!( - AdminUtils::sudo_set_total_issuance( - <::RuntimeOrigin>::signed(U256::from(0)), - to_be_set - ), - Err(DispatchError::BadOrigin) - ); - assert_ok!(AdminUtils::sudo_set_total_issuance( - <::RuntimeOrigin>::root(), - to_be_set - )); - assert_eq!(SubtensorModule::get_total_issuance(), to_be_set); - }); -} - #[test] fn test_sudo_set_immunity_period() { new_test_ext().execute_with(|| { @@ -1260,7 +1241,7 @@ fn test_sudo_get_set_alpha() { pallet_subtensor::migrations::migrate_create_root_network::migrate_create_root_network::< Test, >(); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_000_u64.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_000_u64.into()); assert_ok!(SubtensorModule::root_register(signer.clone(), hotkey,)); // Should fail as signer does not own the subnet diff --git a/pallets/admin-utils/src/weights.rs b/pallets/admin-utils/src/weights.rs index 499e81fc51..e01e97237b 100644 --- a/pallets/admin-utils/src/weights.rs +++ b/pallets/admin-utils/src/weights.rs @@ -2,9 +2,9 @@ //! Autogenerated weights for `pallet_admin_utils` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 49.1.0 -//! DATE: 2026-04-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2026-05-04, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runnervmrg6be`, CPU: `AMD EPYC 7763 64-Core Processor` +//! HOSTNAME: `runnervmeorf1`, CPU: `AMD EPYC 7763 64-Core Processor` //! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` // Executed Command: @@ -22,7 +22,7 @@ // --no-storage-info // --no-min-squares // --no-median-slopes -// --output=/tmp/tmp.m2HtKiBFjt +// --output=/tmp/tmp.bzwIs210x3 // --template=/home/runner/work/subtensor/subtensor/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -103,10 +103,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_948_000 picoseconds. - Weight::from_parts(4_548_142, 0) - // Standard Error: 748 - .saturating_add(Weight::from_parts(27_191, 0).saturating_mul(a.into())) + // Minimum execution time: 4_107_000 picoseconds. + Weight::from_parts(4_669_529, 0) + // Standard Error: 694 + .saturating_add(Weight::from_parts(28_939, 0).saturating_mul(a.into())) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Grandpa::PendingChange` (r:1 w:1) @@ -116,10 +116,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `174` // Estimated: `2779` - // Minimum execution time: 7_224_000 picoseconds. - Weight::from_parts(7_810_888, 2779) - // Standard Error: 825 - .saturating_add(Weight::from_parts(19_930, 0).saturating_mul(a.into())) + // Minimum execution time: 7_354_000 picoseconds. + Weight::from_parts(7_977_975, 2779) + // Standard Error: 897 + .saturating_add(Weight::from_parts(19_089, 0).saturating_mul(a.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -129,8 +129,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_300_000 picoseconds. - Weight::from_parts(5_661_000, 0) + // Minimum execution time: 5_460_000 picoseconds. + Weight::from_parts(5_660_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) @@ -143,8 +143,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `627` // Estimated: `4092` - // Minimum execution time: 20_689_000 picoseconds. - Weight::from_parts(21_229_000, 4092) + // Minimum execution time: 21_369_000 picoseconds. + Weight::from_parts(21_761_000, 4092) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -160,8 +160,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `760` // Estimated: `4225` - // Minimum execution time: 26_169_000 picoseconds. - Weight::from_parts(26_830_000, 4225) + // Minimum execution time: 26_941_000 picoseconds. + Weight::from_parts(27_481_000, 4225) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -177,8 +177,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `760` // Estimated: `4225` - // Minimum execution time: 25_989_000 picoseconds. - Weight::from_parts(26_741_000, 4225) + // Minimum execution time: 26_770_000 picoseconds. + Weight::from_parts(28_022_000, 4225) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -190,8 +190,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `609` // Estimated: `4074` - // Minimum execution time: 16_271_000 picoseconds. - Weight::from_parts(16_902_000, 4074) + // Minimum execution time: 16_942_000 picoseconds. + Weight::from_parts(17_423_000, 4074) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -207,8 +207,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `760` // Estimated: `4225` - // Minimum execution time: 26_099_000 picoseconds. - Weight::from_parts(26_910_000, 4225) + // Minimum execution time: 27_061_000 picoseconds. + Weight::from_parts(27_863_000, 4225) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -224,8 +224,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `760` // Estimated: `4225` - // Minimum execution time: 25_918_000 picoseconds. - Weight::from_parts(26_910_000, 4225) + // Minimum execution time: 26_761_000 picoseconds. + Weight::from_parts(27_592_000, 4225) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -241,8 +241,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `760` // Estimated: `4225` - // Minimum execution time: 26_169_000 picoseconds. - Weight::from_parts(26_971_000, 4225) + // Minimum execution time: 26_731_000 picoseconds. + Weight::from_parts(27_391_000, 4225) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -260,8 +260,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `760` // Estimated: `4225` - // Minimum execution time: 27_702_000 picoseconds. - Weight::from_parts(28_414_000, 4225) + // Minimum execution time: 28_133_000 picoseconds. + Weight::from_parts(29_195_000, 4225) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -277,8 +277,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `760` // Estimated: `4225` - // Minimum execution time: 25_698_000 picoseconds. - Weight::from_parts(26_760_000, 4225) + // Minimum execution time: 26_921_000 picoseconds. + Weight::from_parts(27_752_000, 4225) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -290,8 +290,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `609` // Estimated: `4074` - // Minimum execution time: 16_090_000 picoseconds. - Weight::from_parts(16_641_000, 4074) + // Minimum execution time: 16_871_000 picoseconds. + Weight::from_parts(17_273_000, 4074) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -307,8 +307,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `760` // Estimated: `4225` - // Minimum execution time: 25_999_000 picoseconds. - Weight::from_parts(26_801_000, 4225) + // Minimum execution time: 26_489_000 picoseconds. + Weight::from_parts(27_502_000, 4225) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -326,8 +326,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `822` // Estimated: `4287` - // Minimum execution time: 28_313_000 picoseconds. - Weight::from_parts(29_455_000, 4287) + // Minimum execution time: 29_044_000 picoseconds. + Weight::from_parts(29_806_000, 4287) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -343,8 +343,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `760` // Estimated: `4225` - // Minimum execution time: 23_273_000 picoseconds. - Weight::from_parts(24_045_000, 4225) + // Minimum execution time: 24_015_000 picoseconds. + Weight::from_parts(24_687_000, 4225) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -356,8 +356,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `609` // Estimated: `4074` - // Minimum execution time: 15_980_000 picoseconds. - Weight::from_parts(16_521_000, 4074) + // Minimum execution time: 16_671_000 picoseconds. + Weight::from_parts(17_192_000, 4074) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -377,8 +377,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `760` // Estimated: `4225` - // Minimum execution time: 29_235_000 picoseconds. - Weight::from_parts(30_317_000, 4225) + // Minimum execution time: 30_287_000 picoseconds. + Weight::from_parts(30_909_000, 4225) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -400,8 +400,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `795` // Estimated: `4260` - // Minimum execution time: 32_470_000 picoseconds. - Weight::from_parts(33_493_000, 4260) + // Minimum execution time: 33_323_000 picoseconds. + Weight::from_parts(34_174_000, 4260) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -417,8 +417,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `760` // Estimated: `4225` - // Minimum execution time: 25_999_000 picoseconds. - Weight::from_parts(27_291_000, 4225) + // Minimum execution time: 26_830_000 picoseconds. + Weight::from_parts(27_822_000, 4225) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -434,8 +434,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `760` // Estimated: `4225` - // Minimum execution time: 26_059_000 picoseconds. - Weight::from_parts(26_770_000, 4225) + // Minimum execution time: 26_620_000 picoseconds. + Weight::from_parts(27_331_000, 4225) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -451,8 +451,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `760` // Estimated: `4225` - // Minimum execution time: 26_029_000 picoseconds. - Weight::from_parts(26_680_000, 4225) + // Minimum execution time: 26_721_000 picoseconds. + Weight::from_parts(27_672_000, 4225) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -470,8 +470,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `787` // Estimated: `4252` - // Minimum execution time: 29_015_000 picoseconds. - Weight::from_parts(29_836_000, 4252) + // Minimum execution time: 30_026_000 picoseconds. + Weight::from_parts(30_938_000, 4252) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -489,8 +489,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `762` // Estimated: `4227` - // Minimum execution time: 28_925_000 picoseconds. - Weight::from_parts(29_665_000, 4227) + // Minimum execution time: 29_987_000 picoseconds. + Weight::from_parts(31_009_000, 4227) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -500,8 +500,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_352_000 picoseconds. - Weight::from_parts(6_763_000, 0) + // Minimum execution time: 6_712_000 picoseconds. + Weight::from_parts(7_083_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:1) @@ -514,8 +514,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `760` // Estimated: `4225` - // Minimum execution time: 25_728_000 picoseconds. - Weight::from_parts(26_550_000, 4225) + // Minimum execution time: 26_389_000 picoseconds. + Weight::from_parts(27_351_000, 4225) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -531,8 +531,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `760` // Estimated: `4225` - // Minimum execution time: 26_149_000 picoseconds. - Weight::from_parts(27_061_000, 4225) + // Minimum execution time: 27_111_000 picoseconds. + Weight::from_parts(27_852_000, 4225) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -548,8 +548,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `760` // Estimated: `4225` - // Minimum execution time: 26_340_000 picoseconds. - Weight::from_parts(26_920_000, 4225) + // Minimum execution time: 26_490_000 picoseconds. + Weight::from_parts(27_582_000, 4225) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -559,8 +559,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_631_000 picoseconds. - Weight::from_parts(5_961_000, 0) + // Minimum execution time: 6_071_000 picoseconds. + Weight::from_parts(6_272_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::TxRateLimit` (r:0 w:1) @@ -569,19 +569,16 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_129_000 picoseconds. - Weight::from_parts(5_460_000, 0) + // Minimum execution time: 5_330_000 picoseconds. + Weight::from_parts(5_651_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: `SubtensorModule::TotalIssuance` (r:0 w:1) - /// Proof: `SubtensorModule::TotalIssuance` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn sudo_set_total_issuance() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_575_000 picoseconds. - Weight::from_parts(2_775_000, 0) - .saturating_add(T::DbWeight::get().writes(1_u64)) + // Minimum execution time: 5_841_000 picoseconds. + Weight::from_parts(6_101_000, 0) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -591,8 +588,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `609` // Estimated: `4074` - // Minimum execution time: 16_020_000 picoseconds. - Weight::from_parts(16_912_000, 4074) + // Minimum execution time: 16_622_000 picoseconds. + Weight::from_parts(17_112_000, 4074) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -602,8 +599,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_240_000 picoseconds. - Weight::from_parts(5_611_000, 0) + // Minimum execution time: 5_490_000 picoseconds. + Weight::from_parts(5_721_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::NominatorMinRequiredStake` (r:1 w:1) @@ -616,10 +613,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_nominator_min_required_stake() -> Weight { // Proof Size summary in bytes: - // Measured: `935` - // Estimated: `6875` - // Minimum execution time: 28_353_000 picoseconds. - Weight::from_parts(29_555_000, 6875) + // Measured: `912` + // Estimated: `6852` + // Minimum execution time: 28_945_000 picoseconds. + Weight::from_parts(29_506_000, 6852) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -629,8 +626,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_260_000 picoseconds. - Weight::from_parts(5_600_000, 0) + // Minimum execution time: 5_360_000 picoseconds. + Weight::from_parts(5_631_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::MinDelegateTake` (r:0 w:1) @@ -639,8 +636,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_279_000 picoseconds. - Weight::from_parts(5_501_000, 0) + // Minimum execution time: 5_420_000 picoseconds. + Weight::from_parts(5_661_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) @@ -653,8 +650,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `657` // Estimated: `4122` - // Minimum execution time: 17_803_000 picoseconds. - Weight::from_parts(18_775_000, 4122) + // Minimum execution time: 18_154_000 picoseconds. + Weight::from_parts(18_965_000, 4122) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -670,8 +667,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `804` // Estimated: `4269` - // Minimum execution time: 25_789_000 picoseconds. - Weight::from_parts(27_011_000, 4269) + // Minimum execution time: 26_549_000 picoseconds. + Weight::from_parts(27_282_000, 4269) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -681,8 +678,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_289_000 picoseconds. - Weight::from_parts(5_691_000, 0) + // Minimum execution time: 5_470_000 picoseconds. + Weight::from_parts(5_761_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::ColdkeySwapReannouncementDelay` (r:0 w:1) @@ -691,8 +688,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_140_000 picoseconds. - Weight::from_parts(5_450_000, 0) + // Minimum execution time: 5_320_000 picoseconds. + Weight::from_parts(5_571_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::DissolveNetworkScheduleDuration` (r:0 w:1) @@ -701,8 +698,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_200_000 picoseconds. - Weight::from_parts(5_561_000, 0) + // Minimum execution time: 5_340_000 picoseconds. + Weight::from_parts(5_651_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) @@ -715,8 +712,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `657` // Estimated: `4122` - // Minimum execution time: 19_937_000 picoseconds. - Weight::from_parts(20_770_000, 4122) + // Minimum execution time: 20_618_000 picoseconds. + Weight::from_parts(21_260_000, 4122) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -726,8 +723,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `42` // Estimated: `3507` - // Minimum execution time: 6_031_000 picoseconds. - Weight::from_parts(6_282_000, 3507) + // Minimum execution time: 6_071_000 picoseconds. + Weight::from_parts(6_342_000, 3507) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `SubtensorModule::SubnetMovingAlpha` (r:0 w:1) @@ -736,8 +733,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_825_000 picoseconds. - Weight::from_parts(2_996_000, 0) + // Minimum execution time: 2_765_000 picoseconds. + Weight::from_parts(2_945_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::EMAPriceHalvingBlocks` (r:0 w:1) @@ -746,8 +743,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_687_000 picoseconds. - Weight::from_parts(4_027_000, 0) + // Minimum execution time: 4_008_000 picoseconds. + Weight::from_parts(4_188_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) @@ -762,8 +759,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `760` // Estimated: `4225` - // Minimum execution time: 23_214_000 picoseconds. - Weight::from_parts(23_774_000, 4225) + // Minimum execution time: 23_985_000 picoseconds. + Weight::from_parts(24_706_000, 4225) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -777,8 +774,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `657` // Estimated: `4122` - // Minimum execution time: 20_609_000 picoseconds. - Weight::from_parts(21_140_000, 4122) + // Minimum execution time: 20_799_000 picoseconds. + Weight::from_parts(21_410_000, 4122) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -792,8 +789,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `657` // Estimated: `4122` - // Minimum execution time: 22_402_000 picoseconds. - Weight::from_parts(23_103_000, 4122) + // Minimum execution time: 22_873_000 picoseconds. + Weight::from_parts(23_705_000, 4122) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -807,8 +804,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `702` // Estimated: `4167` - // Minimum execution time: 21_560_000 picoseconds. - Weight::from_parts(22_221_000, 4167) + // Minimum execution time: 25_538_000 picoseconds. + Weight::from_parts(26_420_000, 4167) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -822,8 +819,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `657` // Estimated: `4122` - // Minimum execution time: 17_513_000 picoseconds. - Weight::from_parts(17_914_000, 4122) + // Minimum execution time: 17_713_000 picoseconds. + Weight::from_parts(18_284_000, 4122) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -834,7 +831,7 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 5_300_000 picoseconds. - Weight::from_parts(5_601_000, 0) + Weight::from_parts(5_611_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::OwnerHyperparamRateLimit` (r:0 w:1) @@ -843,8 +840,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_250_000 picoseconds. - Weight::from_parts(5_550_000, 0) + // Minimum execution time: 5_390_000 picoseconds. + Weight::from_parts(5_651_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) @@ -857,8 +854,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `657` // Estimated: `4122` - // Minimum execution time: 17_553_000 picoseconds. - Weight::from_parts(18_034_000, 4122) + // Minimum execution time: 17_874_000 picoseconds. + Weight::from_parts(18_214_000, 4122) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -878,8 +875,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `760` // Estimated: `4225` - // Minimum execution time: 27_832_000 picoseconds. - Weight::from_parts(28_463_000, 4225) + // Minimum execution time: 28_153_000 picoseconds. + Weight::from_parts(28_744_000, 4225) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -889,8 +886,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_532_000 picoseconds. - Weight::from_parts(7_073_000, 0) + // Minimum execution time: 6_803_000 picoseconds. + Weight::from_parts(7_233_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } } @@ -904,10 +901,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_948_000 picoseconds. - Weight::from_parts(4_548_142, 0) - // Standard Error: 748 - .saturating_add(Weight::from_parts(27_191, 0).saturating_mul(a.into())) + // Minimum execution time: 4_107_000 picoseconds. + Weight::from_parts(4_669_529, 0) + // Standard Error: 694 + .saturating_add(Weight::from_parts(28_939, 0).saturating_mul(a.into())) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Grandpa::PendingChange` (r:1 w:1) @@ -917,10 +914,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `174` // Estimated: `2779` - // Minimum execution time: 7_224_000 picoseconds. - Weight::from_parts(7_810_888, 2779) - // Standard Error: 825 - .saturating_add(Weight::from_parts(19_930, 0).saturating_mul(a.into())) + // Minimum execution time: 7_354_000 picoseconds. + Weight::from_parts(7_977_975, 2779) + // Standard Error: 897 + .saturating_add(Weight::from_parts(19_089, 0).saturating_mul(a.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -930,8 +927,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_300_000 picoseconds. - Weight::from_parts(5_661_000, 0) + // Minimum execution time: 5_460_000 picoseconds. + Weight::from_parts(5_660_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) @@ -944,8 +941,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `627` // Estimated: `4092` - // Minimum execution time: 20_689_000 picoseconds. - Weight::from_parts(21_229_000, 4092) + // Minimum execution time: 21_369_000 picoseconds. + Weight::from_parts(21_761_000, 4092) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -961,8 +958,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `760` // Estimated: `4225` - // Minimum execution time: 26_169_000 picoseconds. - Weight::from_parts(26_830_000, 4225) + // Minimum execution time: 26_941_000 picoseconds. + Weight::from_parts(27_481_000, 4225) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -978,8 +975,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `760` // Estimated: `4225` - // Minimum execution time: 25_989_000 picoseconds. - Weight::from_parts(26_741_000, 4225) + // Minimum execution time: 26_770_000 picoseconds. + Weight::from_parts(28_022_000, 4225) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -991,8 +988,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `609` // Estimated: `4074` - // Minimum execution time: 16_271_000 picoseconds. - Weight::from_parts(16_902_000, 4074) + // Minimum execution time: 16_942_000 picoseconds. + Weight::from_parts(17_423_000, 4074) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1008,8 +1005,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `760` // Estimated: `4225` - // Minimum execution time: 26_099_000 picoseconds. - Weight::from_parts(26_910_000, 4225) + // Minimum execution time: 27_061_000 picoseconds. + Weight::from_parts(27_863_000, 4225) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1025,8 +1022,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `760` // Estimated: `4225` - // Minimum execution time: 25_918_000 picoseconds. - Weight::from_parts(26_910_000, 4225) + // Minimum execution time: 26_761_000 picoseconds. + Weight::from_parts(27_592_000, 4225) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1042,8 +1039,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `760` // Estimated: `4225` - // Minimum execution time: 26_169_000 picoseconds. - Weight::from_parts(26_971_000, 4225) + // Minimum execution time: 26_731_000 picoseconds. + Weight::from_parts(27_391_000, 4225) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1061,8 +1058,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `760` // Estimated: `4225` - // Minimum execution time: 27_702_000 picoseconds. - Weight::from_parts(28_414_000, 4225) + // Minimum execution time: 28_133_000 picoseconds. + Weight::from_parts(29_195_000, 4225) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1078,8 +1075,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `760` // Estimated: `4225` - // Minimum execution time: 25_698_000 picoseconds. - Weight::from_parts(26_760_000, 4225) + // Minimum execution time: 26_921_000 picoseconds. + Weight::from_parts(27_752_000, 4225) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1091,8 +1088,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `609` // Estimated: `4074` - // Minimum execution time: 16_090_000 picoseconds. - Weight::from_parts(16_641_000, 4074) + // Minimum execution time: 16_871_000 picoseconds. + Weight::from_parts(17_273_000, 4074) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1108,8 +1105,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `760` // Estimated: `4225` - // Minimum execution time: 25_999_000 picoseconds. - Weight::from_parts(26_801_000, 4225) + // Minimum execution time: 26_489_000 picoseconds. + Weight::from_parts(27_502_000, 4225) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1127,8 +1124,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `822` // Estimated: `4287` - // Minimum execution time: 28_313_000 picoseconds. - Weight::from_parts(29_455_000, 4287) + // Minimum execution time: 29_044_000 picoseconds. + Weight::from_parts(29_806_000, 4287) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1144,8 +1141,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `760` // Estimated: `4225` - // Minimum execution time: 23_273_000 picoseconds. - Weight::from_parts(24_045_000, 4225) + // Minimum execution time: 24_015_000 picoseconds. + Weight::from_parts(24_687_000, 4225) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1157,8 +1154,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `609` // Estimated: `4074` - // Minimum execution time: 15_980_000 picoseconds. - Weight::from_parts(16_521_000, 4074) + // Minimum execution time: 16_671_000 picoseconds. + Weight::from_parts(17_192_000, 4074) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1178,8 +1175,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `760` // Estimated: `4225` - // Minimum execution time: 29_235_000 picoseconds. - Weight::from_parts(30_317_000, 4225) + // Minimum execution time: 30_287_000 picoseconds. + Weight::from_parts(30_909_000, 4225) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1201,8 +1198,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `795` // Estimated: `4260` - // Minimum execution time: 32_470_000 picoseconds. - Weight::from_parts(33_493_000, 4260) + // Minimum execution time: 33_323_000 picoseconds. + Weight::from_parts(34_174_000, 4260) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1218,8 +1215,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `760` // Estimated: `4225` - // Minimum execution time: 25_999_000 picoseconds. - Weight::from_parts(27_291_000, 4225) + // Minimum execution time: 26_830_000 picoseconds. + Weight::from_parts(27_822_000, 4225) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1235,8 +1232,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `760` // Estimated: `4225` - // Minimum execution time: 26_059_000 picoseconds. - Weight::from_parts(26_770_000, 4225) + // Minimum execution time: 26_620_000 picoseconds. + Weight::from_parts(27_331_000, 4225) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1252,8 +1249,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `760` // Estimated: `4225` - // Minimum execution time: 26_029_000 picoseconds. - Weight::from_parts(26_680_000, 4225) + // Minimum execution time: 26_721_000 picoseconds. + Weight::from_parts(27_672_000, 4225) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1271,8 +1268,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `787` // Estimated: `4252` - // Minimum execution time: 29_015_000 picoseconds. - Weight::from_parts(29_836_000, 4252) + // Minimum execution time: 30_026_000 picoseconds. + Weight::from_parts(30_938_000, 4252) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1290,8 +1287,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `762` // Estimated: `4227` - // Minimum execution time: 28_925_000 picoseconds. - Weight::from_parts(29_665_000, 4227) + // Minimum execution time: 29_987_000 picoseconds. + Weight::from_parts(31_009_000, 4227) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1301,8 +1298,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_352_000 picoseconds. - Weight::from_parts(6_763_000, 0) + // Minimum execution time: 6_712_000 picoseconds. + Weight::from_parts(7_083_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:1) @@ -1315,8 +1312,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `760` // Estimated: `4225` - // Minimum execution time: 25_728_000 picoseconds. - Weight::from_parts(26_550_000, 4225) + // Minimum execution time: 26_389_000 picoseconds. + Weight::from_parts(27_351_000, 4225) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1332,8 +1329,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `760` // Estimated: `4225` - // Minimum execution time: 26_149_000 picoseconds. - Weight::from_parts(27_061_000, 4225) + // Minimum execution time: 27_111_000 picoseconds. + Weight::from_parts(27_852_000, 4225) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1349,8 +1346,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `760` // Estimated: `4225` - // Minimum execution time: 26_340_000 picoseconds. - Weight::from_parts(26_920_000, 4225) + // Minimum execution time: 26_490_000 picoseconds. + Weight::from_parts(27_582_000, 4225) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1360,8 +1357,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_631_000 picoseconds. - Weight::from_parts(5_961_000, 0) + // Minimum execution time: 6_071_000 picoseconds. + Weight::from_parts(6_272_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::TxRateLimit` (r:0 w:1) @@ -1370,19 +1367,16 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_129_000 picoseconds. - Weight::from_parts(5_460_000, 0) + // Minimum execution time: 5_330_000 picoseconds. + Weight::from_parts(5_651_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: `SubtensorModule::TotalIssuance` (r:0 w:1) - /// Proof: `SubtensorModule::TotalIssuance` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn sudo_set_total_issuance() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_575_000 picoseconds. - Weight::from_parts(2_775_000, 0) - .saturating_add(RocksDbWeight::get().writes(1_u64)) + // Minimum execution time: 5_841_000 picoseconds. + Weight::from_parts(6_101_000, 0) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -1392,8 +1386,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `609` // Estimated: `4074` - // Minimum execution time: 16_020_000 picoseconds. - Weight::from_parts(16_912_000, 4074) + // Minimum execution time: 16_622_000 picoseconds. + Weight::from_parts(17_112_000, 4074) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1403,8 +1397,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_240_000 picoseconds. - Weight::from_parts(5_611_000, 0) + // Minimum execution time: 5_490_000 picoseconds. + Weight::from_parts(5_721_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::NominatorMinRequiredStake` (r:1 w:1) @@ -1417,10 +1411,10 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_nominator_min_required_stake() -> Weight { // Proof Size summary in bytes: - // Measured: `935` - // Estimated: `6875` - // Minimum execution time: 28_353_000 picoseconds. - Weight::from_parts(29_555_000, 6875) + // Measured: `912` + // Estimated: `6852` + // Minimum execution time: 28_945_000 picoseconds. + Weight::from_parts(29_506_000, 6852) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1430,8 +1424,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_260_000 picoseconds. - Weight::from_parts(5_600_000, 0) + // Minimum execution time: 5_360_000 picoseconds. + Weight::from_parts(5_631_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::MinDelegateTake` (r:0 w:1) @@ -1440,8 +1434,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_279_000 picoseconds. - Weight::from_parts(5_501_000, 0) + // Minimum execution time: 5_420_000 picoseconds. + Weight::from_parts(5_661_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) @@ -1454,8 +1448,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `657` // Estimated: `4122` - // Minimum execution time: 17_803_000 picoseconds. - Weight::from_parts(18_775_000, 4122) + // Minimum execution time: 18_154_000 picoseconds. + Weight::from_parts(18_965_000, 4122) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1471,8 +1465,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `804` // Estimated: `4269` - // Minimum execution time: 25_789_000 picoseconds. - Weight::from_parts(27_011_000, 4269) + // Minimum execution time: 26_549_000 picoseconds. + Weight::from_parts(27_282_000, 4269) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1482,8 +1476,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_289_000 picoseconds. - Weight::from_parts(5_691_000, 0) + // Minimum execution time: 5_470_000 picoseconds. + Weight::from_parts(5_761_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::ColdkeySwapReannouncementDelay` (r:0 w:1) @@ -1492,8 +1486,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_140_000 picoseconds. - Weight::from_parts(5_450_000, 0) + // Minimum execution time: 5_320_000 picoseconds. + Weight::from_parts(5_571_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::DissolveNetworkScheduleDuration` (r:0 w:1) @@ -1502,8 +1496,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_200_000 picoseconds. - Weight::from_parts(5_561_000, 0) + // Minimum execution time: 5_340_000 picoseconds. + Weight::from_parts(5_651_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) @@ -1516,8 +1510,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `657` // Estimated: `4122` - // Minimum execution time: 19_937_000 picoseconds. - Weight::from_parts(20_770_000, 4122) + // Minimum execution time: 20_618_000 picoseconds. + Weight::from_parts(21_260_000, 4122) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1527,8 +1521,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `42` // Estimated: `3507` - // Minimum execution time: 6_031_000 picoseconds. - Weight::from_parts(6_282_000, 3507) + // Minimum execution time: 6_071_000 picoseconds. + Weight::from_parts(6_342_000, 3507) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: `SubtensorModule::SubnetMovingAlpha` (r:0 w:1) @@ -1537,8 +1531,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_825_000 picoseconds. - Weight::from_parts(2_996_000, 0) + // Minimum execution time: 2_765_000 picoseconds. + Weight::from_parts(2_945_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::EMAPriceHalvingBlocks` (r:0 w:1) @@ -1547,8 +1541,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_687_000 picoseconds. - Weight::from_parts(4_027_000, 0) + // Minimum execution time: 4_008_000 picoseconds. + Weight::from_parts(4_188_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) @@ -1563,8 +1557,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `760` // Estimated: `4225` - // Minimum execution time: 23_214_000 picoseconds. - Weight::from_parts(23_774_000, 4225) + // Minimum execution time: 23_985_000 picoseconds. + Weight::from_parts(24_706_000, 4225) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1578,8 +1572,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `657` // Estimated: `4122` - // Minimum execution time: 20_609_000 picoseconds. - Weight::from_parts(21_140_000, 4122) + // Minimum execution time: 20_799_000 picoseconds. + Weight::from_parts(21_410_000, 4122) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1593,8 +1587,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `657` // Estimated: `4122` - // Minimum execution time: 22_402_000 picoseconds. - Weight::from_parts(23_103_000, 4122) + // Minimum execution time: 22_873_000 picoseconds. + Weight::from_parts(23_705_000, 4122) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1608,8 +1602,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `702` // Estimated: `4167` - // Minimum execution time: 21_560_000 picoseconds. - Weight::from_parts(22_221_000, 4167) + // Minimum execution time: 25_538_000 picoseconds. + Weight::from_parts(26_420_000, 4167) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1623,8 +1617,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `657` // Estimated: `4122` - // Minimum execution time: 17_513_000 picoseconds. - Weight::from_parts(17_914_000, 4122) + // Minimum execution time: 17_713_000 picoseconds. + Weight::from_parts(18_284_000, 4122) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1635,7 +1629,7 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 5_300_000 picoseconds. - Weight::from_parts(5_601_000, 0) + Weight::from_parts(5_611_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::OwnerHyperparamRateLimit` (r:0 w:1) @@ -1644,8 +1638,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_250_000 picoseconds. - Weight::from_parts(5_550_000, 0) + // Minimum execution time: 5_390_000 picoseconds. + Weight::from_parts(5_651_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) @@ -1658,8 +1652,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `657` // Estimated: `4122` - // Minimum execution time: 17_553_000 picoseconds. - Weight::from_parts(18_034_000, 4122) + // Minimum execution time: 17_874_000 picoseconds. + Weight::from_parts(18_214_000, 4122) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1679,8 +1673,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `760` // Estimated: `4225` - // Minimum execution time: 27_832_000 picoseconds. - Weight::from_parts(28_463_000, 4225) + // Minimum execution time: 28_153_000 picoseconds. + Weight::from_parts(28_744_000, 4225) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1690,8 +1684,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_532_000 picoseconds. - Weight::from_parts(7_073_000, 0) + // Minimum execution time: 6_803_000 picoseconds. + Weight::from_parts(7_233_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } } diff --git a/pallets/alpha-assets/Cargo.toml b/pallets/alpha-assets/Cargo.toml new file mode 100644 index 0000000000..d71fc2b744 --- /dev/null +++ b/pallets/alpha-assets/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "pallet-alpha-assets" +version = "0.1.0" +edition.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +codec = { workspace = true, features = ["derive"] } +frame-support.workspace = true +frame-system.workspace = true +log.workspace = true +scale-info = { workspace = true, features = ["derive"] } +sp-runtime.workspace = true +subtensor-macros.workspace = true +subtensor-runtime-common.workspace = true + +[dev-dependencies] +sp-core.workspace = true +sp-io.workspace = true + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-support/std", + "frame-system/std", + "log/std", + "scale-info/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "subtensor-runtime-common/std", +] diff --git a/pallets/alpha-assets/src/lib.rs b/pallets/alpha-assets/src/lib.rs new file mode 100644 index 0000000000..6e856975aa --- /dev/null +++ b/pallets/alpha-assets/src/lib.rs @@ -0,0 +1,353 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; + +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::pallet_prelude::*; +use frame_support::traits::{Imbalance, SameOrOther, TryDrop, tokens::imbalance::TryMerge}; +use scale_info::TypeInfo; +use sp_runtime::traits::Zero; +use subtensor_macros::freeze_struct; +use subtensor_runtime_common::{AlphaBalance, NetUid, Token}; + +pub use pallet::*; + +/// Lightweight mint record that can later be resolved to a subnet or user alpha balance. +#[freeze_struct("2da64a64e80a7880")] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)] +pub struct PositiveAlphaImbalance { + netuid: NetUid, + amount: AlphaBalance, +} + +#[freeze_struct("1f16c8937e05cf36")] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)] +pub struct NegativeAlphaImbalance { + netuid: NetUid, + amount: AlphaBalance, +} + +impl PositiveAlphaImbalance { + pub fn new(netuid: NetUid, amount: AlphaBalance) -> Self { + Self { netuid, amount } + } + + pub fn netuid(&self) -> NetUid { + self.netuid + } + + pub fn amount(&self) -> AlphaBalance { + self.amount + } +} + +impl NegativeAlphaImbalance { + pub fn new(netuid: NetUid, amount: AlphaBalance) -> Self { + Self { netuid, amount } + } +} + +fn log_netuid_mismatch(context: &'static str, left: NetUid, right: NetUid) { + log::error!( + target: "runtime::alpha-assets", + "{context}: attempted to combine alpha imbalances from different netuids: left={left}, right={right}" + ); +} + +impl TryDrop for PositiveAlphaImbalance { + fn try_drop(self) -> Result<(), Self> { + if self.amount.is_zero() { + Ok(()) + } else { + Err(self) + } + } +} + +impl TryDrop for NegativeAlphaImbalance { + fn try_drop(self) -> Result<(), Self> { + if self.amount.is_zero() { + Ok(()) + } else { + Err(self) + } + } +} + +impl TryMerge for PositiveAlphaImbalance { + fn try_merge(self, other: Self) -> Result { + if self.netuid == other.netuid { + Ok(Self::new( + self.netuid, + self.amount.saturating_add(other.amount), + )) + } else { + Err((self, other)) + } + } +} + +impl TryMerge for NegativeAlphaImbalance { + fn try_merge(self, other: Self) -> Result { + if self.netuid == other.netuid { + Ok(Self::new( + self.netuid, + self.amount.saturating_add(other.amount), + )) + } else { + Err((self, other)) + } + } +} + +impl Imbalance for PositiveAlphaImbalance { + type Opposite = NegativeAlphaImbalance; + + fn zero() -> Self { + Self::default() + } + + fn drop_zero(self) -> Result<(), Self> { + self.try_drop() + } + + fn split(self, amount: AlphaBalance) -> (Self, Self) { + let first = self.amount.min(amount); + let second = self.amount.saturating_sub(first); + ( + Self::new(self.netuid, first), + Self::new(self.netuid, second), + ) + } + + fn extract(&mut self, amount: AlphaBalance) -> Self { + let extracted = self.amount.min(amount); + self.amount = self.amount.saturating_sub(extracted); + Self::new(self.netuid, extracted) + } + + fn merge(self, other: Self) -> Self { + match self.try_merge(other) { + Ok(merged) => merged, + Err((left, right)) => { + log_netuid_mismatch("merge(positive)", left.netuid, right.netuid); + left + } + } + } + + fn subsume(&mut self, other: Self) { + if self.netuid != other.netuid { + log_netuid_mismatch("subsume(positive)", self.netuid, other.netuid); + return; + } + self.amount = self.amount.saturating_add(other.amount); + } + + fn offset(self, other: Self::Opposite) -> SameOrOther { + if self.netuid != other.netuid { + log_netuid_mismatch("offset(positive)", self.netuid, other.netuid); + return SameOrOther::Same(self); + } + if self.amount > other.amount { + SameOrOther::Same(Self::new( + self.netuid, + self.amount.saturating_sub(other.amount), + )) + } else if other.amount > self.amount { + SameOrOther::Other(NegativeAlphaImbalance::new( + self.netuid, + other.amount.saturating_sub(self.amount), + )) + } else { + SameOrOther::None + } + } + + fn peek(&self) -> AlphaBalance { + self.amount + } +} + +impl Imbalance for NegativeAlphaImbalance { + type Opposite = PositiveAlphaImbalance; + + fn zero() -> Self { + Self::default() + } + + fn drop_zero(self) -> Result<(), Self> { + self.try_drop() + } + + fn split(self, amount: AlphaBalance) -> (Self, Self) { + let first = self.amount.min(amount); + let second = self.amount.saturating_sub(first); + ( + Self::new(self.netuid, first), + Self::new(self.netuid, second), + ) + } + + fn extract(&mut self, amount: AlphaBalance) -> Self { + let extracted = self.amount.min(amount); + self.amount = self.amount.saturating_sub(extracted); + Self::new(self.netuid, extracted) + } + + fn merge(self, other: Self) -> Self { + match self.try_merge(other) { + Ok(merged) => merged, + Err((left, right)) => { + log_netuid_mismatch("merge(negative)", left.netuid, right.netuid); + left + } + } + } + + fn subsume(&mut self, other: Self) { + if self.netuid != other.netuid { + log_netuid_mismatch("subsume(negative)", self.netuid, other.netuid); + return; + } + self.amount = self.amount.saturating_add(other.amount); + } + + fn offset(self, other: Self::Opposite) -> SameOrOther { + if self.netuid != other.netuid { + log_netuid_mismatch("offset(negative)", self.netuid, other.netuid); + return SameOrOther::Same(self); + } + if self.amount > other.amount { + SameOrOther::Same(Self::new( + self.netuid, + self.amount.saturating_sub(other.amount), + )) + } else if other.amount > self.amount { + SameOrOther::Other(PositiveAlphaImbalance::new( + self.netuid, + other.amount.saturating_sub(self.amount), + )) + } else { + SameOrOther::None + } + } + + fn peek(&self) -> AlphaBalance { + self.amount + } +} + +/// Loose-coupling interface for alpha issuance operations. +pub trait AlphaAssetsInterface { + fn total_alpha_issuance(netuid: NetUid) -> AlphaBalance; + + fn mint_alpha(netuid: NetUid, amount: AlphaBalance) -> PositiveAlphaImbalance; + + fn burn_alpha(netuid: NetUid, amount: AlphaBalance) -> AlphaBalance; + + fn recycle_alpha(netuid: NetUid, amount: AlphaBalance) -> AlphaBalance; +} + +impl AlphaAssetsInterface for () { + fn total_alpha_issuance(_netuid: NetUid) -> AlphaBalance { + AlphaBalance::ZERO + } + + fn mint_alpha(netuid: NetUid, amount: AlphaBalance) -> PositiveAlphaImbalance { + PositiveAlphaImbalance::new(netuid, amount) + } + + fn burn_alpha(_netuid: NetUid, amount: AlphaBalance) -> AlphaBalance { + amount + } + + fn recycle_alpha(_netuid: NetUid, amount: AlphaBalance) -> AlphaBalance { + amount + } +} + +#[deny(missing_docs)] +#[frame_support::pallet] +#[allow(clippy::expect_used)] +pub mod pallet { + use super::*; + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config {} + + /// Total alpha issuance tracked by the pallet. + #[pallet::storage] + #[pallet::getter(fn total_alpha_issuance)] + pub type TotalAlphaIssuance = StorageMap<_, Twox64Concat, NetUid, AlphaBalance, ValueQuery>; + + /// Total alpha burned per subnet through this pallet. + #[pallet::storage] + #[pallet::getter(fn alpha_burned)] + pub type AlphaBurned = StorageMap<_, Twox64Concat, NetUid, AlphaBalance, ValueQuery>; + + /// Total alpha recycled per subnet through this pallet. + #[pallet::storage] + #[pallet::getter(fn alpha_recycled)] + pub type AlphaRecycled = StorageMap<_, Twox64Concat, NetUid, AlphaBalance, ValueQuery>; +} + +impl Pallet { + pub fn mint_alpha(netuid: NetUid, amount: AlphaBalance) -> PositiveAlphaImbalance { + if !amount.is_zero() { + TotalAlphaIssuance::::mutate(netuid, |issuance| { + *issuance = (*issuance).saturating_add(amount); + }); + } + + PositiveAlphaImbalance::new(netuid, amount) + } + + pub fn burn_alpha(netuid: NetUid, amount: AlphaBalance) -> AlphaBalance { + if !amount.is_zero() { + AlphaBurned::::mutate(netuid, |burned| { + *burned = (*burned).saturating_add(amount); + }); + } + + amount + } + + pub fn recycle_alpha(netuid: NetUid, amount: AlphaBalance) -> AlphaBalance { + if !amount.is_zero() { + AlphaRecycled::::mutate(netuid, |recycled| { + *recycled = (*recycled).saturating_add(amount); + }); + TotalAlphaIssuance::::mutate(netuid, |issuance| { + *issuance = (*issuance).saturating_sub(amount); + }); + } + + amount + } +} + +impl AlphaAssetsInterface for Pallet { + fn total_alpha_issuance(netuid: NetUid) -> AlphaBalance { + TotalAlphaIssuance::::get(netuid) + } + + fn mint_alpha(netuid: NetUid, amount: AlphaBalance) -> PositiveAlphaImbalance { + Self::mint_alpha(netuid, amount) + } + + fn burn_alpha(netuid: NetUid, amount: AlphaBalance) -> AlphaBalance { + Self::burn_alpha(netuid, amount) + } + + fn recycle_alpha(netuid: NetUid, amount: AlphaBalance) -> AlphaBalance { + Self::recycle_alpha(netuid, amount) + } +} diff --git a/pallets/alpha-assets/src/mock.rs b/pallets/alpha-assets/src/mock.rs new file mode 100644 index 0000000000..e118ace555 --- /dev/null +++ b/pallets/alpha-assets/src/mock.rs @@ -0,0 +1,53 @@ +#![allow(clippy::arithmetic_side_effects, clippy::expect_used)] + +use frame_support::derive_impl; +use frame_support::weights::constants::RocksDbWeight; +use frame_system as system; +use sp_core::H256; +use sp_runtime::BuildStorage; +use sp_runtime::traits::{BlakeTwo256, IdentityLookup}; + +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Test { + System: frame_system = 1, + AlphaAssets: crate = 2, + } +); + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = RocksDbWeight; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = frame_support::traits::ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = frame_support::traits::ConstU16<42>; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; + type Nonce = u64; + type Block = Block; +} + +impl crate::pallet::Config for Test {} + +pub fn new_test_ext() -> sp_io::TestExternalities { + let storage = frame_system::GenesisConfig::::default() + .build_storage() + .expect("frame_system storage should build"); + sp_io::TestExternalities::new(storage) +} diff --git a/pallets/alpha-assets/src/tests.rs b/pallets/alpha-assets/src/tests.rs new file mode 100644 index 0000000000..48608f15a4 --- /dev/null +++ b/pallets/alpha-assets/src/tests.rs @@ -0,0 +1,76 @@ +#![allow(clippy::unwrap_used)] + +use frame_support::traits::{Imbalance, tokens::imbalance::TryMerge}; +use subtensor_runtime_common::Token; +use subtensor_runtime_common::{AlphaBalance, NetUid}; + +use crate::{ + AlphaAssetsInterface, AlphaBurned, AlphaRecycled, PositiveAlphaImbalance, TotalAlphaIssuance, +}; + +use super::mock::*; + +#[test] +fn mint_alpha_increases_total_issuance_and_returns_imbalance() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(3u16); + let amount = AlphaBalance::from(75u64); + + let minted = AlphaAssets::mint_alpha(netuid, amount); + + assert_eq!(TotalAlphaIssuance::::get(netuid), amount); + assert_eq!(minted, PositiveAlphaImbalance::new(netuid, amount)); + assert_eq!(minted.netuid(), netuid); + assert_eq!(minted.amount(), amount); + }); +} + +#[test] +fn burn_alpha_does_not_change_total_issuance() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(4u16); + let minted = AlphaAssets::mint_alpha(netuid, 100u64.into()); + + let burned = AlphaAssets::burn_alpha(netuid, 40u64.into()); + + assert_eq!(minted.amount(), 100u64.into()); + assert_eq!(burned, 40u64.into()); + assert_eq!(AlphaBurned::::get(netuid), 40u64.into()); + assert_eq!(TotalAlphaIssuance::::get(netuid), 100u64.into()); + }); +} + +#[test] +fn recycle_alpha_reduces_total_issuance_saturating_at_zero() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(5u16); + + AlphaAssets::mint_alpha(netuid, 90u64.into()); + let recycled = ::recycle_alpha(netuid, 30u64.into()); + assert_eq!(recycled, 30u64.into()); + assert_eq!(AlphaRecycled::::get(netuid), 30u64.into()); + assert_eq!(TotalAlphaIssuance::::get(netuid), 60u64.into()); + + AlphaAssets::recycle_alpha(netuid, 100u64.into()); + assert_eq!(AlphaRecycled::::get(netuid), 130u64.into()); + assert_eq!(TotalAlphaIssuance::::get(netuid), AlphaBalance::ZERO); + }); +} + +#[test] +fn positive_imbalance_only_merges_with_same_netuid() { + new_test_ext().execute_with(|| { + let netuid_a = NetUid::from(1u16); + let netuid_b = NetUid::from(2u16); + + let merged = PositiveAlphaImbalance::new(netuid_a, 10u64.into()) + .merge(PositiveAlphaImbalance::new(netuid_a, 15u64.into())); + assert_eq!(merged.peek(), 25u64.into()); + + let merge_result = TryMerge::try_merge( + PositiveAlphaImbalance::new(netuid_a, 10u64.into()), + PositiveAlphaImbalance::new(netuid_b, 15u64.into()), + ); + assert!(merge_result.is_err()); + }); +} diff --git a/pallets/proxy/src/weights.rs b/pallets/proxy/src/weights.rs index 01c74167c6..a3c4f86593 100644 --- a/pallets/proxy/src/weights.rs +++ b/pallets/proxy/src/weights.rs @@ -2,9 +2,9 @@ //! Autogenerated weights for `pallet_subtensor_proxy` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 49.1.0 -//! DATE: 2026-04-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2026-05-04, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runnervm35a4x`, CPU: `AMD EPYC 7763 64-Core Processor` +//! HOSTNAME: `runnervmeorf1`, CPU: `AMD EPYC 7763 64-Core Processor` //! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` // Executed Command: @@ -22,7 +22,7 @@ // --no-storage-info // --no-min-squares // --no-median-slopes -// --output=/tmp/tmp.9EbSf4VvRZ +// --output=/tmp/tmp.9knXnirNE8 // --template=/home/runner/work/subtensor/subtensor/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -66,10 +66,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `637 + p * (37 ±0)` // Estimated: `4254 + p * (37 ±0)` - // Minimum execution time: 25_647_000 picoseconds. - Weight::from_parts(26_843_168, 4254) - // Standard Error: 3_436 - .saturating_add(Weight::from_parts(63_244, 0).saturating_mul(p.into())) + // Minimum execution time: 26_350_000 picoseconds. + Weight::from_parts(27_408_803, 4254) + // Standard Error: 3_640 + .saturating_add(Weight::from_parts(65_542, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 37).saturating_mul(p.into())) @@ -92,10 +92,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `894 + a * (68 ±0) + p * (37 ±0)` // Estimated: `8615 + a * (68 ±0) + p * (37 ±0)` - // Minimum execution time: 49_944_000 picoseconds. - Weight::from_parts(52_503_282, 8615) - // Standard Error: 2_497 - .saturating_add(Weight::from_parts(216_567, 0).saturating_mul(a.into())) + // Minimum execution time: 51_918_000 picoseconds. + Weight::from_parts(52_350_307, 8615) + // Standard Error: 1_900 + .saturating_add(Weight::from_parts(216_569, 0).saturating_mul(a.into())) + // Standard Error: 7_612 + .saturating_add(Weight::from_parts(48_719, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 68).saturating_mul(a.into())) @@ -111,12 +113,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `299 + a * (68 ±0)` // Estimated: `8615` - // Minimum execution time: 24_506_000 picoseconds. - Weight::from_parts(24_531_799, 8615) - // Standard Error: 1_117 - .saturating_add(Weight::from_parts(191_518, 0).saturating_mul(a.into())) - // Standard Error: 4_477 - .saturating_add(Weight::from_parts(47_993, 0).saturating_mul(p.into())) + // Minimum execution time: 25_298_000 picoseconds. + Weight::from_parts(25_519_010, 8615) + // Standard Error: 1_285 + .saturating_add(Weight::from_parts(199_662, 0).saturating_mul(a.into())) + // Standard Error: 5_148 + .saturating_add(Weight::from_parts(12_673, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -130,12 +132,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `299 + a * (68 ±0)` // Estimated: `8615` - // Minimum execution time: 24_646_000 picoseconds. - Weight::from_parts(25_377_466, 8615) - // Standard Error: 1_170 - .saturating_add(Weight::from_parts(191_897, 0).saturating_mul(a.into())) - // Standard Error: 4_688 - .saturating_add(Weight::from_parts(10_603, 0).saturating_mul(p.into())) + // Minimum execution time: 25_387_000 picoseconds. + Weight::from_parts(25_517_797, 8615) + // Standard Error: 1_246 + .saturating_add(Weight::from_parts(193_411, 0).saturating_mul(a.into())) + // Standard Error: 4_993 + .saturating_add(Weight::from_parts(29_999, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -151,12 +153,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `308 + a * (68 ±0) + p * (37 ±0)` // Estimated: `8615` - // Minimum execution time: 31_980_000 picoseconds. - Weight::from_parts(32_625_067, 8615) - // Standard Error: 1_191 - .saturating_add(Weight::from_parts(194_396, 0).saturating_mul(a.into())) - // Standard Error: 4_771 - .saturating_add(Weight::from_parts(32_404, 0).saturating_mul(p.into())) + // Minimum execution time: 32_732_000 picoseconds. + Weight::from_parts(33_019_988, 8615) + // Standard Error: 1_111 + .saturating_add(Weight::from_parts(194_225, 0).saturating_mul(a.into())) + // Standard Error: 4_452 + .saturating_add(Weight::from_parts(51_072, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -167,10 +169,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `119 + p * (37 ±0)` // Estimated: `4254` - // Minimum execution time: 23_393_000 picoseconds. - Weight::from_parts(24_228_885, 4254) - // Standard Error: 2_353 - .saturating_add(Weight::from_parts(59_058, 0).saturating_mul(p.into())) + // Minimum execution time: 24_486_000 picoseconds. + Weight::from_parts(25_216_335, 4254) + // Standard Error: 2_643 + .saturating_add(Weight::from_parts(67_253, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -183,10 +185,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `119 + p * (37 ±0)` // Estimated: `4254` - // Minimum execution time: 24_886_000 picoseconds. - Weight::from_parts(26_026_566, 4254) - // Standard Error: 2_820 - .saturating_add(Weight::from_parts(61_530, 0).saturating_mul(p.into())) + // Minimum execution time: 25_999_000 picoseconds. + Weight::from_parts(27_109_216, 4254) + // Standard Error: 2_754 + .saturating_add(Weight::from_parts(71_289, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -197,10 +199,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `119 + p * (37 ±0)` // Estimated: `4254` - // Minimum execution time: 24_566_000 picoseconds. - Weight::from_parts(25_878_725, 4254) - // Standard Error: 3_203 - .saturating_add(Weight::from_parts(47_554, 0).saturating_mul(p.into())) + // Minimum execution time: 25_848_000 picoseconds. + Weight::from_parts(26_822_162, 4254) + // Standard Error: 4_056 + .saturating_add(Weight::from_parts(57_793, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -211,10 +213,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `139` // Estimated: `4254` - // Minimum execution time: 25_177_000 picoseconds. - Weight::from_parts(26_179_682, 4254) - // Standard Error: 2_818 - .saturating_add(Weight::from_parts(21_434, 0).saturating_mul(p.into())) + // Minimum execution time: 26_069_000 picoseconds. + Weight::from_parts(27_224_850, 4254) + // Standard Error: 2_890 + .saturating_add(Weight::from_parts(26_102, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -225,10 +227,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `156 + p * (37 ±0)` // Estimated: `4254` - // Minimum execution time: 24_286_000 picoseconds. - Weight::from_parts(25_243_103, 4254) - // Standard Error: 2_546 - .saturating_add(Weight::from_parts(40_266, 0).saturating_mul(p.into())) + // Minimum execution time: 24_847_000 picoseconds. + Weight::from_parts(25_942_688, 4254) + // Standard Error: 3_765 + .saturating_add(Weight::from_parts(50_048, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -242,8 +244,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `412` // Estimated: `8615` - // Minimum execution time: 42_890_000 picoseconds. - Weight::from_parts(43_922_000, 8615) + // Minimum execution time: 44_584_000 picoseconds. + Weight::from_parts(45_826_000, 8615) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -256,10 +258,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `119 + p * (37 ±0)` // Estimated: `4254` - // Minimum execution time: 13_245_000 picoseconds. - Weight::from_parts(13_801_801, 4254) - // Standard Error: 1_780 - .saturating_add(Weight::from_parts(50_093, 0).saturating_mul(p.into())) + // Minimum execution time: 13_766_000 picoseconds. + Weight::from_parts(14_373_774, 4254) + // Standard Error: 2_247 + .saturating_add(Weight::from_parts(42_752, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -280,10 +282,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `637 + p * (37 ±0)` // Estimated: `4254 + p * (37 ±0)` - // Minimum execution time: 25_647_000 picoseconds. - Weight::from_parts(26_843_168, 4254) - // Standard Error: 3_436 - .saturating_add(Weight::from_parts(63_244, 0).saturating_mul(p.into())) + // Minimum execution time: 26_350_000 picoseconds. + Weight::from_parts(27_408_803, 4254) + // Standard Error: 3_640 + .saturating_add(Weight::from_parts(65_542, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 37).saturating_mul(p.into())) @@ -306,10 +308,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `894 + a * (68 ±0) + p * (37 ±0)` // Estimated: `8615 + a * (68 ±0) + p * (37 ±0)` - // Minimum execution time: 49_944_000 picoseconds. - Weight::from_parts(52_503_282, 8615) - // Standard Error: 2_497 - .saturating_add(Weight::from_parts(216_567, 0).saturating_mul(a.into())) + // Minimum execution time: 51_918_000 picoseconds. + Weight::from_parts(52_350_307, 8615) + // Standard Error: 1_900 + .saturating_add(Weight::from_parts(216_569, 0).saturating_mul(a.into())) + // Standard Error: 7_612 + .saturating_add(Weight::from_parts(48_719, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 68).saturating_mul(a.into())) @@ -325,12 +329,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `299 + a * (68 ±0)` // Estimated: `8615` - // Minimum execution time: 24_506_000 picoseconds. - Weight::from_parts(24_531_799, 8615) - // Standard Error: 1_117 - .saturating_add(Weight::from_parts(191_518, 0).saturating_mul(a.into())) - // Standard Error: 4_477 - .saturating_add(Weight::from_parts(47_993, 0).saturating_mul(p.into())) + // Minimum execution time: 25_298_000 picoseconds. + Weight::from_parts(25_519_010, 8615) + // Standard Error: 1_285 + .saturating_add(Weight::from_parts(199_662, 0).saturating_mul(a.into())) + // Standard Error: 5_148 + .saturating_add(Weight::from_parts(12_673, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -344,12 +348,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `299 + a * (68 ±0)` // Estimated: `8615` - // Minimum execution time: 24_646_000 picoseconds. - Weight::from_parts(25_377_466, 8615) - // Standard Error: 1_170 - .saturating_add(Weight::from_parts(191_897, 0).saturating_mul(a.into())) - // Standard Error: 4_688 - .saturating_add(Weight::from_parts(10_603, 0).saturating_mul(p.into())) + // Minimum execution time: 25_387_000 picoseconds. + Weight::from_parts(25_517_797, 8615) + // Standard Error: 1_246 + .saturating_add(Weight::from_parts(193_411, 0).saturating_mul(a.into())) + // Standard Error: 4_993 + .saturating_add(Weight::from_parts(29_999, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -365,12 +369,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `308 + a * (68 ±0) + p * (37 ±0)` // Estimated: `8615` - // Minimum execution time: 31_980_000 picoseconds. - Weight::from_parts(32_625_067, 8615) - // Standard Error: 1_191 - .saturating_add(Weight::from_parts(194_396, 0).saturating_mul(a.into())) - // Standard Error: 4_771 - .saturating_add(Weight::from_parts(32_404, 0).saturating_mul(p.into())) + // Minimum execution time: 32_732_000 picoseconds. + Weight::from_parts(33_019_988, 8615) + // Standard Error: 1_111 + .saturating_add(Weight::from_parts(194_225, 0).saturating_mul(a.into())) + // Standard Error: 4_452 + .saturating_add(Weight::from_parts(51_072, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -381,10 +385,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `119 + p * (37 ±0)` // Estimated: `4254` - // Minimum execution time: 23_393_000 picoseconds. - Weight::from_parts(24_228_885, 4254) - // Standard Error: 2_353 - .saturating_add(Weight::from_parts(59_058, 0).saturating_mul(p.into())) + // Minimum execution time: 24_486_000 picoseconds. + Weight::from_parts(25_216_335, 4254) + // Standard Error: 2_643 + .saturating_add(Weight::from_parts(67_253, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -397,10 +401,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `119 + p * (37 ±0)` // Estimated: `4254` - // Minimum execution time: 24_886_000 picoseconds. - Weight::from_parts(26_026_566, 4254) - // Standard Error: 2_820 - .saturating_add(Weight::from_parts(61_530, 0).saturating_mul(p.into())) + // Minimum execution time: 25_999_000 picoseconds. + Weight::from_parts(27_109_216, 4254) + // Standard Error: 2_754 + .saturating_add(Weight::from_parts(71_289, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -411,10 +415,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `119 + p * (37 ±0)` // Estimated: `4254` - // Minimum execution time: 24_566_000 picoseconds. - Weight::from_parts(25_878_725, 4254) - // Standard Error: 3_203 - .saturating_add(Weight::from_parts(47_554, 0).saturating_mul(p.into())) + // Minimum execution time: 25_848_000 picoseconds. + Weight::from_parts(26_822_162, 4254) + // Standard Error: 4_056 + .saturating_add(Weight::from_parts(57_793, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -425,10 +429,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `139` // Estimated: `4254` - // Minimum execution time: 25_177_000 picoseconds. - Weight::from_parts(26_179_682, 4254) - // Standard Error: 2_818 - .saturating_add(Weight::from_parts(21_434, 0).saturating_mul(p.into())) + // Minimum execution time: 26_069_000 picoseconds. + Weight::from_parts(27_224_850, 4254) + // Standard Error: 2_890 + .saturating_add(Weight::from_parts(26_102, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -439,10 +443,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `156 + p * (37 ±0)` // Estimated: `4254` - // Minimum execution time: 24_286_000 picoseconds. - Weight::from_parts(25_243_103, 4254) - // Standard Error: 2_546 - .saturating_add(Weight::from_parts(40_266, 0).saturating_mul(p.into())) + // Minimum execution time: 24_847_000 picoseconds. + Weight::from_parts(25_942_688, 4254) + // Standard Error: 3_765 + .saturating_add(Weight::from_parts(50_048, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -456,8 +460,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `412` // Estimated: `8615` - // Minimum execution time: 42_890_000 picoseconds. - Weight::from_parts(43_922_000, 8615) + // Minimum execution time: 44_584_000 picoseconds. + Weight::from_parts(45_826_000, 8615) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -470,10 +474,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `119 + p * (37 ±0)` // Estimated: `4254` - // Minimum execution time: 13_245_000 picoseconds. - Weight::from_parts(13_801_801, 4254) - // Standard Error: 1_780 - .saturating_add(Weight::from_parts(50_093, 0).saturating_mul(p.into())) + // Minimum execution time: 13_766_000 picoseconds. + Weight::from_parts(14_373_774, 4254) + // Standard Error: 2_247 + .saturating_add(Weight::from_parts(42_752, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/pallets/subtensor/Cargo.toml b/pallets/subtensor/Cargo.toml index 01407e9020..99ba71629f 100644 --- a/pallets/subtensor/Cargo.toml +++ b/pallets/subtensor/Cargo.toml @@ -43,6 +43,7 @@ subtensor-swap-interface.workspace = true runtime-common.workspace = true subtensor-runtime-common = { workspace = true, features = ["approx"] } sp-keyring.workspace = true +pallet-alpha-assets.workspace = true pallet-drand.workspace = true pallet-commitments.workspace = true @@ -115,6 +116,7 @@ std = [ "sp-version/std", "sp-keyring/std", "subtensor-runtime-common/std", + "pallet-alpha-assets/std", "pallet-commitments/std", "pallet-crowdloan/std", "pallet-drand/std", diff --git a/pallets/subtensor/rpc/src/lib.rs b/pallets/subtensor/rpc/src/lib.rs index 98e2df2f62..6feff774ad 100644 --- a/pallets/subtensor/rpc/src/lib.rs +++ b/pallets/subtensor/rpc/src/lib.rs @@ -109,6 +109,8 @@ pub trait SubtensorCustomApi { ) -> RpcResult>; #[method(name = "subnetInfo_getSubnetToPrune")] fn get_subnet_to_prune(&self, at: Option) -> RpcResult>; + #[method(name = "subnetInfo_getSubnetAccountId")] + fn get_subnet_account_id(&self, netuid: NetUid, at: Option) -> RpcResult>; } pub struct SubtensorCustom { @@ -531,4 +533,18 @@ where } } } + + fn get_subnet_account_id( + &self, + netuid: NetUid, + at: Option<::Hash>, + ) -> RpcResult> { + let api = self.client.runtime_api(); + let at = at.unwrap_or_else(|| self.client.info().best_hash); + + match api.get_subnet_account_id(at, netuid) { + Ok(result) => Ok(result.encode()), + Err(_) => Err(Error::RuntimeError("Subnet does not exist".to_string()).into()), + } + } } diff --git a/pallets/subtensor/runtime-api/Cargo.toml b/pallets/subtensor/runtime-api/Cargo.toml index b427fc333c..a83c3b3178 100644 --- a/pallets/subtensor/runtime-api/Cargo.toml +++ b/pallets/subtensor/runtime-api/Cargo.toml @@ -15,6 +15,7 @@ workspace = true sp-api.workspace = true sp-runtime.workspace = true codec = { workspace = true, features = ["derive"] } +substrate-fixed.workspace = true subtensor-runtime-common.workspace = true # local pallet-subtensor.workspace = true @@ -26,6 +27,7 @@ std = [ "pallet-subtensor/std", "sp-api/std", "sp-runtime/std", + "substrate-fixed/std", "subtensor-runtime-common/std", ] pow-faucet = [] diff --git a/pallets/subtensor/runtime-api/src/lib.rs b/pallets/subtensor/runtime-api/src/lib.rs index 84da95cd36..741facfc87 100644 --- a/pallets/subtensor/runtime-api/src/lib.rs +++ b/pallets/subtensor/runtime-api/src/lib.rs @@ -12,6 +12,7 @@ use pallet_subtensor::rpc_info::{ subnet_info::{SubnetHyperparams, SubnetHyperparamsV2, SubnetInfo, SubnetInfov2}, }; use sp_runtime::AccountId32; +use substrate_fixed::types::U64F64; use subtensor_runtime_common::{AlphaBalance, MechId, NetUid, TaoBalance}; // Here we declare the runtime API. It is implemented it the `impl` block in @@ -48,6 +49,7 @@ sp_api::decl_runtime_apis! { fn get_coldkey_auto_stake_hotkey(coldkey: AccountId32, netuid: NetUid) -> Option; fn get_selective_mechagraph(netuid: NetUid, subid: MechId, metagraph_indexes: Vec) -> Option>; fn get_subnet_to_prune() -> Option; + fn get_subnet_account_id(netuid: NetUid) -> Option; } pub trait StakeInfoRuntimeApi { @@ -55,6 +57,8 @@ sp_api::decl_runtime_apis! { fn get_stake_info_for_coldkeys( coldkey_accounts: Vec ) -> Vec<(AccountId32, Vec>)>; fn get_stake_info_for_hotkey_coldkey_netuid( hotkey_account: AccountId32, coldkey_account: AccountId32, netuid: NetUid ) -> Option>; fn get_stake_fee( origin: Option<(AccountId32, NetUid)>, origin_coldkey_account: AccountId32, destination: Option<(AccountId32, NetUid)>, destination_coldkey_account: AccountId32, amount: u64 ) -> u64; + fn get_hotkey_conviction(hotkey: AccountId32, netuid: NetUid) -> U64F64; + fn get_most_convicted_hotkey_on_subnet(netuid: NetUid) -> Option; } pub trait SubnetRegistrationRuntimeApi { diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index c79505a85d..563dd211fe 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -16,7 +16,7 @@ use sp_runtime::{ }; use sp_std::collections::btree_set::BTreeSet; use sp_std::vec; -use substrate_fixed::types::U96F32; +use substrate_fixed::types::{U64F64, U96F32}; use subtensor_runtime_common::{AlphaBalance, NetUid, TaoBalance}; use subtensor_swap_interface::SwapHandler; @@ -43,6 +43,11 @@ mod pallet_benchmarks { TaoBalance::from(1_000_000) } + fn add_balance_to_coldkey_account(coldkey: &T::AccountId, tao: TaoBalance) { + let credit = Subtensor::::mint_tao(tao); + let _ = Subtensor::::spend_tao(coldkey, credit, tao).unwrap(); + } + /// This helper funds an account with: /// - 2x burn fee /// - 100x DefaultMinStake @@ -54,7 +59,31 @@ mod pallet_benchmarks { .saturating_mul(2.into()) .saturating_add(min_stake.saturating_mul(100.into())); - Subtensor::::add_balance_to_coldkey_account(who, deposit.into()); + add_balance_to_coldkey_account::(who, deposit.into()); + } + + /// Add a zero lock to a random hotkey just so that the lock records exist + fn add_lock(coldkey: &T::AccountId, netuid: NetUid) { + let hotkey: T::AccountId = account("RandomHotkey", 0, 999); + Lock::::insert( + (coldkey, netuid, hotkey.clone()), + LockState { + locked_mass: AlphaBalance::ZERO, + unlocked_mass: AlphaBalance::ZERO, + conviction: U64F64::from_num(0), + last_update: 0, + }, + ); + HotkeyLock::::insert( + netuid, + hotkey, + LockState { + locked_mass: AlphaBalance::ZERO, + unlocked_mass: AlphaBalance::ZERO, + conviction: U64F64::from_num(0), + last_update: 0, + }, + ); } #[benchmark] @@ -127,6 +156,9 @@ mod pallet_benchmarks { RegistrationsThisInterval::::insert(netuid, 0); + // Reset burn so that we don't hit maximum issuance + Burn::::insert(netuid, TaoBalance::from(1_000_000)); + assert_ok!(Subtensor::::burned_register( RawOrigin::Signed(coldkey.clone()).into(), netuid, @@ -168,7 +200,8 @@ mod pallet_benchmarks { let amount = TaoBalance::from(60_000_000); seed_swap_reserves::(netuid); - Subtensor::::add_balance_to_coldkey_account(&coldkey, total_stake.into()); + add_balance_to_coldkey_account::(&coldkey, total_stake.into()); + add_lock::(&coldkey, netuid); assert_ok!(Subtensor::::burned_register( RawOrigin::Signed(coldkey.clone()).into(), @@ -277,7 +310,7 @@ mod pallet_benchmarks { Subtensor::::set_burn(netuid, benchmark_registration_burn()); let amount: u64 = 1_000_000; - Subtensor::::add_balance_to_coldkey_account(&coldkey, amount.into()); + add_balance_to_coldkey_account::(&coldkey, amount.into()); #[extrinsic_call] _(RawOrigin::Signed(coldkey.clone()), netuid, hotkey.clone()); @@ -303,7 +336,7 @@ mod pallet_benchmarks { let amount: u64 = 100_000_000_000_000; seed_swap_reserves::(netuid); - Subtensor::::add_balance_to_coldkey_account(&coldkey, amount.into()); + add_balance_to_coldkey_account::(&coldkey, amount.into()); assert_ok!(Subtensor::::burned_register( RawOrigin::Signed(coldkey.clone()).into(), @@ -323,7 +356,7 @@ mod pallet_benchmarks { Subtensor::::set_network_rate_limit(1); let amount: u64 = 100_000_000_000_000u64.saturating_mul(2); - Subtensor::::add_balance_to_coldkey_account(&coldkey, amount.into()); + add_balance_to_coldkey_account::(&coldkey, amount.into()); #[extrinsic_call] _(RawOrigin::Signed(coldkey.clone()), hotkey.clone()); @@ -479,7 +512,7 @@ mod pallet_benchmarks { let ed = ::ExistentialDeposit::get(); let swap_cost = Subtensor::::get_key_swap_cost(); - Subtensor::::add_balance_to_coldkey_account(&coldkey, swap_cost + ed); + add_balance_to_coldkey_account::(&coldkey, swap_cost + ed); #[extrinsic_call] _(RawOrigin::Signed(coldkey), new_coldkey_hash); @@ -549,7 +582,7 @@ mod pallet_benchmarks { hotkey1.clone(), )); - Subtensor::::add_balance_to_coldkey_account(&old_coldkey, free_balance_old); + add_balance_to_coldkey_account::(&old_coldkey, free_balance_old); let name: Vec = b"The fourth Coolest Identity".to_vec(); let identity = ChainIdentityV2 { name, @@ -828,7 +861,8 @@ mod pallet_benchmarks { let hotkey: T::AccountId = account("Alice", 0, seed); let initial_balance = TaoBalance::from(900_000_000_000_u64); - Subtensor::::add_balance_to_coldkey_account(&coldkey.clone(), initial_balance); + add_balance_to_coldkey_account::(&coldkey.clone(), initial_balance); + add_lock::(&coldkey, netuid); let tao_reserve = TaoBalance::from(1_000_000_000_000_u64); let alpha_in = AlphaBalance::from(100_000_000_000_000_u64); @@ -875,7 +909,8 @@ mod pallet_benchmarks { let burn_fee = Subtensor::::get_burn(netuid); let stake_tao = DefaultMinStake::::get().saturating_mul(10.into()); let deposit = burn_fee.saturating_mul(2.into()).saturating_add(stake_tao); - Subtensor::::add_balance_to_coldkey_account(&coldkey, deposit.into()); + add_balance_to_coldkey_account::(&coldkey, deposit.into()); + add_lock::(&coldkey, netuid); assert_ok!(Subtensor::::burned_register( RawOrigin::Signed(coldkey.clone()).into(), @@ -898,7 +933,7 @@ mod pallet_benchmarks { let alpha_to_move = Subtensor::::get_stake_for_hotkey_and_coldkey_on_subnet(&origin, &coldkey, netuid); - Subtensor::::create_account_if_non_existent(&coldkey, &destination); + let _ = Subtensor::::create_account_if_non_existent(&coldkey, &destination); StakingOperationRateLimiter::::remove((origin.clone(), coldkey.clone(), netuid)); @@ -913,6 +948,61 @@ mod pallet_benchmarks { ); } + #[benchmark] + fn remove_stake() { + let netuid = NetUid::from(1); + let tempo: u16 = 1; + let seed: u32 = 1; + + Subtensor::::increase_total_stake(1_000_000_000_000_u64.into()); + + Subtensor::::init_new_network(netuid, tempo); + Subtensor::::set_network_registration_allowed(netuid, true); + SubtokenEnabled::::insert(netuid, true); + + Subtensor::::set_max_allowed_uids(netuid, 4096); + assert_eq!(Subtensor::::get_max_allowed_uids(netuid), 4096); + + let coldkey: T::AccountId = account("Test", 0, seed); + let hotkey: T::AccountId = account("Alice", 0, seed); + Subtensor::::set_burn(netuid, benchmark_registration_burn()); + + let tao_reserve = TaoBalance::from(1_000_000_000_000_u64); + let alpha_in = AlphaBalance::from(100_000_000_000_000_u64); + set_reserves::(netuid, tao_reserve, alpha_in); + + let wallet_bal = 1000000u32.into(); + add_balance_to_coldkey_account::(&coldkey.clone(), wallet_bal); + + assert_ok!(Subtensor::::burned_register( + RawOrigin::Signed(coldkey.clone()).into(), + netuid, + hotkey.clone() + )); + + let staked_amt = TaoBalance::from(100_000_000_000_u64); + add_balance_to_coldkey_account::(&coldkey.clone(), staked_amt); + + assert_ok!(Subtensor::::add_stake( + RawOrigin::Signed(coldkey.clone()).into(), + hotkey.clone(), + netuid, + staked_amt + )); + + let amount_unstaked = AlphaBalance::from(30_000_000_000_u64); + + StakingOperationRateLimiter::::remove((hotkey.clone(), coldkey.clone(), netuid)); + + #[extrinsic_call] + _( + RawOrigin::Signed(coldkey.clone()), + hotkey.clone(), + netuid, + amount_unstaked, + ); + } + #[benchmark] fn remove_stake_limit() { let netuid = NetUid::from(1); @@ -937,7 +1027,8 @@ mod pallet_benchmarks { set_reserves::(netuid, tao_reserve, alpha_in); let wallet_bal = 1000000u32.into(); - Subtensor::::add_balance_to_coldkey_account(&coldkey.clone(), wallet_bal); + add_balance_to_coldkey_account::(&coldkey.clone(), wallet_bal); + add_lock::(&coldkey, netuid); assert_ok!(Subtensor::::burned_register( RawOrigin::Signed(coldkey.clone()).into(), @@ -946,7 +1037,7 @@ mod pallet_benchmarks { )); let staked_amt = TaoBalance::from(100_000_000_000_u64); - Subtensor::::add_balance_to_coldkey_account(&coldkey.clone(), staked_amt); + add_balance_to_coldkey_account::(&coldkey.clone(), staked_amt); assert_ok!(Subtensor::::add_stake( RawOrigin::Signed(coldkey.clone()).into(), @@ -1004,7 +1095,9 @@ mod pallet_benchmarks { let limit_swap = TaoBalance::from(1_000_000_000_u64); let amount_to_be_staked = TaoBalance::from(440_000_000_000_u64); let amount_swapped = AlphaBalance::from(30_000_000_000_u64); - Subtensor::::add_balance_to_coldkey_account(&coldkey.clone(), amount); + add_balance_to_coldkey_account::(&coldkey.clone(), amount); + add_lock::(&coldkey, netuid1); + add_lock::(&coldkey, netuid2); assert_ok!(Subtensor::::burned_register( RawOrigin::Signed(coldkey.clone()).into(), @@ -1054,7 +1147,8 @@ mod pallet_benchmarks { let reg_fee = Subtensor::::get_burn(netuid); let stake_tao = DefaultMinStake::::get().saturating_mul(10.into()); let deposit = reg_fee.saturating_mul(2.into()).saturating_add(stake_tao); - Subtensor::::add_balance_to_coldkey_account(&coldkey, deposit.into()); + add_balance_to_coldkey_account::(&coldkey, deposit.into()); + add_lock::(&coldkey, netuid); assert_ok!(Subtensor::::burned_register( RawOrigin::Signed(coldkey.clone()).into(), @@ -1077,7 +1171,7 @@ mod pallet_benchmarks { let alpha_to_transfer = Subtensor::::get_stake_for_hotkey_and_coldkey_on_subnet(&hot, &coldkey, netuid); - Subtensor::::create_account_if_non_existent(&dest, &hot); + let _ = Subtensor::::create_account_if_non_existent(&dest, &hot); StakingOperationRateLimiter::::remove((hot.clone(), coldkey.clone(), netuid)); @@ -1110,7 +1204,9 @@ mod pallet_benchmarks { let reg_fee = Subtensor::::get_burn(netuid1); let stake_tao = DefaultMinStake::::get().saturating_mul(10.into()); let deposit = reg_fee.saturating_mul(2.into()).saturating_add(stake_tao); - Subtensor::::add_balance_to_coldkey_account(&coldkey, deposit.into()); + add_balance_to_coldkey_account::(&coldkey, deposit.into()); + add_lock::(&coldkey, netuid1); + add_lock::(&coldkey, netuid2); assert_ok!(Subtensor::::burned_register( RawOrigin::Signed(coldkey.clone()).into(), @@ -1262,7 +1358,7 @@ mod pallet_benchmarks { Subtensor::::set_network_registration_allowed(1.into(), true); Subtensor::::set_network_rate_limit(1); let amount: u64 = 9_999_999_999_999; - Subtensor::::add_balance_to_coldkey_account(&coldkey, amount.into()); + add_balance_to_coldkey_account::(&coldkey, amount.into()); #[extrinsic_call] _( @@ -1327,7 +1423,7 @@ mod pallet_benchmarks { let descr = vec![]; let add = vec![]; - Subtensor::::create_account_if_non_existent(&coldkey, &hotkey); + let _ = Subtensor::::create_account_if_non_existent(&coldkey, &hotkey); Subtensor::::init_new_network(netuid, 1); Subtensor::::set_network_registration_allowed(netuid, true); SubtokenEnabled::::insert(netuid, true); @@ -1335,7 +1431,7 @@ mod pallet_benchmarks { seed_swap_reserves::(netuid); let deposit: u64 = 1_000_000_000u64.saturating_mul(2); - Subtensor::::add_balance_to_coldkey_account(&coldkey, deposit.into()); + add_balance_to_coldkey_account::(&coldkey, deposit.into()); assert_ok!(Subtensor::::burned_register( RawOrigin::Signed(coldkey.clone()).into(), @@ -1403,7 +1499,7 @@ mod pallet_benchmarks { let reg_balance = TaoBalance::from(1_000_000_u64); seed_swap_reserves::(netuid); - Subtensor::::add_balance_to_coldkey_account(&coldkey, reg_balance.into()); + add_balance_to_coldkey_account::(&coldkey, reg_balance.into()); assert_ok!(Subtensor::::burned_register( RawOrigin::Signed(coldkey.clone()).into(), @@ -1423,7 +1519,7 @@ mod pallet_benchmarks { Owner::::insert(&old, &coldkey); let cost = Subtensor::::get_key_swap_cost(); - Subtensor::::add_balance_to_coldkey_account(&coldkey, cost.into()); + add_balance_to_coldkey_account::(&coldkey, cost.into()); #[extrinsic_call] _(RawOrigin::Signed(coldkey.clone()), old, new, None); @@ -1442,7 +1538,7 @@ mod pallet_benchmarks { fn unstake_all() { let coldkey: T::AccountId = whitelisted_caller(); let hotkey: T::AccountId = account("A", 0, 14); - Subtensor::::create_account_if_non_existent(&coldkey, &hotkey); + let _ = Subtensor::::create_account_if_non_existent(&coldkey, &hotkey); #[extrinsic_call] _(RawOrigin::Signed(coldkey.clone()), hotkey); @@ -1471,7 +1567,7 @@ mod pallet_benchmarks { AlphaBalance::from(100_000_000_000_u64), ); - Subtensor::::add_balance_to_coldkey_account(&coldkey.clone(), 1000000u32.into()); + add_balance_to_coldkey_account::(&coldkey.clone(), 1000000u32.into()); assert_ok!(Subtensor::::burned_register( RawOrigin::Signed(coldkey.clone()).into(), @@ -1480,7 +1576,7 @@ mod pallet_benchmarks { )); let staked_amt = TaoBalance::from(100_000_000_000_u64); - Subtensor::::add_balance_to_coldkey_account(&coldkey.clone(), staked_amt); + add_balance_to_coldkey_account::(&coldkey.clone(), staked_amt); assert_ok!(Subtensor::::add_stake( RawOrigin::Signed(coldkey.clone()).into(), @@ -1519,7 +1615,8 @@ mod pallet_benchmarks { set_reserves::(netuid, tao_reserve, alpha_in); let wallet_bal = 1000000u32.into(); - Subtensor::::add_balance_to_coldkey_account(&coldkey.clone(), wallet_bal); + add_balance_to_coldkey_account::(&coldkey.clone(), wallet_bal); + add_lock::(&coldkey, netuid); assert_ok!(Subtensor::::burned_register( RawOrigin::Signed(coldkey.clone()).into(), @@ -1535,7 +1632,7 @@ mod pallet_benchmarks { .saturating_to_num::() .into(); let staked_amt = TaoBalance::from(1_000_000_000_u64); - Subtensor::::add_balance_to_coldkey_account(&coldkey.clone(), staked_amt); + add_balance_to_coldkey_account::(&coldkey.clone(), staked_amt); assert_ok!(Subtensor::::add_stake( RawOrigin::Signed(coldkey.clone()).into(), @@ -1566,7 +1663,7 @@ mod pallet_benchmarks { let cap = TaoBalance::from(2_000_000_000_000_u64); // 2000 TAO let funds_account: T::AccountId = account("funds", 0, 0); - Subtensor::::add_balance_to_coldkey_account(&funds_account, cap.into()); + add_balance_to_coldkey_account::(&funds_account, cap.into()); pallet_crowdloan::Crowdloans::::insert( crowdloan_id, @@ -1625,7 +1722,7 @@ mod pallet_benchmarks { let cap = TaoBalance::from(2_000_000_000_000_u64); // 2000 TAO let funds_account: T::AccountId = account("funds", 0, 0); - Subtensor::::add_balance_to_coldkey_account(&funds_account, cap); + add_balance_to_coldkey_account::(&funds_account, cap); pallet_crowdloan::Crowdloans::::insert( crowdloan_id, @@ -1670,7 +1767,7 @@ mod pallet_benchmarks { let lease_id = 0; let lease = SubnetLeases::::get(0).unwrap(); let hotkey = account::("beneficiary_hotkey", 0, 0); - Subtensor::::create_account_if_non_existent(&beneficiary, &hotkey); + let _ = Subtensor::::create_account_if_non_existent(&beneficiary, &hotkey); #[extrinsic_call] _( @@ -1752,7 +1849,7 @@ mod pallet_benchmarks { Subtensor::::set_network_registration_allowed(netuid, true); let amount = 900_000_000_000u64; - Subtensor::::add_balance_to_coldkey_account(&coldkey.clone(), amount.into()); + add_balance_to_coldkey_account::(&coldkey.clone(), amount.into()); assert_ok!(Subtensor::::burned_register( RawOrigin::Signed(coldkey.clone()).into(), @@ -1780,7 +1877,7 @@ mod pallet_benchmarks { let netuid = Subtensor::::get_next_netuid(); let lock_cost = Subtensor::::get_network_lock_cost(); - Subtensor::::add_balance_to_coldkey_account(&coldkey, lock_cost.into()); + add_balance_to_coldkey_account::(&coldkey, lock_cost.into()); assert_ok!(Subtensor::::register_network( RawOrigin::Signed(coldkey.clone()).into(), @@ -1854,7 +1951,7 @@ mod pallet_benchmarks { let netuid = Subtensor::::get_next_netuid(); let lock_cost = Subtensor::::get_network_lock_cost(); - Subtensor::::add_balance_to_coldkey_account(&coldkey, lock_cost.into()); + add_balance_to_coldkey_account::(&coldkey, lock_cost.into()); assert_ok!(Subtensor::::register_network( RawOrigin::Signed(coldkey.clone()).into(), @@ -1905,7 +2002,8 @@ mod pallet_benchmarks { let balance_update = TaoBalance::from(900_000_000_000_u64); let limit = TaoBalance::from(6_000_000_000_u64); let amount = TaoBalance::from(44_000_000_000_u64); - Subtensor::::add_balance_to_coldkey_account(&coldkey.clone(), balance_update); + add_balance_to_coldkey_account::(&coldkey.clone(), balance_update); + add_lock::(&coldkey, netuid); let tao_reserve = TaoBalance::from(150_000_000_000_u64); let alpha_in = AlphaBalance::from(100_000_000_000_u64); @@ -1937,6 +2035,167 @@ mod pallet_benchmarks { assert_eq!(PendingChildKeyCooldown::::get(), cooldown); } + #[benchmark] + fn lock_stake() { + let netuid = NetUid::from(1); + let tempo: u16 = 1; + + Subtensor::::init_new_network(netuid, tempo); + SubtokenEnabled::::insert(netuid, true); + Subtensor::::set_burn(netuid, benchmark_registration_burn()); + Subtensor::::set_network_registration_allowed(netuid, true); + Subtensor::::set_max_allowed_uids(netuid, 4096); + + let seed: u32 = 1; + let coldkey: T::AccountId = account("Test", 0, seed); + let hotkey: T::AccountId = account("Alice", 0, seed); + let total_stake = TaoBalance::from(1_000_000_000); + let amount = AlphaBalance::from(60_000_000); + + seed_swap_reserves::(netuid); + let burn = Subtensor::::get_burn(netuid); + add_balance_to_coldkey_account::( + &coldkey, + total_stake + .saturating_mul(2.into()) + .saturating_add(burn.saturating_mul(2.into())) + .into(), + ); + + assert_ok!(Subtensor::::burned_register( + RawOrigin::Signed(coldkey.clone()).into(), + netuid, + hotkey.clone() + )); + + assert_ok!(Subtensor::::add_stake( + RawOrigin::Signed(coldkey.clone()).into(), + hotkey.clone(), + netuid, + total_stake + )); + + #[extrinsic_call] + _( + RawOrigin::Signed(coldkey.clone()), + hotkey.clone(), + netuid, + amount, + ); + } + + #[benchmark] + fn unlock_stake() { + let netuid = NetUid::from(1); + let tempo: u16 = 1; + + Subtensor::::init_new_network(netuid, tempo); + SubtokenEnabled::::insert(netuid, true); + Subtensor::::set_burn(netuid, benchmark_registration_burn()); + Subtensor::::set_network_registration_allowed(netuid, true); + Subtensor::::set_max_allowed_uids(netuid, 4096); + + let seed: u32 = 1; + let coldkey: T::AccountId = account("Test", 0, seed); + let hotkey: T::AccountId = account("Alice", 0, seed); + let total_stake = TaoBalance::from(1_000_000_000); + let amount = AlphaBalance::from(60_000_000); + + seed_swap_reserves::(netuid); + let burn = Subtensor::::get_burn(netuid); + add_balance_to_coldkey_account::( + &coldkey, + total_stake + .saturating_mul(2.into()) + .saturating_add(burn.saturating_mul(2.into())) + .into(), + ); + + assert_ok!(Subtensor::::burned_register( + RawOrigin::Signed(coldkey.clone()).into(), + netuid, + hotkey.clone() + )); + + assert_ok!(Subtensor::::add_stake( + RawOrigin::Signed(coldkey.clone()).into(), + hotkey.clone(), + netuid, + total_stake + )); + + assert_ok!(Subtensor::::do_lock_stake( + &coldkey, netuid, &hotkey, amount, + )); + + #[extrinsic_call] + _(RawOrigin::Signed(coldkey.clone()), netuid, amount); + } + + #[benchmark] + fn move_lock() { + let netuid = NetUid::from(1); + let tempo: u16 = 1; + + Subtensor::::init_new_network(netuid, tempo); + SubtokenEnabled::::insert(netuid, true); + Subtensor::::set_burn(netuid, benchmark_registration_burn()); + Subtensor::::set_network_registration_allowed(netuid, true); + Subtensor::::set_max_allowed_uids(netuid, 4096); + + let seed: u32 = 1; + let coldkey: T::AccountId = account("Test", 0, seed); + let hotkey: T::AccountId = account("Alice", 0, seed); + let hotkey_dest: T::AccountId = account("Bob", 0, seed); + let total_stake = TaoBalance::from(1_000_000_000); + let amount = AlphaBalance::from(60_000_000); + + seed_swap_reserves::(netuid); + let burn = Subtensor::::get_burn(netuid); + add_balance_to_coldkey_account::( + &coldkey, + total_stake + .saturating_mul(2.into()) + .saturating_add(burn.saturating_mul(2.into())) + .into(), + ); + + assert_ok!(Subtensor::::burned_register( + RawOrigin::Signed(coldkey.clone()).into(), + netuid, + hotkey.clone() + )); + + assert_ok!(Subtensor::::burned_register( + RawOrigin::Signed(coldkey.clone()).into(), + netuid, + hotkey_dest.clone() + )); + + assert_ok!(Subtensor::::add_stake( + RawOrigin::Signed(coldkey.clone()).into(), + hotkey.clone(), + netuid, + total_stake + )); + + assert_ok!(Subtensor::::do_lock_stake( + &coldkey, netuid, &hotkey, amount, + )); + + #[extrinsic_call] + _( + RawOrigin::Signed(coldkey.clone()), + hotkey_dest.clone(), + netuid, + ); + + assert!( + Lock::::iter_prefix((coldkey, netuid)) + .any(|(locked_hotkey, _)| locked_hotkey == hotkey_dest) + ); + } + impl_benchmark_test_suite!( Subtensor, crate::tests::mock::new_test_ext(1), diff --git a/pallets/subtensor/src/coinbase/alpha.rs b/pallets/subtensor/src/coinbase/alpha.rs new file mode 100644 index 0000000000..98ad874471 --- /dev/null +++ b/pallets/subtensor/src/coinbase/alpha.rs @@ -0,0 +1,59 @@ +use pallet_alpha_assets::{AlphaAssetsInterface, PositiveAlphaImbalance}; +use subtensor_runtime_common::{AlphaBalance, NetUid, Token}; + +use super::*; + +impl Pallet { + /// Create alpha and return the resulting imbalance for later resolution. + pub fn mint_alpha(netuid: NetUid, amount: AlphaBalance) -> PositiveAlphaImbalance { + T::AlphaAssets::mint_alpha(netuid, amount) + } + + /// Resolve alpha imbalance into outstanding alpha on the subnet. + pub fn resolve_to_alpha_out(imbalance: PositiveAlphaImbalance) { + let netuid = imbalance.netuid(); + let amount = imbalance.amount(); + if amount.is_zero() { + return; + } + + SubnetAlphaOut::::mutate(netuid, |total| { + *total = total.saturating_add(amount); + }); + } + + /// Resolve alpha imbalance into alpha held in the subnet reserve. + pub fn resolve_to_alpha_in(imbalance: PositiveAlphaImbalance) { + let netuid = imbalance.netuid(); + let amount = imbalance.amount(); + if amount.is_zero() { + return; + } + + SubnetAlphaIn::::mutate(netuid, |total| { + *total = total.saturating_add(amount); + }); + } + + /// Recycle alpha (reduce total alpha issuance) + pub fn recycle_subnet_alpha(netuid: NetUid, amount: AlphaBalance) { + if amount.is_zero() { + return; + } + + SubnetAlphaOut::::mutate(netuid, |total| { + *total = total.saturating_sub(amount); + }); + + let _ = T::AlphaAssets::recycle_alpha(netuid, amount); + } + + /// Burn alpha (no change to total alpha issuance) + pub fn burn_subnet_alpha(netuid: NetUid, amount: AlphaBalance) { + if amount.is_zero() { + return; + } + + let _ = T::AlphaAssets::burn_alpha(netuid, amount); + } +} diff --git a/pallets/subtensor/src/coinbase/block_emission.rs b/pallets/subtensor/src/coinbase/block_emission.rs index 6d04c35cb8..d4adcddbee 100644 --- a/pallets/subtensor/src/coinbase/block_emission.rs +++ b/pallets/subtensor/src/coinbase/block_emission.rs @@ -1,12 +1,13 @@ use super::*; // use frame_support::traits::{Currency as BalancesCurrency, Get, Imbalance}; -use frame_support::traits::Get; +use crate::coinbase::tao::CreditOf; +use frame_support::traits::{Get, Imbalance}; use safe_math::*; use substrate_fixed::{transcendental::log2, types::I96F32}; -use subtensor_runtime_common::TaoBalance; impl Pallet { - /// Calculates the block emission based on the total issuance. + /// Calculates the block emission based on the total issuance and mints corresponding + /// amount of TAO. /// /// This function computes the block emission by applying a logarithmic function /// to the total issuance of the network. The formula used takes into account @@ -17,7 +18,18 @@ impl Pallet { /// # Returns /// * 'Result': The calculated block emission rate or error. /// - pub fn get_block_emission() -> Result { + pub fn get_block_emission() -> CreditOf { + let maybe_tao_to_mint = Self::calculate_block_emission(); + if let Ok(tao_to_mint) = maybe_tao_to_mint + && !tao_to_mint.is_zero() + { + return Self::mint_tao(tao_to_mint.into()); + } + CreditOf::::zero() + } + + /// Calculates the block emission based on the total issuance only, no minting happens. + pub fn calculate_block_emission() -> Result { // Convert the total issuance to a fixed-point number for calculation. Self::get_block_emission_for_issuance(Self::get_total_issuance().into()).map(Into::into) } diff --git a/pallets/subtensor/src/coinbase/block_step.rs b/pallets/subtensor/src/coinbase/block_step.rs index 20dc9c5e6f..fac924ccf4 100644 --- a/pallets/subtensor/src/coinbase/block_step.rs +++ b/pallets/subtensor/src/coinbase/block_step.rs @@ -1,6 +1,6 @@ use super::*; use substrate_fixed::types::U96F32; -use subtensor_runtime_common::{NetUid, TaoBalance}; +use subtensor_runtime_common::NetUid; impl Pallet { /// Executes the necessary operations for each block. @@ -12,11 +12,7 @@ impl Pallet { Self::update_registration_prices_for_networks(); // --- 2. Get the current coinbase emission. - let block_emission: U96F32 = U96F32::saturating_from_num( - Self::get_block_emission() - .unwrap_or(TaoBalance::ZERO) - .to_u64(), - ); + let block_emission = Self::get_block_emission(); log::debug!("Block emission: {block_emission:?}"); // --- 3. Reveal matured weights. @@ -70,7 +66,7 @@ impl Pallet { pub fn root_proportion(netuid: NetUid) -> U96F32 { let alpha_issuance = U96F32::from_num(Self::get_alpha_issuance(netuid)); - let root_tao: U96F32 = U96F32::from_num(SubnetTAO::::get(NetUid::ROOT)); + let root_tao: U96F32 = U96F32::from_num(Self::get_subnet_tao(NetUid::ROOT)); let tao_weight: U96F32 = root_tao.saturating_mul(Self::get_tao_weight()); let root_proportion: U96F32 = tao_weight diff --git a/pallets/subtensor/src/coinbase/mod.rs b/pallets/subtensor/src/coinbase/mod.rs index 8d06228593..c51bf58d1d 100644 --- a/pallets/subtensor/src/coinbase/mod.rs +++ b/pallets/subtensor/src/coinbase/mod.rs @@ -1,7 +1,9 @@ use super::*; +pub mod alpha; pub mod block_emission; pub mod block_step; pub mod reveal_commits; pub mod root; pub mod run_coinbase; pub mod subnet_emissions; +pub mod tao; diff --git a/pallets/subtensor/src/coinbase/root.rs b/pallets/subtensor/src/coinbase/root.rs index ac157b2b30..b2926323db 100644 --- a/pallets/subtensor/src/coinbase/root.rs +++ b/pallets/subtensor/src/coinbase/root.rs @@ -110,7 +110,7 @@ impl Pallet { ); // --- 6. Create a network account for the user if it doesn't exist. - Self::create_account_if_non_existent(&coldkey, &hotkey); + Self::create_account_if_non_existent(&coldkey, &hotkey)?; // --- 7. Fetch the current size of the subnetwork. let current_num_root_validators: u16 = Self::get_num_root_validators(); @@ -298,6 +298,10 @@ impl Pallet { SubnetMovingPrice::::remove(netuid); SubnetTaoFlow::::remove(netuid); SubnetEmaTaoFlow::::remove(netuid); + SubnetProtocolFlow::::remove(netuid); + SubnetEmaProtocolFlow::::remove(netuid); + SubnetExcessTao::::remove(netuid); + SubnetRootSellTao::::remove(netuid); SubnetTaoProvided::::remove(netuid); // --- 13. Token / mechanism / registration toggles. @@ -575,7 +579,7 @@ impl Pallet { let interval: I64F64 = I64F64::saturating_from_num(NetworkLockReductionInterval::::get()); let block_emission: I64F64 = I64F64::saturating_from_num( - Self::get_block_emission() + Self::calculate_block_emission() .unwrap_or(1_000_000_000.into()) .to_u64(), ); diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 460a754d45..2854777abc 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -1,5 +1,7 @@ use super::*; +use crate::coinbase::tao::CreditOf; use alloc::collections::BTreeMap; +use frame_support::traits::Imbalance; use safe_math::*; use substrate_fixed::types::U96F32; use subtensor_runtime_common::{AlphaBalance, NetUid, TaoBalance, Token}; @@ -19,12 +21,19 @@ macro_rules! tou64 { } impl Pallet { - pub fn run_coinbase(block_emission: U96F32) { + pub fn run_coinbase(block_emission_credit: CreditOf) { // --- 0. Get current block. let current_block: u64 = Self::get_current_block_as_u64(); + let block_emission = U96F32::saturating_from_num(block_emission_credit.peek()); log::debug!( "Running coinbase for block {current_block:?} with block emission: {block_emission:?}" ); + + // Reset per-block root sell counters from the previous block. + // Root sells happen after coinbase, so their accumulated values + // are consumed here at the start of the next block. + let _ = SubnetRootSellTao::::clear(u32::MAX, None); + // --- 1. Get all subnets (excluding root). let subnets: Vec = Self::get_all_subnet_netuids() .into_iter() @@ -44,7 +53,12 @@ impl Pallet { log::debug!("Root sell flag: {root_sell_flag:?}"); // --- 4. Emit to subnets for this block. - Self::emit_to_subnets(&subnets_to_emit_to, &subnet_emissions, root_sell_flag); + Self::emit_to_subnets( + &subnets_to_emit_to, + &subnet_emissions, + block_emission_credit, + root_sell_flag, + ); // --- 5. Drain pending emissions. let emissions_to_distribute = Self::drain_pending(&subnets, current_block); @@ -58,55 +72,98 @@ impl Pallet { tao_in: &BTreeMap, alpha_in: &BTreeMap, excess_tao: &BTreeMap, + credit: CreditOf, ) { + let mut remaining_credit = credit; for netuid_i in subnets_to_emit_to.iter() { - let tao_in_i: TaoBalance = tou64!(*tao_in.get(netuid_i).unwrap_or(&asfloat!(0))).into(); - let alpha_in_i: AlphaBalance = - tou64!(*alpha_in.get(netuid_i).unwrap_or(&asfloat!(0))).into(); - let tao_to_swap_with: TaoBalance = - 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 tao_to_swap_with > TaoBalance::ZERO { - let buy_swap_result = Self::swap_tao_for_alpha( - *netuid_i, - tao_to_swap_with, - T::SwapInterface::max_price(), - true, - ); - if let Ok(buy_swap_result_ok) = buy_swap_result { - let bought_alpha: AlphaBalance = buy_swap_result_ok.amount_paid_out.into(); - Self::recycle_subnet_alpha(*netuid_i, bought_alpha); + let maybe_subnet_account_id = Self::get_subnet_account_id(*netuid_i); + if let Some(subnet_account_id) = maybe_subnet_account_id { + let tao_in_i: TaoBalance = + tou64!(*tao_in.get(netuid_i).unwrap_or(&asfloat!(0))).into(); + let alpha_in_i: AlphaBalance = + tou64!(*alpha_in.get(netuid_i).unwrap_or(&asfloat!(0))).into(); + let tao_to_swap_with: TaoBalance = + 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 tao_to_swap_with > TaoBalance::ZERO { + // Turn excess_tao portion of credit into TaoBalance on subnet account + match Self::spend_tao(&subnet_account_id, remaining_credit, tao_to_swap_with) { + Ok(remainder) => { + remaining_credit = remainder; + + let buy_swap_result = Self::swap_tao_for_alpha( + *netuid_i, + tao_to_swap_with, + T::SwapInterface::max_price(), + true, + ); + if let Ok(buy_swap_result_ok) = buy_swap_result { + let bought_alpha: AlphaBalance = + buy_swap_result_ok.amount_paid_out.into(); + Self::recycle_subnet_alpha(*netuid_i, bought_alpha); + + // Record actual excess TAO that entered pool. + let actual_excess: TaoBalance = buy_swap_result_ok.amount_paid_in; + SubnetExcessTao::::insert(*netuid_i, actual_excess); + Self::record_protocol_inflow(*netuid_i, actual_excess); + } + } + Err(remainder) => { + remaining_credit = remainder; + let remaining_balance = remaining_credit.peek(); + log::error!( + "Failed to spend credit: tao_to_swap_with = {tao_to_swap_with:?}, netuid_i = {netuid_i:?}, remaining_balance = {remaining_balance:?}" + ); + } + } } - } - - // Inject Alpha in. - let alpha_in_i = - AlphaBalance::from(tou64!(*alpha_in.get(netuid_i).unwrap_or(&asfloat!(0)))); - SubnetAlphaInEmission::::insert(*netuid_i, alpha_in_i); - SubnetAlphaIn::::mutate(*netuid_i, |total| { - *total = total.saturating_add(alpha_in_i); - }); - // Inject TAO in. - let injected_tao: TaoBalance = - tou64!(*tao_in.get(netuid_i).unwrap_or(&asfloat!(0))).into(); - SubnetTaoInEmission::::insert(*netuid_i, injected_tao); - SubnetTAO::::mutate(*netuid_i, |total| { - *total = total.saturating_add(injected_tao); - }); - TotalStake::::mutate(|total| { - *total = total.saturating_add(injected_tao); - }); + // Inject Alpha in. + let alpha_in_i = + AlphaBalance::from(tou64!(*alpha_in.get(netuid_i).unwrap_or(&asfloat!(0)))); + SubnetAlphaInEmission::::insert(*netuid_i, alpha_in_i); + + // Mint alpha and resolve to alpha reserve + Self::resolve_to_alpha_in(Self::mint_alpha(*netuid_i, alpha_in_i)); + + // Inject TAO in. + let injected_tao: TaoBalance = + tou64!(*tao_in.get(netuid_i).unwrap_or(&asfloat!(0))).into(); + if !injected_tao.is_zero() { + match Self::spend_tao(&subnet_account_id, remaining_credit, injected_tao) { + Ok(remainder) => { + remaining_credit = remainder; + + SubnetTaoInEmission::::insert(*netuid_i, injected_tao); + SubnetTAO::::mutate(*netuid_i, |total| { + *total = total.saturating_add(injected_tao); + }); + TotalStake::::mutate(|total| { + *total = total.saturating_add(injected_tao); + }); + + // Record emission injection as protocol inflow. + Self::record_protocol_inflow(*netuid_i, injected_tao); + } + Err(remainder) => { + remaining_credit = remainder; + let remaining_balance = remaining_credit.peek(); + log::error!( + "Failed to spend credit: injected_tao = {injected_tao:?}, netuid_i = {netuid_i:?}, remaining_balance = {remaining_balance:?}" + ); + } + } + } + } + } - // Update total TAO issuance. - let difference_tao = tou64!(*excess_tao.get(netuid_i).unwrap_or(&asfloat!(0))); - TotalIssuance::::mutate(|total| { - *total = total - .saturating_add(injected_tao.into()) - .saturating_add(difference_tao.into()); - }); + // Remaining imbalance should be zero at this point. If not, log error and burn. + let remaining_balance = remaining_credit.peek(); + if !remaining_balance.is_zero() { + // log::error!("Unspent imbalance remains: remaining_balance = {remaining_balance:?}"); + Self::recycle_credit(remaining_credit); } } @@ -124,7 +181,7 @@ impl Pallet { let mut alpha_out: BTreeMap = BTreeMap::new(); let mut excess_tao: BTreeMap = BTreeMap::new(); let tao_block_emission: U96F32 = U96F32::saturating_from_num( - Self::get_block_emission() + Self::calculate_block_emission() .unwrap_or(TaoBalance::ZERO) .to_u64(), ); @@ -166,6 +223,7 @@ impl Pallet { pub fn emit_to_subnets( subnets_to_emit_to: &[NetUid], subnet_emissions: &BTreeMap, + credit: CreditOf, root_sell_flag: bool, ) { // --- 1. Get subnet terms (tao_in, alpha_in, and alpha_out) @@ -178,7 +236,13 @@ impl Pallet { 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); + Self::inject_and_maybe_swap( + subnets_to_emit_to, + &tao_in, + &alpha_in, + &excess_amount, + credit, + ); // --- 3. Inject ALPHA for participants. let cut_percent: U96F32 = Self::get_float_subnet_owner_cut(); @@ -189,9 +253,9 @@ impl Pallet { let alpha_created: AlphaBalance = AlphaBalance::from(tou64!(alpha_out_i)); SubnetAlphaOutEmission::::insert(*netuid_i, alpha_created); - SubnetAlphaOut::::mutate(*netuid_i, |total| { - *total = total.saturating_add(alpha_created); - }); + + // Mint and resolve outstanding alpha + Self::resolve_to_alpha_out(Self::mint_alpha(*netuid_i, alpha_created)); // Calculate the owner cut. let owner_cut_i: U96F32 = alpha_out_i.saturating_mul(cut_percent); @@ -525,6 +589,9 @@ impl Pallet { if let Some(lease_id) = SubnetUidToLeaseId::::get(netuid) { Self::distribute_leased_network_dividends(lease_id, owner_cut); } + + // Auto-lock owner's cut + Self::auto_lock_owner_cut(netuid, owner_cut); } // Distribute mining incentives. diff --git a/pallets/subtensor/src/coinbase/subnet_emissions.rs b/pallets/subtensor/src/coinbase/subnet_emissions.rs index b856ff3d7f..485e2cf662 100644 --- a/pallets/subtensor/src/coinbase/subnet_emissions.rs +++ b/pallets/subtensor/src/coinbase/subnet_emissions.rs @@ -51,6 +51,45 @@ impl Pallet { SubnetTaoFlow::::remove(netuid); } + pub fn record_protocol_inflow(netuid: NetUid, tao: TaoBalance) { + SubnetProtocolFlow::::mutate(netuid, |flow| { + *flow = flow.saturating_add(u64::from(tao) as i64); + }); + } + + pub fn record_protocol_outflow(netuid: NetUid, tao: TaoBalance) { + SubnetProtocolFlow::::mutate(netuid, |flow| { + *flow = flow.saturating_sub(u64::from(tao) as i64); + }); + } + + pub fn reset_protocol_flow(netuid: NetUid) { + SubnetProtocolFlow::::remove(netuid); + } + + fn update_ema_protocol_flow(netuid: NetUid) -> I64F64 { + let current_block: u64 = Self::get_current_block_as_u64(); + + let block_flow = I64F64::saturating_from_num(SubnetProtocolFlow::::get(netuid)); + let (last_block, last_block_ema) = + SubnetEmaProtocolFlow::::get(netuid).unwrap_or((0, I64F64::saturating_from_num(0))); + + if last_block != current_block { + let flow_alpha = I64F64::saturating_from_num(FlowEmaSmoothingFactor::::get()) + .safe_div(I64F64::saturating_from_num(i64::MAX)); + let one = I64F64::saturating_from_num(1); + let ema_flow = (one.saturating_sub(flow_alpha)) + .saturating_mul(last_block_ema) + .saturating_add(flow_alpha.saturating_mul(block_flow)); + SubnetEmaProtocolFlow::::insert(netuid, (current_block, ema_flow)); + + Self::reset_protocol_flow(netuid); + ema_flow + } else { + last_block_ema + } + } + // Update SubnetEmaTaoFlow if needed and return its value for // the current block #[allow(dead_code)] @@ -177,12 +216,23 @@ 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 + let net_flow_enabled = NetTaoFlowEnabled::::get(); + + // Always update the protocol EMA (keeps it warm for when toggled on) + let ema_flows: BTreeMap = subnets_to_emit_to .iter() - .map(|netuid| (*netuid, Self::get_ema_flow(*netuid))) + .map(|netuid| { + let user_ema = Self::get_ema_flow(*netuid); + let net = if net_flow_enabled { + let protocol_ema = Self::update_ema_protocol_flow(*netuid); + user_ema.saturating_sub(protocol_ema) + } else { + user_ema + }; + (*netuid, net) + }) .collect(); - log::debug!("EMA flows: {ema_flows:?}"); + log::debug!("EMA flows (net_flow_enabled={net_flow_enabled}): {ema_flows:?}"); // Clip the EMA flow with lower limit L // z[i] = max{S[i] − L, 0} diff --git a/pallets/subtensor/src/coinbase/tao.rs b/pallets/subtensor/src/coinbase/tao.rs new file mode 100644 index 0000000000..33dbda57fb --- /dev/null +++ b/pallets/subtensor/src/coinbase/tao.rs @@ -0,0 +1,303 @@ +/// This file contains all critical operations with TAO and Alpha: +/// +/// - Minting, burning, recycling, and transferring +/// - Reading colkey TAO balances +/// - Access to subnet TAO reserves +/// +use frame_support::traits::{ + Imbalance, + fungible::Mutate, + tokens::{ + Fortitude, Precision, Preservation, + fungible::{Balanced, Credit, Inspect}, + }, +}; +use sp_runtime::traits::AccountIdConversion; +use sp_runtime::{DispatchError, DispatchResult}; +use subtensor_runtime_common::{NetUid, TaoBalance}; + +use super::*; + +pub type BalanceOf = + <::Currency as fungible::Inspect<::AccountId>>::Balance; + +pub type CreditOf = Credit<::AccountId, ::Currency>; + +pub const MAX_TAO_ISSUANCE: u64 = 21_000_000_000_000_000_u64; + +impl Pallet { + /// Returns Subnet TAO reserve using SubnetTAO map. + /// Do not use subnet account balance because it may also contain + /// locked TAO. + pub fn get_subnet_tao(netuid: NetUid) -> TaoBalance { + SubnetTAO::::get(netuid) + } + + /// Internal function that transfers and updates subtensor pallet total issuance + /// in case of dust collection. + fn transfer_allow_death_update_ti( + origin_coldkey: &T::AccountId, + destination_coldkey: &T::AccountId, + amount: BalanceOf, + ) -> DispatchResult { + // If account balance remainder drops below ED, then account is killed, balance + // is lost, and we need to reduce total issuance in subtensor pallet. Measure + // balance TI before and after to detect the dust. + let balances_ti_before = ::Currency::total_issuance(); + + ::Currency::transfer( + origin_coldkey, + destination_coldkey, + amount, + Preservation::Expendable, + )?; + + let balances_ti_after = ::Currency::total_issuance(); + if balances_ti_after < balances_ti_before { + let burned = balances_ti_before.saturating_sub(balances_ti_after); + TotalIssuance::::mutate(|total| { + *total = total.saturating_sub(burned); + }); + } + + Ok(()) + } + + /// Transfer TAO from one coldkey account to another. + /// + /// This is a plain transfer and may reap the origin account if `amount` reduces + /// its balance below the existential deposit (ED). + pub fn transfer_tao( + origin_coldkey: &T::AccountId, + destination_coldkey: &T::AccountId, + amount: BalanceOf, + ) -> DispatchResult { + // Get full balance including ED + let max_transferrable = Self::get_coldkey_balance(origin_coldkey); + ensure!(amount <= max_transferrable, Error::::InsufficientBalance); + + Self::transfer_allow_death_update_ti(origin_coldkey, destination_coldkey, amount) + } + + /// Transfer all transferable TAO from `origin_coldkey` to `destination_coldkey`, + /// allowing the origin account to be reaped. + /// + /// # Parameters + /// - `origin_coldkey`: Source account. + /// - `destination_coldkey`: Destination account. + /// + /// # Returns + /// DispatchResult of the operation. + /// + /// # Errors + /// - Any error returned by the underlying currency transfer. + pub fn transfer_all_tao_and_kill( + origin_coldkey: &T::AccountId, + destination_coldkey: &T::AccountId, + ) -> DispatchResult { + let amount_to_transfer = ::Currency::reducible_balance( + origin_coldkey, + Preservation::Expendable, + Fortitude::Polite, + ); + + if !amount_to_transfer.is_zero() { + Self::transfer_allow_death_update_ti( + origin_coldkey, + destination_coldkey, + amount_to_transfer, + )?; + } + + Ok(()) + } + + /// Transfer TAO from a coldkey account for staking. + /// + /// If transferring the full `amount` would reap the origin account, this + /// function leaves the existential deposit (ED) in place and transfers less. + /// + /// # Parameters + /// - `netuid`: Subnet identifier. + /// - `origin_coldkey`: Account to transfer TAO from. + /// - `destination_coldkey`: Account to transfer TAO to. + /// - `amount`: Requested amount to transfer. + /// + /// # Returns + /// Returns the actual amount transferred. + /// + /// # Errors + /// Returns [`Error::::InsufficientBalance`] if no positive amount can be + /// transferred while preserving the origin account. + /// + /// Propagates any other transfer error from the underlying currency. + pub fn transfer_tao_to_subnet( + netuid: NetUid, + origin_coldkey: &T::AccountId, + amount: BalanceOf, + ) -> Result, DispatchError> { + if amount.is_zero() { + return Ok(0.into()); + } + + let subnet_account: T::AccountId = + Self::get_subnet_account_id(netuid).ok_or(Error::::SubnetNotExists)?; + + let max_preserving_amount = ::Currency::reducible_balance( + origin_coldkey, + Preservation::Preserve, + Fortitude::Polite, + ); + + let amount_to_transfer = amount.min(max_preserving_amount); + + ensure!( + !amount_to_transfer.is_zero(), + Error::::InsufficientBalance + ); + + ::Currency::transfer( + origin_coldkey, + &subnet_account, + amount_to_transfer, + Preservation::Preserve, + )?; + + Ok(amount_to_transfer) + } + + /// Move unstaked TAO from subnet account to coldkey. + pub fn transfer_tao_from_subnet( + netuid: NetUid, + coldkey: &T::AccountId, + amount: BalanceOf, + ) -> DispatchResult { + let subnet_account: T::AccountId = + Self::get_subnet_account_id(netuid).ok_or(Error::::SubnetNotExists)?; + Self::transfer_tao(&subnet_account, coldkey, amount) + } + + /// Permanently remove TAO amount from existence by moving to the burn + /// address. Does not effect issuance rate + pub fn burn_tao(coldkey: &T::AccountId, amount: BalanceOf) -> DispatchResult { + let burn_address: T::AccountId = T::BurnAccountId::get().into_account_truncating(); + Self::transfer_tao(coldkey, &burn_address, amount)?; + Ok(()) + } + + /// Remove TAO from existence and reduce total issuance. + /// Effects issuance rate by reducing TI. + /// Does not allow the account to drop below ED. + pub fn recycle_tao(coldkey: &T::AccountId, amount: BalanceOf) -> DispatchResult { + // Ensure that the coldkey doesn't drop below ED + let max_preserving_amount = ::Currency::reducible_balance( + coldkey, + Preservation::Preserve, + Fortitude::Polite, + ); + ensure!( + amount <= max_preserving_amount, + Error::::InsufficientBalance + ); + + // Decrease subtensor pallet total issuance + TotalIssuance::::mutate(|total| { + *total = total.saturating_sub(amount); + }); + + let _ = ::Currency::withdraw( + coldkey, + amount, + Precision::Exact, + Preservation::Expendable, + Fortitude::Force, + ) + .map_err(|_| Error::::BalanceWithdrawalError)? + .peek(); + + Ok(()) + } + + pub fn can_remove_balance_from_coldkey_account( + coldkey: &T::AccountId, + amount: BalanceOf, + ) -> bool { + amount <= Self::get_coldkey_balance(coldkey) + } + + /// Returns the full coldkey balance including existential deposit + pub fn get_coldkey_balance(coldkey: &T::AccountId) -> BalanceOf { + ::Currency::reducible_balance( + coldkey, + Preservation::Expendable, + Fortitude::Polite, + ) + } + + /// Returns the balance that can be transfered without killing account + pub fn get_keep_alive_balance(coldkey: &T::AccountId) -> BalanceOf { + ::Currency::reducible_balance( + coldkey, + Preservation::Preserve, + Fortitude::Polite, + ) + } + + /// Create TAO and return the imbalance. + /// + /// The mint workflow is following: + /// 1. mint_tao in block_emission + /// 2. spend_tao in run_coinbase (distribute to subnets) + /// 3. None should be left, so burn the remainder using burn_credit for records + pub fn mint_tao(amount: BalanceOf) -> CreditOf { + // Hard-limit maximum issuance to 21M TAO. Never issue more. + let current_issuance = ::Currency::total_issuance(); + + let remaining_issuance = + TaoBalance::from(MAX_TAO_ISSUANCE).saturating_sub(current_issuance); + let amount_to_issue = amount.min(remaining_issuance); + + // Increase subtensor pallet total issuance + TotalIssuance::::mutate(|total| { + *total = total.saturating_add(amount_to_issue); + }); + + ::Currency::issue(amount_to_issue) + } + + /// Spend part of the imbalance + /// The part parameter is the balance itself that will be credited to the coldkey + /// Return the remaining credit or error + pub fn spend_tao( + coldkey: &T::AccountId, + credit: CreditOf, + part: BalanceOf, + ) -> Result, CreditOf> { + let (to_spend, remainder) = credit.split(part); + + match ::Currency::resolve(coldkey, to_spend) { + Ok(()) => Ok(remainder), + Err(unresolved_to_spend) => Err(unresolved_to_spend.merge(remainder)), + } + } + + /// Finalizes the unused part of the minted TAO. + pub fn recycle_credit(credit: CreditOf) { + let amount = credit.peek(); + if !amount.is_zero() { + // Some credit is remaining: Decrease subtensor pallet total issuance + log::debug!( + "recycle_credit received non-zero credit ({}); will reduce TotalIssuance", + amount, + ); + + TotalIssuance::::mutate(|total| { + *total = total.saturating_sub(amount); + }); + } + } + + pub fn get_total_issuance() -> TaoBalance { + TotalIssuance::::get() + } +} diff --git a/pallets/subtensor/src/extensions/subtensor.rs b/pallets/subtensor/src/extensions/subtensor.rs index 7455dbb78a..64571843e7 100644 --- a/pallets/subtensor/src/extensions/subtensor.rs +++ b/pallets/subtensor/src/extensions/subtensor.rs @@ -7,7 +7,6 @@ use sp_runtime::traits::{ AsSystemOriginSigner, DispatchInfoOf, Dispatchable, Implication, TransactionExtension, ValidateResult, }; -use sp_runtime::transaction_validity::{InvalidTransaction, TransactionValidityError}; use sp_runtime::{ impl_tx_ext_default, transaction_validity::{TransactionSource, TransactionValidity, ValidTransaction}, @@ -17,8 +16,6 @@ use sp_std::vec::Vec; use subtensor_macros::freeze_struct; use subtensor_runtime_common::{CustomTransactionError, NetUid, NetUidStorageIndex}; -const ADD_STAKE_BURN_PRIORITY_BOOST: u64 = 100; - type CallOf = ::RuntimeCall; type OriginOf = ::RuntimeOrigin; @@ -262,13 +259,6 @@ where .map_err(|_| CustomTransactionError::EvmKeyAssociateRateLimitExceeded)?; Ok((Default::default(), (), origin)) } - Some(Call::add_stake_burn { netuid, .. }) => { - Pallet::::ensure_subnet_owner(origin.clone(), *netuid).map_err(|_| { - TransactionValidityError::Invalid(InvalidTransaction::BadSigner) - })?; - - Ok((Self::validity_ok(ADD_STAKE_BURN_PRIORITY_BOOST), (), origin)) - } _ => Ok((Default::default(), (), origin)), } } diff --git a/pallets/subtensor/src/guards/check_coldkey_swap.rs b/pallets/subtensor/src/guards/check_coldkey_swap.rs index 730b694bee..14b1a25ac9 100644 --- a/pallets/subtensor/src/guards/check_coldkey_swap.rs +++ b/pallets/subtensor/src/guards/check_coldkey_swap.rs @@ -88,7 +88,7 @@ mod tests { use pallet_subtensor_proxy::Call as ProxyCall; use sp_core::U256; use sp_runtime::traits::{Dispatchable, Hash}; - use subtensor_runtime_common::ProxyType; + use subtensor_runtime_common::{ProxyType, TaoBalance}; type HashingOf = ::Hashing; @@ -149,6 +149,11 @@ mod tests { RuntimeCall::System(SystemCall::remark { remark: vec![] }) } + fn add_balance_to_coldkey_account(coldkey: &U256, tao: TaoBalance) { + let credit = SubtensorModule::mint_tao(tao); + let _ = SubtensorModule::spend_tao(coldkey, credit, tao).unwrap(); + } + #[test] fn no_active_swap_allows_calls() { new_test_ext(1).execute_with(|| { @@ -241,8 +246,8 @@ mod tests { ColdkeySwapAnnouncements::::insert(real, (now, hash)); // Give delegate enough balance for proxy deposit - SubtensorModule::add_balance_to_coldkey_account(&real, 1_000_000_000.into()); - SubtensorModule::add_balance_to_coldkey_account(&delegate, 1_000_000_000.into()); + add_balance_to_coldkey_account(&real, 1_000_000_000.into()); + add_balance_to_coldkey_account(&delegate, 1_000_000_000.into()); // Register proxy: delegate can act on behalf of real assert_ok!(Proxy::add_proxy( @@ -280,9 +285,9 @@ mod tests { let hash = HashingOf::::hash_of(&U256::from(42)); ColdkeySwapAnnouncements::::insert(real, (now, hash)); - SubtensorModule::add_balance_to_coldkey_account(&real, 1_000_000_000.into()); - SubtensorModule::add_balance_to_coldkey_account(&delegate1, 1_000_000_000.into()); - SubtensorModule::add_balance_to_coldkey_account(&delegate2, 1_000_000_000.into()); + add_balance_to_coldkey_account(&real, 1_000_000_000.into()); + add_balance_to_coldkey_account(&delegate1, 1_000_000_000.into()); + add_balance_to_coldkey_account(&delegate2, 1_000_000_000.into()); // delegate1 can proxy for real, delegate2 can proxy for delegate1 assert_ok!(Proxy::add_proxy( diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index a1ae6cc3bb..75735c7471 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1344,6 +1344,16 @@ pub mod pallet { pub type SubnetTaoInEmission = StorageMap<_, Identity, NetUid, TaoBalance, ValueQuery, DefaultZeroTao>; + /// --- MAP ( netuid ) --> excess_tao | Returns the excess TAO swapped (chain buys) into this subnet on the last block. + #[pallet::storage] + pub type SubnetExcessTao = + StorageMap<_, Identity, NetUid, TaoBalance, ValueQuery, DefaultZeroTao>; + + /// --- MAP ( netuid ) --> root_sell_tao | Returns the TAO received from root dividend sells on this subnet on the last block. + #[pallet::storage] + pub type SubnetRootSellTao = + StorageMap<_, Identity, NetUid, TaoBalance, ValueQuery, DefaultZeroTao>; + /// --- MAP ( netuid ) --> alpha_supply_in_pool | Returns the amount of alpha in the pool. #[pallet::storage] pub type SubnetAlphaIn = @@ -1492,6 +1502,65 @@ pub mod pallet { ValueQuery, >; + /// Lock state for a coldkey on a subnet. + #[crate::freeze_struct("13703236126f1b2b")] + #[derive(Encode, Decode, DecodeWithMemTracking, Clone, PartialEq, Eq, Debug, TypeInfo)] + pub struct LockState { + /// Locked amount, stays constant unless user makes changes. + pub locked_mass: AlphaBalance, + /// Unlocked amount, gradually decays over time. + pub unlocked_mass: AlphaBalance, + /// Matured decaying score (converges to locked_mass over time with MaturityRate rate). + pub conviction: U64F64, + /// Block number of last roll-forward. + pub last_update: u64, + } + + /// --- DMAP ( coldkey, netuid, hotkey ) --> LockState | Exponential lock per coldkey per subnet. + #[pallet::storage] + pub type Lock = StorageNMap< + _, + ( + NMapKey, // coldkey + NMapKey, // subnet + NMapKey, // hotkey + ), + LockState, + OptionQuery, + >; + + /// --- DMAP ( netuid, hotkey ) --> LockState | Total lock per hotkey per subnet. + #[pallet::storage] + pub type HotkeyLock = StorageDoubleMap< + _, + Identity, + NetUid, // subnet + Blake2_128Concat, + T::AccountId, // hotkey + LockState, // Total merged lock + OptionQuery, + >; + + /// Default lock maturity timescale: ~90 days at 12s blocks. + #[pallet::type_value] + pub fn DefaultMaturityRate() -> u64 { + 7200 * 90 + } + + /// --- ITEM( tau_blocks ) | Maturity timescale in blocks for exponential lock. + #[pallet::storage] + pub type MaturityRate = StorageValue<_, u64, ValueQuery, DefaultMaturityRate>; + + /// Default unlock timescale: ~30 days at 12s blocks. + #[pallet::type_value] + pub fn DefaultUnlockRate() -> u64 { + 7200 * 30 + } + + /// --- ITEM( tau_blocks ) | Unlock timescale in blocks for exponential unlocking. + #[pallet::storage] + pub type UnlockRate = StorageValue<_, u64, ValueQuery, DefaultUnlockRate>; + /// Contains last Alpha storage map key to iterate (check first) #[pallet::storage] pub type AlphaMapLastKey = @@ -1517,6 +1586,25 @@ pub mod pallet { pub type SubnetEmaTaoFlow = StorageMap<_, Identity, NetUid, (u64, I64F64), OptionQuery>; + /// --- ITEM --> net_tao_flow_enabled | When true, emission shares use net flow (user - protocol). When false, uses gross user flow only. + #[pallet::type_value] + pub fn DefaultNetTaoFlowEnabled() -> bool { + true + } + #[pallet::storage] + pub type NetTaoFlowEnabled = + StorageValue<_, bool, ValueQuery, DefaultNetTaoFlowEnabled>; + + /// --- MAP ( netuid ) --> subnet_protocol_flow | Per-block accumulator for protocol cost (emission + chain buys - root sells). + #[pallet::storage] + pub type SubnetProtocolFlow = + StorageMap<_, Identity, NetUid, i64, ValueQuery, DefaultZeroI64>; + + /// --- MAP ( netuid ) --> subnet_ema_protocol_flow | EMA of protocol cost flow, same smoothing as SubnetEmaTaoFlow. + #[pallet::storage] + pub type SubnetEmaProtocolFlow = + StorageMap<_, Identity, NetUid, (u64, I64F64), OptionQuery>; + /// Default value for flow cutoff. #[pallet::type_value] pub fn DefaultFlowCutoff() -> I64F64 { @@ -2674,17 +2762,6 @@ impl> Self::get_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid) } - fn increase_balance(coldkey: &T::AccountId, tao: TaoBalance) { - Self::add_balance_to_coldkey_account(coldkey, tao.into()) - } - - fn decrease_balance( - coldkey: &T::AccountId, - tao: TaoBalance, - ) -> Result { - Self::remove_balance_from_coldkey_account(coldkey, tao.into()) - } - fn increase_stake( coldkey: &T::AccountId, hotkey: &T::AccountId, diff --git a/pallets/subtensor/src/macros/config.rs b/pallets/subtensor/src/macros/config.rs index b3da63e437..8eec97a5be 100644 --- a/pallets/subtensor/src/macros/config.rs +++ b/pallets/subtensor/src/macros/config.rs @@ -7,6 +7,8 @@ use frame_support::pallet_macros::pallet_section; mod config { use crate::{CommitmentsInterface, GetAlphaForTao, GetTaoForAlpha}; + use frame_support::PalletId; + use pallet_alpha_assets::AlphaAssetsInterface; use pallet_commitments::GetCommitments; use subtensor_runtime_common::AuthorshipInfo; use subtensor_swap_interface::{SwapEngine, SwapHandler}; @@ -60,6 +62,9 @@ mod config { /// Interface to clean commitments on network dissolution. type CommitmentsInterface: CommitmentsInterface; + /// Interface to mint, burn, and recycle subnet alpha. + type AlphaAssets: AlphaAssetsInterface; + /// Rate limit for associating an EVM key. type EvmKeyAssociateRateLimit: Get; @@ -256,5 +261,11 @@ mod config { /// Maximum percentage of immune UIDs. #[pallet::constant] type MaxImmuneUidsPercentage: Get; + /// Pallet account ID + #[pallet::constant] + type SubtensorPalletId: Get; + /// Burn account ID + #[pallet::constant] + type BurnAccountId: Get; } } diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index b098b58425..a98578d813 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -736,9 +736,7 @@ mod dispatches { /// - Thrown if there is not enough stake on the hotkey to withdwraw this amount. /// #[pallet::call_index(3)] - #[pallet::weight((Weight::from_parts(196_800_000, 0) - .saturating_add(T::DbWeight::get().reads(19)) - .saturating_add(T::DbWeight::get().writes(10)), DispatchClass::Normal, Pays::Yes))] + #[pallet::weight(::WeightInfo::remove_stake())] pub fn remove_stake( origin: OriginFor, hotkey: T::AccountId, @@ -1057,7 +1055,7 @@ mod dispatches { #[pallet::call_index(72)] #[pallet::weight((Weight::from_parts(275_300_000, 0) .saturating_add(T::DbWeight::get().reads(52_u64)) - .saturating_add(T::DbWeight::get().writes(35_u64)), DispatchClass::Normal, Pays::No))] + .saturating_add(T::DbWeight::get().writes(35_u64)), DispatchClass::Normal, Pays::Yes))] pub fn swap_hotkey_v2( origin: OriginFor, hotkey: T::AccountId, @@ -1081,7 +1079,7 @@ mod dispatches { ) -> DispatchResult { ensure_root(origin)?; - if swap_cost.to_u64() > 0 { + if !swap_cost.is_zero() { Self::charge_swap_cost(&old_coldkey, swap_cost)?; } Self::do_swap_coldkey(&old_coldkey, &new_coldkey)?; @@ -1778,7 +1776,7 @@ mod dispatches { pub fn try_associate_hotkey(origin: OriginFor, hotkey: T::AccountId) -> DispatchResult { let coldkey = ensure_signed(origin)?; - let _ = Self::do_try_associate_hotkey(&coldkey, &hotkey); + Self::do_try_associate_hotkey(&coldkey, &hotkey)?; Ok(()) } @@ -1857,7 +1855,7 @@ mod dispatches { amount: AlphaBalance, netuid: NetUid, ) -> DispatchResult { - Self::do_recycle_alpha(origin, hotkey, amount, netuid) + Self::do_recycle_alpha(origin, hotkey, amount, netuid).map(|_| ()) } /// Burns alpha from a cold/hot key pair without reducing `AlphaOut` @@ -1878,7 +1876,7 @@ mod dispatches { amount: AlphaBalance, netuid: NetUid, ) -> DispatchResult { - Self::do_burn_alpha(origin, hotkey, amount, netuid) + Self::do_burn_alpha(origin, hotkey, amount, netuid).map(|_| ()) } /// Sets the pending childkey cooldown (in blocks). Root only. @@ -2533,5 +2531,68 @@ mod dispatches { Self::deposit_event(Event::AutoParentDelegationEnabledSet { hotkey, enabled }); Ok(()) } + + /// Locks stake on a subnet to a specific hotkey, building conviction over time. + /// + /// If no lock exists for (coldkey, subnet), a new one is created. + /// If a lock exists, the destination hotkey must match the existing lock's hotkey. + /// Top-up adds to the locked amount after rolling the lock state forward. + /// + /// # Arguments + /// * `origin` - Must be signed by the coldkey. + /// * `hotkey` - The hotkey to lock stake to. + /// * `netuid` - The subnet on which to lock. + /// * `amount` - The alpha amount to lock. + #[pallet::call_index(136)] + #[pallet::weight(::WeightInfo::lock_stake())] + pub fn lock_stake( + origin: OriginFor, + hotkey: T::AccountId, + netuid: NetUid, + amount: AlphaBalance, + ) -> DispatchResult { + let coldkey = ensure_signed(origin)?; + Self::do_lock_stake(&coldkey, netuid, &hotkey, amount) + } + + /// Unlocks stake on a subnet from a specific hotkey, reducing conviction. + /// + /// # Arguments + /// * `origin` - Must be signed by the coldkey. + /// * `netuid` - The subnet on which the lock exists. + /// * `amount` - The alpha amount to unlock. + #[pallet::call_index(137)] + #[pallet::weight(::WeightInfo::unlock_stake())] + pub fn unlock_stake( + origin: OriginFor, + netuid: NetUid, + amount: AlphaBalance, + ) -> DispatchResult { + let coldkey = ensure_signed(origin)?; + Self::do_unlock_stake(&coldkey, netuid, amount) + } + + /// Moves an existing lock for a coldkey on a subnet from one hotkey to another. + /// + /// The lock is rolled forward to the current block before switching the + /// associated hotkey, preserving the decayed locked mass. The conviction is + /// reset to zero. + /// + /// # Arguments + /// * `origin` - Must be signed by the coldkey that owns the lock. + /// * `destination_hotkey` - The hotkey the lock should target after the move. + /// * `netuid` - The subnet on which the lock exists. + /// # Errors: + /// * `Error::::NoExistingLock` - If no lock exists for the given coldkey and subnet. + #[pallet::call_index(138)] + #[pallet::weight(::WeightInfo::move_lock())] + pub fn move_lock( + origin: OriginFor, + destination_hotkey: T::AccountId, + netuid: NetUid, + ) -> DispatchResult { + let coldkey = ensure_signed(origin)?; + Self::do_move_lock(&coldkey, &destination_hotkey, netuid) + } } } diff --git a/pallets/subtensor/src/macros/errors.rs b/pallets/subtensor/src/macros/errors.rs index dda057bb07..cb120b56b5 100644 --- a/pallets/subtensor/src/macros/errors.rs +++ b/pallets/subtensor/src/macros/errors.rs @@ -171,8 +171,8 @@ mod errors { InvalidIdentity, /// Subnet mechanism does not exist. MechanismDoesNotExist, - /// Trying to unstake your lock amount. - CannotUnstakeLock, + /// Trying to unstake or re-lock the locked amount. + StakeUnavailable, /// Trying to perform action on non-existent subnet. SubnetNotExists, /// Maximum commit limit reached @@ -293,5 +293,17 @@ mod errors { DisabledTemporarily, /// Registration Price Limit Exceeded RegistrationPriceLimitExceeded, + /// Lock hotkey mismatch: existing lock is for a different hotkey. + LockHotkeyMismatch, + /// Insufficient stake on subnet to cover the lock amount. + InsufficientStakeForLock, + /// No existing lock found for the given coldkey and subnet. + NoExistingLock, + /// There is already an active lock for the given coldkey. + ActiveLockExists, + /// A system account cannot be used in this operation + CannotUseSystemAccount, + /// Trying to unlock more than locked + UnlockAmountTooHigh, } } diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index fe1eecb2fc..cdb37bb0dd 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -562,7 +562,7 @@ mod events { /// The subnet identifier. netuid: NetUid, /// The burn increase multiplier value for neuron registration. - burn_increase_mult: u64, + burn_increase_mult: U64F64, }, /// A root validator toggled the "auto parent delegation" flag. @@ -572,5 +572,41 @@ mod events { /// Whether delegation is now enabled. enabled: bool, }, + + /// Stake has been locked to a hotkey on a subnet. + StakeLocked { + /// The coldkey that locked the stake. + coldkey: T::AccountId, + /// The hotkey the stake is locked to. + hotkey: T::AccountId, + /// The subnet the stake is locked on. + netuid: NetUid, + /// The alpha amount locked. + amount: AlphaBalance, + }, + + /// Stake has been unlocked from a hotkey on a subnet. + StakeUnlocked { + /// The coldkey that unlocked the stake. + coldkey: T::AccountId, + /// The hotkey the stake was locked to. + hotkey: T::AccountId, + /// The subnet the stake was locked on. + netuid: NetUid, + /// The alpha amount unlocked. + amount: AlphaBalance, + }, + + /// Stake has been unlocked from a hotkey on a subnet. + LockMoved { + /// The coldkey that moved the lock. + coldkey: T::AccountId, + /// The hotkey the lock was moved from. + origin_hotkey: T::AccountId, + /// The hotkey the lock was moved to. + destination_hotkey: T::AccountId, + /// The subnet the lock is on. + netuid: NetUid, + }, } } diff --git a/pallets/subtensor/src/macros/hooks.rs b/pallets/subtensor/src/macros/hooks.rs index 615e9e62a6..ecd8d4212a 100644 --- a/pallets/subtensor/src/macros/hooks.rs +++ b/pallets/subtensor/src/macros/hooks.rs @@ -131,6 +131,8 @@ mod hooks { .saturating_add(migrations::migrate_rate_limiting_last_blocks::migrate_obsolete_rate_limiting_last_blocks_storage::()) // Re-encode rate limit keys after introducing OwnerHyperparamUpdate variant .saturating_add(migrations::migrate_rate_limit_keys::migrate_rate_limit_keys::()) + // Remove AddStakeBurn entries from LastRateLimitedBlock + .saturating_add(migrations::migrate_remove_add_stake_burn_rate_limit::migrate_remove_add_stake_burn_rate_limit::()) // Migrate remove network modality .saturating_add(migrations::migrate_remove_network_modality::migrate_remove_network_modality::()) // Migrate Immunity Period @@ -170,7 +172,9 @@ mod hooks { // Migrate fix bad hk swap .saturating_add(migrations::migrate_fix_bad_hk_swap::migrate_fix_bad_hk_swap::()) // Fix RootClaimed overclaim caused by single-subnet hotkey swap bug - .saturating_add(migrations::migrate_fix_root_claimed_overclaim::migrate_fix_root_claimed_overclaim::()); + .saturating_add(migrations::migrate_fix_root_claimed_overclaim::migrate_fix_root_claimed_overclaim::()) + // Mint missing SubnetTAO and SubnetLocked into subnet accounts to make TotalIssuance match in balances and subtensor + .saturating_add(migrations::migrate_subnet_balances::migrate_subnet_balances::()); weight } diff --git a/pallets/subtensor/src/migrations/migrate_init_total_issuance.rs b/pallets/subtensor/src/migrations/migrate_init_total_issuance.rs index 3d625aee30..665c340076 100644 --- a/pallets/subtensor/src/migrations/migrate_init_total_issuance.rs +++ b/pallets/subtensor/src/migrations/migrate_init_total_issuance.rs @@ -14,7 +14,70 @@ pub mod deprecated_loaded_emission_format { StorageMap, Identity, u16, Vec<(AccountIdOf, u64)>, OptionQuery>; } +/// This on-going migration is disabled as part of imbalances work. pub(crate) fn migrate_init_total_issuance() -> Weight { + // let subnets_len = crate::NetworksAdded::::iter().count() as u64; + + // // Retrieve the total balance of all accounts + // let total_account_balances = <::Currency as fungible::Inspect< + // ::AccountId, + // >>::total_issuance(); + + // // Get the total stake from the system + // let prev_total_stake = crate::TotalStake::::get(); + + // // Calculate new total stake using the sum of all subnet TAO + // let total_subnet_tao = + // crate::SubnetTAO::::iter().fold(TaoBalance::ZERO, |acc, (_, v)| acc.saturating_add(v)); + + // let total_stake = total_subnet_tao; + // // Update the total stake in storage + // crate::TotalStake::::put(total_stake); + // log::info!( + // "Subtensor Pallet Total Stake Updated: previous: {prev_total_stake:?}, new: {total_stake:?}" + // ); + // // Retrieve the previous total issuance for logging purposes + // let prev_total_issuance = crate::TotalIssuance::::get(); + + // // Calculate the new total issuance + // let new_total_issuance: TaoBalance = total_account_balances.saturating_add(total_stake).into(); + + // // Update the total issuance in storage + // crate::TotalIssuance::::put(new_total_issuance); + + // // Log the change in total issuance + // log::info!( + // "Subtensor Pallet Total Issuance Updated: previous: {prev_total_issuance:?}, new: {new_total_issuance:?}" + // ); + + // // Return the weight of the operation + // // We performed subnets_len + 5 reads and 1 write + // ::DbWeight::get().reads_writes(subnets_len.saturating_add(5), 2) + + log::info!("Subtensor Pallet Total Issuance ongoing update migration is disabled."); + T::DbWeight::get().reads(0) +} + +/// This on-going migration is disabled as part of imbalances work. +pub(crate) fn migrate_init_total_issuance_once() -> Weight { + let migration_name = b"migrate_init_total_issuance_once".to_vec(); + let 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 subnets_len = crate::NetworksAdded::::iter().count() as u64; // Retrieve the total balance of all accounts @@ -49,9 +112,19 @@ pub(crate) fn migrate_init_total_issuance() -> Weight { "Subtensor Pallet Total Issuance Updated: previous: {prev_total_issuance:?}, new: {new_total_issuance:?}" ); + //////////////////////////////////////////////////////// + + HasMigrationRun::::insert(&migration_name, true); + + log::info!( + target: "runtime", + "Migration '{}' completed successfully.", + String::from_utf8_lossy(&migration_name) + ); + // Return the weight of the operation // We performed subnets_len + 5 reads and 1 write - ::DbWeight::get().reads_writes(subnets_len.saturating_add(5), 2) + ::DbWeight::get().reads_writes(subnets_len.saturating_add(6), 3) } pub mod initialise_total_issuance { @@ -72,7 +145,9 @@ pub mod initialise_total_issuance { /// /// Returns the weight of the migration operation. fn on_runtime_upgrade() -> Weight { - super::migrate_init_total_issuance::() + let mut weight = super::migrate_init_total_issuance::(); + weight.saturating_accrue(super::migrate_init_total_issuance_once::()); + weight } /// Performs post-upgrade checks to ensure the migration was successful. diff --git a/pallets/subtensor/src/migrations/migrate_rao.rs b/pallets/subtensor/src/migrations/migrate_rao.rs index 6092d41c6f..e4d181e813 100644 --- a/pallets/subtensor/src/migrations/migrate_rao.rs +++ b/pallets/subtensor/src/migrations/migrate_rao.rs @@ -91,7 +91,12 @@ pub fn migrate_rao() -> Weight { // .checked_div(I96F32::from_num(1_000_000_000)) // .unwrap_or(I96F32::from_num(0.0)), // ); - Pallet::::add_balance_to_coldkey_account(&owner, remaining_lock.into()); + + // This code mimics what used to be here previously (add_balance_to_coldkey_account) as + // close as reasonably possible. + let credit = Pallet::::mint_tao(remaining_lock.into()); + let _ = Pallet::::spend_tao(&owner, credit, remaining_lock.into()); + SubnetLocked::::insert(netuid, TaoBalance::ZERO); // Clear lock amount. SubnetTAO::::insert(netuid, pool_initial_tao); TotalStake::::mutate(|total| { @@ -107,8 +112,9 @@ pub fn migrate_rao() -> Weight { if let Ok(owner_coldkey) = SubnetOwner::::try_get(netuid) { // Set Owner as the coldkey. SubnetOwnerHotkey::::insert(netuid, owner_coldkey.clone()); - // Associate the coldkey to coldkey. - Pallet::::create_account_if_non_existent(&owner_coldkey, &owner_coldkey); + // Associate the coldkey to coldkey. The function only fails if hotkey is a system + // account, which is never the case in this migration. Hence, the result can be ignored. + let _ = Pallet::::create_account_if_non_existent(&owner_coldkey, &owner_coldkey); // Only register the owner coldkey if it's not already a hotkey on the subnet. if !Uids::::contains_key(*netuid, &owner_coldkey) { diff --git a/pallets/subtensor/src/migrations/migrate_remove_add_stake_burn_rate_limit.rs b/pallets/subtensor/src/migrations/migrate_remove_add_stake_burn_rate_limit.rs new file mode 100644 index 0000000000..dcbf307855 --- /dev/null +++ b/pallets/subtensor/src/migrations/migrate_remove_add_stake_burn_rate_limit.rs @@ -0,0 +1,53 @@ +use alloc::string::String; +use alloc::vec::Vec; +use frame_support::{traits::Get, weights::Weight}; + +use crate::{Config, HasMigrationRun, LastRateLimitedBlock, RateLimitKey}; + +const MIGRATION_NAME: &[u8] = b"migrate_remove_add_stake_burn_rate_limit"; + +pub fn migrate_remove_add_stake_burn_rate_limit() -> Weight { + let mut weight = T::DbWeight::get().reads(1); + + if HasMigrationRun::::get(MIGRATION_NAME) { + log::info!( + "Migration '{}' already executed - skipping", + String::from_utf8_lossy(MIGRATION_NAME) + ); + return weight; + } + + log::info!( + "Running migration '{}'", + String::from_utf8_lossy(MIGRATION_NAME) + ); + + let mut scanned_count = 0u64; + let keys_to_remove = LastRateLimitedBlock::::iter_keys() + .filter_map(|key| { + scanned_count = scanned_count.saturating_add(1); + matches!(key, RateLimitKey::AddStakeBurn(_)).then_some(key) + }) + .collect::>(); + let removed_count = keys_to_remove.len() as u64; + + weight = weight.saturating_add(T::DbWeight::get().reads(scanned_count)); + + for key in &keys_to_remove { + LastRateLimitedBlock::::remove(key); + } + + weight = weight.saturating_add(T::DbWeight::get().writes(removed_count)); + + HasMigrationRun::::insert(MIGRATION_NAME, true); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + + log::info!( + "Migration '{}' completed. scanned_entries={}, removed_add_stake_burn_entries={}", + String::from_utf8_lossy(MIGRATION_NAME), + scanned_count, + removed_count + ); + + weight +} diff --git a/pallets/subtensor/src/migrations/migrate_subnet_balances.rs b/pallets/subtensor/src/migrations/migrate_subnet_balances.rs new file mode 100644 index 0000000000..b1c5b04202 --- /dev/null +++ b/pallets/subtensor/src/migrations/migrate_subnet_balances.rs @@ -0,0 +1,127 @@ +use super::*; +use frame_support::{ + traits::{Get, fungible::Inspect}, + weights::Weight, +}; + +/// Performs migration to mint SubnetTAO and subnet locked funds into subnet accounts. +/// +/// # Arguments +/// +/// # Returns +/// +/// * `Weight` - The computational weight of this operation. +/// +pub fn migrate_subnet_balances() -> Weight { + let migration_name = b"migrate_subnet_balances".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 balances_total_issuance_before = ::Currency::total_issuance(); + let subtensor_total_issuance_before = TotalIssuance::::get(); + + // Mint SubnetTAO into subnet accounts + // The mint_tao will be adding to subtensor TotalIssuance (which is not the intention + // and will be corrected below). There is no u64 saturation possible, so it is safe to + // add the whole amount to TI and then reduce back. + let mut total_subnet_tao = TaoBalance::ZERO; + SubnetTAO::::iter().for_each(|(netuid, tao)| { + if let Some(subnet_account) = Pallet::::get_subnet_account_id(netuid) { + let credit = Pallet::::mint_tao(tao); + let _ = Pallet::::spend_tao(&subnet_account, credit, tao); + total_subnet_tao = total_subnet_tao.saturating_add(tao); + weight = weight.saturating_add(T::DbWeight::get().reads_writes(2, 2)); + } + }); + + // Mint SubnetLocked into subnet accounts + // Currently (3.3.13) we still burn TAO less initial pool value (which is min lock cost) on + // registrations and record full lock cost (including initial pool TAO) in SubnetLocked. + // Initial pool TAO is accounted for both in SubnetLocked and SubnetTAO. The double-accounted + // initial pool TAO equals NetworkMinLockCost, which is 1 TAO per subnet. It is only + // double-accounted in situation when owner emission is zero and subnet is dissolved, which is + // only known in the future and is uncertain currently. To make accounting accurate and certain, + // we stay with pessimistic approach and rather avoid minting more than 21M TAO. This means that + // subnet accounts will be credited SubnetLocked amount less initial pool TAO, but the + // TotalIssuance recorded will be increased by the full SubnetLocked amount. + let mut total_subnet_locked = TaoBalance::ZERO; + SubnetLocked::::iter().for_each(|(netuid, tao)| { + if let Some(subnet_account) = Pallet::::get_subnet_account_id(netuid) { + let initial_pool_tao = NetworkMinLockCost::::get(); + let tao_lock = tao.saturating_sub(initial_pool_tao); + let credit = Pallet::::mint_tao(tao_lock); + let _ = Pallet::::spend_tao(&subnet_account, credit, tao_lock); + total_subnet_locked = total_subnet_locked.saturating_add(tao_lock); + weight = weight.saturating_add(T::DbWeight::get().reads_writes(2, 2)); + } + }); + + // Remark about migrate_restore_subnet_locked migration: + // + // In rao release (v2.0.0) the lock was burned (TotalIssuance reduction), in the subsequent + // migration migrate_restore_subnet_locked in the version v3.2.8 we restored locks into SubnetLocked, + // but did not increase the TotalIssuance back, which is correct because in v3.2.8 we keep SubnetLocked + // in non-issued state. This TAO is added to TotalIssuance when subnet is dissolved. + // + // mint_tao increases subtensor TotalIssuance, but this is not the intention for SubnetTAO + // because staked TAO is already accounted for in it subtensor pallet TotalIssuance. Reduce + // it back. + // + // SubnetLocked, in opposite, was not previously included in the subtensor TotalIssuance + // because we call recycle_tao in subnet registration. + // + TotalIssuance::::mutate(|total| *total = total.saturating_sub(total_subnet_tao)); + + // Update the total issuance in storage + let balances_total_issuance = ::Currency::total_issuance(); + let subtensor_total_issuance = TotalIssuance::::get(); + weight = weight.saturating_add(T::DbWeight::get().reads(2)); + log::warn!( + " balances TI initial = {}", + balances_total_issuance_before + ); + log::warn!(" balances TI final = {}", balances_total_issuance); + log::warn!( + " subtensor TI initial = {}", + subtensor_total_issuance_before + ); + log::warn!(" subtensor TI final = {}", subtensor_total_issuance); + log::warn!(" total_subnet_tao = {}", total_subnet_tao); + log::warn!(" total_subnet_locked = {}", total_subnet_locked); + if balances_total_issuance != subtensor_total_issuance { + log::warn!( + "Balances and Subtensor total issuance still do not match: {} vs {}. Making them match now.", + balances_total_issuance, + subtensor_total_issuance + ); + TotalIssuance::::put(balances_total_issuance); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + } + + //////////////////////////////////////////////////////// + + HasMigrationRun::::insert(&migration_name, true); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + + log::info!( + target: "runtime", + "Migration '{}' completed successfully.", + String::from_utf8_lossy(&migration_name) + ); + weight +} diff --git a/pallets/subtensor/src/migrations/migrate_subnet_locked.rs b/pallets/subtensor/src/migrations/migrate_subnet_locked.rs index 69850e7daf..73bb183e00 100644 --- a/pallets/subtensor/src/migrations/migrate_subnet_locked.rs +++ b/pallets/subtensor/src/migrations/migrate_subnet_locked.rs @@ -10,15 +10,6 @@ pub fn migrate_restore_subnet_locked() -> Weight { let migration_name = b"migrate_restore_subnet_locked".to_vec(); let mut weight = T::DbWeight::get().reads(1); - if HasMigrationRun::::get(&migration_name) { - log::info!( - target: "runtime", - "Migration '{}' already run - skipping.", - String::from_utf8_lossy(&migration_name) - ); - return weight; - } - // Snapshot: NetworkLastLockCost at (registration_block + 1) for each netuid. const SUBNET_LOCKED: &[(u16, u64)] = &[ (65, 37_274_536_408), @@ -87,6 +78,15 @@ pub fn migrate_restore_subnet_locked() -> Weight { (128, 145_645_807_991), ]; + if HasMigrationRun::::get(&migration_name) { + log::info!( + target: "runtime", + "Migration '{}' already run - skipping.", + String::from_utf8_lossy(&migration_name) + ); + return weight; + } + let mut inserted: u32 = 0; let mut total_rao: u128 = 0; diff --git a/pallets/subtensor/src/migrations/migrate_total_issuance.rs b/pallets/subtensor/src/migrations/migrate_total_issuance.rs index 53dee0d622..ba11ce363c 100644 --- a/pallets/subtensor/src/migrations/migrate_total_issuance.rs +++ b/pallets/subtensor/src/migrations/migrate_total_issuance.rs @@ -19,6 +19,12 @@ pub mod deprecated_loaded_emission_format { StorageMap, Identity, u16, Vec<(AccountIdOf, u64)>, OptionQuery>; } +/// Note: This migration is now disabled. We needed it to sync up two different total issuance counters: +/// 1. Balances pallet +/// 2. Subtensor pallet +/// Now that two total issuances are naturally synched, it is not needed anymore, and it will lead to an +/// incorrect state if it runs. +/// /// Performs migration to update the total issuance based on the sum of stakes and total balances. /// /// This migration is applicable only if the current storage version is 5, after which it updates the storage version to 6. @@ -37,48 +43,50 @@ pub mod deprecated_loaded_emission_format { /// let weight = migrate_total_issuance::(false); /// ``` pub fn migrate_total_issuance(test: bool) -> Weight { - // Initialize migration weight with the cost of reading the storage version - let mut weight = T::DbWeight::get().reads(1); + // // Initialize migration weight with the cost of reading the storage version + // let mut weight = T::DbWeight::get().reads(1); - // Execute migration if the current storage version is 5 or if in test mode - if Pallet::::on_chain_storage_version() == StorageVersion::new(5) || test { - // Calculate the sum of all stake values - let stake_sum = Owner::::iter() - .map(|(hotkey, _coldkey)| Pallet::::get_total_stake_for_hotkey(&hotkey)) - .fold(TaoBalance::ZERO, |acc, stake| acc.saturating_add(stake)); - // Add weight for reading all Owner and TotalHotkeyStake entries - weight = weight.saturating_add( - T::DbWeight::get().reads((Owner::::iter().count() as u64).saturating_mul(2)), - ); + // // Execute migration if the current storage version is 5 or if in test mode + // if Pallet::::on_chain_storage_version() == StorageVersion::new(5) || test { + // // Calculate the sum of all stake values + // let stake_sum = Owner::::iter() + // .map(|(hotkey, _coldkey)| Pallet::::get_total_stake_for_hotkey(&hotkey)) + // .fold(TaoBalance::ZERO, |acc, stake| acc.saturating_add(stake)); + // // Add weight for reading all Owner and TotalHotkeyStake entries + // weight = weight.saturating_add( + // T::DbWeight::get().reads((Owner::::iter().count() as u64).saturating_mul(2)), + // ); - // Retrieve the total balance sum - let total_balance = ::Currency::total_issuance(); - // Add weight for reading total issuance - weight = weight.saturating_add(T::DbWeight::get().reads(1)); + // // Retrieve the total balance sum + // let total_balance = ::Currency::total_issuance(); + // // Add weight for reading total issuance + // weight = weight.saturating_add(T::DbWeight::get().reads(1)); - // Attempt to convert total balance to u64 - match TryInto::::try_into(total_balance) { - Ok(total_balance_sum) => { - // Compute the total issuance value - let total_issuance_value = stake_sum - .saturating_add(total_balance_sum.into()); + // // Attempt to convert total balance to u64 + // match TryInto::::try_into(total_balance) { + // Ok(total_balance_sum) => { + // // Compute the total issuance value + // let total_issuance_value = stake_sum + // .saturating_add(total_balance_sum.into()); - // Update the total issuance in storage - TotalIssuance::::put(total_issuance_value); + // // Update the total issuance in storage + // TotalIssuance::::put(total_issuance_value); - // Update the storage version to 6 - StorageVersion::new(6).put::>(); + // // Update the storage version to 6 + // StorageVersion::new(6).put::>(); - // Add weight for writing total issuance and storage version - weight = weight.saturating_add(T::DbWeight::get().writes(2)); - } - Err(_) => { - // TODO: Implement proper error handling for conversion failure - log::error!("Failed to convert total balance to u64, migration aborted"); - } - } - } + // // Add weight for writing total issuance and storage version + // weight = weight.saturating_add(T::DbWeight::get().writes(2)); + // } + // Err(_) => { + // // TODO: Implement proper error handling for conversion failure + // log::error!("Failed to convert total balance to u64, migration aborted"); + // } + // } + // } - // Return the computed weight of the migration process - weight + // // Return the computed weight of the migration process + // weight + + T::DbWeight::get().reads(0) } diff --git a/pallets/subtensor/src/migrations/mod.rs b/pallets/subtensor/src/migrations/mod.rs index b95d38fa4c..d8177a8ccf 100644 --- a/pallets/subtensor/src/migrations/mod.rs +++ b/pallets/subtensor/src/migrations/mod.rs @@ -35,6 +35,7 @@ pub mod migrate_populate_owned_hotkeys; pub mod migrate_rao; pub mod migrate_rate_limit_keys; pub mod migrate_rate_limiting_last_blocks; +pub mod migrate_remove_add_stake_burn_rate_limit; pub mod migrate_remove_commitments_rate_limit; pub mod migrate_remove_network_modality; pub mod migrate_remove_old_identity_maps; @@ -54,6 +55,7 @@ pub mod migrate_set_nominator_min_stake; pub mod migrate_set_registration_enable; pub mod migrate_set_subtoken_enabled; pub mod migrate_stake_threshold; +pub mod migrate_subnet_balances; pub mod migrate_subnet_limit_to_default; pub mod migrate_subnet_locked; pub mod migrate_subnet_symbols; diff --git a/pallets/subtensor/src/staking/account.rs b/pallets/subtensor/src/staking/account.rs index 20a6ff3036..3252c0836f 100644 --- a/pallets/subtensor/src/staking/account.rs +++ b/pallets/subtensor/src/staking/account.rs @@ -6,7 +6,7 @@ impl Pallet { hotkey: &T::AccountId, ) -> DispatchResult { // Ensure the hotkey is not already associated with a coldkey - Self::create_account_if_non_existent(coldkey, hotkey); + Self::create_account_if_non_existent(coldkey, hotkey)?; Ok(()) } diff --git a/pallets/subtensor/src/staking/add_stake.rs b/pallets/subtensor/src/staking/add_stake.rs index a4ad9b92cf..b88e75cd31 100644 --- a/pallets/subtensor/src/staking/add_stake.rs +++ b/pallets/subtensor/src/staking/add_stake.rs @@ -1,4 +1,3 @@ -use substrate_fixed::types::I96F32; use subtensor_runtime_common::{NetUid, TaoBalance}; use subtensor_swap_interface::{Order, SwapHandler}; @@ -49,8 +48,6 @@ impl Pallet { "do_add_stake( origin:{coldkey:?} hotkey:{hotkey:?}, netuid:{netuid:?}, stake_to_be_added:{stake_to_be_added:?} )" ); - Self::ensure_subtoken_enabled(netuid)?; - // 2. Validate user input Self::validate_add_stake( &coldkey, @@ -61,19 +58,13 @@ impl Pallet { false, )?; - // 3. Ensure the remove operation from the coldkey is a success. - let tao_staked: I96F32 = - Self::remove_balance_from_coldkey_account(&coldkey, stake_to_be_added.into())? - .to_u64() - .into(); - - // 4. Swap the stake into alpha on the subnet and increase counters. + // 3. Swap the stake into alpha on the subnet and increase counters. // Emit the staking event. Self::stake_into_subnet( &hotkey, &coldkey, netuid, - tao_staked.saturating_to_num::().into(), + stake_to_be_added, T::SwapInterface::max_price(), true, false, @@ -156,17 +147,13 @@ impl Pallet { Self::maybe_become_delegate(&hotkey); } - // 5. Ensure the remove operation from the coldkey is a success. - let tao_staked = - Self::remove_balance_from_coldkey_account(&coldkey, possible_stake.into())?; - - // 6. Swap the stake into alpha on the subnet and increase counters. + // 5. Swap the stake into alpha on the subnet and increase counters. // Emit the staking event. Self::stake_into_subnet( &hotkey, &coldkey, netuid, - tao_staked, + possible_stake, limit_price, true, false, diff --git a/pallets/subtensor/src/staking/claim_root.rs b/pallets/subtensor/src/staking/claim_root.rs index 58babb79a6..304eb37e5b 100644 --- a/pallets/subtensor/src/staking/claim_root.rs +++ b/pallets/subtensor/src/staking/claim_root.rs @@ -179,12 +179,45 @@ impl Pallet { } }; - Self::increase_stake_for_hotkey_and_coldkey_on_subnet( - hotkey, - coldkey, - NetUid::ROOT, - owed_tao.amount_paid_out.to_u64().into(), - ); + // Record root sell as protocol outflow (reduces protocol cost). + let root_sell_tao: TaoBalance = owed_tao.amount_paid_out; + SubnetRootSellTao::::mutate(netuid, |total| { + *total = total.saturating_add(root_sell_tao); + }); + Self::record_protocol_outflow(netuid, root_sell_tao); + + // Transfer unstaked TAO from subnet account to the root subnet account + // and increase root stake. + if let Some(root_subnet_account_id) = Self::get_subnet_account_id(NetUid::ROOT) + && Self::transfer_tao_from_subnet( + netuid, + &root_subnet_account_id, + owed_tao.amount_paid_out.into(), + ) + .is_ok() + { + Self::increase_stake_for_hotkey_and_coldkey_on_subnet( + hotkey, + coldkey, + NetUid::ROOT, + owed_tao.amount_paid_out.to_u64().into(), + ); + + // Increase root subnet SubnetTAO + SubnetTAO::::mutate(NetUid::ROOT, |total| { + *total = total.saturating_add(owed_tao.amount_paid_out.into()); + }); + + // Increase root SubnetAlphaOut + SubnetAlphaOut::::mutate(NetUid::ROOT, |total| { + *total = total.saturating_add(u64::from(owed_tao.amount_paid_out).into()); + }); + + // Increase Total Stake + TotalStake::::mutate(|total| { + *total = total.saturating_add(owed_tao.amount_paid_out.into()); + }); + } Self::add_stake_adjust_root_claimed_for_hotkey_and_coldkey( hotkey, diff --git a/pallets/subtensor/src/staking/helpers.rs b/pallets/subtensor/src/staking/helpers.rs index 5c785f199b..70e7f2ae57 100644 --- a/pallets/subtensor/src/staking/helpers.rs +++ b/pallets/subtensor/src/staking/helpers.rs @@ -1,11 +1,4 @@ use alloc::collections::BTreeMap; -use frame_support::traits::{ - Imbalance, - tokens::{ - Fortitude, Precision, Preservation, - fungible::{Balanced as _, Inspect as _}, - }, -}; use safe_math::*; use share_pool::SafeFloat; use substrate_fixed::types::U96F32; @@ -132,7 +125,16 @@ impl Pallet { // Creates a cold - hot pairing account if the hotkey is not already an active account. // - pub fn create_account_if_non_existent(coldkey: &T::AccountId, hotkey: &T::AccountId) { + pub fn create_account_if_non_existent( + coldkey: &T::AccountId, + hotkey: &T::AccountId, + ) -> DispatchResult { + // Only allow to register non-system hotkeys + ensure!( + Self::is_subnet_account_id(hotkey).is_none(), + Error::::CannotUseSystemAccount + ); + if !Self::hotkey_account_exists(hotkey) { Owner::::insert(hotkey, coldkey); @@ -150,6 +152,17 @@ impl Pallet { StakingHotkeys::::insert(coldkey, staking_hotkeys); } } + Ok(()) + } + + pub fn set_hotkey_owner(coldkey: &T::AccountId, hotkey: &T::AccountId) -> DispatchResult { + // Only allow to register non-system hotkeys + ensure!( + Self::is_subnet_account_id(hotkey).is_none(), + Error::::CannotUseSystemAccount + ); + Owner::::insert(hotkey, coldkey); + Ok(()) } //// If the hotkey is not a delegate, make it a delegate. @@ -233,19 +246,18 @@ impl Pallet { // Remove the stake from the nominator account. (this is a more forceful unstake operation which ) // Actually deletes the staking account. // Do not apply any fees - let maybe_cleared_stake = Self::unstake_from_subnet( + if Self::unstake_from_subnet( hotkey, coldkey, + coldkey, netuid, alpha_stake, T::SwapInterface::min_price(), false, - ); - - if let Ok(cleared_stake) = maybe_cleared_stake { - // Add the stake to the coldkey account. - Self::add_balance_to_coldkey_account(coldkey, cleared_stake.into()); - } else { + ) + .is_err() + { + // Ignore errors if unstaking fails // Just clear small alpha let alpha = Self::get_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid); @@ -253,6 +265,9 @@ impl Pallet { hotkey, coldkey, netuid, alpha, ); } + + // Reduce lock (if exists) by the cleaned stake amount + Self::force_reduce_lock(coldkey, netuid, alpha_stake); } } } @@ -268,103 +283,10 @@ impl Pallet { } } - pub fn add_balance_to_coldkey_account( - coldkey: &T::AccountId, - amount: <::Currency as fungible::Inspect<::AccountId>>::Balance, - ) { - // infallible - let _ = ::Currency::deposit(coldkey, amount, Precision::BestEffort); - } - - pub fn can_remove_balance_from_coldkey_account( - coldkey: &T::AccountId, - amount: <::Currency as fungible::Inspect<::AccountId>>::Balance, - ) -> bool { - let current_balance = Self::get_coldkey_balance(coldkey); - if amount > current_balance { - return false; - } - - // This bit is currently untested. @todo - - ::Currency::can_withdraw(coldkey, amount) - .into_result(false) - .is_ok() - } - - pub fn get_coldkey_balance( - coldkey: &T::AccountId, - ) -> <::Currency as fungible::Inspect<::AccountId>>::Balance - { - ::Currency::reducible_balance( - coldkey, - Preservation::Expendable, - Fortitude::Polite, - ) - } - - #[must_use = "Balance must be used to preserve total issuance of token"] - pub fn remove_balance_from_coldkey_account( - coldkey: &T::AccountId, - amount: <::Currency as fungible::Inspect<::AccountId>>::Balance, - ) -> Result { - if amount.is_zero() { - return Ok(TaoBalance::ZERO); - } - - let credit = ::Currency::withdraw( - coldkey, - amount, - Precision::BestEffort, - Preservation::Preserve, - Fortitude::Polite, - ) - .map_err(|_| Error::::BalanceWithdrawalError)? - .peek(); - - if credit.is_zero() { - return Err(Error::::ZeroBalanceAfterWithdrawn.into()); - } - - Ok(credit.into()) - } - - pub fn kill_coldkey_account( - coldkey: &T::AccountId, - amount: <::Currency as fungible::Inspect<::AccountId>>::Balance, - ) -> Result { - if amount.is_zero() { - return Ok(0.into()); - } - - let credit = ::Currency::withdraw( - coldkey, - amount, - Precision::Exact, - Preservation::Expendable, - Fortitude::Force, - ) - .map_err(|_| Error::::BalanceWithdrawalError)? - .peek(); - - if credit.is_zero() { - return Err(Error::::ZeroBalanceAfterWithdrawn.into()); - } - - Ok(credit) - } - pub fn is_user_liquidity_enabled(netuid: NetUid) -> bool { T::SwapInterface::is_user_liquidity_enabled(netuid) } - pub fn recycle_subnet_alpha(netuid: NetUid, amount: AlphaBalance) { - // TODO: record recycled alpha in a tracker - SubnetAlphaOut::::mutate(netuid, |total| { - *total = total.saturating_sub(amount); - }); - } - /// The function clears Alpha map in batches. Each run will check ALPHA_MAP_BATCH_SIZE /// alphas. It keeps the alpha value stored when it's >= than MIN_ALPHA. /// The function uses AlphaMapLastKey as a storage for key iterator between runs. @@ -464,10 +386,6 @@ impl Pallet { } } - pub fn burn_subnet_alpha(_netuid: NetUid, _amount: AlphaBalance) { - // Do nothing; TODO: record burned alpha in a tracker - } - /// Several alpha iteration helpers that merge key space from Alpha and AlphaV2 maps pub fn alpha_iter() -> impl Iterator { // Old Alpha shares format: U64F64 -> SafeFloat diff --git a/pallets/subtensor/src/staking/lock.rs b/pallets/subtensor/src/staking/lock.rs new file mode 100644 index 0000000000..05a2ebde61 --- /dev/null +++ b/pallets/subtensor/src/staking/lock.rs @@ -0,0 +1,748 @@ +use super::*; +use sp_std::collections::btree_map::BTreeMap; +use sp_std::ops::Neg; +use substrate_fixed::transcendental::exp; +use substrate_fixed::types::{I64F64, U64F64}; +use subtensor_runtime_common::NetUid; + +impl Pallet { + pub fn insert_lock_state( + coldkey: &T::AccountId, + netuid: NetUid, + hotkey: &T::AccountId, + lock_state: LockState, + ) { + if !lock_state.locked_mass.is_zero() || !lock_state.unlocked_mass.is_zero() { + Lock::::insert((coldkey, netuid, hotkey), lock_state); + } else { + // If there is no record previously, this is a no-op + Lock::::remove((coldkey, netuid, hotkey)); + } + } + + pub fn insert_hotkey_lock_state(netuid: NetUid, hotkey: &T::AccountId, lock_state: LockState) { + if !lock_state.locked_mass.is_zero() || !lock_state.unlocked_mass.is_zero() { + HotkeyLock::::insert(netuid, hotkey, lock_state); + } else { + HotkeyLock::::remove(netuid, hotkey); + } + } + + /// Computes exp(-dt / tau) as a U64F64 decay factor. + pub fn exp_decay(dt: u64, tau: u64) -> U64F64 { + if tau == 0 || dt == 0 { + if dt == 0 { + return U64F64::saturating_from_num(1); + } + return U64F64::saturating_from_num(0); + } + let min_ratio = I64F64::saturating_from_num(-40); + let neg_ratio = I64F64::saturating_from_num((dt as i128).neg()) + .checked_div(I64F64::saturating_from_num(tau)) + .unwrap_or(min_ratio); + let clamped = neg_ratio.max(min_ratio); + let decay: I64F64 = exp(clamped).unwrap_or(I64F64::saturating_from_num(0)); + if decay < I64F64::saturating_from_num(0) { + U64F64::saturating_from_num(0) + } else { + U64F64::saturating_from_num(decay) + } + } + + /// Calculates decayed unlocked mass and matured conviction. + /// + /// Matured conviction is calculated as c1 = m - (m - c0) * decay + /// Decayed unlocked mass is calculated as m1 = m0 * unlock_decay + /// + /// Note: It is important to roll forward every time locked mass changes + /// because this formula is for discrete time and it assumes there are + /// no changes in m between time points. + fn calculate_matured_values( + locked_mass: AlphaBalance, + unlocked_mass: AlphaBalance, + conviction: U64F64, + dt: u64, + ) -> (AlphaBalance, U64F64) { + let tau = MaturityRate::::get(); + let unlock_rate = UnlockRate::::get(); + + let decay = Self::exp_decay(dt, tau); + let unlock_decay = Self::exp_decay(dt, unlock_rate); + let mass_fixed = U64F64::saturating_from_num(locked_mass); + let unlocked_mass_fixed = U64F64::saturating_from_num(unlocked_mass); + let new_unlocked_mass = unlock_decay + .saturating_mul(unlocked_mass_fixed) + .saturating_to_num::() + .into(); + let new_conviction = + mass_fixed.saturating_sub(decay.saturating_mul(mass_fixed.saturating_sub(conviction))); + (new_unlocked_mass, new_conviction) + } + + /// Rolls a LockState forward to `now` using exponential maturity. + pub fn roll_forward_lock(lock: LockState, now: u64) -> LockState { + if now <= lock.last_update { + return lock; + } + let dt = now.saturating_sub(lock.last_update); + let (new_unlocked_mass, new_conviction) = Self::calculate_matured_values( + lock.locked_mass, + lock.unlocked_mass, + lock.conviction, + dt, + ); + + LockState { + locked_mass: lock.locked_mass, + unlocked_mass: new_unlocked_mass, + conviction: new_conviction, + last_update: now, + } + } + + /// Returns the sum of raw alpha shares for a coldkey across all hotkeys on a given subnet. + pub fn total_coldkey_alpha_on_subnet(coldkey: &T::AccountId, netuid: NetUid) -> AlphaBalance { + StakingHotkeys::::get(coldkey) + .into_iter() + .map(|hotkey| { + Self::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, coldkey, netuid) + }) + .fold(AlphaBalance::ZERO, |acc, stake| acc.saturating_add(stake)) + } + + /// Returns the current locked amount for a coldkey on a subnet. + /// No rolling forward is needed because locked mass does not decay over time. + pub fn get_current_locked(coldkey: &T::AccountId, netuid: NetUid) -> AlphaBalance { + Lock::::iter_prefix((coldkey, netuid)) + .next() + .map(|(_hotkey, lock)| lock.locked_mass) + .unwrap_or(AlphaBalance::ZERO) + } + + /// Returns the current unlocked amount for a coldkey on a subnet (rolled forward to now). + pub fn get_current_unlocked(coldkey: &T::AccountId, netuid: NetUid) -> AlphaBalance { + let now = Self::get_current_block_as_u64(); + Lock::::iter_prefix((coldkey, netuid)) + .next() + .map(|(_hotkey, lock)| Self::roll_forward_lock(lock, now).unlocked_mass) + .unwrap_or(AlphaBalance::ZERO) + } + + /// Returns the current conviction for a coldkey on a subnet (rolled forward to now). + pub fn get_conviction(coldkey: &T::AccountId, netuid: NetUid) -> U64F64 { + let now = Self::get_current_block_as_u64(); + Lock::::iter_prefix((coldkey, netuid)) + .next() + .map(|(_hotkey, lock)| Self::roll_forward_lock(lock, now).conviction) + .unwrap_or_else(|| U64F64::saturating_from_num(0)) + } + + /// Returns the alpha amount available to unstake or re-lock for a coldkey on a subnet. + /// Algorithm: + /// 1. Calculate total coldkey alpha on the subnet + /// 2. Reduce by locked amount + /// 3. Reduce by the amount that has not been unlocked yet + pub fn available_stake(coldkey: &T::AccountId, netuid: NetUid) -> AlphaBalance { + let total = Self::total_coldkey_alpha_on_subnet(coldkey, netuid); + let locked = Self::get_current_locked(coldkey, netuid); + let unlocked = Self::get_current_unlocked(coldkey, netuid); + let unavailable = locked.saturating_add(unlocked); + if total > unavailable { + total.saturating_sub(unavailable) + } else { + AlphaBalance::ZERO + } + } + + /// Ensures that the amount can be unstaked + pub fn ensure_available_stake( + coldkey: &T::AccountId, + netuid: NetUid, + amount: AlphaBalance, + ) -> Result<(), Error> { + let alpha_available = Self::available_stake(coldkey, netuid); + ensure!(alpha_available >= amount, Error::::StakeUnavailable); + Ok(()) + } + + /// Locks stake for a coldkey on a subnet to a specific hotkey. + /// If no lock exists, creates one. If one exists, the hotkey must match. + /// Top-up adds to locked_mass after rolling forward. + pub fn do_lock_stake( + coldkey: &T::AccountId, + netuid: NetUid, + hotkey: &T::AccountId, + amount: AlphaBalance, + ) -> dispatch::DispatchResult { + ensure!(!amount.is_zero(), Error::::AmountTooLow); + Self::ensure_available_stake(coldkey, netuid, amount) + .map_err(|_| Error::::InsufficientStakeForLock)?; + + let now = Self::get_current_block_as_u64(); + let existing = Lock::::iter_prefix((coldkey, netuid)).next(); + + match existing { + None => { + Self::insert_lock_state( + coldkey, + netuid, + hotkey, + LockState { + locked_mass: amount, + unlocked_mass: 0.into(), + conviction: U64F64::saturating_from_num(0), + last_update: now, + }, + ); + } + Some((existing_hotkey, existing)) => { + ensure!(*hotkey == existing_hotkey, Error::::LockHotkeyMismatch); + + let lock = Self::roll_forward_lock(existing, now); + let new_locked = lock.locked_mass.saturating_add(amount); + Self::insert_lock_state( + coldkey, + netuid, + hotkey, + LockState { + locked_mass: new_locked, + unlocked_mass: lock.unlocked_mass, + conviction: lock.conviction, + last_update: now, + }, + ); + } + } + + // Update the total hotkey lock + Self::upsert_hotkey_lock(hotkey, netuid, amount); + + Self::deposit_event(Event::StakeLocked { + coldkey: coldkey.clone(), + hotkey: hotkey.clone(), + netuid, + amount, + }); + + Ok(()) + } + + pub fn do_unlock_stake( + coldkey: &T::AccountId, + netuid: NetUid, + amount: AlphaBalance, + ) -> dispatch::DispatchResult { + let now = Self::get_current_block_as_u64(); + if let Some((existing_hotkey, existing)) = Lock::::iter_prefix((coldkey, netuid)).next() + { + let lock = Self::roll_forward_lock(existing, now); + ensure!(amount <= lock.locked_mass, Error::::UnlockAmountTooHigh); + + let new_locked = lock.locked_mass.saturating_sub(amount); + let amount_fixed = U64F64::saturating_from_num(amount); + let new_conviction = lock.conviction.saturating_sub(amount_fixed); + let new_unlocked = lock.unlocked_mass.saturating_add(amount); + Self::insert_lock_state( + coldkey, + netuid, + &existing_hotkey, + LockState { + locked_mass: new_locked, + unlocked_mass: new_unlocked, + conviction: new_conviction, + last_update: now, + }, + ); + + // Reduce the total hotkey lock by the rolled locked mass and conviction + Self::reduce_hotkey_lock(&existing_hotkey, netuid, amount, amount_fixed); + + Self::deposit_event(Event::StakeUnlocked { + coldkey: coldkey.clone(), + hotkey: existing_hotkey.clone(), + netuid, + amount, + }); + } + + Ok(()) + } + + /// Reduces the coldkey lock, the coldkey conviction, and the unlocked mass + /// by a specified alpha amount. + pub fn force_reduce_lock(coldkey: &T::AccountId, netuid: NetUid, amount: AlphaBalance) { + if let Some((existing_hotkey, lock)) = Lock::::iter_prefix((coldkey, netuid)).next() { + let now = Self::get_current_block_as_u64(); + let rolled = Self::roll_forward_lock(lock, now); + let new_locked_mass = rolled.locked_mass.saturating_sub(amount); + let new_unlocked_mass = rolled.unlocked_mass.saturating_sub(amount); + + // Remove or update lock + let conviction_diff = if new_locked_mass.is_zero() && new_unlocked_mass.is_zero() { + Lock::::remove((coldkey.clone(), netuid, existing_hotkey.clone())); + rolled.conviction + } else { + let new_conviction = rolled + .conviction + .saturating_sub(U64F64::saturating_from_num(amount)); + Lock::::insert( + (coldkey.clone(), netuid, existing_hotkey.clone()), + LockState { + locked_mass: new_locked_mass, + unlocked_mass: new_unlocked_mass, + conviction: new_conviction, + last_update: now, + }, + ); + rolled.conviction.saturating_sub(new_conviction) + }; + + // Reduce the total hotkey lock by the rolled locked mass and conviction + Self::reduce_hotkey_lock(&existing_hotkey, netuid, amount, conviction_diff); + } + } + + /// Rolls the lock forward to now and persists it if the locked mass is zero. This is used when we want to + /// update the lock when a user stakes or unstakes. + pub fn cleanup_lock_if_zero(coldkey: &T::AccountId, netuid: NetUid) { + let now = Self::get_current_block_as_u64(); + + // Cleanup locks for the specific coldkey and hotkey + if let Some((hotkey, lock)) = Lock::::iter_prefix((coldkey.clone(), netuid)).next() { + let rolled = Self::roll_forward_lock(lock, now); + if rolled.locked_mass.is_zero() && rolled.unlocked_mass.is_zero() { + Lock::::remove((coldkey.clone(), netuid, hotkey.clone())); + } + + // Also cleanup the hotkey lock (no need to check for unlocked mass here) + if let Some(lock) = HotkeyLock::::get(netuid, &hotkey) { + let rolled = Self::roll_forward_lock(lock, now); + if rolled.locked_mass.is_zero() { + HotkeyLock::::remove(netuid, hotkey); + } + } + } + } + + /// Update the total lock for a hotkey on a subnet or create one if + /// it doesn't exist. + /// + /// Roll the existing hotkey lock forward to now, then add the + /// latest conviction and locked mass. + pub fn upsert_hotkey_lock(hotkey: &T::AccountId, netuid: NetUid, amount: AlphaBalance) { + let total_lock = HotkeyLock::::get(netuid, hotkey); + + // Roll forward the total lock to now + let now = Self::get_current_block_as_u64(); + let rolled_hotkey_lock = if let Some(lock) = total_lock { + Self::roll_forward_lock(lock, now) + } else { + LockState { + locked_mass: 0.into(), + unlocked_mass: 0.into(), + conviction: U64F64::saturating_from_num(0), + last_update: now, + } + }; + + // Merge the new lock into the rolled total lock (only add mass) + let new_locked_mass = rolled_hotkey_lock.locked_mass.saturating_add(amount); + let new_hotkey_lock = LockState { + locked_mass: new_locked_mass, + unlocked_mass: 0.into(), + conviction: rolled_hotkey_lock.conviction, + last_update: now, + }; + Self::insert_hotkey_lock_state(netuid, hotkey, new_hotkey_lock); + } + + /// Reduce the total lock for a hotkey on a subnet. This is called when a lock is removed or reduced. + pub fn reduce_hotkey_lock( + hotkey: &T::AccountId, + netuid: NetUid, + amount: AlphaBalance, + conviction: U64F64, + ) { + if let Some(lock) = HotkeyLock::::get(netuid, hotkey) { + let now = Self::get_current_block_as_u64(); + let rolled_hotkey_lock = Self::roll_forward_lock(lock, now); + let new_locked_mass = rolled_hotkey_lock.locked_mass.saturating_sub(amount); + let new_conviction = rolled_hotkey_lock.conviction.saturating_sub(conviction); + Self::insert_hotkey_lock_state( + netuid, + hotkey, + LockState { + locked_mass: new_locked_mass, + unlocked_mass: 0.into(), + conviction: new_conviction, + last_update: now, + }, + ); + } + } + + /// Returns the total conviction for a hotkey on a subnet, + /// summed over all coldkeys that have locked to this hotkey. + pub fn hotkey_conviction(hotkey: &T::AccountId, netuid: NetUid) -> U64F64 { + let lock = HotkeyLock::::get(netuid, hotkey); + if let Some(lock) = lock { + Self::roll_forward_lock(lock, Self::get_current_block_as_u64()).conviction + } else { + U64F64::saturating_from_num(0) + } + } + + /// Finds the hotkey with the highest conviction on a given subnet. + pub fn subnet_king(netuid: NetUid) -> Option { + let now = Self::get_current_block_as_u64(); + let mut scores: BTreeMap = BTreeMap::new(); + + HotkeyLock::::iter_prefix(netuid).for_each(|(hotkey, lock)| { + let rolled = Self::roll_forward_lock(lock, now); + let entry = scores + .entry(hotkey) + .or_insert_with(|| U64F64::saturating_from_num(0)); + *entry = entry.saturating_add(rolled.conviction); + }); + + scores + .into_iter() + .max_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(core::cmp::Ordering::Equal)) + .map(|(hotkey, _)| hotkey) + } + + /// Ensure the coldkey does not have an active lock on any subnets. + pub fn ensure_no_active_locks(coldkey: &T::AccountId) -> Result<(), Error> { + let now = Self::get_current_block_as_u64(); + + for ((_netuid, _hotkey), lock) in Lock::::iter_prefix((coldkey,)) { + let rolled = Self::roll_forward_lock(lock, now); + if rolled.locked_mass > AlphaBalance::ZERO { + return Err(Error::::ActiveLockExists); + } + } + + Ok(()) + } + + /// Transfers the lock from one coldkey to another for all subnets. This is used when a + /// user swaps their coldkey and we want to preserve their locks. + /// + /// The hotkey and netuid remain the same, only the coldkey changes. + /// + /// Because unlocked_mass decays over time, both source and destination lock state must be + /// rolled forward to the current block before the transfer is applied. The HotkeyLock map + /// does not change because it only contains subnet-wide hotkey totals, not per-coldkey locks. + pub fn swap_coldkey_locks(old_coldkey: &T::AccountId, new_coldkey: &T::AccountId) { + let now = Self::get_current_block_as_u64(); + let mut locks_to_transfer: Vec<(NetUid, T::AccountId, LockState)> = Vec::new(); + + // Gather locks for old coldkey + for ((netuid, hotkey), lock) in Lock::::iter_prefix((old_coldkey,)) { + locks_to_transfer.push((netuid, hotkey, Self::roll_forward_lock(lock, now))); + } + + // Remove locks for old coldkey and insert for new + for (netuid, hotkey, lock) in locks_to_transfer { + Lock::::remove((old_coldkey.clone(), netuid, hotkey.clone())); + + if let Some((new_hotkey, new_lock)) = + Lock::::iter_prefix((new_coldkey, netuid)).next() + { + let mut new_lock_rolled = Self::roll_forward_lock(new_lock, now); + + // The new coldkey does not have active locks, ensure_no_active_locks guarantees + // that, so overwrite the mass and conviction with old coldkey's. + new_lock_rolled.locked_mass = lock.locked_mass; + new_lock_rolled.conviction = lock.conviction; + new_lock_rolled.unlocked_mass = new_lock_rolled + .unlocked_mass + .saturating_add(lock.unlocked_mass); + Self::insert_lock_state(new_coldkey, netuid, &new_hotkey, new_lock_rolled); + } else { + Self::insert_lock_state(new_coldkey, netuid, &hotkey, lock); + } + } + } + + /// Swap all locks made to the old_hotkey to new_hotkey on all netuids + /// + /// There is no need to roll the locks, they can be just copied "as is": + /// The lock relation between coldkeys and hotkey is 1:1, so if old hotkey has a + /// coldkey locking to it, then the same coldkey cannot lock to the new hotkey. + /// And in reverse: If a coldkey is locking to the new hotkey, it will not appear + /// in the transfer list because it does not lock to the old hotkey. + /// + /// Conviction is not reset because the hotkey ownership does not change, it's still + /// the same hotkey owner who will own the new hotkey. + pub fn swap_hotkey_locks(old_hotkey: &T::AccountId, new_hotkey: &T::AccountId) -> (u64, u64) { + let mut locks_to_transfer: Vec<(T::AccountId, NetUid, T::AccountId, LockState)> = + Vec::new(); + let mut hotkey_locks_to_transfer: Vec<(NetUid, LockState)> = Vec::new(); + let mut reads: u64 = 0; + let mut writes: u64 = 0; + + let netuids = Self::get_all_subnet_netuids(); + + // Gather hotkey locks for old hotkey + for netuid in netuids { + if let Some(lock) = HotkeyLock::::get(netuid, old_hotkey) { + hotkey_locks_to_transfer.push((netuid, lock)); + } + reads = reads.saturating_add(1); + } + + // Gather locks for old hotkey (only if hotkey locks exist, otherwise skip to save reads) + if !hotkey_locks_to_transfer.is_empty() { + for ((coldkey, netuid, hotkey), lock) in Lock::::iter() { + if hotkey == *old_hotkey { + locks_to_transfer.push((coldkey, netuid, hotkey, lock)); + } + reads = reads.saturating_add(1); + } + } + + // Remove locks for old hotkey and insert for new + for (coldkey, netuid, _hotkey, lock) in locks_to_transfer { + Lock::::remove((coldkey.clone(), netuid, old_hotkey.clone())); + Self::insert_lock_state(&coldkey, netuid, new_hotkey, lock); + writes = writes.saturating_add(2); + } + + // Remove hotkey locks for old hotkey and insert for new + for (netuid, lock) in hotkey_locks_to_transfer { + HotkeyLock::::remove(netuid, old_hotkey); + Self::insert_hotkey_lock_state(netuid, new_hotkey, lock); + writes = writes.saturating_add(2); + } + (reads, writes) + } + + /// Moves lock from one hotkey to another and clears conviction + /// + /// The lock is rolled forward to the current block before switching the + /// associated hotkey so that the lock stays mathematically correct and + /// preserves current decayed locked mass. + /// + /// The conviction is reset to zero if the destination and source hotkeys + /// are owned by different coldkeys, otherwise it is preserved. + pub fn do_move_lock( + coldkey: &T::AccountId, + destination_hotkey: &T::AccountId, + netuid: NetUid, + ) -> DispatchResult { + let now = Self::get_current_block_as_u64(); + + match Lock::::iter_prefix((coldkey, netuid)).next() { + Some((origin_hotkey, existing)) => { + let old_hotkey_owner = Self::get_owning_coldkey_for_hotkey(&origin_hotkey); + let new_hotkey_owner = Self::get_owning_coldkey_for_hotkey(destination_hotkey); + let same_owner = old_hotkey_owner != DefaultAccount::::get() + && new_hotkey_owner != DefaultAccount::::get() + && old_hotkey_owner == new_hotkey_owner; + + let mut existing_rolled = Self::roll_forward_lock(existing, now); + let existing_conviction = existing_rolled.conviction; + if !same_owner { + existing_rolled.conviction = U64F64::saturating_from_num(0); + } + + Lock::::remove((coldkey.clone(), netuid, origin_hotkey.clone())); + Self::insert_lock_state( + coldkey, + netuid, + destination_hotkey, + LockState { + locked_mass: existing_rolled.locked_mass, + unlocked_mass: existing_rolled.unlocked_mass, + conviction: existing_rolled.conviction, + last_update: now, + }, + ); + + // Update the total hotkey locks for destination hotkey + Self::upsert_hotkey_lock(destination_hotkey, netuid, existing_rolled.locked_mass); + + // Reduce the total hotkey locks and conviction for the origin hotkey + Self::reduce_hotkey_lock( + &origin_hotkey, + netuid, + existing_rolled.locked_mass, + existing_conviction, + ); + + // If the same coldkey owns both the origin and destination hotkeys, also + // transfer the conviction instead of resetting it + if same_owner { + HotkeyLock::::mutate(netuid, destination_hotkey, |dest_lock_opt| { + if let Some(dest_lock) = dest_lock_opt { + dest_lock.conviction = + dest_lock.conviction.saturating_add(existing_conviction); + } + }); + } + + Self::deposit_event(Event::LockMoved { + coldkey: coldkey.clone(), + origin_hotkey, + destination_hotkey: destination_hotkey.clone(), + netuid, + }); + Ok(()) + } + None => Err(Error::::NoExistingLock.into()), + } + } + + pub fn auto_lock_owner_cut(netuid: NetUid, amount: AlphaBalance) { + let subnet_owner_coldkey = Self::get_subnet_owner(netuid); + + // Determine the lock hotkey. If no locks exist, assign subnet owner's hotkey, otherwise + // auto-lock to existing lock hotkey + let lock_hotkey = if let Some((existing_hotkey, _existing)) = + Lock::::iter_prefix((&subnet_owner_coldkey, netuid)).next() + { + existing_hotkey + } else { + SubnetOwnerHotkey::::get(netuid) + }; + + // Ignore the result. It may only fail if amount is zero, which is OK to ignore because nothing + // needs to happen in that case + let _ = Self::do_lock_stake(&subnet_owner_coldkey, netuid, &lock_hotkey, amount); + } + + /// When locked stake is transfered, the lock should follow the stake + /// + /// First, this function rolls the lock forward and checks if amount is over available + /// stake and if it is, the stake that's over the available amount on the destination + /// coldkey is locked in the same way as the original stake: + /// + /// - If original stake is actively locked to a hotkey, it remains actively locked to + /// the same hotkey + /// - If original stake is being unlocked, the lock is created on the destination coldkey + /// with this amount of unlocked_mass + pub fn transfer_lock( + origin_coldkey: &T::AccountId, + destination_coldkey: &T::AccountId, + netuid: NetUid, + amount: AlphaBalance, + ) -> DispatchResult { + let now = Self::get_current_block_as_u64(); + + // If no actual transfer happens, this is ok + if origin_coldkey == destination_coldkey || amount.is_zero() { + return Ok(()); + } + + // Read total alpha of the coldkey on this netuid. Do not check if total alpha is + // lower than amount transferred, this is responsibility of a higher level, this + // function needs to act protectively. + let total_alpha = Self::total_coldkey_alpha_on_subnet(origin_coldkey, netuid); + let mut remaining_to_transfer = amount; + + // Read the locks for source and destination coldkey (if exist) and roll forward + let Some((source_hotkey, source_lock)) = + Lock::::iter_prefix((origin_coldkey, netuid)).next() + else { + return Ok(()); + }; + + let mut source_lock = Self::roll_forward_lock(source_lock, now); + let maybe_destination_lock = Lock::::iter_prefix((destination_coldkey, netuid)) + .next() + .map(|(hotkey, lock)| (hotkey, Self::roll_forward_lock(lock, now))); + + let mut destination_hotkey = maybe_destination_lock + .as_ref() + .map(|(hotkey, _)| hotkey.clone()) + .unwrap_or_else(|| source_hotkey.clone()); + let mut destination_lock = maybe_destination_lock + .as_ref() + .map(|(_, lock)| lock.clone()) + .unwrap_or(LockState { + locked_mass: AlphaBalance::ZERO, + unlocked_mass: AlphaBalance::ZERO, + conviction: U64F64::saturating_from_num(0), + last_update: now, + }); + + // Calculate available stake by subtracting locked_mass and unlocked_mass from total alpha. + let unavailable = source_lock + .locked_mass + .saturating_add(source_lock.unlocked_mass); + let available_stake = total_alpha.saturating_sub(unavailable); + + // Reduce remaining_to_transfer by min(remaining_to_transfer, available stake) + let available_transfer = remaining_to_transfer.min(available_stake); + remaining_to_transfer = remaining_to_transfer.saturating_sub(available_transfer); + + // If result is non-zero, reduce remaining_to_transfer by min(unlocked_mass, remaining_to_transfer), + // reduce unlocked_mass on the source coldkey by the same amount, increase unlocked_mass on the + // destination coldkey by the same amount. + if !remaining_to_transfer.is_zero() { + let unlocked_transfer = source_lock.unlocked_mass.min(remaining_to_transfer); + remaining_to_transfer = remaining_to_transfer.saturating_sub(unlocked_transfer); + source_lock.unlocked_mass = source_lock.unlocked_mass.saturating_sub(unlocked_transfer); + destination_lock.unlocked_mass = destination_lock + .unlocked_mass + .saturating_add(unlocked_transfer); + } + + // If result is non-zero, check the hotkey match between source and destination coldkey locks + // (if destination coldkey lock exists). If no match, error out with LockHotkeyMismatch, otherwise, + // reduce remaining_to_transfer by min(remaining_to_transfer, locked_mass), reduce locked_mass on + // the source coldkey by the same amount, increase locked_mass on the destination coldkey by the + // same amount, reduce conviction on the source coldkey proportionally, and increase conviction + // on the destination coldkey proportionally. + if !remaining_to_transfer.is_zero() { + if let Some((existing_hotkey, _)) = maybe_destination_lock.as_ref() { + ensure!( + existing_hotkey == &source_hotkey, + Error::::LockHotkeyMismatch + ); + destination_hotkey = existing_hotkey.clone(); + } + + let locked_transfer = remaining_to_transfer.min(source_lock.locked_mass); + let conviction_transfer = + if locked_transfer.is_zero() || source_lock.locked_mass.is_zero() { + U64F64::saturating_from_num(0) + } else { + // Conviction never exceeds locked_mass, so we can scale it proportionally + // using integer arithmetic without overflowing fixed-point multiplication. + let conviction_u128 = source_lock.conviction.saturating_to_num::(); + let locked_transfer_u128 = locked_transfer.to_u64() as u128; + let source_locked_u128 = source_lock.locked_mass.to_u64() as u128; + let transferred_conviction_u128 = conviction_u128 + .saturating_mul(locked_transfer_u128) + .checked_div(source_locked_u128) + .unwrap_or(0); + U64F64::saturating_from_num(transferred_conviction_u128) + }; + + source_lock.locked_mass = source_lock.locked_mass.saturating_sub(locked_transfer); + source_lock.conviction = source_lock.conviction.saturating_sub(conviction_transfer); + destination_lock.locked_mass = + destination_lock.locked_mass.saturating_add(locked_transfer); + destination_lock.conviction = destination_lock + .conviction + .saturating_add(conviction_transfer); + } + + source_lock.last_update = now; + destination_lock.last_update = now; + + // Upsert updated locks (only once per this fn) even if there were no updates because + // of roll-forward + Self::insert_lock_state(origin_coldkey, netuid, &source_hotkey, source_lock); + Self::insert_lock_state( + destination_coldkey, + netuid, + &destination_hotkey, + destination_lock, + ); + + Ok(()) + } +} diff --git a/pallets/subtensor/src/staking/mod.rs b/pallets/subtensor/src/staking/mod.rs index ad2b66189f..a10908eca3 100644 --- a/pallets/subtensor/src/staking/mod.rs +++ b/pallets/subtensor/src/staking/mod.rs @@ -5,6 +5,7 @@ mod claim_root; pub mod decrease_take; pub mod helpers; pub mod increase_take; +pub mod lock; pub mod move_stake; pub mod recycle_alpha; pub mod remove_stake; diff --git a/pallets/subtensor/src/staking/move_stake.rs b/pallets/subtensor/src/staking/move_stake.rs index 8e94339339..aafefa28ed 100644 --- a/pallets/subtensor/src/staking/move_stake.rs +++ b/pallets/subtensor/src/staking/move_stake.rs @@ -358,12 +358,18 @@ impl Pallet { let tao_unstaked = Self::unstake_from_subnet( origin_hotkey, origin_coldkey, + origin_coldkey, origin_netuid, move_amount, T::SwapInterface::min_price(), drop_fee_origin, )?; + // Transfer unstaked TAO from origin_coldkey to destination_coldkey + if origin_coldkey != destination_coldkey { + Self::transfer_tao(origin_coldkey, destination_coldkey, tao_unstaked)?; + } + // Stake the unstaked amount into the destination. // Because of the fee, the tao_unstaked may be too low if initial stake is low. In that case, // do not restake. @@ -471,9 +477,9 @@ impl Pallet { } // Corner case: SubnetTAO for any of two subnets is zero - let subnet_tao_1 = SubnetTAO::::get(origin_netuid) + let subnet_tao_1 = Self::get_subnet_tao(origin_netuid) .saturating_add(SubnetTaoProvided::::get(origin_netuid)); - let subnet_tao_2 = SubnetTAO::::get(destination_netuid) + let subnet_tao_2 = Self::get_subnet_tao(destination_netuid) .saturating_add(SubnetTaoProvided::::get(destination_netuid)); if subnet_tao_1.is_zero() || subnet_tao_2.is_zero() { return Err(Error::::ZeroMaxStakeAmount.into()); diff --git a/pallets/subtensor/src/staking/recycle_alpha.rs b/pallets/subtensor/src/staking/recycle_alpha.rs index bb93c12818..cacde73d74 100644 --- a/pallets/subtensor/src/staking/recycle_alpha.rs +++ b/pallets/subtensor/src/staking/recycle_alpha.rs @@ -14,13 +14,13 @@ impl Pallet { /// /// # Returns /// - /// * `DispatchResult` - Success or error - pub(crate) fn do_recycle_alpha( + /// * `Result` - The actual amount recycled, or error + pub fn do_recycle_alpha( origin: OriginFor, hotkey: T::AccountId, amount: AlphaBalance, netuid: NetUid, - ) -> DispatchResult { + ) -> Result { let coldkey: T::AccountId = ensure_signed(origin)?; ensure!(Self::if_subnet_exist(netuid), Error::::SubnetNotExists); @@ -50,6 +50,9 @@ impl Pallet { Error::::InsufficientLiquidity ); + // Ensure that recycled amount is not greater than available to unstake (due to locks) + Self::ensure_available_stake(&coldkey, netuid, amount)?; + // Deduct from the coldkey's stake. Self::decrease_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid, amount); @@ -58,7 +61,7 @@ impl Pallet { Self::deposit_event(Event::AlphaRecycled(coldkey, hotkey, amount, netuid)); - Ok(()) + Ok(amount) } /// Burns alpha from a cold/hot key pair without reducing AlphaOut @@ -72,13 +75,13 @@ impl Pallet { /// /// # Returns /// - /// * `DispatchResult` - Success or error - pub(crate) fn do_burn_alpha( + /// * `Result` - The actual amount burned, or error + pub fn do_burn_alpha( origin: OriginFor, hotkey: T::AccountId, amount: AlphaBalance, netuid: NetUid, - ) -> DispatchResult { + ) -> Result { let coldkey = ensure_signed(origin)?; ensure!(Self::if_subnet_exist(netuid), Error::::SubnetNotExists); @@ -108,6 +111,9 @@ impl Pallet { Error::::InsufficientLiquidity ); + // Ensure that burned amount is not greater than available to unstake (due to locks) + Self::ensure_available_stake(&coldkey, netuid, amount)?; + // Deduct from the coldkey's stake. Self::decrease_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid, amount); @@ -116,8 +122,9 @@ impl Pallet { // Deposit event Self::deposit_event(Event::AlphaBurned(coldkey, hotkey, amount, netuid)); - Ok(()) + Ok(amount) } + pub(crate) fn do_add_stake_burn( origin: OriginFor, hotkey: T::AccountId, @@ -125,17 +132,6 @@ impl Pallet { amount: TaoBalance, limit: Option, ) -> DispatchResult { - Self::ensure_subnet_owner(origin.clone(), netuid)?; - - let current_block = Self::get_current_block_as_u64(); - let last_block = Self::get_rate_limited_last_block(&RateLimitKey::AddStakeBurn(netuid)); - let rate_limit = TransactionType::AddStakeBurn.rate_limit_on_subnet::(netuid); - - ensure!( - last_block.is_zero() || current_block.saturating_sub(last_block) >= rate_limit, - Error::::AddStakeBurnRateLimitExceeded - ); - let alpha = if let Some(limit) = limit { Self::do_add_stake_limit(origin.clone(), hotkey.clone(), netuid, amount, limit, false)? } else { @@ -144,8 +140,6 @@ impl Pallet { Self::do_burn_alpha(origin, hotkey.clone(), alpha, netuid)?; - Self::set_rate_limited_last_block(&RateLimitKey::AddStakeBurn(netuid), current_block); - Self::deposit_event(Event::AddStakeBurn { netuid, hotkey, @@ -155,4 +149,31 @@ impl Pallet { Ok(()) } + + /// Atomically stakes TAO and recycles the resulting alpha. + /// Permissionless counterpart used by the chain extension so that contracts + /// can compose the two operations without leaving residual stake if the + /// second leg fails. + pub fn do_add_stake_recycle( + origin: OriginFor, + hotkey: T::AccountId, + netuid: NetUid, + amount: TaoBalance, + ) -> Result { + let alpha = Self::do_add_stake(origin.clone(), hotkey.clone(), netuid, amount)?; + Self::do_recycle_alpha(origin, hotkey, alpha, netuid) + } + + /// Atomically stakes TAO and burns the resulting alpha. Permissionless + /// counterpart to `do_add_stake_burn`: return the amount of alpha burned. + /// limit. Used by the chain extension. + pub fn do_add_stake_burn_permissionless( + origin: OriginFor, + hotkey: T::AccountId, + netuid: NetUid, + amount: TaoBalance, + ) -> Result { + let alpha = Self::do_add_stake(origin.clone(), hotkey.clone(), netuid, amount)?; + Self::do_burn_alpha(origin, hotkey, alpha, netuid) + } } diff --git a/pallets/subtensor/src/staking/remove_stake.rs b/pallets/subtensor/src/staking/remove_stake.rs index 0750456106..f2d07189a4 100644 --- a/pallets/subtensor/src/staking/remove_stake.rs +++ b/pallets/subtensor/src/staking/remove_stake.rs @@ -67,18 +67,16 @@ impl Pallet { )?; // 3. Swap the alpba to tao and update counters for this subnet. - let tao_unstaked = Self::unstake_from_subnet( + Self::unstake_from_subnet( &hotkey, &coldkey, + &coldkey, netuid, alpha_unstaked, T::SwapInterface::min_price(), false, )?; - // 4. We add the balance to the coldkey. If the above fails we will not credit this coldkey. - Self::add_balance_to_coldkey_account(&coldkey, tao_unstaked.into()); - // 5. If the stake is below the minimum, we clear the nomination from storage. Self::clear_small_nomination_if_required(&hotkey, &coldkey, netuid); @@ -159,18 +157,16 @@ impl Pallet { if !alpha_unstaked.is_zero() { // Swap the alpha to tao and update counters for this subnet. - let tao_unstaked = Self::unstake_from_subnet( + Self::unstake_from_subnet( &hotkey, &coldkey, + &coldkey, netuid, alpha_unstaked, T::SwapInterface::min_price(), false, )?; - // Add the balance to the coldkey. If the above fails we will not credit this coldkey. - Self::add_balance_to_coldkey_account(&coldkey, tao_unstaked.into()); - // If the stake is below the minimum, we clear the nomination from storage. Self::clear_small_nomination_if_required(&hotkey, &coldkey, netuid); } @@ -255,6 +251,7 @@ impl Pallet { let tao_unstaked = Self::unstake_from_subnet( &hotkey, &coldkey, + &coldkey, netuid, alpha_unstaked, T::SwapInterface::min_price(), @@ -358,22 +355,20 @@ impl Pallet { )?; // 4. Swap the alpha to tao and update counters for this subnet. - let tao_unstaked = Self::unstake_from_subnet( + Self::unstake_from_subnet( &hotkey, &coldkey, + &coldkey, netuid, possible_alpha, limit_price, false, )?; - // 5. We add the balance to the coldkey. If the above fails we will not credit this coldkey. - Self::add_balance_to_coldkey_account(&coldkey, tao_unstaked.into()); - - // 6. If the stake is below the minimum, we clear the nomination from storage. + // 5. If the stake is below the minimum, we clear the nomination from storage. Self::clear_small_nomination_if_required(&hotkey, &coldkey, netuid); - // 7. Check if stake lowered below MinStake and remove Pending children if it did + // 6. Check if stake lowered below MinStake and remove Pending children if it did if Self::get_total_stake_for_hotkey(&hotkey) < StakeThreshold::::get().into() { Self::get_all_subnet_netuids().iter().for_each(|netuid| { PendingChildKeys::::remove(netuid, &hotkey); @@ -561,7 +556,8 @@ impl Pallet { // Credit each share directly to coldkey free balance. for p in portions { if p.share > 0 { - Self::add_balance_to_coldkey_account(&p.cold, p.share.into()); + // Cannot fail the whole transaction if this transfer fails + let _ = Self::transfer_tao_from_subnet(netuid, &p.cold, p.share.into()); } } } @@ -596,8 +592,38 @@ impl Pallet { TaoBalance::ZERO }; - if !refund.is_zero() { - Self::add_balance_to_coldkey_account(&owner_coldkey, refund); + if !refund.is_zero() + && let Some(subnet_account) = Self::get_subnet_account_id(netuid) + { + // Transfer maximum transferrable up to refund to owner + let transferrable = Self::get_coldkey_balance(&subnet_account); + // We do our best effort to refund owner to as full amount of refund as possible, but + // we cannot fail new subnet registration, so the result is ignored. + let _ = Self::transfer_tao(&subnet_account, &owner_coldkey, refund.min(transferrable)); + } + + // 9) Recycle TAO remaining on the subnet account, forgive errors. + if let Some(subnet_account) = Self::get_subnet_account_id(netuid) { + let remaining_subnet_balance = Self::get_keep_alive_balance(&subnet_account); + if Self::recycle_tao(&subnet_account, remaining_subnet_balance).is_ok() { + RAORecycledForRegistration::::insert(netuid, remaining_subnet_balance); + } + } + + // 9) Cleanup all subnet stake locks if any. + let lock_keys: Vec<(T::AccountId, NetUid, T::AccountId)> = Lock::::iter_keys() + .filter(|(_, this_netuid, _)| *this_netuid == netuid) + .collect(); + for (coldkey, netuid, hotkey) in lock_keys { + Lock::::remove((coldkey, netuid, hotkey)); + } + + // 10) Cleanup all subnet hotkey locks if any. + let hotkey_lock_keys: Vec<(NetUid, T::AccountId)> = HotkeyLock::::iter_keys() + .filter(|(this_netuid, _)| *this_netuid == netuid) + .collect(); + for (netuid, hotkey) in hotkey_lock_keys { + HotkeyLock::::remove(netuid, hotkey); } Ok(()) diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index 5e22cc09ac..a7826d1268 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -665,9 +665,7 @@ impl Pallet { *total = total.saturating_add(swap_result.amount_paid_out.into()); }); - // Increase only the protocol TAO reserve. We only use the sum of - // (SubnetTAO + SubnetTaoProvided) in tao_reserve(), so it is irrelevant - // which one to increase. + // Increase the protocol TAO reserve SubnetTAO::::mutate(netuid, |total| { let delta = swap_result.paid_in_reserve_delta_i64().unsigned_abs(); *total = total.saturating_add(delta.into()); @@ -739,9 +737,11 @@ impl Pallet { /// Unstakes alpha from a subnet for a given hotkey and coldkey pair. /// /// We update the pools associated with a subnet as well as update hotkey alpha shares. + /// Credits the unstaked TAO to the beneficiary account pub fn unstake_from_subnet( hotkey: &T::AccountId, coldkey: &T::AccountId, + beneficiary: &T::AccountId, netuid: NetUid, alpha: AlphaBalance, price_limit: TaoBalance, @@ -764,6 +764,9 @@ impl Pallet { Self::increase_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid, refund); } + // Transfer unstaked TAO from subnet account to the coldkey. + Self::transfer_tao_from_subnet(netuid, beneficiary, swap_result.amount_paid_out.into())?; + // Swap (in a fee-less way) the block builder alpha fee let mut fee_outflow = 0_u64; let maybe_block_author_coldkey = T::AuthorshipProvider::author(); @@ -774,10 +777,11 @@ impl Pallet { T::SwapInterface::min_price::(), true, )?; - Self::add_balance_to_coldkey_account( + Self::transfer_tao_from_subnet( + netuid, &block_author_coldkey, bb_swap_result.amount_paid_out.into(), - ); + )?; fee_outflow = bb_swap_result.amount_paid_out.into(); } else { // block author is not found, burn this alpha @@ -806,6 +810,9 @@ impl Pallet { .saturating_add(fee_outflow.into()), ); + // Cleanup locks if needed + Self::cleanup_lock_if_zero(coldkey, netuid); + LastColdkeyHotkeyStakeBlock::::insert(coldkey, hotkey, Self::get_current_block_as_u64()); // Deposit and log the unstaking event. @@ -843,8 +850,12 @@ impl Pallet { set_limit: bool, drop_fees: bool, ) -> Result { + // Transfer TAO from coldkey to the subnet account. + // Actual transfered may be different within ED amount. + let tao_staked = Self::transfer_tao_to_subnet(netuid, coldkey, tao)?; + // Swap the tao to alpha. - let swap_result = Self::swap_tao_for_alpha(netuid, tao, price_limit, drop_fees)?; + let swap_result = Self::swap_tao_for_alpha(netuid, tao_staked, price_limit, drop_fees)?; ensure!( !swap_result.amount_paid_out.is_zero(), @@ -878,21 +889,28 @@ impl Pallet { // Increase the balance of the block author let maybe_block_author_coldkey = T::AuthorshipProvider::author(); if let Some(block_author_coldkey) = maybe_block_author_coldkey { - Self::add_balance_to_coldkey_account( + // TAO was transferred to subnet account in the beginning of this fn + // swap_tao_for_alpha guarantees that input amount of TAO was split into + // reserve delta + fee_to_block_author. + // Now transfer the fee from subnet account to block builder. + Self::transfer_tao_from_subnet( + netuid, &block_author_coldkey, swap_result.fee_to_block_author.into(), - ); + )?; } else { // Block author is not found - burn this TAO - // Pallet balances total issuance was taken care of when balance was withdrawn for this swap - TotalIssuance::::mutate(|ti| { - *ti = ti.saturating_sub(swap_result.fee_to_block_author); - }); + if let Some(subnet_account_id) = Self::get_subnet_account_id(netuid) { + let _ = Self::burn_tao(&subnet_account_id, swap_result.fee_to_block_author.into()); + } } // Record TAO inflow Self::record_tao_inflow(netuid, swap_result.amount_paid_in.into()); + // Cleanup locks if needed + Self::cleanup_lock_if_zero(coldkey, netuid); + LastColdkeyHotkeyStakeBlock::::insert(coldkey, hotkey, Self::get_current_block_as_u64()); if set_limit { @@ -911,7 +929,7 @@ impl Pallet { Self::deposit_event(Event::StakeAdded( coldkey.clone(), hotkey.clone(), - tao, + tao_staked, swap_result.amount_paid_out.into(), netuid, swap_result.fee_paid.to_u64(), @@ -921,7 +939,7 @@ impl Pallet { "StakeAdded( coldkey: {:?}, hotkey:{:?}, tao: {:?}, alpha:{:?}, netuid: {:?}, fee {} )", coldkey.clone(), hotkey.clone(), - tao, + tao_staked, swap_result.amount_paid_out, netuid, swap_result.fee_paid, @@ -942,6 +960,9 @@ impl Pallet { netuid: NetUid, alpha: AlphaBalance, ) -> Result { + // Transfer lock (may fail if destination coldkey has a conflicting lock) + Self::transfer_lock(origin_coldkey, destination_coldkey, netuid, alpha)?; + // Decrease alpha on origin keys Self::decrease_stake_for_hotkey_and_coldkey_on_subnet( origin_hotkey, @@ -1157,6 +1178,9 @@ impl Pallet { Error::::HotKeyAccountNotExists ); + // Ensure that unstaked amount is not greater than available to unstake (due to locks) + Self::ensure_available_stake(coldkey, netuid, alpha_unstaked)?; + Ok(()) } @@ -1185,6 +1209,10 @@ impl Pallet { // Get user's stake in this subnet let alpha = Self::get_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, *netuid); + // Ensure that unstaked amount is not greater than available to unstake (due to locks) + // for this subnet. + Self::ensure_available_stake(coldkey, *netuid, alpha)?; + if Self::validate_remove_stake(coldkey, hotkey, *netuid, alpha, alpha, false).is_ok() { unstaking_any = true; } @@ -1302,6 +1330,12 @@ impl Pallet { } } + // Enforce lock invariant: if the is cross-subnet move, the remaining amount must + // cover the lock. + if origin_netuid != destination_netuid { + Self::ensure_available_stake(origin_coldkey, origin_netuid, alpha_amount)?; + } + Ok(()) } diff --git a/pallets/subtensor/src/subnets/leasing.rs b/pallets/subtensor/src/subnets/leasing.rs index cc1094d227..d3e1b84df5 100644 --- a/pallets/subtensor/src/subnets/leasing.rs +++ b/pallets/subtensor/src/subnets/leasing.rs @@ -219,7 +219,7 @@ impl Pallet { Error::::BeneficiaryDoesNotOwnHotkey ); SubnetOwner::::insert(lease.netuid, lease.beneficiary.clone()); - Self::set_subnet_owner_hotkey(lease.netuid, &hotkey); + Self::set_subnet_owner_hotkey(lease.netuid, &hotkey)?; // Stop tracking the lease coldkey and hotkey let _ = frame_system::Pallet::::dec_providers(&lease.coldkey).defensive(); diff --git a/pallets/subtensor/src/subnets/registration.rs b/pallets/subtensor/src/subnets/registration.rs index afb09ed0eb..103afdcc46 100644 --- a/pallets/subtensor/src/subnets/registration.rs +++ b/pallets/subtensor/src/subnets/registration.rs @@ -74,7 +74,7 @@ impl Pallet { ); // 6) ensure pairing exists and is correct - Self::create_account_if_non_existent(&coldkey, &hotkey); + Self::create_account_if_non_existent(&coldkey, &hotkey)?; ensure!( Self::coldkey_owns_hotkey(&coldkey, &hotkey), Error::::NonAssociatedColdKey @@ -97,7 +97,7 @@ impl Pallet { // 8) burn payment (same mechanics as old burned_register) let actual_burn_amount = - Self::remove_balance_from_coldkey_account(&coldkey, registration_cost.into())?; + Self::transfer_tao_to_subnet(netuid, &coldkey, registration_cost.into())?; let burned_alpha = Self::swap_tao_for_alpha( netuid, @@ -121,6 +121,9 @@ impl Pallet { RegistrationsThisBlock::::mutate(netuid, |val| val.saturating_inc()); Self::increase_rao_recycled(netuid, registration_cost.into()); + // Record TAO inflow + Self::record_tao_inflow(netuid, actual_burn_amount); + // 12) event log::debug!("NeuronRegistered( netuid:{netuid:?} uid:{neuron_uid:?} hotkey:{hotkey:?} )"); Self::deposit_event(Event::NeuronRegistered(netuid, neuron_uid, hotkey)); @@ -197,11 +200,10 @@ impl Pallet { ensure!(seal == work_hash, Error::::InvalidSeal); UsedWork::::insert(work.clone(), current_block_number); - // --- 5. Add Balance via faucet. + // --- 5. Add Balance via faucet (mint free TAO) let balance_to_add: u64 = 1_000_000_000_000; - Self::increase_issuance(100_000_000_000_u64.into()); // We are creating tokens here from the coinbase. - - Self::add_balance_to_coldkey_account(&coldkey, balance_to_add.into()); + let credit = Self::mint_tao(balance_to_add.into()); + let _ = Self::spend_tao(&coldkey, credit, balance_to_add.into()); // --- 6. Deposit successful event. log::debug!("Faucet( coldkey:{coldkey:?} amount:{balance_to_add:?} ) "); diff --git a/pallets/subtensor/src/subnets/subnet.rs b/pallets/subtensor/src/subnets/subnet.rs index fd8b61b5dc..e1aa5eb744 100644 --- a/pallets/subtensor/src/subnets/subnet.rs +++ b/pallets/subtensor/src/subnets/subnet.rs @@ -1,6 +1,8 @@ use super::*; +use frame_support::PalletId; use safe_math::FixedExt; use sp_core::Get; +use sp_runtime::traits::AccountIdConversion; use substrate_fixed::types::U96F32; use subtensor_runtime_common::{NetUid, TaoBalance}; impl Pallet { @@ -125,6 +127,12 @@ impl Pallet { Error::::NonAssociatedColdKey ); + // Ensure that hotkey is not a special account + ensure!( + Self::is_subnet_account_id(hotkey).is_none(), + Error::::CannotUseSystemAccount + ); + // --- 3. Ensure the mechanism is Dynamic. ensure!(mechid == 1, Error::::MechanismDoesNotExist); @@ -165,21 +173,12 @@ impl Pallet { Error::::CannotAffordLockCost ); - // --- 7. Perform the lock operation. - let actual_tao_lock_amount = - Self::remove_balance_from_coldkey_account(&coldkey, lock_amount.into())?; - log::debug!("actual_tao_lock_amount: {actual_tao_lock_amount:?}"); - - // --- 8. Set the lock amount for use to determine pricing. - Self::set_network_last_lock(actual_tao_lock_amount); - Self::set_network_last_lock_block(current_block); - - // --- 9. If we identified a subnet to prune, do it now. + // --- 7. If we identified a subnet to prune, do it now. if let Some(prune_netuid) = recycle_netuid { Self::do_dissolve_network(prune_netuid)?; } - // --- 10. Determine netuid to register. If we pruned a subnet, reuse that netuid. + // --- 8. Determine netuid to register. If we pruned a subnet, reuse that netuid. let netuid_to_register: NetUid = match recycle_netuid { Some(prune_netuid) => prune_netuid, None => Self::get_next_netuid(), @@ -193,8 +192,17 @@ impl Pallet { Self::init_new_network(netuid_to_register, default_tempo); log::debug!("init_new_network: {netuid_to_register:?}"); - // --- 13. Add the caller to the neuron set. - Self::create_account_if_non_existent(&coldkey, hotkey); + // --- 10. Perform the lock operation (transfer TAO from owner's coldkey to subnet account). + let actual_tao_lock_amount = + Self::transfer_tao_to_subnet(netuid_to_register, &coldkey, lock_amount.into())?; + log::debug!("actual_tao_lock_amount: {actual_tao_lock_amount:?}"); + + // --- 11. Set the lock amount for use to determine pricing. + Self::set_network_last_lock(actual_tao_lock_amount); + Self::set_network_last_lock_block(current_block); + + // --- 12. Add the caller to the neuron set. + Self::create_account_if_non_existent(&coldkey, hotkey)?; Self::append_neuron(netuid_to_register, hotkey, current_block); log::debug!("Appended neuron for netuid {netuid_to_register:?}, hotkey: {hotkey:?}"); @@ -211,7 +219,6 @@ impl Pallet { TokenSymbol::::insert(netuid_to_register, symbol); // Keep the locked TAO in the pool instead of recycling the excess. - // Mint the owner alpha separately at the median subnet alpha price. // Size the pool alpha reserve from the total TAO reserve at that same price. let pool_initial_tao: TaoBalance = Self::get_network_min_lock(); let total_pool_tao: TaoBalance = if actual_tao_lock_amount >= pool_initial_tao { @@ -219,8 +226,6 @@ impl Pallet { } else { pool_initial_tao }; - let owner_alpha_tao_equivalent: TaoBalance = - total_pool_tao.saturating_sub(pool_initial_tao); let total_pool_alpha: AlphaBalance = U96F32::saturating_from_num(total_pool_tao.to_u64()) .safe_div(median_subnet_alpha_price) @@ -228,40 +233,18 @@ impl Pallet { .saturating_to_num::() .into(); - let owner_alpha_stake: AlphaBalance = - U96F32::saturating_from_num(owner_alpha_tao_equivalent.to_u64()) - .safe_div(median_subnet_alpha_price) - .saturating_floor() - .saturating_to_num::() - .into(); - - // With the full lock retained in the reserve, this will normally be zero. - let tao_recycled_for_registration = actual_tao_lock_amount.saturating_sub(total_pool_tao); + let owner_alpha_stake = AlphaBalance::ZERO; // Core pool + ownership SubnetTAO::::insert(netuid_to_register, total_pool_tao); SubnetAlphaIn::::insert(netuid_to_register, total_pool_alpha); SubnetOwner::::insert(netuid_to_register, coldkey.clone()); - SubnetOwnerHotkey::::insert(netuid_to_register, hotkey.clone()); + Self::set_subnet_owner_hotkey(netuid_to_register, hotkey)?; SubnetLocked::::insert(netuid_to_register, actual_tao_lock_amount); SubnetTaoProvided::::insert(netuid_to_register, TaoBalance::ZERO); SubnetAlphaInProvided::::insert(netuid_to_register, AlphaBalance::ZERO); SubnetAlphaOut::::insert(netuid_to_register, owner_alpha_stake); SubnetVolume::::insert(netuid_to_register, 0u128); - RAORecycledForRegistration::::insert(netuid_to_register, tao_recycled_for_registration); - - if !owner_alpha_stake.is_zero() { - Self::increase_stake_for_hotkey_and_coldkey_on_subnet( - hotkey, - &coldkey, - netuid_to_register, - owner_alpha_stake, - ); - } - - if tao_recycled_for_registration > TaoBalance::ZERO { - Self::recycle_tao(tao_recycled_for_registration); - } if total_pool_tao > TaoBalance::ZERO { // Record in TotalStake the initial TAO in the pool. @@ -453,7 +436,7 @@ impl Pallet { ); // Insert/update the hotkey - SubnetOwnerHotkey::::insert(netuid, hotkey); + Self::set_subnet_owner_hotkey(netuid, hotkey)?; // Return success. Ok(()) @@ -462,4 +445,21 @@ impl Pallet { pub fn is_valid_subnet_for_emission(netuid: NetUid) -> bool { FirstEmissionBlockNumber::::get(netuid).is_some() } + + pub fn get_subnet_account_id(netuid: NetUid) -> Option { + if NetworksAdded::::contains_key(netuid) || netuid == NetUid::ROOT { + Some(T::SubtensorPalletId::get().into_sub_account_truncating(u16::from(netuid))) + } else { + None + } + } + + pub fn is_subnet_account_id(account: &T::AccountId) -> Option { + let pallet_id = T::SubtensorPalletId::get(); + + match PalletId::try_from_sub_account::(account) { + Some((decoded_pallet_id, netuid)) if decoded_pallet_id == pallet_id => Some(netuid), + _ => None, + } + } } diff --git a/pallets/subtensor/src/swap/swap_coldkey.rs b/pallets/subtensor/src/swap/swap_coldkey.rs index 68cf6d8b56..c99a70d5af 100644 --- a/pallets/subtensor/src/swap/swap_coldkey.rs +++ b/pallets/subtensor/src/swap/swap_coldkey.rs @@ -29,14 +29,16 @@ impl Pallet { Self::transfer_coldkey_stake(netuid, old_coldkey, new_coldkey); } Self::transfer_staking_hotkeys(old_coldkey, new_coldkey); - Self::transfer_hotkeys_ownership(old_coldkey, new_coldkey); + Self::transfer_hotkeys_ownership(old_coldkey, new_coldkey)?; + + // Ensure the new coldkey has no active locks on any subnet before proceeding with the swap. + Self::ensure_no_active_locks(new_coldkey)?; + + // Transfer stake locks + Self::swap_coldkey_locks(old_coldkey, new_coldkey); // Transfer any remaining balance from old_coldkey to new_coldkey - let remaining_balance = Self::get_coldkey_balance(old_coldkey); - if remaining_balance > 0.into() { - Self::kill_coldkey_account(old_coldkey, remaining_balance)?; - Self::add_balance_to_coldkey_account(new_coldkey, remaining_balance); - } + Self::transfer_all_tao_and_kill(old_coldkey, new_coldkey)?; Self::set_last_tx_block(new_coldkey, Self::get_current_block_as_u64()); @@ -49,15 +51,8 @@ impl Pallet { /// Charges the swap cost from the coldkey's account and recycles the tokens. pub fn charge_swap_cost(coldkey: &T::AccountId, swap_cost: TaoBalance) -> DispatchResult { - let burn_amount = Self::remove_balance_from_coldkey_account(coldkey, swap_cost.into()) + Self::recycle_tao(coldkey, swap_cost) .map_err(|_| Error::::NotEnoughBalanceToPaySwapColdKey)?; - - if burn_amount < swap_cost { - return Err(Error::::NotEnoughBalanceToPaySwapColdKey.into()); - } - - Self::recycle_tao(burn_amount); - Ok(()) } @@ -148,14 +143,17 @@ impl Pallet { } /// Transfer the ownership of the hotkeys owned by the old coldkey to the new coldkey. - fn transfer_hotkeys_ownership(old_coldkey: &T::AccountId, new_coldkey: &T::AccountId) { + fn transfer_hotkeys_ownership( + old_coldkey: &T::AccountId, + new_coldkey: &T::AccountId, + ) -> DispatchResult { let old_owned_hotkeys: Vec = OwnedHotkeys::::get(old_coldkey); let mut new_owned_hotkeys: Vec = OwnedHotkeys::::get(new_coldkey); for owned_hotkey in old_owned_hotkeys.iter() { // Remove the hotkey from the old coldkey. Owner::::remove(owned_hotkey); // Add the hotkey to the new coldkey. - Owner::::insert(owned_hotkey, new_coldkey.clone()); + Self::set_hotkey_owner(new_coldkey, owned_hotkey)?; // Addd the owned hotkey to the new set of owned hotkeys. if !new_owned_hotkeys.contains(owned_hotkey) { new_owned_hotkeys.push(owned_hotkey.clone()); @@ -163,5 +161,6 @@ impl Pallet { } OwnedHotkeys::::remove(old_coldkey); OwnedHotkeys::::insert(new_coldkey, new_owned_hotkeys); + Ok(()) } } diff --git a/pallets/subtensor/src/swap/swap_hotkey.rs b/pallets/subtensor/src/swap/swap_hotkey.rs index 2883d41e61..944ea5877f 100644 --- a/pallets/subtensor/src/swap/swap_hotkey.rs +++ b/pallets/subtensor/src/swap/swap_hotkey.rs @@ -131,12 +131,8 @@ impl Pallet { weight.saturating_accrue(T::DbWeight::get().reads_writes(3, 0)); - // 14. Remove the swap cost from the coldkey's account - let actual_recycle_amount = - Self::remove_balance_from_coldkey_account(&coldkey, swap_cost.into())?; - - // 18. Recycle the tokens - Self::recycle_tao(actual_recycle_amount); + // 14. Remove the swap cost from the coldkey's account + Recycle the tokens + Self::recycle_tao(&coldkey, swap_cost.into())?; weight.saturating_accrue(T::DbWeight::get().reads_writes(0, 2)); // 19. Perform the hotkey swap @@ -208,13 +204,17 @@ impl Pallet { Self::alpha_iter_single_prefix(old_hotkey).collect(); weight.saturating_accrue(T::DbWeight::get().reads(old_alpha_values.len() as u64)); - // 2. Swap owner. + // 2. Swap the stake locks + let (reads, writes) = Self::swap_hotkey_locks(old_hotkey, new_hotkey); + weight.saturating_accrue(T::DbWeight::get().reads_writes(reads, writes)); + + // 3. Swap owner. // Owner( hotkey ) -> coldkey -- the coldkey that owns the hotkey. Owner::::remove(old_hotkey); - Owner::::insert(new_hotkey, coldkey.clone()); + Self::set_hotkey_owner(coldkey, new_hotkey)?; weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); - // 3. Swap OwnedHotkeys. + // 4. Swap OwnedHotkeys. // OwnedHotkeys( coldkey ) -> Vec -- the hotkeys that the coldkey owns. let mut hotkeys = OwnedHotkeys::::get(coldkey); // Add the new key if needed. @@ -222,35 +222,35 @@ impl Pallet { hotkeys.push(new_hotkey.clone()); } - // 4. Remove the old key. + // 5. Remove the old key. hotkeys.retain(|hk| *hk != *old_hotkey); OwnedHotkeys::::insert(coldkey, hotkeys); weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); - // 5. execute the hotkey swap on all subnets + // 6. execute the hotkey swap on all subnets for netuid in Self::get_all_subnet_netuids() { Self::perform_hotkey_swap_on_one_subnet( old_hotkey, new_hotkey, weight, netuid, keep_stake, )?; } - // 6. Swap LastTxBlock + // 7. Swap LastTxBlock // LastTxBlock( hotkey ) --> u64 -- the last transaction block for the hotkey. Self::remove_last_tx_block(old_hotkey); weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2)); - // 7. Swap LastTxBlockDelegateTake + // 8. Swap LastTxBlockDelegateTake // LastTxBlockDelegateTake( hotkey ) --> u64 -- the last transaction block for the hotkey delegate take. Self::remove_last_tx_block_delegate_take(old_hotkey); weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2)); - // 8. Swap LastTxBlockChildKeyTake + // 9. Swap LastTxBlockChildKeyTake // LastTxBlockChildKeyTake( hotkey ) --> u64 -- the last transaction block for the hotkey child key take. Self::remove_last_tx_block_childkey(old_hotkey); weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2)); - // 9. Swap delegates. + // 10. Swap delegates. // Delegates( hotkey ) -> take value -- the hotkey delegate take value. if Delegates::::contains_key(old_hotkey) { let old_delegate_take = Delegates::::get(old_hotkey); @@ -259,7 +259,7 @@ impl Pallet { weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2)); } - // 10. Alphas already update in perform_hotkey_swap_on_one_subnet + // 11. Alphas already update in perform_hotkey_swap_on_one_subnet // Update the StakingHotkeys for the case where hotkey staked by multiple coldkeys. if !keep_stake { for (coldkey, _netuid, alpha_share) in old_alpha_values { @@ -279,6 +279,7 @@ impl Pallet { } } } + // Return successful after swapping all the relevant terms. Ok(()) } @@ -304,6 +305,12 @@ impl Pallet { ); weight.saturating_accrue(T::DbWeight::get().reads_writes(3, 0)); + // Check that new hotkey is a non-system hotkey + ensure!( + Self::is_subnet_account_id(new_hotkey).is_none(), + Error::::CannotUseSystemAccount + ); + // 2. Ensure the hotkey not registered on the network before. ensure!( !Self::is_hotkey_registered_on_specific_network(new_hotkey, netuid), @@ -323,12 +330,8 @@ impl Pallet { Error::::NotEnoughBalanceToPaySwapHotKey ); - // 5. Remove the swap cost from the coldkey's account - let actual_recycle_amount = Self::remove_balance_from_coldkey_account(coldkey, swap_cost)?; - weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 0)); - - // 6. Recycle the tokens - Self::recycle_tao(actual_recycle_amount); + // 5. Remove the swap cost from the coldkey's account + Recycle the tokens + Self::recycle_tao(coldkey, swap_cost)?; weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); // 7. Swap owner. @@ -502,7 +505,7 @@ impl Pallet { if let Ok(old_subnet_owner_hotkey) = SubnetOwnerHotkey::::try_get(netuid) { weight.saturating_accrue(T::DbWeight::get().reads(1)); if old_subnet_owner_hotkey == *old_hotkey { - SubnetOwnerHotkey::::insert(netuid, new_hotkey); + Self::set_subnet_owner_hotkey(netuid, new_hotkey)?; weight.saturating_accrue(T::DbWeight::get().writes(1)); } } diff --git a/pallets/subtensor/src/tests/children.rs b/pallets/subtensor/src/tests/children.rs index a0a2edf719..a7d4b1b273 100644 --- a/pallets/subtensor/src/tests/children.rs +++ b/pallets/subtensor/src/tests/children.rs @@ -5,7 +5,7 @@ use super::mock; use super::mock::*; use approx::assert_abs_diff_eq; use frame_support::{assert_err, assert_noop, assert_ok}; -use substrate_fixed::types::{I64F64, I96F32, U96F32}; +use substrate_fixed::types::{I64F64, I96F32}; use subtensor_runtime_common::{AlphaBalance, NetUidStorageIndex, TaoBalance}; use subtensor_swap_interface::SwapHandler; @@ -341,7 +341,7 @@ fn test_add_singular_child() { ), Err(Error::::NonAssociatedColdKey.into()) ); - SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); step_rate_limit(&TransactionType::SetChildren, netuid); assert_eq!( SubtensorModule::do_schedule_children( @@ -2233,7 +2233,7 @@ fn test_do_remove_stake_clears_pending_childkeys() { // Add network and register hotkey add_network(netuid, 13, 0); register_ok_neuron(netuid, hotkey, coldkey, 0); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, 10_000_000_000_000_u64.into()); + add_balance_to_coldkey_account(&coldkey, 10_000_000_000_000_u64.into()); SubtokenEnabled::::insert(netuid, true); let reserve = 1_000_000_000_000_000_u64; @@ -2644,12 +2644,9 @@ fn test_childkey_set_weights_single_parent() { let stake_to_give_child = AlphaBalance::from(109_999); // Register parent with minimal stake and child with high stake - SubtensorModule::add_balance_to_coldkey_account(&coldkey_parent, 1.into()); - SubtensorModule::add_balance_to_coldkey_account( - &coldkey_child, - balance_to_give_child + 10.into(), - ); - SubtensorModule::add_balance_to_coldkey_account(&coldkey_weight_setter, 1_000_000.into()); + add_balance_to_coldkey_account(&coldkey_parent, 1.into()); + add_balance_to_coldkey_account(&coldkey_child, balance_to_give_child + 10.into()); + add_balance_to_coldkey_account(&coldkey_weight_setter, 1_000_000.into()); // Add neurons for parent, child and weight_setter register_ok_neuron(netuid, parent, coldkey_parent, 1); @@ -2752,10 +2749,7 @@ fn test_set_weights_no_parent() { let balance_to_give_child = TaoBalance::from(109_999); let stake_to_give_child = AlphaBalance::from(109_999); - SubtensorModule::add_balance_to_coldkey_account( - &coldkey, - balance_to_give_child + 10.into(), - ); + add_balance_to_coldkey_account(&coldkey, balance_to_give_child + 10.into()); // Is registered register_ok_neuron(netuid, hotkey, coldkey, 1); @@ -2864,11 +2858,11 @@ fn test_childkey_take_drain() { register_ok_neuron(netuid, child_hotkey, child_coldkey, 0); register_ok_neuron(netuid, parent_hotkey, parent_coldkey, 1); register_ok_neuron(netuid, miner_hotkey, miner_coldkey, 1); - SubtensorModule::add_balance_to_coldkey_account( + add_balance_to_coldkey_account( &parent_coldkey, TaoBalance::from(stake) + ExistentialDeposit::get(), ); - SubtensorModule::add_balance_to_coldkey_account( + add_balance_to_coldkey_account( &nominator, TaoBalance::from(stake) + ExistentialDeposit::get(), ); @@ -3004,9 +2998,9 @@ fn test_parent_child_chain_emission() { register_ok_neuron(netuid, hotkey_c, coldkey_c, 0); // Add initial stakes - SubtensorModule::add_balance_to_coldkey_account(&coldkey_a, 1_000.into()); - SubtensorModule::add_balance_to_coldkey_account(&coldkey_b, 1_000.into()); - SubtensorModule::add_balance_to_coldkey_account(&coldkey_c, 1_000.into()); + add_balance_to_coldkey_account(&coldkey_a, 1_000.into()); + add_balance_to_coldkey_account(&coldkey_b, 1_000.into()); + add_balance_to_coldkey_account(&coldkey_c, 1_000.into()); // Swap to alpha let stake_a = 300_000_000_000_u64; @@ -3098,17 +3092,14 @@ fn test_parent_child_chain_emission() { // Set the weight of root TAO to be 0%, so only alpha is effective. SubtensorModule::set_tao_weight(0); - let emission = U96F32::from_num( - SubtensorModule::get_block_emission() - .unwrap_or(TaoBalance::ZERO) - .to_u64(), - ); + let emission = SubtensorModule::get_block_emission(); // Set pending emission to 0 PendingValidatorEmission::::insert(netuid, AlphaBalance::ZERO); PendingServerEmission::::insert(netuid, AlphaBalance::ZERO); // Run epoch with emission value + let emission_value = u64::from(emission.peek()); SubtensorModule::run_coinbase(emission); // Log new stake @@ -3175,8 +3166,8 @@ fn test_parent_child_chain_emission() { assert_abs_diff_eq!( total_stake_inc.to_num::(), - emission.to_num::(), - epsilon = emission.to_num::() / 1000, + emission_value, + epsilon = emission_value / 1000, ); }); } @@ -3212,9 +3203,9 @@ fn test_parent_child_chain_epoch() { register_ok_neuron(netuid, hotkey_c, coldkey_c, 0); // Add initial stakes - SubtensorModule::add_balance_to_coldkey_account(&coldkey_a, 1_000.into()); - SubtensorModule::add_balance_to_coldkey_account(&coldkey_b, 1_000.into()); - SubtensorModule::add_balance_to_coldkey_account(&coldkey_c, 1_000.into()); + add_balance_to_coldkey_account(&coldkey_a, 1_000.into()); + add_balance_to_coldkey_account(&coldkey_b, 1_000.into()); + add_balance_to_coldkey_account(&coldkey_c, 1_000.into()); mock::setup_reserves( netuid, @@ -3366,9 +3357,9 @@ fn test_dividend_distribution_with_children() { register_ok_neuron(netuid, hotkey_c, coldkey_c, 0); // Add initial stakes - SubtensorModule::add_balance_to_coldkey_account(&coldkey_a, 1_000.into()); - SubtensorModule::add_balance_to_coldkey_account(&coldkey_b, 1_000.into()); - SubtensorModule::add_balance_to_coldkey_account(&coldkey_c, 1_000.into()); + add_balance_to_coldkey_account(&coldkey_a, 1_000.into()); + add_balance_to_coldkey_account(&coldkey_b, 1_000.into()); + add_balance_to_coldkey_account(&coldkey_c, 1_000.into()); // Swap to alpha let total_tao = I96F32::from_num(300_000 + 100_000 + 50_000); @@ -3600,9 +3591,9 @@ fn test_dynamic_parent_child_relationships() { log::info!("child take 2: {chk_take_2:?}"); // Add initial stakes - SubtensorModule::add_balance_to_coldkey_account(&coldkey_parent, (500_000 + 1_000).into()); - SubtensorModule::add_balance_to_coldkey_account(&coldkey_child1, (50_000 + 1_000).into()); - SubtensorModule::add_balance_to_coldkey_account(&coldkey_child2, (30_000 + 1_000).into()); + add_balance_to_coldkey_account(&coldkey_parent, (500_000 + 1_000).into()); + add_balance_to_coldkey_account(&coldkey_child1, (50_000 + 1_000).into()); + add_balance_to_coldkey_account(&coldkey_child2, (30_000 + 1_000).into()); let reserve = 1_000_000_000_000_u64; mock::setup_reserves(netuid, reserve.into(), reserve.into()); @@ -3898,8 +3889,8 @@ fn test_dividend_distribution_with_children_same_coldkey_owner() { register_ok_neuron(netuid, hotkey_b, coldkey_a, 0); // Add initial stakes - SubtensorModule::add_balance_to_coldkey_account(&coldkey_a, 1_000.into()); - SubtensorModule::add_balance_to_coldkey_account(&coldkey_a, 1_000.into()); + add_balance_to_coldkey_account(&coldkey_a, 1_000.into()); + add_balance_to_coldkey_account(&coldkey_a, 1_000.into()); // Swap to alpha let total_tao = 300_000 + 100_000; @@ -4451,7 +4442,7 @@ fn test_register_network_schedules_root_validators() { let subnet_owner_coldkey = U256::from(1001); let subnet_owner_hotkey = U256::from(1002); let lock_cost = SubtensorModule::get_network_lock_cost(); - SubtensorModule::add_balance_to_coldkey_account(&subnet_owner_coldkey, lock_cost.into()); + add_balance_to_coldkey_account(&subnet_owner_coldkey, lock_cost.into()); TotalIssuance::::mutate(|total| { *total = total.saturating_add(lock_cost); }); @@ -4574,7 +4565,7 @@ fn test_register_network_schedules_root_validators_auto_parent_delegation_flag() let subnet_owner_coldkey = U256::from(1001); let subnet_owner_hotkey = U256::from(1002); let lock_cost = SubtensorModule::get_network_lock_cost(); - SubtensorModule::add_balance_to_coldkey_account(&subnet_owner_coldkey, lock_cost.into()); + add_balance_to_coldkey_account(&subnet_owner_coldkey, lock_cost.into()); TotalIssuance::::mutate(|total| { *total = total.saturating_add(lock_cost); }); diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index f86ca0b1e6..bd5761f376 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -1,10 +1,7 @@ #![allow(clippy::expect_used)] use crate::RootAlphaDividendsPerSubnet; -use crate::tests::mock::{ - RuntimeOrigin, SubtensorModule, Test, add_dynamic_network, new_test_ext, - remove_owner_registration_stake, run_to_block, -}; +use crate::tests::mock::*; use crate::{ DefaultMinRootClaimAmount, Error, MAX_NUM_ROOT_CLAIMS, MAX_ROOT_CLAIM_THRESHOLD, NetworksAdded, NumRootClaim, NumStakingColdkeys, PendingRootAlphaDivs, RootClaimable, RootClaimableThreshold, @@ -20,7 +17,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, U64F64, U96F32}; +use substrate_fixed::types::{I96F32, U64F64}; use subtensor_runtime_common::{AlphaBalance, NetUid, TaoBalance, Token}; use subtensor_swap_interface::SwapHandler; @@ -50,7 +47,7 @@ fn test_claim_root_with_drain_emissions() { SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 let root_stake = 2_000_000u64; - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey, NetUid::ROOT, @@ -58,7 +55,7 @@ fn test_claim_root_with_drain_emissions() { ); let initial_total_hotkey_alpha = 10_000_000u64; - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &owner_coldkey, netuid, @@ -203,13 +200,13 @@ fn test_claim_root_adding_stake_proportionally_for_two_stakers() { SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 let root_stake = 1_000_000u64; - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &alice_coldkey, NetUid::ROOT, root_stake.into(), ); - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &bob_coldkey, NetUid::ROOT, @@ -217,7 +214,7 @@ fn test_claim_root_adding_stake_proportionally_for_two_stakers() { ); let root_stake_rate = 0.1f64; - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &other_coldkey, NetUid::ROOT, @@ -225,7 +222,7 @@ fn test_claim_root_adding_stake_proportionally_for_two_stakers() { ); let initial_total_hotkey_alpha = 10_000_000u64; - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &owner_coldkey, netuid, @@ -306,20 +303,20 @@ fn test_claim_root_adding_stake_disproportionally_for_two_stakers() { let other_root_stake = 7_000_000u64; let alice_root_stake_rate = 0.1f64; - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &alice_coldkey, NetUid::ROOT, alice_root_stake.into(), ); - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &bob_coldkey, NetUid::ROOT, bob_root_stake.into(), ); - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &other_coldkey, NetUid::ROOT, @@ -327,7 +324,7 @@ fn test_claim_root_adding_stake_disproportionally_for_two_stakers() { ); let initial_total_hotkey_alpha = 10_000_000u64; - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &owner_coldkey, netuid, @@ -405,13 +402,13 @@ fn test_claim_root_with_changed_stake() { NetworksAdded::::insert(NetUid::ROOT, true); let root_stake = 8_000_000u64; - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &alice_coldkey, NetUid::ROOT, root_stake.into(), ); - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &bob_coldkey, NetUid::ROOT, @@ -419,7 +416,7 @@ fn test_claim_root_with_changed_stake() { ); let initial_total_hotkey_alpha = 10_000_000u64; - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &owner_coldkey, netuid, @@ -612,14 +609,14 @@ fn test_claim_root_with_drain_emissions_and_swap_claim_type() { assert_eq!(current_price, 0.5f64); let root_stake = 2_000_000u64; - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey, NetUid::ROOT, root_stake.into(), ); let root_stake_rate = 0.1f64; - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &other_coldkey, NetUid::ROOT, @@ -627,7 +624,7 @@ fn test_claim_root_with_drain_emissions_and_swap_claim_type() { ); let initial_total_hotkey_alpha = 10_000_000u64; - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &owner_coldkey, netuid, @@ -775,7 +772,7 @@ fn test_claim_root_with_run_coinbase() { let root_stake = 200_000_000u64; SubnetTAO::::insert(NetUid::ROOT, TaoBalance::from(root_stake)); - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey, NetUid::ROOT, @@ -783,7 +780,7 @@ fn test_claim_root_with_run_coinbase() { ); let initial_total_hotkey_alpha = 10_000_000u64; - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &owner_coldkey, netuid, @@ -809,8 +806,8 @@ fn test_claim_root_with_run_coinbase() { .into(); assert_eq!(initial_stake, 0u64); - let block_emissions = 1_000_000u64; - SubtensorModule::run_coinbase(U96F32::from(block_emissions)); + let block_emissions = SubtensorModule::mint_tao(1_000_000u64.into()); + SubtensorModule::run_coinbase(block_emissions); // Claim root alpha @@ -894,7 +891,7 @@ fn test_claim_root_with_block_emissions() { let root_stake = 200_000_000u64; SubnetTAO::::insert(NetUid::ROOT, TaoBalance::from(root_stake)); - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey, NetUid::ROOT, @@ -915,7 +912,7 @@ fn test_claim_root_with_block_emissions() { 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( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &owner_coldkey, netuid, @@ -959,19 +956,19 @@ fn test_populate_staking_maps() { let netuid2 = NetUid::from(2); let root_stake = 200_000_000u64; - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey1, NetUid::ROOT, root_stake.into(), ); - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey2, NetUid::ROOT, root_stake.into(), ); - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey3, netuid2, @@ -1010,7 +1007,7 @@ fn test_claim_root_coinbase_distribution() { let initial_tao = 200_000_000u64; SubnetTAO::::insert(NetUid::ROOT, TaoBalance::from(initial_tao)); - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey, NetUid::ROOT, @@ -1018,7 +1015,7 @@ fn test_claim_root_coinbase_distribution() { ); let initial_total_hotkey_alpha = 10_000_000u64; - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &owner_coldkey, netuid, @@ -1127,7 +1124,7 @@ fn test_claim_root_with_swap_coldkey() { SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 let root_stake = 2_000_000u64; - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey, NetUid::ROOT, @@ -1135,7 +1132,7 @@ fn test_claim_root_with_swap_coldkey() { ); let initial_total_hotkey_alpha = 10_000_000u64; - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &owner_coldkey, netuid, @@ -1214,7 +1211,7 @@ fn test_claim_root_with_swap_hotkey() { SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 let root_stake = 2_000_000u64; - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey, NetUid::ROOT, @@ -1222,7 +1219,7 @@ fn test_claim_root_with_swap_hotkey() { ); let initial_total_hotkey_alpha = 10_000_000u64; - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &owner_coldkey, netuid, @@ -1330,13 +1327,13 @@ fn test_claim_root_on_network_deregistration() { assert_eq!(current_price, 0.5f64); let root_stake = 2_000_000u64; - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey, NetUid::ROOT, root_stake.into(), ); - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &other_coldkey, NetUid::ROOT, @@ -1344,7 +1341,7 @@ fn test_claim_root_on_network_deregistration() { ); let initial_total_hotkey_alpha = 10_000_000u64; - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &owner_coldkey, netuid, @@ -1471,7 +1468,7 @@ fn test_claim_root_with_unrelated_subnets() { SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 let root_stake = 2_000_000u64; - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey, NetUid::ROOT, @@ -1479,7 +1476,7 @@ fn test_claim_root_with_unrelated_subnets() { ); let initial_total_hotkey_alpha = 10_000_000u64; - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &owner_coldkey, netuid, @@ -1575,13 +1572,13 @@ fn test_claim_root_fill_root_alpha_dividends_per_subnet() { SubnetAlphaIn::::insert(netuid, alpha_in); let root_stake = 2_000_000u64; - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey, NetUid::ROOT, root_stake.into(), ); - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &other_coldkey, NetUid::ROOT, @@ -1589,7 +1586,7 @@ fn test_claim_root_fill_root_alpha_dividends_per_subnet() { ); let initial_total_hotkey_alpha = 10_000_000u64; - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &owner_coldkey, netuid, @@ -1650,7 +1647,7 @@ fn test_claim_root_with_keep_subnets() { SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 let root_stake = 2_000_000u64; - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey, NetUid::ROOT, @@ -1658,7 +1655,7 @@ fn test_claim_root_with_keep_subnets() { ); let initial_total_hotkey_alpha = 10_000_000u64; - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &owner_coldkey, netuid, @@ -1746,14 +1743,14 @@ fn test_claim_root_keep_subnets_swap_claim_type() { assert_eq!(current_price, 0.5f64); let root_stake = 2_000_000u64; - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey, NetUid::ROOT, root_stake.into(), ); let root_stake_rate = 0.1f64; - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &other_coldkey, NetUid::ROOT, @@ -1761,7 +1758,7 @@ fn test_claim_root_keep_subnets_swap_claim_type() { ); let initial_total_hotkey_alpha = 10_000_000u64; - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &owner_coldkey, netuid, @@ -1843,13 +1840,13 @@ fn test_claim_root_with_moved_stake() { NetworksAdded::::insert(NetUid::ROOT, true); let root_stake = 8_000_000u64; - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &alice_coldkey, NetUid::ROOT, root_stake.into(), ); - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &bob_coldkey, NetUid::ROOT, @@ -1857,7 +1854,7 @@ fn test_claim_root_with_moved_stake() { ); let initial_total_hotkey_alpha = 10_000_000u64; - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &owner_coldkey, netuid, diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 8667ef9be0..6199aa9952 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -54,7 +54,8 @@ fn test_hotkey_take() { #[test] fn test_coinbase_basecase() { new_test_ext(1).execute_with(|| { - SubtensorModule::run_coinbase(U96F32::from_num(0.0)); + let zero_emission = SubtensorModule::mint_tao(0.into()); + SubtensorModule::run_coinbase(zero_emission); }); } @@ -74,7 +75,8 @@ fn test_coinbase_tao_issuance_base() { SubnetTaoFlow::::insert(netuid, 1234567_i64); let tao_in_before = SubnetTAO::::get(netuid); let total_stake_before = TotalStake::::get(); - SubtensorModule::run_coinbase(U96F32::from_num(emission)); + let emission_credit = SubtensorModule::mint_tao(emission); + SubtensorModule::run_coinbase(emission_credit); assert_eq!(SubnetTAO::::get(netuid), tao_in_before + emission); assert_eq!( TotalIssuance::::get(), @@ -90,11 +92,12 @@ fn test_coinbase_tao_issuance_base_low() { new_test_ext(1).execute_with(|| { let netuid = NetUid::from(1); let emission = TaoBalance::from(1); + let emission_credit = SubtensorModule::mint_tao(emission); add_network(netuid, 1, 0); assert_eq!(SubnetTAO::::get(netuid), TaoBalance::ZERO); // Set subnet flow to non-zero SubnetTaoFlow::::insert(netuid, 33433_i64); - SubtensorModule::run_coinbase(U96F32::from_num(emission)); + SubtensorModule::run_coinbase(emission_credit); assert_eq!(SubnetTAO::::get(netuid), emission); assert_eq!(TotalIssuance::::get(), emission); assert_eq!(TotalStake::::get(), emission); @@ -139,6 +142,7 @@ fn test_coinbase_tao_issuance_multiple() { let netuid2 = NetUid::from(2); let netuid3 = NetUid::from(3); let emission = TaoBalance::from(3_333_333); + let emission_credit = SubtensorModule::mint_tao(emission); add_network(netuid1, 1, 0); add_network(netuid2, 1, 0); add_network(netuid3, 1, 0); @@ -149,7 +153,7 @@ fn test_coinbase_tao_issuance_multiple() { SubnetTaoFlow::::insert(netuid1, 100_000_000_i64); SubnetTaoFlow::::insert(netuid2, 100_000_000_i64); SubnetTaoFlow::::insert(netuid3, 100_000_000_i64); - SubtensorModule::run_coinbase(U96F32::from_num(emission)); + SubtensorModule::run_coinbase(emission_credit); assert_abs_diff_eq!( SubnetTAO::::get(netuid1), emission / 3.into(), @@ -182,6 +186,7 @@ fn test_coinbase_tao_issuance_different_prices() { let netuid1 = NetUid::from(1); let netuid2 = NetUid::from(2); let emission = 100_000_000; + let emission_credit = SubtensorModule::mint_tao(emission.into()); add_network(netuid1, 1, 0); add_network(netuid2, 1, 0); @@ -222,7 +227,7 @@ fn test_coinbase_tao_issuance_different_prices() { assert_eq!(SubnetTAO::::get(netuid2), initial_tao.into()); // Run the coinbase with the emission amount. - SubtensorModule::run_coinbase(U96F32::from_num(emission)); + SubtensorModule::run_coinbase(emission_credit); // Assert tao emission is split evenly. assert_abs_diff_eq!( @@ -454,6 +459,7 @@ fn test_coinbase_alpha_issuance_base() { let netuid1 = NetUid::from(1); let netuid2 = NetUid::from(2); let emission: u64 = 1_000_000; + let emission_credit = SubtensorModule::mint_tao(emission.into()); add_network(netuid1, 1, 0); add_network(netuid2, 1, 0); // Set up prices 1 and 1 @@ -466,7 +472,7 @@ fn test_coinbase_alpha_issuance_base() { SubnetTaoFlow::::insert(netuid1, 100_000_000_i64); SubnetTaoFlow::::insert(netuid2, 100_000_000_i64); // Check initial - SubtensorModule::run_coinbase(U96F32::from_num(emission)); + SubtensorModule::run_coinbase(emission_credit); // tao_in = 500_000 // alpha_in = 500_000/price = 500_000 assert_eq!( @@ -492,6 +498,7 @@ fn test_coinbase_alpha_issuance_different() { let netuid1 = NetUid::from(1); let netuid2 = NetUid::from(2); let emission: u64 = 1_000_000; + let emission_credit = SubtensorModule::mint_tao(emission.into()); add_network(netuid1, 1, 0); add_network(netuid2, 1, 0); // Make subnets dynamic. @@ -508,7 +515,7 @@ fn test_coinbase_alpha_issuance_different() { SubnetTaoFlow::::insert(netuid2, 200_000_000_i64); // Do NOT Set tao flow, let it initialize // Run coinbase - SubtensorModule::run_coinbase(U96F32::from_num(emission)); + SubtensorModule::run_coinbase(emission_credit); // tao_in = 333_333 // alpha_in = 333_333/price = 333_333 + initial assert_eq!( @@ -531,6 +538,7 @@ fn test_coinbase_alpha_issuance_with_cap_trigger() { let netuid1 = NetUid::from(1); let netuid2 = NetUid::from(2); let emission: u64 = 1_000_000; + let emission_credit = SubtensorModule::mint_tao(emission.into()); add_network(netuid1, 1, 0); add_network(netuid2, 1, 0); // Make subnets dynamic. @@ -547,7 +555,7 @@ fn test_coinbase_alpha_issuance_with_cap_trigger() { SubnetMovingPrice::::insert(netuid1, I96F32::from_num(1)); SubnetMovingPrice::::insert(netuid2, I96F32::from_num(2)); // Run coinbase - SubtensorModule::run_coinbase(U96F32::from_num(emission)); + SubtensorModule::run_coinbase(emission_credit); // tao_in = 333_333 // alpha_in = 333_333/price > 1_000_000_000 --> 1_000_000_000 + initial_alpha assert!(SubnetAlphaIn::::get(netuid1) < (initial_alpha + 1_000_000_000).into()); @@ -566,6 +574,7 @@ fn test_coinbase_alpha_issuance_with_cap_trigger_and_block_emission() { let netuid1 = NetUid::from(1); let netuid2 = NetUid::from(2); let emission: u64 = 1_000_000; + let emission_credit = SubtensorModule::mint_tao(emission.into()); add_network(netuid1, 1, 0); add_network(netuid2, 1, 0); @@ -611,7 +620,7 @@ fn test_coinbase_alpha_issuance_with_cap_trigger_and_block_emission() { SubnetAlphaOut::::insert(netuid2, AlphaBalance::from(21_000_000_000_000_000_u64)); // Set issuance above 21M // Run coinbase - SubtensorModule::run_coinbase(U96F32::from_num(emission)); + SubtensorModule::run_coinbase(emission_credit); // Get the prices after the run_coinbase let price_1_after = ::SwapInterface::current_alpha_price(netuid1); @@ -647,10 +656,10 @@ fn test_owner_cut_base() { ); SubtensorModule::set_tempo(netuid, 10000); // Large number (dont drain) SubtensorModule::set_subnet_owner_cut(0); - SubtensorModule::run_coinbase(U96F32::from_num(0)); + SubtensorModule::run_coinbase(SubtensorModule::mint_tao(0.into())); assert_eq!(PendingOwnerCut::::get(netuid), 0.into()); // No cut SubtensorModule::set_subnet_owner_cut(u16::MAX); - SubtensorModule::run_coinbase(U96F32::from_num(0)); + SubtensorModule::run_coinbase(SubtensorModule::mint_tao(0.into())); assert_eq!(PendingOwnerCut::::get(netuid), 1_000_000_000.into()); // Full cut. }); } @@ -659,7 +668,6 @@ fn test_owner_cut_base() { #[test] fn test_pending_emission() { new_test_ext(1).execute_with(|| { - let emission: u64 = 1_000_000; let hotkey = U256::from(1); let coldkey = U256::from(2); let netuid = add_dynamic_network(&hotkey, &coldkey); @@ -668,9 +676,9 @@ fn test_pending_emission() { FirstEmissionBlockNumber::::insert(netuid, 0); mock::setup_reserves(netuid, 1_000_000.into(), 1.into()); - SubtensorModule::run_coinbase(U96F32::from_num(0)); + SubtensorModule::run_coinbase(SubtensorModule::mint_tao(0.into())); SubnetTAO::::insert(NetUid::ROOT, TaoBalance::from(1_000_000_000)); // Add root weight. - SubtensorModule::run_coinbase(U96F32::from_num(0)); + SubtensorModule::run_coinbase(SubtensorModule::mint_tao(0.into())); SubtensorModule::set_tempo(netuid, 10000); // Large number (dont drain) SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 @@ -681,7 +689,7 @@ fn test_pending_emission() { 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)); + SubtensorModule::run_coinbase(SubtensorModule::mint_tao(0.into())); // 1 TAO / ( 1 + 3 ) = 0.25 * 1 / 2 = 125000000 assert_abs_diff_eq!( @@ -2587,7 +2595,8 @@ fn test_run_coinbase_not_started() { assert!(SubtensorModule::should_run_epoch(netuid, current_block)); // Run coinbase with emission. - SubtensorModule::run_coinbase(U96F32::from_num(100_000_000)); + let emission_credit = SubtensorModule::mint_tao(100_000_000.into()); + SubtensorModule::run_coinbase(emission_credit); // We expect that the epoch ran. assert_eq!(BlocksSinceLastStep::::get(netuid), 0); @@ -2678,7 +2687,8 @@ fn test_run_coinbase_not_started_start_after() { assert!(SubtensorModule::should_run_epoch(netuid, current_block)); // Run coinbase with emission. - SubtensorModule::run_coinbase(U96F32::from_num(100_000_000)); + let emission_credit = SubtensorModule::mint_tao(100_000_000.into()); + SubtensorModule::run_coinbase(emission_credit); // We expect that the epoch ran. assert_eq!(BlocksSinceLastStep::::get(netuid), 0); @@ -2698,7 +2708,8 @@ fn test_run_coinbase_not_started_start_after() { ); // Run coinbase with emission. - SubtensorModule::run_coinbase(U96F32::from_num(100_000_000)); + let emission_credit = SubtensorModule::mint_tao(100_000_000.into()); + SubtensorModule::run_coinbase(emission_credit); // We expect that the epoch ran. assert_eq!(BlocksSinceLastStep::::get(netuid), 0); @@ -2741,10 +2752,11 @@ fn test_coinbase_v3_liquidity_update() { // Enable emissions and run coinbase (which will increase position liquidity) let emission: u64 = 1_234_567; + let emission_credit = SubtensorModule::mint_tao(emission.into()); // Set the TAO flow to non-zero SubnetTaoFlow::::insert(netuid, 8348383_i64); FirstEmissionBlockNumber::::insert(netuid, 0); - SubtensorModule::run_coinbase(U96F32::from_num(emission)); + SubtensorModule::run_coinbase(emission_credit); let position_after = pallet_subtensor_swap::Positions::::get(( netuid, @@ -2938,6 +2950,7 @@ fn test_zero_shares_zero_emission() { let netuid1 = add_dynamic_network(&subnet_owner_hk, &subnet_owner_ck); let netuid2 = add_dynamic_network(&subnet_owner_hk, &subnet_owner_ck); let emission: u64 = 1_000_000; + let emission_credit = SubtensorModule::mint_tao(emission.into()); // Setup prices 1 and 1 let initial: u64 = 1_000_000; SubnetTAO::::insert(netuid1, TaoBalance::from(initial)); @@ -2950,7 +2963,7 @@ fn test_zero_shares_zero_emission() { SubnetMovingPrice::::insert(netuid1, I96F32::from_num(0)); SubnetMovingPrice::::insert(netuid2, I96F32::from_num(0)); // Run coinbase - SubtensorModule::run_coinbase(U96F32::from_num(emission)); + SubtensorModule::run_coinbase(emission_credit); // Netuid 1 is cut off by lower limit, all emission goes to netuid2 assert_eq!(SubnetAlphaIn::::get(netuid1), initial.into()); assert_eq!(SubnetAlphaIn::::get(netuid2), initial.into()); @@ -2987,15 +3000,15 @@ fn test_mining_emission_distribution_with_no_root_sell() { 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( + add_balance_to_coldkey_account( &validator_coldkey, TaoBalance::from(stake) + ExistentialDeposit::get(), ); - SubtensorModule::add_balance_to_coldkey_account( + add_balance_to_coldkey_account( &validator_miner_coldkey, TaoBalance::from(stake) + ExistentialDeposit::get(), ); - SubtensorModule::add_balance_to_coldkey_account( + add_balance_to_coldkey_account( &miner_coldkey, TaoBalance::from(stake) + ExistentialDeposit::get(), ); @@ -3037,7 +3050,7 @@ fn test_mining_emission_distribution_with_no_root_sell() { step_block(subnet_tempo); // Add stake to validator so it has root stake - SubtensorModule::add_balance_to_coldkey_account(&validator_coldkey, root_stake.into()); + add_balance_to_coldkey_account(&validator_coldkey, root_stake.into()); // init root assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(validator_coldkey), @@ -3182,15 +3195,15 @@ fn test_mining_emission_distribution_with_root_sell() { 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( + add_balance_to_coldkey_account( &validator_coldkey, TaoBalance::from(stake) + ExistentialDeposit::get(), ); - SubtensorModule::add_balance_to_coldkey_account( + add_balance_to_coldkey_account( &validator_miner_coldkey, TaoBalance::from(stake) + ExistentialDeposit::get(), ); - SubtensorModule::add_balance_to_coldkey_account( + add_balance_to_coldkey_account( &miner_coldkey, TaoBalance::from(stake) + ExistentialDeposit::get(), ); @@ -3232,7 +3245,7 @@ fn test_mining_emission_distribution_with_root_sell() { step_block(subnet_tempo); // Add stake to validator so it has root stake - SubtensorModule::add_balance_to_coldkey_account(&validator_coldkey, root_stake.into()); + add_balance_to_coldkey_account(&validator_coldkey, root_stake.into()); // init root assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(validator_coldkey), @@ -3522,7 +3535,8 @@ fn test_coinbase_inject_and_maybe_swap_does_not_skew_reserves() { 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 credit = SubtensorModule::mint_tao((123 + 789100).into()); + SubtensorModule::inject_and_maybe_swap(&[netuid0], &tao_in, &alpha_in, &excess_tao, credit); let tao_in_after = SubnetTAO::::get(netuid0); let alpha_in_after = SubnetAlphaIn::::get(netuid0); @@ -3669,7 +3683,8 @@ fn test_coinbase_emit_to_subnets_with_no_root_sell() { assert!(tao_emission / price <= alpha_emission); // ==== Run the emit to subnets ===== - SubtensorModule::emit_to_subnets(&[netuid0], &subnet_emissions, root_sell_flag); + let credit = SubtensorModule::mint_tao(12345678.into()); + SubtensorModule::emit_to_subnets(&[netuid0], &subnet_emissions, credit, root_sell_flag); // Find the owner cut expected let owner_cut: U96F32 = SubtensorModule::get_float_subnet_owner_cut(); @@ -3760,7 +3775,8 @@ fn test_coinbase_emit_to_subnets_with_root_sell() { assert!(tao_emission / price <= alpha_emission); // ==== Run the emit to subnets ===== - SubtensorModule::emit_to_subnets(&[netuid0], &subnet_emissions, root_sell_flag); + let credit = SubtensorModule::mint_tao(12345678.into()); + SubtensorModule::emit_to_subnets(&[netuid0], &subnet_emissions, credit, root_sell_flag); // Find the owner cut expected let owner_cut: U96F32 = SubtensorModule::get_float_subnet_owner_cut(); @@ -3842,7 +3858,7 @@ fn test_pending_emission_start_call_not_done() { Tempo::::insert(netuid, subnet_tempo); register_ok_neuron(netuid, validator_hotkey, validator_coldkey, 0); - SubtensorModule::add_balance_to_coldkey_account( + add_balance_to_coldkey_account( &validator_coldkey, TaoBalance::from(stake) + ExistentialDeposit::get(), ); @@ -3854,7 +3870,7 @@ fn test_pending_emission_start_call_not_done() { 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()); + add_balance_to_coldkey_account(&validator_coldkey, root_stake.into()); // init root assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(validator_coldkey), @@ -3969,7 +3985,7 @@ fn test_get_subnet_terms_alpha_emissions_cap() { let owner_coldkey = U256::from(11); let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); let tao_block_emission: U96F32 = U96F32::saturating_from_num( - SubtensorModule::get_block_emission() + SubtensorModule::calculate_block_emission() .unwrap_or(TaoBalance::ZERO) .to_u64(), ); diff --git a/pallets/subtensor/src/tests/consensus.rs b/pallets/subtensor/src/tests/consensus.rs index 4a85f652a8..495633d131 100644 --- a/pallets/subtensor/src/tests/consensus.rs +++ b/pallets/subtensor/src/tests/consensus.rs @@ -185,7 +185,7 @@ fn init_run_epochs( }; // let stake: u64 = 1; // alternative test: all nodes receive stake, should be same outcome, except stake - SubtensorModule::add_balance_to_coldkey_account(&(U256::from(key)), stake.into()); + add_balance_to_coldkey_account(&(U256::from(key)), stake.into()); SubtensorModule::append_neuron(netuid, &(U256::from(key)), 0); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &U256::from(key), diff --git a/pallets/subtensor/src/tests/delegate_info.rs b/pallets/subtensor/src/tests/delegate_info.rs index 8c04c9d136..d847e3d008 100644 --- a/pallets/subtensor/src/tests/delegate_info.rs +++ b/pallets/subtensor/src/tests/delegate_info.rs @@ -119,10 +119,7 @@ fn test_get_delegated() { let Some(delegate) = delegate else { continue; }; - SubtensorModule::add_balance_to_coldkey_account( - delegatee, - (*amount + 500_000).into(), - ); + add_balance_to_coldkey_account(delegatee, (*amount + 500_000).into()); assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(*delegatee), *delegate, diff --git a/pallets/subtensor/src/tests/epoch.rs b/pallets/subtensor/src/tests/epoch.rs index 5c516f9f30..02236d892d 100644 --- a/pallets/subtensor/src/tests/epoch.rs +++ b/pallets/subtensor/src/tests/epoch.rs @@ -178,7 +178,7 @@ fn init_run_epochs( }; // let stake: u64 = 1; // alternative test: all nodes receive stake, should be same outcome, except stake - SubtensorModule::add_balance_to_coldkey_account(&(U256::from(key)), stake.into()); + add_balance_to_coldkey_account(&(U256::from(key)), stake.into()); SubtensorModule::append_neuron(netuid, &(U256::from(key)), 0); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &U256::from(key), @@ -563,7 +563,7 @@ fn test_1_graph() { let stake_amount: TaoBalance = 1_000_000_000.into(); add_network_disable_commit_reveal(netuid, u16::MAX - 1, 0); // set higher tempo to avoid built-in epoch, then manual epoch instead SubtensorModule::set_max_allowed_uids(netuid, 1); - SubtensorModule::add_balance_to_coldkey_account( + add_balance_to_coldkey_account( &coldkey, stake_amount + ExistentialDeposit::get() + SubtensorModule::get_network_min_lock(), ); @@ -1023,7 +1023,7 @@ fn test_bonds() { // === Register [validator1, validator2, validator3, validator4, server1, server2, server3, server4] for key in 0..n as u64 { - SubtensorModule::add_balance_to_coldkey_account( + add_balance_to_coldkey_account( &U256::from(key), max_stake + ExistentialDeposit::get() + SubtensorModule::get_network_min_lock() ); @@ -1317,7 +1317,7 @@ fn test_set_alpha_disabled() { // Enable Liquid Alpha and setup SubtensorModule::set_liquid_alpha_enabled(netuid, true); migrations::migrate_create_root_network::migrate_create_root_network::(); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_000_u64.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_000_u64.into()); assert_ok!(SubtensorModule::root_register(signer.clone(), hotkey,)); let fee = ::SwapInterface::approx_fee_amount( netuid.into(), @@ -1369,7 +1369,7 @@ fn test_active_stake() { // === Register [validator1, validator2, server1, server2] for key in 0..n as u64 { - SubtensorModule::add_balance_to_coldkey_account( + add_balance_to_coldkey_account( &U256::from(key), stake + ExistentialDeposit::get() + SubtensorModule::get_network_min_lock(), ); @@ -1590,7 +1590,7 @@ fn test_outdated_weights() { // === Register [validator1, validator2, server1, server2] for key in 0..n as u64 { - SubtensorModule::add_balance_to_coldkey_account( + add_balance_to_coldkey_account( &U256::from(key), stake + ExistentialDeposit::get() @@ -1683,7 +1683,7 @@ fn test_outdated_weights() { // === Dereg server2 at uid3 (least emission) + register new key over uid3 let new_key: u64 = n as u64; // register a new key while at max capacity, which means the least incentive uid will be deregistered - SubtensorModule::add_balance_to_coldkey_account( + add_balance_to_coldkey_account( &U256::from(new_key), stake + ExistentialDeposit::get() @@ -1788,7 +1788,7 @@ fn test_zero_weights() { // === Register [validator, server] for key in 0..n as u64 { - SubtensorModule::add_balance_to_coldkey_account( + add_balance_to_coldkey_account( &U256::from(key), ExistentialDeposit::get() + (SubtensorModule::get_network_min_lock() * 2.into()), ); @@ -1809,7 +1809,7 @@ fn test_zero_weights() { )); } for validator in 0..(n / 2) as u64 { - SubtensorModule::add_balance_to_coldkey_account(&U256::from(validator), stake.into()); + add_balance_to_coldkey_account(&U256::from(validator), stake.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &U256::from(validator), &U256::from(validator), @@ -1901,7 +1901,7 @@ fn test_zero_weights() { // === Outdate weights by reregistering servers for new_key in n..n + (n / 2) { // register a new key while at max capacity, which means the least emission uid will be deregistered - SubtensorModule::add_balance_to_coldkey_account( + add_balance_to_coldkey_account( &U256::from(new_key), ExistentialDeposit::get() + (SubtensorModule::get_network_min_lock() * 2.into()), ); @@ -2001,7 +2001,7 @@ fn test_deregistered_miner_bonds() { // === Register [validator1, validator2, server1, server2] let block_number = System::block_number(); for key in 0..n as u64 { - SubtensorModule::add_balance_to_coldkey_account( + add_balance_to_coldkey_account( &U256::from(key), stake + ExistentialDeposit::get() @@ -2085,7 +2085,7 @@ fn test_deregistered_miner_bonds() { // === Dereg server2 at uid3 (least emission) + register new key over uid3 let new_key: u64 = n as u64; // register a new key while at max capacity, which means the least incentive uid will be deregistered let block_number = System::block_number(); - SubtensorModule::add_balance_to_coldkey_account( + add_balance_to_coldkey_account( &U256::from(new_key), stake + ExistentialDeposit::get() @@ -2205,7 +2205,7 @@ fn test_validator_permits() { // === Register [validator1, validator2, server1, server2] for key in 0..network_n as u64 { - SubtensorModule::add_balance_to_coldkey_account( + add_balance_to_coldkey_account( &U256::from(key), stake[key as usize] + ExistentialDeposit::get() @@ -2258,7 +2258,7 @@ fn test_validator_permits() { // === Increase server stake above validators for server in &servers { - SubtensorModule::add_balance_to_coldkey_account( + add_balance_to_coldkey_account( &(U256::from(*server as u64)), (2 * network_n as u64).into(), ); @@ -2310,7 +2310,7 @@ fn test_get_set_alpha() { // Enable Liquid Alpha and setup SubtensorModule::set_liquid_alpha_enabled(netuid, true); migrations::migrate_create_root_network::migrate_create_root_network::(); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_000_u64.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_000_u64.into()); assert_ok!(SubtensorModule::root_register(signer.clone(), hotkey,)); // Should fail as signer does not own the subnet @@ -2735,7 +2735,7 @@ fn setup_yuma_3_scenario(netuid: NetUid, n: u16, sparse: bool, max_stake: u64, s // === Register for key in 0..n as u64 { - SubtensorModule::add_balance_to_coldkey_account( + add_balance_to_coldkey_account( &U256::from(key), TaoBalance::from(max_stake) + ExistentialDeposit::get() @@ -3875,7 +3875,7 @@ fn test_last_update_size_mismatch() { let stake_amount: u64 = 1_000_000_000; add_network_disable_commit_reveal(netuid, u16::MAX - 1, 0); SubtensorModule::set_max_allowed_uids(netuid, 1); - SubtensorModule::add_balance_to_coldkey_account( + add_balance_to_coldkey_account( &coldkey, TaoBalance::from(stake_amount) + ExistentialDeposit::get() diff --git a/pallets/subtensor/src/tests/evm.rs b/pallets/subtensor/src/tests/evm.rs index ae0acde27a..d692a72f72 100644 --- a/pallets/subtensor/src/tests/evm.rs +++ b/pallets/subtensor/src/tests/evm.rs @@ -44,7 +44,7 @@ fn test_associate_evm_key_success() { let coldkey = U256::from(1); let hotkey = U256::from(2); - SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); register_ok_neuron(netuid, hotkey, coldkey, 0); @@ -93,7 +93,7 @@ fn test_associate_evm_key_different_block_number_success() { let coldkey = U256::from(1); let hotkey = U256::from(2); - SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); register_ok_neuron(netuid, hotkey, coldkey, 0); @@ -182,7 +182,7 @@ fn test_associate_evm_key_hotkey_not_registered_in_subnet() { let coldkey = U256::from(1); let hotkey = U256::from(2); - SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); let pair = ecdsa::Pair::generate().0; let public = pair.public(); @@ -224,7 +224,7 @@ fn test_associate_evm_key_using_wrong_hash_function() { let coldkey = U256::from(1); let hotkey = U256::from(2); - SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); register_ok_neuron(netuid, hotkey, coldkey, 0); @@ -268,7 +268,7 @@ fn test_associate_evm_key_rate_limit_exceeded() { let coldkey = U256::from(1); let hotkey = U256::from(2); - SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); register_ok_neuron(netuid, hotkey, coldkey, 0); diff --git a/pallets/subtensor/src/tests/leasing.rs b/pallets/subtensor/src/tests/leasing.rs index 0c6ae629c3..cc0715f451 100644 --- a/pallets/subtensor/src/tests/leasing.rs +++ b/pallets/subtensor/src/tests/leasing.rs @@ -264,7 +264,7 @@ fn test_terminate_lease_works() { // Create a hotkey for the beneficiary let hotkey = U256::from(3); - SubtensorModule::create_account_if_non_existent(&beneficiary, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&beneficiary, &hotkey); // Terminate the lease assert_ok!(SubtensorModule::terminate_lease( @@ -356,7 +356,7 @@ fn test_terminate_lease_fails_if_origin_is_not_beneficiary() { // Create a hotkey for the beneficiary let hotkey = U256::from(3); - SubtensorModule::create_account_if_non_existent(&beneficiary, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&beneficiary, &hotkey); // Terminate the lease assert_err!( @@ -389,7 +389,7 @@ fn test_terminate_lease_fails_if_lease_has_no_end_block() { // Create a hotkey for the beneficiary let hotkey = U256::from(3); - SubtensorModule::create_account_if_non_existent(&beneficiary, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&beneficiary, &hotkey); // Terminate the lease assert_err!( @@ -427,7 +427,7 @@ fn test_terminate_lease_fails_if_lease_has_not_ended() { // Create a hotkey for the beneficiary let hotkey = U256::from(3); - SubtensorModule::create_account_if_non_existent(&beneficiary, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&beneficiary, &hotkey); // Terminate the lease assert_err!( @@ -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()[2].event, + System::events()[3].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()[5].event, + System::events()[6].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()[8].event, + System::events()[9].event, RuntimeEvent::SubtensorModule(Event::SubnetLeaseDividendsDistributed { lease_id, contributor: beneficiary.into(), @@ -1074,7 +1074,7 @@ fn setup_crowdloan( pallet_crowdloan::Contributions::::insert(id, contributor, amount); } - SubtensorModule::add_balance_to_coldkey_account(&funds_account, cap); + add_balance_to_coldkey_account(&funds_account, cap); // Mark the crowdloan as finalizing pallet_crowdloan::CurrentCrowdloanId::::set(Some(0)); @@ -1099,7 +1099,7 @@ fn setup_leased_network( SubtokenEnabled::::insert(netuid, true); if let Some(tao_to_stake) = tao_to_stake { - SubtensorModule::add_balance_to_coldkey_account(&lease.coldkey, tao_to_stake.into()); + add_balance_to_coldkey_account(&lease.coldkey, tao_to_stake.into()); assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(lease.coldkey), lease.hotkey, diff --git a/pallets/subtensor/src/tests/locks.rs b/pallets/subtensor/src/tests/locks.rs new file mode 100644 index 0000000000..00472bebe5 --- /dev/null +++ b/pallets/subtensor/src/tests/locks.rs @@ -0,0 +1,2734 @@ +#![allow( + clippy::arithmetic_side_effects, + clippy::expect_used, + clippy::indexing_slicing, + clippy::unwrap_used +)] + +use approx::assert_abs_diff_eq; +use frame_support::weights::Weight; +use frame_support::{assert_noop, assert_ok}; +use sp_core::U256; +use substrate_fixed::types::U64F64; +use subtensor_runtime_common::{AlphaBalance, NetUidStorageIndex, TaoBalance}; +use subtensor_swap_interface::SwapHandler; + +use super::mock::*; +use crate::*; + +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + +fn setup_subnet_with_stake( + coldkey: U256, + hotkey: U256, + stake_tao: u64, +) -> subtensor_runtime_common::NetUid { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + + let amount: TaoBalance = (stake_tao).into(); + setup_reserves( + netuid, + (stake_tao * 1_000_000).into(), + (stake_tao * 10_000_000).into(), + ); + + assert_ok!(SubtensorModule::create_account_if_non_existent( + &coldkey, &hotkey + )); + add_balance_to_coldkey_account(&coldkey, amount); + SubtensorModule::stake_into_subnet( + &hotkey, + &coldkey, + netuid, + amount, + ::SwapInterface::max_price(), + false, + false, + ) + .unwrap(); + + netuid +} + +fn get_alpha( + hotkey: &U256, + coldkey: &U256, + netuid: subtensor_runtime_common::NetUid, +) -> AlphaBalance { + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid) +} + +// ========================================================================= +// GROUP 1: Green-path — basic lock creation +// ========================================================================= + +#[test] +fn test_lock_stake_creates_new_lock() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + let alpha = get_alpha(&hotkey, &coldkey, netuid); + let lock_amount = alpha.to_u64() / 2; + + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, + netuid, + &hotkey, + lock_amount.into(), + )); + + let lock = Lock::::get((coldkey, netuid, hotkey)).expect("Lock should exist"); + assert_eq!(lock.locked_mass, lock_amount.into()); + assert_eq!(lock.conviction, U64F64::from_num(0)); + assert_eq!( + lock.last_update, + SubtensorModule::get_current_block_as_u64() + ); + + // Hotkey lock should also be created + let hotkey_lock = HotkeyLock::::get(netuid, hotkey); + assert!(hotkey_lock.is_some()); + assert_eq!(hotkey_lock.unwrap().locked_mass, lock_amount.into()); + }); +} + +#[test] +fn test_lock_stake_emits_event() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + let lock_amount: u64 = 1000; + + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, + netuid, + &hotkey, + lock_amount.into(), + )); + + System::assert_last_event( + Event::StakeLocked { + coldkey, + hotkey, + netuid, + amount: lock_amount.into(), + } + .into(), + ); + }); +} + +#[test] +fn test_lock_stake_full_amount() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + let total_alpha = SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey, netuid); + assert!(!total_alpha.is_zero()); + + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, + netuid, + &hotkey, + total_alpha, + )); + + let lock = Lock::::get((coldkey, netuid, hotkey)).unwrap(); + assert_eq!(lock.locked_mass, total_alpha); + }); +} + +// ========================================================================= +// GROUP 2: Green-path — lock queries +// ========================================================================= + +#[test] +fn test_get_current_locked_no_lock() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let netuid = subtensor_runtime_common::NetUid::from(1); + assert_eq!( + SubtensorModule::get_current_locked(&coldkey, netuid), + AlphaBalance::ZERO + ); + }); +} + +#[test] +fn test_get_conviction_no_lock() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let netuid = subtensor_runtime_common::NetUid::from(1); + assert_eq!( + SubtensorModule::get_conviction(&coldkey, netuid), + U64F64::from_num(0) + ); + }); +} + +#[test] +fn test_available_to_unstake_no_lock() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + let total = SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey, netuid); + let available = SubtensorModule::available_stake(&coldkey, netuid); + assert_eq!(available, total); + }); +} + +#[test] +fn test_available_to_unstake_with_lock() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + let total = SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey, netuid); + let lock_amount = total / 2.into(); + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, + netuid, + &hotkey, + lock_amount, + )); + + let available = SubtensorModule::available_stake(&coldkey, netuid); + assert_eq!(available, total - lock_amount); + }); +} + +#[test] +fn test_available_to_unstake_fully_locked() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + let total = SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey, netuid); + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, netuid, &hotkey, total, + )); + + let available = SubtensorModule::available_stake(&coldkey, netuid); + assert_eq!(available, AlphaBalance::ZERO); + }); +} + +// ========================================================================= +// GROUP 3: Incremental locks (top-up) +// ========================================================================= + +#[test] +fn test_lock_stake_topup() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + let first_lock = 1000u64; + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, + netuid, + &hotkey, + first_lock.into() + )); + + step_block(100); + + let second_lock = 500u64; + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, + netuid, + &hotkey, + second_lock.into() + )); + + let lock = Lock::::get((coldkey, netuid, hotkey)).unwrap(); + // locked_mass should be decayed(first_lock) + second_lock + // Since tau is large (216000), decay over 100 blocks is small; locked_mass ~ 1000 + 500 + assert!(lock.locked_mass > 1490.into()); + assert!(lock.locked_mass < 1501.into()); + // conviction should have grown from the time the first lock was active + assert!(lock.conviction > U64F64::from_num(0)); + assert_eq!( + lock.last_update, + SubtensorModule::get_current_block_as_u64() + ); + + // Hotkey lock should also be created + let hotkey_lock = HotkeyLock::::get(netuid, hotkey).unwrap(); + assert!(hotkey_lock.locked_mass > 1490.into()); + assert_eq!(hotkey_lock.locked_mass, lock.locked_mass); + assert!(hotkey_lock.conviction > U64F64::from_num(0)); + }); +} + +#[test] +fn test_lock_stake_topup_multiple_times() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + let chunk = 500u64.into(); + + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, netuid, &hotkey, chunk + )); + step_block(50); + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, netuid, &hotkey, chunk + )); + step_block(50); + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, netuid, &hotkey, chunk + )); + + let lock = Lock::::get((coldkey, netuid, hotkey)).unwrap(); + // After three top-ups with small decay, should be close to 1500 + assert!(lock.locked_mass > 1490.into()); + assert!(lock.locked_mass <= 1500.into()); + assert!(lock.conviction > U64F64::from_num(0)); + + // Hotkey lock should also be updated + let hotkey_lock = HotkeyLock::::get(netuid, hotkey).unwrap(); + assert!(hotkey_lock.locked_mass > 1490.into()); + assert_eq!(hotkey_lock.locked_mass, lock.locked_mass); + assert!(hotkey_lock.conviction > U64F64::from_num(0)); + }); +} + +#[test] +fn test_lock_stake_topup_same_block() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + let first = 1000u64.into(); + let second = 500u64.into(); + + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, netuid, &hotkey, first + )); + // No block advancement — same block top-up + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, netuid, &hotkey, second + )); + + let lock = Lock::::get((coldkey, netuid, hotkey)).unwrap(); + // dt=0 means no decay, simple addition + assert_eq!(lock.locked_mass, first + second); + assert_eq!(lock.conviction, U64F64::from_num(0)); + + // Hotkey lock should also be updated + let hotkey_lock = HotkeyLock::::get(netuid, hotkey).unwrap(); + assert_eq!(hotkey_lock.locked_mass, first + second); + assert_eq!(hotkey_lock.conviction, U64F64::from_num(0)); + }); +} + +// ========================================================================= +// GROUP 4: Lock rejection cases +// ========================================================================= + +#[test] +fn test_lock_stake_zero_amount() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + assert_noop!( + SubtensorModule::do_lock_stake(&coldkey, netuid, &hotkey, AlphaBalance::ZERO,), + Error::::AmountTooLow + ); + }); +} + +#[test] +fn test_lock_stake_exceeds_total_alpha() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + let total = SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey, netuid); + let too_much = total + 1.into(); + + assert_noop!( + SubtensorModule::do_lock_stake(&coldkey, netuid, &hotkey, too_much), + Error::::InsufficientStakeForLock + ); + }); +} + +#[test] +fn test_lock_stake_wrong_hotkey() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey_a = U256::from(2); + let hotkey_b = U256::from(3); + let netuid = setup_subnet_with_stake(coldkey, hotkey_a, 100_000_000_000); + + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, + netuid, + &hotkey_a, + 1000u64.into(), + )); + + assert_noop!( + SubtensorModule::do_lock_stake(&coldkey, netuid, &hotkey_b, 500u64.into(),), + Error::::LockHotkeyMismatch + ); + }); +} + +#[test] +fn test_lock_stake_topup_exceeds_total() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + let total = SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey, netuid); + // Lock 80% initially + let initial = total * 8.into() / 10.into(); + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, netuid, &hotkey, initial + )); + + // Try to top up the remaining 30% (exceeds total by 10%) + let topup = total * 3.into() / 10.into(); + assert_noop!( + SubtensorModule::do_lock_stake(&coldkey, netuid, &hotkey, topup), + Error::::InsufficientStakeForLock + ); + }); +} + +// ========================================================================= +// GROUP 5: Exponential decay math +// ========================================================================= + +#[test] +fn test_exp_decay_zero_dt() { + new_test_ext(1).execute_with(|| { + let result = SubtensorModule::exp_decay(0, 216000); + assert_eq!(result, U64F64::from_num(1)); + }); +} + +#[test] +fn test_exp_decay_zero_tau() { + new_test_ext(1).execute_with(|| { + let result = SubtensorModule::exp_decay(1000, 0); + assert_eq!(result, U64F64::from_num(0)); + }); +} + +#[test] +fn test_exp_decay_one_tau() { + new_test_ext(1).execute_with(|| { + let tau = 216000u64; + let result = SubtensorModule::exp_decay(tau, tau); + // exp(-1) ~= 0.36787944 + let expected = U64F64::from_num(0.36787944f64); + let diff = if result > expected { + result - expected + } else { + expected - result + }; + assert!(diff < U64F64::from_num(0.001)); + }); +} + +#[test] +fn test_exp_decay_clamps_large_dt_to_min_ratio() { + new_test_ext(1).execute_with(|| { + let tau = 216000u64; + let clamped_result = SubtensorModule::exp_decay(40 * tau, tau); + let oversized_result = SubtensorModule::exp_decay(100 * tau, tau); + + let diff = if oversized_result > clamped_result { + oversized_result - clamped_result + } else { + clamped_result - oversized_result + }; + + assert!(diff < U64F64::from_num(0.000000001)); + assert!(oversized_result > U64F64::from_num(0)); + }); +} + +#[test] +fn test_roll_forward_locked_mass_no_change() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + let lock_amount = 10000u64; + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, + netuid, + &hotkey, + lock_amount.into() + )); + + // Advance one full tau via direct block number jump (step_block overflows u16 for tau=216000) + let tau = MaturityRate::::get(); + let target = System::block_number() + tau; + System::set_block_number(target); + + let locked = SubtensorModule::get_current_locked(&coldkey, netuid); + + // No changes to locked mass + assert_eq!(locked, lock_amount.into()); + }); +} + +#[test] +fn test_roll_forward_conviction_converges_to_lock() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + let lock_amount = 10000u64.into(); + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, + netuid, + &hotkey, + lock_amount + )); + + // Conviction at t=0 is 0 + let c0 = SubtensorModule::get_conviction(&coldkey, netuid); + assert_eq!(c0, U64F64::from_num(0)); + + // After some time, conviction should have grown + step_block(100); + let c1 = SubtensorModule::get_conviction(&coldkey, netuid); + assert!(c1 > U64F64::from_num(0)); + + // After more time, conviction should be even higher + step_block(1000); + let c2 = SubtensorModule::get_conviction(&coldkey, netuid); + println!("c1 = {}", c1); + println!("c2 = {}", c2); + // assert!(c2 > c1); + + // After a very long time (many taus), conviction is close to lock amount + let tau = MaturityRate::::get(); + let target = System::block_number() + tau * 1000; + System::set_block_number(target); + let c_late = SubtensorModule::get_conviction(&coldkey, netuid); + println!("c_late = {}", c_late); + assert_abs_diff_eq!( + c_late.to_num::(), + u64::from(lock_amount) as f64, + epsilon = 0.0000001 + ); + }); +} + +#[test] +fn test_roll_forward_no_change_when_now_equals_last_update() { + new_test_ext(1).execute_with(|| { + let lock = LockState { + locked_mass: 5000.into(), + unlocked_mass: 0.into(), + conviction: U64F64::from_num(1234), + last_update: 100, + }; + let rolled = SubtensorModule::roll_forward_lock(lock.clone(), 100); + assert_eq!(rolled.locked_mass, lock.locked_mass); + assert_eq!(rolled.conviction, lock.conviction); + assert_eq!(rolled.last_update, 100); + }); +} + +// ========================================================================= +// GROUP 6: Unstake invariant enforcement +// ========================================================================= + +#[test] +fn test_unstake_allowed_when_no_lock() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + let alpha = get_alpha(&hotkey, &coldkey, netuid); + assert!(alpha > AlphaBalance::ZERO); + + assert_ok!(SubtensorModule::do_remove_stake( + RuntimeOrigin::signed(coldkey), + hotkey, + netuid, + alpha, + )); + }); +} + +#[test] +fn test_unstake_allowed_up_to_available() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + let total = SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey, netuid); + let lock_amount = total / 2.into(); + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, + netuid, + &hotkey, + lock_amount + )); + + // Unstake the unlocked half + let alpha = get_alpha(&hotkey, &coldkey, netuid); + let available_alpha: u64 = (alpha.to_u64()) / 2; + // Need to step a block to pass rate limiter + step_block(1); + assert_ok!(SubtensorModule::do_remove_stake( + RuntimeOrigin::signed(coldkey), + hotkey, + netuid, + available_alpha.into(), + )); + }); +} + +#[test] +fn test_unstake_blocked_by_lock() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + let total = SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey, netuid); + // Lock the entire amount + assert_ok!(SubtensorModule::do_lock_stake(&coldkey, netuid, &hotkey, total)); + + step_block(1); + + let alpha = get_alpha(&hotkey, &coldkey, netuid); + assert_noop!( + SubtensorModule::do_remove_stake( + RuntimeOrigin::signed(coldkey), + hotkey, + netuid, + alpha, + ), + Error::::StakeUnavailable + ); + }); +} + +// ========================================================================= +// GROUP 7: Move/transfer invariant enforcement +// ========================================================================= + +#[test] +fn test_move_stake_same_coldkey_same_subnet_allowed() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey_a = U256::from(2); + let hotkey_b = U256::from(3); + let netuid = setup_subnet_with_stake(coldkey, hotkey_a, 100_000_000_000); + + assert_ok!(SubtensorModule::create_account_if_non_existent( + &coldkey, &hotkey_b + )); + + let total = SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey, netuid); + // Lock the full amount to hotkey_a + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, netuid, &hotkey_a, total + )); + + // Move from hotkey_a to hotkey_b on same subnet — total coldkey alpha unchanged + let alpha = get_alpha(&hotkey_a, &coldkey, netuid); + let move_amount = alpha / 2.into(); + assert_ok!(SubtensorModule::do_move_stake( + RuntimeOrigin::signed(coldkey), + hotkey_a, + hotkey_b, + netuid, + netuid, + move_amount, + )); + }); +} + +#[test] +fn test_do_transfer_stake_same_subnet_transfers_lock_to_destination_hotkey() { + new_test_ext(1).execute_with(|| { + let coldkey_sender = U256::from(1); + let coldkey_receiver = U256::from(5); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey_sender, hotkey, 100_000_000_000); + + let total = SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey_sender, netuid); + let lock_half = total / 2.into(); + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey_sender, + netuid, + &hotkey, + lock_half, + )); + + let sender_lock_before = + Lock::::get((coldkey_sender, netuid, hotkey)).expect("sender lock should exist"); + let hotkey_lock_before = + HotkeyLock::::get(netuid, hotkey).expect("hotkey lock should exist"); + + step_block(1); + + let transfer_amount = total; + assert_ok!(SubtensorModule::do_transfer_stake( + RuntimeOrigin::signed(coldkey_sender), + coldkey_receiver, + hotkey, + netuid, + netuid, + transfer_amount, + )); + + let expected_sender_lock = SubtensorModule::roll_forward_lock( + sender_lock_before, + SubtensorModule::get_current_block_as_u64(), + ); + + assert!(Lock::::get((coldkey_sender, netuid, hotkey)).is_none()); + + let receiver_lock = Lock::::get((coldkey_receiver, netuid, hotkey)) + .expect("receiver lock should exist after transfer"); + assert_eq!(receiver_lock.locked_mass, expected_sender_lock.locked_mass); + assert_eq!( + receiver_lock.unlocked_mass, + expected_sender_lock.unlocked_mass + ); + assert!(receiver_lock.conviction > U64F64::from_num(0)); + assert!(receiver_lock.conviction <= expected_sender_lock.conviction); + + let hotkey_lock_after = + HotkeyLock::::get(netuid, hotkey).expect("hotkey lock should remain"); + assert_eq!( + hotkey_lock_after.locked_mass, + hotkey_lock_before.locked_mass + ); + }); +} + +#[test] +fn test_move_stake_cross_subnet_blocked_by_lock() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid_a = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + let subnet_owner2_ck = U256::from(2001); + let subnet_owner2_hk = U256::from(2002); + let netuid_b = add_dynamic_network(&subnet_owner2_hk, &subnet_owner2_ck); + setup_reserves( + netuid_b, + (100_000_000_000u64 * 1_000_000).into(), + (100_000_000_000u64 * 10_000_000).into(), + ); + + let total = SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey, netuid_a); + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, netuid_a, &hotkey, total + )); + + step_block(1); + + let alpha = get_alpha(&hotkey, &coldkey, netuid_a); + assert_noop!( + SubtensorModule::do_move_stake( + RuntimeOrigin::signed(coldkey), + hotkey, + hotkey, + netuid_a, + netuid_b, + alpha, + ), + Error::::StakeUnavailable + ); + }); +} + +#[test] +fn test_transfer_stake_cross_coldkey_allowed_partial() { + new_test_ext(1).execute_with(|| { + let coldkey_sender = U256::from(1); + let coldkey_receiver = U256::from(5); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey_sender, hotkey, 100_000_000_000); + + let total = SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey_sender, netuid); + let lock_half = total / 2.into(); + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey_sender, + netuid, + &hotkey, + lock_half, + )); + + let sender_lock_before = + Lock::::get((coldkey_sender, netuid, hotkey)).expect("sender lock should exist"); + + step_block(1); + + // Transfer the unlocked portion + let alpha = get_alpha(&hotkey, &coldkey_sender, netuid); + let transfer_amount = alpha / 4.into(); // well within the unlocked half + assert_ok!(SubtensorModule::do_transfer_stake( + RuntimeOrigin::signed(coldkey_sender), + coldkey_receiver, + hotkey, + netuid, + netuid, + transfer_amount, + )); + + let sender_lock_after = + Lock::::get((coldkey_sender, netuid, hotkey)).expect("sender lock should remain"); + assert_eq!( + sender_lock_after.locked_mass, + sender_lock_before.locked_mass + ); + assert_eq!( + sender_lock_after.unlocked_mass, + SubtensorModule::roll_forward_lock( + sender_lock_before, + SubtensorModule::get_current_block_as_u64() + ) + .unlocked_mass + ); + assert!(Lock::::get((coldkey_receiver, netuid, hotkey)).is_none()); + }); +} + +// ========================================================================= +// GROUP 8: Multi-subnet locks +// ========================================================================= + +#[test] +fn test_lock_on_multiple_subnets() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey_a = U256::from(2); + let hotkey_b = U256::from(3); + + let netuid_a = setup_subnet_with_stake(coldkey, hotkey_a, 100_000_000_000); + + let subnet_owner2_ck = U256::from(2001); + let subnet_owner2_hk = U256::from(2002); + let netuid_b = add_dynamic_network(&subnet_owner2_hk, &subnet_owner2_ck); + setup_reserves( + netuid_b, + (100_000_000_000u64 * 1_000_000).into(), + (100_000_000_000u64 * 10_000_000).into(), + ); + assert_ok!(SubtensorModule::create_account_if_non_existent( + &coldkey, &hotkey_b + )); + add_balance_to_coldkey_account(&coldkey, 100_000_000_000u64.into()); + SubtensorModule::stake_into_subnet( + &hotkey_b, + &coldkey, + netuid_b, + 100_000_000_000u64.into(), + ::SwapInterface::max_price(), + false, + false, + ) + .unwrap(); + + // Lock on subnet A to hotkey_a + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, + netuid_a, + &hotkey_a, + 1000u64.into(), + )); + + // Lock on subnet B to hotkey_b (different hotkey is fine — different subnet) + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, + netuid_b, + &hotkey_b, + 2000u64.into(), + )); + + let lock_a = Lock::::get((coldkey, netuid_a, hotkey_a)).unwrap(); + let lock_b = Lock::::get((coldkey, netuid_b, hotkey_b)).unwrap(); + assert_eq!(lock_a.locked_mass, 1000u64.into()); + assert_eq!(lock_b.locked_mass, 2000u64.into()); + + // Hotkey locks should also be separate + let hotkey_lock_a = HotkeyLock::::get(netuid_a, hotkey_a).unwrap(); + let hotkey_lock_b = HotkeyLock::::get(netuid_b, hotkey_b).unwrap(); + assert_eq!(hotkey_lock_a.locked_mass, 1000u64.into()); + assert_eq!(hotkey_lock_b.locked_mass, 2000u64.into()); + }); +} + +#[test] +fn test_unstake_one_subnet_does_not_affect_other() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid_a = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + // Lock on subnet A + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, + netuid_a, + &hotkey, + 5000u64.into(), + )); + + // Subnet B — no lock, just stake + let subnet_owner2_ck = U256::from(2001); + let subnet_owner2_hk = U256::from(2002); + let netuid_b = add_dynamic_network(&subnet_owner2_hk, &subnet_owner2_ck); + setup_reserves( + netuid_b, + (100_000_000_000u64 * 1_000_000).into(), + (100_000_000_000u64 * 10_000_000).into(), + ); + assert_ok!(SubtensorModule::create_account_if_non_existent( + &coldkey, &hotkey + )); + add_balance_to_coldkey_account(&coldkey, 100_000_000_000u64.into()); + SubtensorModule::stake_into_subnet( + &hotkey, + &coldkey, + netuid_b, + 100_000_000_000u64.into(), + ::SwapInterface::max_price(), + false, + false, + ) + .unwrap(); + + step_block(1); + + // Unstake from subnet B — should succeed (no lock there) + let alpha_b = get_alpha(&hotkey, &coldkey, netuid_b); + assert_ok!(SubtensorModule::do_remove_stake( + RuntimeOrigin::signed(coldkey), + hotkey, + netuid_b, + alpha_b, + )); + + // Lock on subnet A unaffected + let lock_a = Lock::::get((coldkey, netuid_a, hotkey)).unwrap(); + assert_eq!(lock_a.locked_mass, 5000u64.into()); + + // Hotkey lock on subnet A also unaffected + let hotkey_lock_a = HotkeyLock::::get(netuid_a, hotkey).unwrap(); + assert_eq!(hotkey_lock_a.locked_mass, 5000u64.into()); + }); +} + +// ========================================================================= +// GROUP 9: Hotkey conviction and subnet king +// ========================================================================= + +#[test] +fn test_hotkey_conviction_single_locker() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, + netuid, + &hotkey, + 5000u64.into(), + )); + + // Initially conviction is 0 (just created) + let c = SubtensorModule::hotkey_conviction(&hotkey, netuid); + assert_eq!(c, U64F64::from_num(0)); + + // After time, conviction grows + step_block(1000); + let c = SubtensorModule::hotkey_conviction(&hotkey, netuid); + assert!(c > U64F64::from_num(0)); + }); +} + +#[test] +fn test_hotkey_conviction_multiple_lockers() { + new_test_ext(1).execute_with(|| { + let coldkey1 = U256::from(1); + let coldkey2 = U256::from(5); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey1, hotkey, 100_000_000_000); + + // Also give coldkey2 stake on same hotkey + add_balance_to_coldkey_account(&coldkey2, 100_000_000_000u64.into()); + assert_ok!(SubtensorModule::create_account_if_non_existent( + &coldkey2, &hotkey + )); + SubtensorModule::stake_into_subnet( + &hotkey, + &coldkey2, + netuid, + 50_000_000_000u64.into(), + ::SwapInterface::max_price(), + false, + false, + ) + .unwrap(); + + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey1, + netuid, + &hotkey, + 3000u64.into(), + )); + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey2, + netuid, + &hotkey, + 2000u64.into(), + )); + + step_block(500); + + let total_conviction = SubtensorModule::hotkey_conviction(&hotkey, netuid); + let c1 = SubtensorModule::get_conviction(&coldkey1, netuid); + let c2 = SubtensorModule::get_conviction(&coldkey2, netuid); + + // Total conviction should be approximately sum of individual convictions + let diff = if total_conviction > (c1 + c2) { + total_conviction - (c1 + c2) + } else { + (c1 + c2) - total_conviction + }; + assert!(diff < U64F64::from_num(1)); + }); +} + +#[test] +fn test_subnet_king_single_hotkey() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, + netuid, + &hotkey, + 5000u64.into(), + )); + + step_block(100); + + let king = SubtensorModule::subnet_king(netuid); + assert_eq!(king, Some(hotkey)); + }); +} + +#[test] +fn test_subnet_king_highest_conviction_wins() { + new_test_ext(1).execute_with(|| { + let coldkey1 = U256::from(1); + let coldkey2 = U256::from(5); + let hotkey_a = U256::from(2); + let hotkey_b = U256::from(3); + + let netuid = setup_subnet_with_stake(coldkey1, hotkey_a, 100_000_000_000); + + add_balance_to_coldkey_account(&coldkey2, 100_000_000_000u64.into()); + assert_ok!(SubtensorModule::create_account_if_non_existent( + &coldkey2, &hotkey_b + )); + SubtensorModule::stake_into_subnet( + &hotkey_b, + &coldkey2, + netuid, + 50_000_000_000u64.into(), + ::SwapInterface::max_price(), + false, + false, + ) + .unwrap(); + + // coldkey1 locks more to hotkey_a + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey1, + netuid, + &hotkey_a, + 8000u64.into(), + )); + // coldkey2 locks less to hotkey_b + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey2, + netuid, + &hotkey_b, + 2000u64.into(), + )); + + step_block(500); + + let king = SubtensorModule::subnet_king(netuid); + assert_eq!(king, Some(hotkey_a)); + }); +} + +#[test] +fn test_subnet_king_no_locks() { + new_test_ext(1).execute_with(|| { + let netuid = subtensor_runtime_common::NetUid::from(99); + let king = SubtensorModule::subnet_king(netuid); + assert_eq!(king, None); + }); +} + +// ========================================================================= +// GROUP 10: Lock force-reduction +// ========================================================================= + +#[test] +fn test_reduce_lock_removes_dust() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + let lock_amount = AlphaBalance::from(50u64); + + // Lock a small amount + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, + netuid, + &hotkey, + lock_amount, + )); + + // Advance many taus so everything decays well below dust (100) + let tau = MaturityRate::::get(); + let target = System::block_number() + tau * 50; + System::set_block_number(target); + + // Remove full lock amount + SubtensorModule::force_reduce_lock(&coldkey, netuid, lock_amount); + + assert!(Lock::::get((coldkey, netuid, hotkey)).is_none()); + assert!(HotkeyLock::::get(netuid, hotkey).is_none()); + }); +} + +#[test] +fn test_reduce_lock_partial_reduction() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + let lock_amount = AlphaBalance::from(100u64); + let reduce_amount = AlphaBalance::from(40u64); + let now = SubtensorModule::get_current_block_as_u64(); + + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, + netuid, + &hotkey, + lock_amount, + )); + + let conviction = U64F64::from_num(100); + Lock::::insert( + (coldkey, netuid, hotkey), + LockState { + locked_mass: lock_amount, + unlocked_mass: 0.into(), + conviction, + last_update: now, + }, + ); + HotkeyLock::::insert( + netuid, + hotkey, + LockState { + locked_mass: lock_amount, + unlocked_mass: 0.into(), + conviction, + last_update: now, + }, + ); + + SubtensorModule::force_reduce_lock(&coldkey, netuid, reduce_amount); + + let lock = Lock::::get((coldkey, netuid, hotkey)).expect("lock should remain"); + assert_eq!(lock.locked_mass, 60u64.into()); + assert_abs_diff_eq!(lock.conviction.to_num::(), 60., epsilon = 0.0000000001); + + let hotkey_lock = + HotkeyLock::::get(netuid, hotkey).expect("hotkey lock should remain"); + assert_eq!(hotkey_lock.locked_mass, 60u64.into()); + assert_abs_diff_eq!( + hotkey_lock.conviction.to_num::(), + 60., + epsilon = 0.0000000001 + ); + }); +} + +#[test] +fn test_reduce_lock_no_lock() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let netuid = subtensor_runtime_common::NetUid::from(1); + // Should be a no-op, no panic + SubtensorModule::force_reduce_lock(&coldkey, netuid, 100u64.into()); + assert!( + Lock::::iter_prefix((coldkey, netuid)) + .next() + .is_none() + ); + }); +} + +#[test] +fn test_reduce_lock_two_coldkeys() { + new_test_ext(1).execute_with(|| { + let coldkey1 = U256::from(1001); + let coldkey2 = U256::from(1002); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey1, hotkey, 100_000_000_000); + + // Add stake on coldkey 2 + add_balance_to_coldkey_account(&coldkey2, 100_000_000_000u64.into()); + assert_ok!(SubtensorModule::create_account_if_non_existent( + &coldkey2, &hotkey + )); + SubtensorModule::stake_into_subnet( + &hotkey, + &coldkey2, + netuid, + 100_000_000_000u64.into(), + ::SwapInterface::max_price(), + false, + false, + ) + .unwrap(); + + // Mock a non-zero conviction for both coldkeys + let lock1 = Lock::::get((coldkey1, netuid, hotkey)).unwrap_or(LockState { + locked_mass: 0.into(), + unlocked_mass: 0.into(), + conviction: U64F64::from_num(1234), + last_update: System::block_number(), + }); + let lock2 = Lock::::get((coldkey2, netuid, hotkey)).unwrap_or(LockState { + locked_mass: 0.into(), + unlocked_mass: 0.into(), + conviction: U64F64::from_num(1234), + last_update: System::block_number(), + }); + Lock::::insert((coldkey1, netuid, hotkey), lock1); + Lock::::insert((coldkey2, netuid, hotkey), lock2); + HotkeyLock::::insert( + netuid, + hotkey, + LockState { + locked_mass: 0.into(), + unlocked_mass: 0.into(), + conviction: U64F64::from_num(1234 * 2), + last_update: System::block_number(), + }, + ); + + // Lock a small amount from both coldkeys + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey1, + netuid, + &hotkey, + 50u64.into(), + )); + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey2, + netuid, + &hotkey, + 50u64.into(), + )); + + SubtensorModule::force_reduce_lock(&coldkey1, netuid, 50u64.into()); + + // Should only clean up coldkey1's lock, not coldkey2's + assert!( + Lock::::iter_prefix((coldkey1, netuid)) + .next() + .is_none() + ); + assert!(Lock::::get((coldkey2, netuid, hotkey)).is_some()); + + // Hotkey lock should reduce according to coldkey1 lock + let hotkey_lock = HotkeyLock::::get(netuid, hotkey).unwrap(); + assert_eq!(hotkey_lock.locked_mass, 50u64.into()); + + // Conviction should be reduced by coldkey1's lock conviction, + // but not fully reset because coldkey2 still has a lock + assert!(hotkey_lock.conviction == U64F64::from_num(1234)); + }); +} + +// ========================================================================= +// GROUP 11: Coldkey swap interaction +// ========================================================================= + +#[test] +fn test_coldkey_swap_swaps_lock() { + new_test_ext(1).execute_with(|| { + let old_coldkey = U256::from(1); + let new_coldkey = U256::from(10); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(old_coldkey, hotkey, 100_000_000_000); + + assert_ok!(SubtensorModule::do_lock_stake( + &old_coldkey, + netuid, + &hotkey, + 5000u64.into(), + )); + + // Perform coldkey swap + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey)); + + // Lock removed on old coldkey + assert!( + Lock::::iter_prefix((old_coldkey, netuid)) + .next() + .is_none() + ); + // New coldkey now has the lock + assert!(Lock::::get((new_coldkey, netuid, hotkey)).is_some()); + }); +} + +#[test] +fn test_coldkey_swap_lock_blocks_unstake() { + new_test_ext(1).execute_with(|| { + let old_coldkey = U256::from(1); + let new_coldkey = U256::from(10); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(old_coldkey, hotkey, 100_000_000_000); + + let total = SubtensorModule::total_coldkey_alpha_on_subnet(&old_coldkey, netuid); + assert_ok!(SubtensorModule::do_lock_stake( + &old_coldkey, + netuid, + &hotkey, + total, + )); + + // Swap coldkey + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey)); + + step_block(1); + + // New coldkey should not be able to unstake + let alpha = get_alpha(&hotkey, &new_coldkey, netuid); + assert!(alpha > AlphaBalance::ZERO); + assert_noop!( + SubtensorModule::do_remove_stake( + RuntimeOrigin::signed(new_coldkey), + hotkey, + netuid, + alpha, + ), + Error::::StakeUnavailable + ); + }); +} + +#[test] +// When both coldkeys already have unlocked-only lock state on the same subnet, the destination +// hotkey key should be preserved and unlocked_mass should be accumulated onto that record. +fn test_coldkey_swap_adds_unlocked_mass_into_existing_destination_lock() { + new_test_ext(1).execute_with(|| { + let old_coldkey = U256::from(1); + let new_coldkey = U256::from(10); + let old_hotkey = U256::from(2); + let new_hotkey = U256::from(20); + let netuid = subtensor_runtime_common::NetUid::from(1); + let old_unlocked = AlphaBalance::from(4_000u64); + let new_unlocked = AlphaBalance::from(6_000u64); + + // Seed unlocked-only lock rows on both coldkeys so the helper has to merge into + // the destination record instead of creating a second lock entry on the subnet. + SubtensorModule::insert_lock_state( + &old_coldkey, + netuid, + &old_hotkey, + LockState { + locked_mass: AlphaBalance::ZERO, + unlocked_mass: old_unlocked, + conviction: U64F64::from_num(0), + last_update: SubtensorModule::get_current_block_as_u64(), + }, + ); + SubtensorModule::insert_lock_state( + &new_coldkey, + netuid, + &new_hotkey, + LockState { + locked_mass: AlphaBalance::ZERO, + unlocked_mass: new_unlocked, + conviction: U64F64::from_num(0), + last_update: SubtensorModule::get_current_block_as_u64(), + }, + ); + + SubtensorModule::swap_coldkey_locks(&old_coldkey, &new_coldkey); + + assert!( + Lock::::iter_prefix((old_coldkey, netuid)) + .next() + .is_none() + ); + assert!(Lock::::get((new_coldkey, netuid, old_hotkey)).is_none()); + + let merged_lock = Lock::::get((new_coldkey, netuid, new_hotkey)) + .expect("destination lock should remain under its original hotkey key"); + assert_eq!(merged_lock.locked_mass, AlphaBalance::ZERO); + assert_eq!(merged_lock.unlocked_mass, old_unlocked + new_unlocked); + assert_eq!(Lock::::iter_prefix((new_coldkey, netuid)).count(), 1); + }); +} + +#[test] +// When the destination already has a lock row on the subnet, the destination hotkey key should +// be preserved, but locked_mass and conviction should be overwritten by the source lock. +fn test_coldkey_swap_overwrites_destination_locked_mass_and_conviction() { + new_test_ext(1).execute_with(|| { + let old_coldkey = U256::from(1); + let new_coldkey = U256::from(10); + let old_hotkey = U256::from(2); + let new_hotkey = U256::from(20); + let netuid = subtensor_runtime_common::NetUid::from(1); + + let old_locked = AlphaBalance::from(7_000u64); + let old_unlocked = AlphaBalance::from(4_000u64); + let old_conviction = U64F64::from_num(77); + + let new_locked = AlphaBalance::from(999u64); + let new_unlocked = AlphaBalance::from(6_000u64); + let new_conviction = U64F64::from_num(11); + + SubtensorModule::insert_lock_state( + &old_coldkey, + netuid, + &old_hotkey, + LockState { + locked_mass: old_locked, + unlocked_mass: old_unlocked, + conviction: old_conviction, + last_update: SubtensorModule::get_current_block_as_u64(), + }, + ); + SubtensorModule::insert_lock_state( + &new_coldkey, + netuid, + &new_hotkey, + LockState { + locked_mass: new_locked, + unlocked_mass: new_unlocked, + conviction: new_conviction, + last_update: SubtensorModule::get_current_block_as_u64(), + }, + ); + + SubtensorModule::swap_coldkey_locks(&old_coldkey, &new_coldkey); + + assert!( + Lock::::iter_prefix((old_coldkey, netuid)) + .next() + .is_none() + ); + assert!(Lock::::get((new_coldkey, netuid, old_hotkey)).is_none()); + + let merged_lock = Lock::::get((new_coldkey, netuid, new_hotkey)) + .expect("destination lock should remain under its original hotkey key"); + assert_eq!(merged_lock.locked_mass, old_locked); + assert_eq!(merged_lock.conviction, old_conviction); + assert_eq!(merged_lock.unlocked_mass, old_unlocked + new_unlocked); + assert_eq!(Lock::::iter_prefix((new_coldkey, netuid)).count(), 1); + }); +} + +#[test] +// The public coldkey swap extrinsic runs inside a storage layer, so a late failure rolls back the earlier writes. +fn test_failed_coldkey_swap_extrinsic_rolls_back_state_changes() { + new_test_ext(1).execute_with(|| { + let old_coldkey = U256::from(1); + let old_hotkey = U256::from(2); + let new_coldkey = U256::from(3); + let blocked_hotkey = U256::from(4); + let netuid = setup_subnet_with_stake(old_coldkey, old_hotkey, 100_000_000_000); + + let original_stake = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &old_hotkey, + &old_coldkey, + netuid, + ); + assert!(!original_stake.is_zero()); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &old_hotkey, + &new_coldkey, + netuid + ), + AlphaBalance::ZERO + ); + + // Seed a lock directly on the destination coldkey so the swap reaches ActiveLockExists + // without tripping the earlier "already associated" guard. + SubtensorModule::insert_lock_state( + &new_coldkey, + netuid, + &blocked_hotkey, + LockState { + locked_mass: 1u64.into(), + unlocked_mass: AlphaBalance::ZERO, + conviction: U64F64::from_num(0), + last_update: SubtensorModule::get_current_block_as_u64(), + }, + ); + + assert_noop!( + SubtensorModule::swap_coldkey( + RuntimeOrigin::root(), + old_coldkey, + new_coldkey, + TaoBalance::ZERO, + ), + Error::::ActiveLockExists + ); + + // The failed extrinsic should roll back the earlier stake transfer. + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &old_hotkey, + &old_coldkey, + netuid + ), + original_stake + ); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &old_hotkey, + &new_coldkey, + netuid + ), + AlphaBalance::ZERO + ); + }); +} + +// ========================================================================= +// GROUP 12: Hotkey swap interaction +// ========================================================================= + +#[test] +fn test_hotkey_swap_swaps_locks_and_convictions() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let old_hotkey = U256::from(2); + let new_hotkey = U256::from(20); + let netuid = setup_subnet_with_stake(coldkey, old_hotkey, 100_000_000_000); + Owner::::insert(old_hotkey, coldkey); + Owner::::insert(new_hotkey, coldkey); + + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, + netuid, + &old_hotkey, + 5000u64.into(), + )); + + // Mock a non-zero conviction + let mut lock = Lock::::get((coldkey, netuid, old_hotkey)).unwrap(); + lock.conviction = U64F64::from_num(1234); + Lock::::insert((coldkey, netuid, old_hotkey), lock); + let mut hotkey_lock = HotkeyLock::::get(netuid, old_hotkey).unwrap(); + hotkey_lock.conviction = U64F64::from_num(1234); + HotkeyLock::::insert(netuid, old_hotkey, hotkey_lock); + + // Perform hotkey swap + let mut weight = Weight::zero(); + assert_ok!(SubtensorModule::perform_hotkey_swap_on_all_subnets( + &old_hotkey, + &new_hotkey, + &coldkey, + &mut weight, + false + )); + + // Lock references new_hotkey, conviction is not reset + let lock = Lock::::get((coldkey, netuid, new_hotkey)).unwrap(); + assert_eq!(lock.locked_mass, 5000u64.into()); + assert!(lock.conviction > U64F64::from_num(0)); + + // Hotkey lock data also updated, conviction is not reset + let hotkey_lock = HotkeyLock::::get(netuid, new_hotkey).unwrap(); + assert_eq!(hotkey_lock.locked_mass, 5000u64.into()); + assert!(hotkey_lock.conviction > U64F64::from_num(0)); + + // Trying to top up to new_hotkey works + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, + netuid, + &new_hotkey, + 100u64.into() + )); + + // Trying to top up to old_hotkey fails (old_hotkey is no longer associated with coldkey) + assert_noop!( + SubtensorModule::do_lock_stake(&coldkey, netuid, &old_hotkey, 100u64.into()), + Error::::LockHotkeyMismatch + ); + }); +} + +// ========================================================================= +// GROUP 13: Lock extrinsic via dispatch +// ========================================================================= + +#[test] +fn test_lock_stake_extrinsic() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + let lock_amount: u64 = 5000; + assert_ok!(SubtensorModule::lock_stake( + RuntimeOrigin::signed(coldkey), + hotkey, + netuid, + lock_amount.into(), + )); + + let lock = Lock::::get((coldkey, netuid, hotkey)).expect("Lock should exist"); + assert_eq!(lock.locked_mass, lock_amount.into()); + assert_eq!(lock.conviction, U64F64::from_num(0)); + + // Hotkey lock should also be updated + let hotkey_lock = + HotkeyLock::::get(netuid, hotkey).expect("Hotkey lock should exist"); + assert_eq!(hotkey_lock.locked_mass, lock_amount.into()); + assert_eq!(hotkey_lock.conviction, U64F64::from_num(0)); + }); +} + +// ========================================================================= +// GROUP 14: Recycle/burn alpha checks against lock +// ========================================================================= + +#[test] +fn test_recycle_alpha_checks_lock() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + let total = SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey, netuid); + assert_ok!(SubtensorModule::do_lock_stake(&coldkey, netuid, &hotkey, total)); + + step_block(1); + + // Unstake should be blocked + let alpha = get_alpha(&hotkey, &coldkey, netuid); + assert_noop!( + SubtensorModule::do_remove_stake( + RuntimeOrigin::signed(coldkey), + hotkey, + netuid, + alpha, + ), + Error::::StakeUnavailable + ); + + // recycle_alpha checks lock and should fail if it would reduce alpha below locked amount + let recycle_amount = alpha / 2.into(); + assert_noop!( + SubtensorModule::do_recycle_alpha( + RuntimeOrigin::signed(coldkey), + hotkey, + recycle_amount, + netuid, + ), + Error::::StakeUnavailable + ); + + // Alpha is not below locked_mass + let total_after = SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey, netuid); + let locked = SubtensorModule::get_current_locked(&coldkey, netuid); + assert!(total_after >= locked); + }); +} + +#[test] +fn test_burn_alpha_checks_lock() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + let total = SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey, netuid); + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, netuid, &hotkey, total + )); + + step_block(1); + + // burn_alpha checks lock and should fail if it would reduce alpha below locked amount + let alpha = get_alpha(&hotkey, &coldkey, netuid); + let burn_amount = alpha / 2.into(); + assert_noop!( + SubtensorModule::do_burn_alpha( + RuntimeOrigin::signed(coldkey), + hotkey, + burn_amount, + netuid, + ), + Error::::StakeUnavailable + ); + + // Alpha is not below locked_mass + let total_after = SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey, netuid); + let locked = SubtensorModule::get_current_locked(&coldkey, netuid); + assert!(total_after >= locked); + }); +} + +// ========================================================================= +// GROUP 15: Subnet dissolution +// ========================================================================= + +#[test] +fn test_subnet_dissolution_orphans_locks() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, + netuid, + &hotkey, + 5000u64.into(), + )); + assert!(Lock::::get((coldkey, netuid, hotkey)).is_some()); + + // Dissolve the subnet + assert_ok!(SubtensorModule::do_dissolve_network(netuid)); + + // All Alpha entries are gone + assert_eq!( + SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey, netuid), + AlphaBalance::ZERO + ); + + // Lock entries are not orphaned + let lock = Lock::::get((coldkey, netuid, hotkey)); + assert!(lock.is_none()); + + // Hotkey lock is also removed + let hotkey_lock = HotkeyLock::::get(netuid, hotkey); + assert!(hotkey_lock.is_none()); + }); +} + +#[test] +fn test_subnet_dissolution_and_netuid_reuse() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey_old = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey_old, 100_000_000_000); + + // Lock on the old subnet + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, + netuid, + &hotkey_old, + 5000u64.into(), + )); + + // Dissolve old subnet + assert_ok!(SubtensorModule::do_dissolve_network(netuid)); + + // No stale lock from old subnet remains + let stale_lock = Lock::::get((coldkey, netuid, hotkey_old)); + assert!(stale_lock.is_none()); + + // No stale hotkey lock remains + let stale_hotkey_lock = HotkeyLock::::get(netuid, hotkey_old); + assert!(stale_hotkey_lock.is_none()); + }); +} + +// ========================================================================= +// GROUP 16: Clear small nomination checks lock +// ========================================================================= + +#[test] +fn test_clear_small_nomination_checks_lock() { + new_test_ext(1).execute_with(|| { + let owner_coldkey = U256::from(100); + let owner_hotkey = U256::from(101); + let netuid = setup_subnet_with_stake(owner_coldkey, owner_hotkey, 100_000_000_000); + + // Set up a nominator (different coldkey, does NOT own the hotkey) + let nominator = U256::from(200); + add_balance_to_coldkey_account(&nominator, 100_000_000_000u64.into()); + assert_ok!(SubtensorModule::create_account_if_non_existent( + &nominator, + &owner_hotkey + )); + SubtensorModule::stake_into_subnet( + &owner_hotkey, + &nominator, + netuid, + 50_000_000_000u64.into(), + ::SwapInterface::max_price(), + false, + false, + ) + .unwrap(); + + let nominator_alpha = get_alpha(&owner_hotkey, &nominator, netuid); + assert!(nominator_alpha > AlphaBalance::ZERO); + + // Nominator locks their full stake + let nominator_total = SubtensorModule::total_coldkey_alpha_on_subnet(&nominator, netuid); + assert_ok!(SubtensorModule::do_lock_stake( + &nominator, + netuid, + &owner_hotkey, + nominator_total, + )); + + // Set a high nominator min stake so the current stake is "small" + SubtensorModule::set_nominator_min_required_stake(u64::MAX); + + // clear_small_nomination removes the lock and unstakes alpha + SubtensorModule::clear_small_nomination_if_required(&owner_hotkey, &nominator, netuid); + + // Nominator alpha has been removed despite lock + let nominator_alpha_after = get_alpha(&owner_hotkey, &nominator, netuid); + assert_eq!(nominator_alpha_after, AlphaBalance::ZERO); + + // Lock entry doesn't exist anymore + assert!( + Lock::::iter_prefix((nominator, netuid)) + .next() + .is_none() + ); + + // Hotkey lock should also be removed + let hotkey_lock = HotkeyLock::::get(netuid, owner_hotkey); + assert!(hotkey_lock.is_none()); + }); +} + +#[test] +// If one coldkey has a large nomination on one hotkey and a tiny nomination on another, +// clearing the tiny nomination should reduce the lock state only by that tiny alpha amount. +fn test_clear_small_nomination_reduces_only_tiny_amount_from_lock_state() { + new_test_ext(1).execute_with(|| { + let coldkey_large = U256::from(100); + let hotkey_large = U256::from(101); + let netuid = setup_subnet_with_stake(coldkey_large, hotkey_large, 100_000_000_000); + + let coldkey_tiny = U256::from(102); + let hotkey_tiny = U256::from(103); + assert_ok!(SubtensorModule::create_account_if_non_existent( + &coldkey_tiny, + &hotkey_tiny + )); + + let nominator = U256::from(200); + let large_tao = TaoBalance::from(50_000_000_000u64); + let tiny_tao = TaoBalance::from(1_000_000u64); + add_balance_to_coldkey_account(&nominator, large_tao + tiny_tao); + + // Create one large nomination and one tiny nomination on the same subnet. + SubtensorModule::stake_into_subnet( + &hotkey_large, + &nominator, + netuid, + large_tao, + ::SwapInterface::max_price(), + false, + false, + ) + .unwrap(); + SubtensorModule::stake_into_subnet( + &hotkey_tiny, + &nominator, + netuid, + tiny_tao, + ::SwapInterface::max_price(), + false, + false, + ) + .unwrap(); + + let large_alpha_before = get_alpha(&hotkey_large, &nominator, netuid); + let tiny_alpha_before = get_alpha(&hotkey_tiny, &nominator, netuid); + assert!(large_alpha_before > tiny_alpha_before); + + // Lock against the large nomination hotkey and seed non-zero unlocked_mass + conviction + // so we can verify each field is reduced only by the tiny nomination's alpha amount. + let total_before = SubtensorModule::total_coldkey_alpha_on_subnet(&nominator, netuid); + assert_ok!(SubtensorModule::do_lock_stake( + &nominator, + netuid, + &hotkey_large, + total_before, + )); + + let unlocked_before = AlphaBalance::from(tiny_alpha_before.to_u64() + 1_000); + let conviction_before = U64F64::from_num(tiny_alpha_before.to_u64() + 2_000); + let last_update = SubtensorModule::get_current_block_as_u64(); + Lock::::insert( + (nominator, netuid, hotkey_large), + LockState { + locked_mass: total_before, + unlocked_mass: unlocked_before, + conviction: conviction_before, + last_update, + }, + ); + HotkeyLock::::insert( + netuid, + hotkey_large, + LockState { + locked_mass: total_before, + unlocked_mass: AlphaBalance::ZERO, + conviction: conviction_before, + last_update, + }, + ); + + // Force the tiny nomination to qualify as "small" and clear only that nomination. + SubtensorModule::set_nominator_min_required_stake(u64::MAX); + SubtensorModule::clear_small_nomination_if_required(&hotkey_tiny, &nominator, netuid); + + // The large nomination stays, the tiny one is removed. + let large_alpha_after = get_alpha(&hotkey_large, &nominator, netuid); + let tiny_alpha_after = get_alpha(&hotkey_tiny, &nominator, netuid); + assert_eq!(large_alpha_after, large_alpha_before); + assert!(!large_alpha_after.is_zero()); + assert_eq!(tiny_alpha_after, AlphaBalance::ZERO); + + // Only the tiny alpha amount should be shaved off the coldkey lock state. + let lock_after = Lock::::get((nominator, netuid, hotkey_large)).unwrap(); + let tiny_alpha_fixed = U64F64::from_num(tiny_alpha_before.to_u64()); + assert!(!lock_after.locked_mass.is_zero()); + assert_eq!(lock_after.locked_mass, total_before - tiny_alpha_before); + assert!(!lock_after.unlocked_mass.is_zero()); + assert_eq!( + lock_after.unlocked_mass, + unlocked_before - tiny_alpha_before + ); + assert!(lock_after.conviction != U64F64::from_num(0)); + assert_eq!(lock_after.conviction, conviction_before - tiny_alpha_fixed); + + // The aggregate hotkey lock on the locked hotkey should also only shrink by the tiny amount. + let hotkey_lock_after = HotkeyLock::::get(netuid, hotkey_large).unwrap(); + assert_eq!( + hotkey_lock_after.locked_mass, + total_before - tiny_alpha_before + ); + assert_eq!( + hotkey_lock_after.conviction, + conviction_before - tiny_alpha_fixed + ); + }); +} + +// ========================================================================= +// GROUP 17: Emission interaction +// ========================================================================= + +#[test] +fn test_emissions_do_not_break_lock_invariant() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + let total_alpha_before = SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey, netuid); + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, + netuid, + &hotkey, + total_alpha_before + )); + + // Simulate emission: directly increase alpha for the hotkey on subnet + // This increases the pool value for all share holders (including our coldkey) + let emission_amount: AlphaBalance = 10_000_000u64.into(); + SubtensorModule::increase_stake_for_hotkey_on_subnet(&hotkey, netuid, emission_amount); + + // After emission, total alpha should increase by emission_amount + let total_alpha_after = SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey, netuid); + assert_eq!(total_alpha_after, total_alpha_before + emission_amount); + + // Lock invariant still holds: total_alpha >= locked_mass + let locked = SubtensorModule::get_current_locked(&coldkey, netuid); + assert!(total_alpha_after >= locked); + + // Available becomes emission_amount + let available = SubtensorModule::available_stake(&coldkey, netuid); + assert_eq!(available, emission_amount); + }); +} + +#[test] +fn test_epoch_distribution_auto_locks_owner_cut() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let validator_coldkey = U256::from(1); + let validator_hotkey = U256::from(2); + let miner_coldkey = U256::from(5); + let miner_hotkey = U256::from(6); + let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let subnet_tempo = 10; + let stake = 100_000_000_000u64; + + SubtensorModule::set_tempo(netuid, subnet_tempo); + SubtensorModule::set_ck_burn(0); + setup_reserves(netuid, (stake * 10_000).into(), (stake * 10_000).into()); + + register_ok_neuron(netuid, validator_hotkey, validator_coldkey, 0); + register_ok_neuron(netuid, miner_hotkey, miner_coldkey, 1); + + add_balance_to_coldkey_account( + &validator_coldkey, + TaoBalance::from(stake) + ExistentialDeposit::get(), + ); + + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_coldkey), + validator_hotkey, + netuid, + stake.into() + )); + + SubtensorModule::set_weights_set_rate_limit(netuid, 0); + SubtensorModule::set_max_allowed_validators(netuid, 1); + step_block(subnet_tempo); + SubnetOwnerCut::::set(u16::MAX / 10); + + let owner_uid = + SubtensorModule::get_uid_for_net_and_hotkey(netuid, &subnet_owner_hotkey).unwrap(); + let validator_uid = + SubtensorModule::get_uid_for_net_and_hotkey(netuid, &validator_hotkey).unwrap(); + let miner_uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &miner_hotkey).unwrap(); + let uid_count = [ + owner_uid as usize, + validator_uid as usize, + miner_uid as usize, + ] + .into_iter() + .max() + .unwrap() + + 1; + + // Setup YUMA so that the next epoch produces non-zero subnet emissions. + Weights::::insert( + NetUidStorageIndex::from(netuid), + validator_uid, + vec![(miner_uid, 0xFFFF)], + ); + BlockAtRegistration::::set(netuid, owner_uid, 1); + BlockAtRegistration::::set(netuid, validator_uid, 1); + BlockAtRegistration::::set(netuid, miner_uid, 1); + LastUpdate::::set(NetUidStorageIndex::from(netuid), vec![2; uid_count]); + Kappa::::set(netuid, u16::MAX / 5); + ActivityCutoff::::set(netuid, u16::MAX); + let mut validator_permit = vec![false; uid_count]; + validator_permit[validator_uid as usize] = true; + ValidatorPermit::::insert(netuid, validator_permit); + + let owner_stake_before = get_alpha(&subnet_owner_hotkey, &subnet_owner_coldkey, netuid); + assert!( + Lock::::iter_prefix((subnet_owner_coldkey, netuid)) + .next() + .is_none() + ); + + // Advance to the next epoch so owner cut is distributed and auto-locked. + step_block(subnet_tempo); + + let owner_stake_after = get_alpha(&subnet_owner_hotkey, &subnet_owner_coldkey, netuid); + let owner_cut_locked = owner_stake_after - owner_stake_before; + assert!(owner_cut_locked > AlphaBalance::ZERO); + + let owner_lock = Lock::::get((subnet_owner_coldkey, netuid, subnet_owner_hotkey)) + .expect("owner cut should be auto-locked to the subnet owner's hotkey"); + assert_eq!(owner_lock.locked_mass, owner_cut_locked); + assert_eq!(owner_lock.unlocked_mass, AlphaBalance::ZERO); + }); +} + +// ========================================================================= +// GROUP 18: Neuron replacement +// ========================================================================= + +#[test] +fn test_neuron_replacement_does_not_affect_lock() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + // Register the hotkey as a neuron + register_ok_neuron(netuid, hotkey, coldkey, 0); + + let lock_amount = 5000u64.into(); + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, + netuid, + &hotkey, + lock_amount + )); + + let total_before = SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey, netuid); + let locked_before = SubtensorModule::get_current_locked(&coldkey, netuid); + + // Replace the neuron with a different hotkey + let new_hotkey = U256::from(99); + let uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey).unwrap(); + SubtensorModule::replace_neuron( + netuid, + uid, + &new_hotkey, + SubtensorModule::get_current_block_as_u64(), + ); + + // Alpha and lock should be unaffected by neuron replacement + let total_after = SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey, netuid); + let locked_after = SubtensorModule::get_current_locked(&coldkey, netuid); + + assert_eq!(total_after, total_before); + assert_eq!(locked_after, locked_before); + + // Lock still references original hotkey + assert!(Lock::::get((coldkey, netuid, hotkey)).is_some()); + + // Hotkey lock still references original hotkey + assert!(HotkeyLock::::get(netuid, hotkey).is_some()); + }); +} + +// ========================================================================= +// GROUP 19: Moving lock +// ========================================================================= + +#[test] +fn test_moving_lock() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey_origin = U256::from(2); + let hotkey_destination = U256::from(3); + let netuid = setup_subnet_with_stake(coldkey, hotkey_origin, 100_000_000_000); + + let lock_amount = 5000u64.into(); + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, + netuid, + &hotkey_origin, + lock_amount + )); + + // Mock a non-zero conviction + let mut lock = Lock::::get((coldkey, netuid, hotkey_origin)).unwrap(); + lock.conviction = U64F64::from_num(1234); + Lock::::insert((coldkey, netuid, hotkey_origin), lock); + let mut hotkey_lock = HotkeyLock::::get(netuid, hotkey_origin).unwrap(); + hotkey_lock.conviction = U64F64::from_num(1234); + HotkeyLock::::insert(netuid, hotkey_origin, hotkey_lock); + + assert_ok!(SubtensorModule::move_lock( + RuntimeOrigin::signed(coldkey), + hotkey_destination, + netuid, + )); + let lock = Lock::::get((coldkey, netuid, hotkey_destination)).unwrap(); + assert_eq!(lock.locked_mass, lock_amount); + assert_eq!(lock.conviction, U64F64::from_num(0)); + + // Hotkey lock is removed on origin and added on destination + assert!(HotkeyLock::::get(netuid, hotkey_origin).is_none()); + let hotkey_lock_destination_after = + HotkeyLock::::get(netuid, hotkey_destination).unwrap(); + assert_eq!(hotkey_lock_destination_after.locked_mass, lock_amount); + assert_eq!( + hotkey_lock_destination_after.conviction, + U64F64::from_num(0) + ); + }); +} + +#[test] +fn test_moving_partial_lock() { + new_test_ext(1).execute_with(|| { + let coldkey1 = U256::from(1); + let coldkey2 = U256::from(2); + let hotkey_origin = U256::from(3); + let hotkey_destination = U256::from(4); + let netuid = setup_subnet_with_stake(coldkey1, hotkey_origin, 100_000_000_000); + + // Make hotkey_origin and hotkey_destination owned by different coldkeys + assert_ok!(SubtensorModule::create_account_if_non_existent( + &coldkey1, + &hotkey_origin + )); + assert_ok!(SubtensorModule::create_account_if_non_existent( + &coldkey2, + &hotkey_destination + )); + + // Add coldkey2 stake + add_balance_to_coldkey_account(&coldkey2, 100_000_000_000u64.into()); + SubtensorModule::stake_into_subnet( + &hotkey_origin, + &coldkey2, + netuid, + 50_000_000_000u64.into(), + ::SwapInterface::max_price(), + false, + false, + ) + .unwrap(); + + let lock_amount = 5000u64.into(); + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey1, + netuid, + &hotkey_origin, + lock_amount + )); + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey2, + netuid, + &hotkey_origin, + lock_amount + )); + + // Mock a non-zero conviction + let mut lock1 = Lock::::get((coldkey1, netuid, hotkey_origin)).unwrap(); + lock1.conviction = U64F64::from_num(1000); + Lock::::insert((coldkey1, netuid, hotkey_origin), lock1); + let mut lock2 = Lock::::get((coldkey2, netuid, hotkey_origin)).unwrap(); + lock2.conviction = U64F64::from_num(1000); + Lock::::insert((coldkey2, netuid, hotkey_origin), lock2); + let mut hotkey_lock = HotkeyLock::::get(netuid, hotkey_origin).unwrap(); + hotkey_lock.conviction = U64F64::from_num(2000); + HotkeyLock::::insert(netuid, hotkey_origin, hotkey_lock); + + // Move lock for coldkey1 to hotkey_destination, coldkey2's lock should be unaffected + assert_ok!(SubtensorModule::move_lock( + RuntimeOrigin::signed(coldkey1), + hotkey_destination, + netuid, + )); + let lock1_after = Lock::::get((coldkey1, netuid, hotkey_destination)).unwrap(); + let lock2_after = Lock::::get((coldkey2, netuid, hotkey_origin)).unwrap(); + assert_eq!(lock1_after.locked_mass, lock_amount); + assert_eq!(lock1_after.conviction, U64F64::from_num(0)); + assert_eq!(lock2_after.locked_mass, lock_amount); + assert_eq!(lock2_after.conviction, U64F64::from_num(1000)); + + // Hotkey lock is removed on origin and added on destination + let hotkey_lock_origin_after = HotkeyLock::::get(netuid, hotkey_origin).unwrap(); + let hotkey_lock_destination_after = + HotkeyLock::::get(netuid, hotkey_destination).unwrap(); + assert_eq!(hotkey_lock_origin_after.locked_mass, lock_amount); + assert_eq!(hotkey_lock_origin_after.conviction, U64F64::from_num(1000)); + assert_eq!(hotkey_lock_destination_after.locked_mass, lock_amount); + assert_eq!( + hotkey_lock_destination_after.conviction, + U64F64::from_num(0) + ); + }); +} + +#[test] +fn test_moving_partial_lock_same_owners() { + new_test_ext(1).execute_with(|| { + let coldkey1 = U256::from(1); + let coldkey2 = U256::from(2); + let hotkey_origin = U256::from(3); + let hotkey_destination = U256::from(4); + let netuid = setup_subnet_with_stake(coldkey1, hotkey_origin, 100_000_000_000); + + // Add coldkey2 stake + add_balance_to_coldkey_account(&coldkey2, 100_000_000_000u64.into()); + + // Make hotkey_origin and hotkey_destination both owned by coldkey1 + assert_ok!(SubtensorModule::create_account_if_non_existent( + &coldkey1, + &hotkey_origin + )); + assert_ok!(SubtensorModule::create_account_if_non_existent( + &coldkey1, + &hotkey_destination + )); + SubtensorModule::stake_into_subnet( + &hotkey_origin, + &coldkey2, + netuid, + 50_000_000_000u64.into(), + ::SwapInterface::max_price(), + false, + false, + ) + .unwrap(); + + let lock_amount = 5000u64.into(); + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey1, + netuid, + &hotkey_origin, + lock_amount + )); + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey2, + netuid, + &hotkey_origin, + lock_amount + )); + + // Mock a non-zero conviction + let mut lock1 = Lock::::get((coldkey1, netuid, hotkey_origin)).unwrap(); + lock1.conviction = U64F64::from_num(1000); + Lock::::insert((coldkey1, netuid, hotkey_origin), lock1); + let mut lock2 = Lock::::get((coldkey2, netuid, hotkey_origin)).unwrap(); + lock2.conviction = U64F64::from_num(1000); + Lock::::insert((coldkey2, netuid, hotkey_origin), lock2); + let mut hotkey_lock = HotkeyLock::::get(netuid, hotkey_origin).unwrap(); + hotkey_lock.conviction = U64F64::from_num(2000); + HotkeyLock::::insert(netuid, hotkey_origin, hotkey_lock); + + // Move lock for coldkey1 to hotkey_destination, coldkey2's lock should be unaffected + assert_ok!(SubtensorModule::move_lock( + RuntimeOrigin::signed(coldkey1), + hotkey_destination, + netuid, + )); + let lock1_after = Lock::::get((coldkey1, netuid, hotkey_destination)).unwrap(); + let lock2_after = Lock::::get((coldkey2, netuid, hotkey_origin)).unwrap(); + assert_eq!(lock1_after.locked_mass, lock_amount); + assert_eq!(lock1_after.conviction, U64F64::from_num(1000)); + assert_eq!(lock2_after.locked_mass, lock_amount); + assert_eq!(lock2_after.conviction, U64F64::from_num(1000)); + + // Hotkey lock is moved to destination with conviction + let hotkey_lock_origin_after = HotkeyLock::::get(netuid, hotkey_origin).unwrap(); + let hotkey_lock_destination_after = + HotkeyLock::::get(netuid, hotkey_destination).unwrap(); + assert_eq!(hotkey_lock_origin_after.locked_mass, lock_amount); + assert_eq!(hotkey_lock_origin_after.conviction, U64F64::from_num(1000)); + assert_eq!(hotkey_lock_destination_after.locked_mass, lock_amount); + assert_eq!( + hotkey_lock_destination_after.conviction, + U64F64::from_num(1000) + ); + }); +} + +#[test] +// Moving a lock after partially unlocking it should preserve the coldkey's unavailable amount. +fn test_moving_unlocked_lock_preserves_unavailable_amount() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let destination_coldkey = U256::from(9); + let hotkey_origin = U256::from(2); + let hotkey_destination = U256::from(3); + let netuid = setup_subnet_with_stake(coldkey, hotkey_origin, 100_000_000_000); + + // Make the destination hotkey exist under a different owner so the move is a real transfer of lock ownership. + assert_ok!(SubtensorModule::create_account_if_non_existent( + &destination_coldkey, + &hotkey_destination + )); + + // Lock some stake, then unlock part of it so unavailable stake is split across locked and unlocked mass. + let lock_amount = AlphaBalance::from(5_000u64); + let unlock_amount = AlphaBalance::from(2_000u64); + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, + netuid, + &hotkey_origin, + lock_amount, + )); + assert_ok!(SubtensorModule::do_unlock_stake( + &coldkey, + netuid, + unlock_amount, + )); + + // Capture the coldkey-level availability view before moving the lock. + let available_before = SubtensorModule::available_stake(&coldkey, netuid); + let locked_before = SubtensorModule::get_current_locked(&coldkey, netuid); + let unlocked_before = SubtensorModule::get_current_unlocked(&coldkey, netuid); + let unavailable_before = locked_before + unlocked_before; + + // Move the lock to the destination hotkey. + assert_ok!(SubtensorModule::move_lock( + RuntimeOrigin::signed(coldkey), + hotkey_destination, + netuid, + )); + + // The origin entry should be gone and the destination entry should preserve locked/unlocked mass. + assert!(Lock::::get((coldkey, netuid, hotkey_origin)).is_none()); + let moved_lock = Lock::::get((coldkey, netuid, hotkey_destination)).unwrap(); + assert_eq!(moved_lock.locked_mass, locked_before); + assert_eq!(moved_lock.unlocked_mass, unlocked_before); + + // The coldkey's unavailable and available stake should be unchanged by the move. + let available_after = SubtensorModule::available_stake(&coldkey, netuid); + let locked_after = SubtensorModule::get_current_locked(&coldkey, netuid); + let unlocked_after = SubtensorModule::get_current_unlocked(&coldkey, netuid); + let unavailable_after = locked_after + unlocked_after; + assert_eq!(available_after, available_before); + assert_eq!(unavailable_after, unavailable_before); + }); +} + +// ========================================================================= +// GROUP 20: Unlocking behavior +// ========================================================================= + +#[test] +// Fully unlocked stake should still be unavailable on the very next block. +fn test_unlocked_amount_cannot_be_unstaked_immediately() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + // Lock then immediately unlock the entire position. + let total = SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey, netuid); + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, netuid, &hotkey, total + )); + assert_ok!(SubtensorModule::do_unlock_stake(&coldkey, netuid, total)); + + // Right after unlock, everything sits in unlocked_mass and nothing is available yet. + assert_eq!(SubtensorModule::available_stake(&coldkey, netuid), AlphaBalance::ZERO); + assert_eq!(SubtensorModule::get_current_locked(&coldkey, netuid), AlphaBalance::ZERO); + assert_eq!(SubtensorModule::get_current_unlocked(&coldkey, netuid), total); + + // Move one block to avoid unrelated rate-limit behavior on stake operations. + step_block(1); + + // Unstaking the just-unlocked amount should still be blocked. + assert_noop!( + SubtensorModule::do_remove_stake( + RuntimeOrigin::signed(coldkey), + hotkey, + netuid, + total, + ), + Error::::StakeUnavailable + ); + }); +} + +#[test] +// Fully unlocked stake should also be unavailable for immediate re-locking. +fn test_unlocked_amount_cannot_be_relocked_immediately() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + // Lock then immediately unlock the entire position. + let total = SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey, netuid); + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, netuid, &hotkey, total + )); + assert_ok!(SubtensorModule::do_unlock_stake(&coldkey, netuid, total)); + + // Nothing should be available to lock again yet. + assert_eq!( + SubtensorModule::available_stake(&coldkey, netuid), + AlphaBalance::ZERO + ); + assert_eq!( + SubtensorModule::get_current_unlocked(&coldkey, netuid), + total + ); + + // Even a tiny re-lock should fail because available stake is still zero. + assert_noop!( + SubtensorModule::do_lock_stake(&coldkey, netuid, &hotkey, 1u64.into()), + Error::::InsufficientStakeForLock + ); + }); +} + +#[test] +// Unlocking more than the currently locked mass must be rejected and leave the lock untouched. +fn test_unlock_stake_rejects_amount_above_locked_mass() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + let locked_amount = AlphaBalance::from(1_000u64); + let unlock_amount_too_high = AlphaBalance::from(1_001u64); + + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, + netuid, + &hotkey, + locked_amount, + )); + + assert_noop!( + SubtensorModule::do_unlock_stake(&coldkey, netuid, unlock_amount_too_high), + Error::::UnlockAmountTooHigh + ); + + let lock = Lock::::get((coldkey, netuid, hotkey)).expect("Lock should exist"); + assert_eq!(lock.locked_mass, locked_amount); + assert_eq!(lock.unlocked_mass, AlphaBalance::ZERO); + }); +} + +#[test] +// After one full UnlockRate period, unlocked_mass should decay to about e^-1 of its original value. +fn test_roll_forward_unlocked_mass_decays() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + let lock_amount = 10000u64; + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, + netuid, + &hotkey, + lock_amount.into() + )); + + // Unlock all + assert_ok!(SubtensorModule::do_unlock_stake( + &coldkey, + netuid, + lock_amount.into() + )); + + // Advance one full unlock rate via direct block number jump (step_block overflows u16 for tau=216000) + let rate = UnlockRate::::get(); + let target = System::block_number() + rate; + System::set_block_number(target); + + // There should be no locked amount + let locked = SubtensorModule::get_current_locked(&coldkey, netuid); + assert_eq!(locked, 0.into()); + + // After one UnlockRate, unlocked should be ~36.8% of original + let unlocked = SubtensorModule::get_current_unlocked(&coldkey, netuid); + let expected = lock_amount as f64 * 0.368; + assert_abs_diff_eq!( + u64::from(unlocked) as f64, + expected, + epsilon = lock_amount as f64 / 10. + ); + }); +} + +#[test] +// Even after one UnlockRate period, a large fraction of a fully unlocked position should remain unavailable. +fn test_unlock_decay_blocks_eighty_percent() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + // Start with a full lock, then fully unlock it. + let original_lock = SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey, netuid); + let attempted_amount = original_lock * 8.into() / 10.into(); + + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, + netuid, + &hotkey, + original_lock, + )); + assert_ok!(SubtensorModule::do_unlock_stake( + &coldkey, + netuid, + original_lock, + )); + + // Advance exactly one unlock time constant. + let rate = UnlockRate::::get(); + let target = System::block_number() + rate; + System::set_block_number(target); + + // Only about 36.8% should remain unavailable here, so 80% is still too much. + let unlocked = SubtensorModule::get_current_unlocked(&coldkey, netuid); + assert!(unlocked < attempted_amount); + + // The same oversized amount should fail for both unstake and re-lock. + assert_noop!( + SubtensorModule::do_remove_stake( + RuntimeOrigin::signed(coldkey), + hotkey, + netuid, + attempted_amount, + ), + Error::::StakeUnavailable + ); + + assert_noop!( + SubtensorModule::do_lock_stake(&coldkey, netuid, &hotkey, attempted_amount), + Error::::InsufficientStakeForLock + ); + }); +} + +#[test] +// If only half the position is unlocked, even 40% of the original position should still be blocked after one UnlockRate. +fn test_unlock_decay_blocks_forty_percent_after_half_unlock() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + // Lock the full position, then unlock only half of it. + let original_lock = SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey, netuid); + let unlocked_amount = original_lock / 2.into(); + let attempted_amount = original_lock * 4.into() / 10.into(); + + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, + netuid, + &hotkey, + original_lock, + )); + assert_ok!(SubtensorModule::do_unlock_stake( + &coldkey, + netuid, + unlocked_amount, + )); + + // Advance exactly one unlock time constant. + let rate = UnlockRate::::get(); + let target = System::block_number() + rate; + System::set_block_number(target); + + // Since only half the original position entered unlocked_mass, 40% of the original is still unavailable. + let unlocked = SubtensorModule::get_current_unlocked(&coldkey, netuid); + assert!(unlocked < attempted_amount); + + // The same oversized amount should fail for both unstake and re-lock. + assert_noop!( + SubtensorModule::do_remove_stake( + RuntimeOrigin::signed(coldkey), + hotkey, + netuid, + attempted_amount, + ), + Error::::StakeUnavailable + ); + + assert_noop!( + SubtensorModule::do_lock_stake(&coldkey, netuid, &hotkey, attempted_amount), + Error::::InsufficientStakeForLock + ); + }); +} + +#[test] +// After one UnlockRate on a fully unlocked position, 60% of the original should be available to re-lock, +// and once re-locked it should no longer be immediately available to unstake. +fn test_unlock_decay_allows_relock_then_blocks_unstake() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + // Lock the full position, then fully unlock it. + let original_lock = SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey, netuid); + let relock_amount = original_lock * 6.into() / 10.into(); + + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, + netuid, + &hotkey, + original_lock, + )); + assert_ok!(SubtensorModule::do_unlock_stake( + &coldkey, + netuid, + original_lock, + )); + + // Advance exactly one unlock time constant. + let rate = UnlockRate::::get(); + let target = System::block_number() + rate; + System::set_block_number(target); + + // About 63.2% of the original position should now be available again, so 60% can be re-locked. + let available = SubtensorModule::available_stake(&coldkey, netuid); + assert!(available >= relock_amount); + + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, + netuid, + &hotkey, + relock_amount, + )); + + // Once re-locked, that amount should no longer be immediately available to unstake. + step_block(1); + assert_noop!( + SubtensorModule::do_remove_stake( + RuntimeOrigin::signed(coldkey), + hotkey, + netuid, + relock_amount, + ), + Error::::StakeUnavailable + ); + }); +} diff --git a/pallets/subtensor/src/tests/mechanism.rs b/pallets/subtensor/src/tests/mechanism.rs index 6cf37fbcc1..ef51c7e8d5 100644 --- a/pallets/subtensor/src/tests/mechanism.rs +++ b/pallets/subtensor/src/tests/mechanism.rs @@ -951,7 +951,7 @@ fn test_set_mechanism_weights_happy_path_sets_row_under_subid() { // Make caller a permitted validator with stake SubtensorModule::set_stake_threshold(0); SubtensorModule::set_validator_permit_for_uid(netuid, uid1, true); - SubtensorModule::add_balance_to_coldkey_account(&ck1, 1.into()); + add_balance_to_coldkey_account(&ck1, 1.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &hk1, &ck1, @@ -1008,7 +1008,7 @@ fn test_set_mechanism_weights_above_mechanism_count_fails() { // Make caller a permitted validator with stake SubtensorModule::set_stake_threshold(0); SubtensorModule::set_validator_permit_for_uid(netuid, uid1, true); - SubtensorModule::add_balance_to_coldkey_account(&ck1, 1.into()); + add_balance_to_coldkey_account(&ck1, 1.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &hk1, &ck1, @@ -1066,7 +1066,7 @@ fn test_commit_reveal_mechanism_weights_ok() { SubtensorModule::set_weights_set_rate_limit(netuid, 5); SubtensorModule::set_validator_permit_for_uid(netuid, uid1, true); SubtensorModule::set_commit_reveal_weights_enabled(netuid, true); - SubtensorModule::add_balance_to_coldkey_account(&ck1, 1.into()); + add_balance_to_coldkey_account(&ck1, 1.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &hk1, &ck1, @@ -1150,7 +1150,7 @@ fn test_commit_reveal_above_mechanism_count_fails() { SubtensorModule::set_weights_set_rate_limit(netuid, 5); SubtensorModule::set_validator_permit_for_uid(netuid, uid1, true); SubtensorModule::set_commit_reveal_weights_enabled(netuid, true); - SubtensorModule::add_balance_to_coldkey_account(&ck1, 1.into()); + add_balance_to_coldkey_account(&ck1, 1.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &hk1, &ck1, @@ -1237,8 +1237,8 @@ fn test_reveal_crv3_commits_sub_success() { SubtensorModule::set_validator_permit_for_uid(netuid, uid1, true); SubtensorModule::set_validator_permit_for_uid(netuid, uid2, true); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(3), 1.into()); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(4), 1.into()); + add_balance_to_coldkey_account(&U256::from(3), 1.into()); + add_balance_to_coldkey_account(&U256::from(4), 1.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet(&hotkey1, &U256::from(3), netuid, 1.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet(&hotkey2, &U256::from(4), netuid, 1.into()); @@ -1342,7 +1342,7 @@ fn test_crv3_above_mechanism_count_fails() { let uid2 = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey2).expect("uid2"); SubtensorModule::set_validator_permit_for_uid(netuid, uid1, true); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(3), 1.into()); + add_balance_to_coldkey_account(&U256::from(3), 1.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet(&hotkey1, &U256::from(3), netuid, 1.into()); let version_key = SubtensorModule::get_weights_version_key(netuid); @@ -1412,7 +1412,7 @@ fn test_do_commit_crv3_mechanism_weights_committing_too_fast() { // make validator with stake SubtensorModule::set_stake_threshold(0); SubtensorModule::set_validator_permit_for_uid(netuid, uid, true); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(2), 1.into()); + add_balance_to_coldkey_account(&U256::from(2), 1.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &U256::from(2), @@ -1523,9 +1523,9 @@ fn epoch_mechanism_emergency_mode_distributes_by_stake() { // (leave Weights/Bonds empty for all rows on this sub-subnet) // stake proportions: uid0:uid1:uid2 = 10:30:60 - SubtensorModule::add_balance_to_coldkey_account(&ck0, 10.into()); - SubtensorModule::add_balance_to_coldkey_account(&ck1, 30.into()); - SubtensorModule::add_balance_to_coldkey_account(&ck2, 60.into()); + add_balance_to_coldkey_account(&ck0, 10.into()); + add_balance_to_coldkey_account(&ck1, 30.into()); + add_balance_to_coldkey_account(&ck2, 60.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &hk0, &ck0, diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index f512b0e656..bf280556e0 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -46,29 +46,6 @@ fn close(value: u64, target: u64, eps: u64) { ) } -#[test] -fn test_initialise_ti() { - use frame_support::traits::OnRuntimeUpgrade; - - new_test_ext(1).execute_with(|| { - pallet_balances::TotalIssuance::::put(TaoBalance::from(1000)); - crate::SubnetTAO::::insert(NetUid::from(1), TaoBalance::from(100)); - crate::SubnetTAO::::insert(NetUid::from(2), TaoBalance::from(5)); - - // Ensure values are NOT initialized prior to running migration - assert!(crate::TotalIssuance::::get().is_zero()); - assert!(crate::TotalStake::::get().is_zero()); - - crate::migrations::migrate_init_total_issuance::initialise_total_issuance::Migration::::on_runtime_upgrade(); - - // Ensure values were initialized correctly - assert_eq!(crate::TotalStake::::get(), TaoBalance::from(105)); - assert_eq!( - crate::TotalIssuance::::get(), TaoBalance::from(105 + 1000) - ); - }); -} - #[test] fn test_migration_transfer_nets_to_foundation() { new_test_ext(1).execute_with(|| { @@ -335,7 +312,7 @@ fn test_migrate_commit_reveal_2() { // 2 * stake_amount // ); // // Increase stake for hotkey1 and coldkey1 on netuid_0 -// SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( +// mock_increase_stake_for_hotkey_and_coldkey_on_subnet( // &hotkey1, // &coldkey1, // netuid_0, @@ -354,7 +331,7 @@ fn test_migrate_commit_reveal_2() { // 3 * stake_amount // ); // // Increase stake for hotkey1 and coldkey1 on netuid_1 -// SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( +// mock_increase_stake_for_hotkey_and_coldkey_on_subnet( // &hotkey1, // &coldkey1, // netuid_1, @@ -1146,6 +1123,45 @@ fn test_migrate_rate_limit_keys() { }); } +#[test] +fn test_migrate_remove_add_stake_burn_rate_limit() { + new_test_ext(1).execute_with(|| { + const MIGRATION_NAME: &[u8] = b"migrate_remove_add_stake_burn_rate_limit"; + let netuid = NetUid::from(1); + let other_netuid = NetUid::from(2); + let preserved_netuid = NetUid::from(3); + let add_stake_burn_key = RateLimitKey::AddStakeBurn(netuid); + let other_add_stake_burn_key = RateLimitKey::AddStakeBurn(other_netuid); + let preserved_key = RateLimitKey::SetSNOwnerHotkey(preserved_netuid); + + SubtensorModule::set_rate_limited_last_block(&add_stake_burn_key, 100); + SubtensorModule::set_rate_limited_last_block(&other_add_stake_burn_key, 200); + SubtensorModule::set_rate_limited_last_block(&preserved_key, 300); + + let weight = + crate::migrations::migrate_remove_add_stake_burn_rate_limit::migrate_remove_add_stake_burn_rate_limit::(); + + assert!( + HasMigrationRun::::get(MIGRATION_NAME.to_vec()), + "Migration should be marked as executed" + ); + assert!(!weight.is_zero(), "Migration weight should be non-zero"); + + assert_eq!( + SubtensorModule::get_rate_limited_last_block(&add_stake_burn_key), + 0 + ); + assert_eq!( + SubtensorModule::get_rate_limited_last_block(&other_add_stake_burn_key), + 0 + ); + assert_eq!( + SubtensorModule::get_rate_limited_last_block(&preserved_key), + 300 + ); + }); +} + #[test] fn test_migrate_fix_staking_hot_keys() { new_test_ext(1).execute_with(|| { @@ -2491,7 +2507,7 @@ fn do_setup_unactive_sn() -> (Vec, Vec) { 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()); + add_balance_to_coldkey_account(&coldkey_account_id, burn_cost.into()); TotalIssuance::::mutate(|total_issuance| { let updated_total = u64::from(*total_issuance) .checked_add(u64::from(burn_cost)) @@ -3154,7 +3170,7 @@ fn test_migrate_fix_bad_hk_swap_only_genesis() { ::AccountId::decode(&mut account_id32_slice).expect("Invalid hotkey"); // Give balance to coldkey - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, 100_000222.into()); + add_balance_to_coldkey_account(&coldkey_account_id, 100_000222.into()); // Give stake to hotkey let stake_added = 222222.into(); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( @@ -3215,7 +3231,7 @@ fn test_migrate_fix_bad_hk_swap_runs_on_mainnet_genesis() { ::AccountId::decode(&mut account_id32_slice).expect("Invalid hotkey"); // Give balance to coldkey - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, 100_000222.into()); + add_balance_to_coldkey_account(&coldkey_account_id, 100_000222.into()); // Give stake to hotkey let stake_added = 222222.into(); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( @@ -4302,3 +4318,41 @@ fn test_migrate_fix_root_claimed_incorrect_genesis() { ); }); } + +#[test] +fn test_migrate_subnet_balances() { + new_test_ext(1).execute_with(|| { + let netuid1 = NetUid::from(1); + let netuid2 = NetUid::from(2); + add_network(netuid1, 1, 0); + add_network(netuid2, 1, 0); + + // Add network locks + let lock1 = TaoBalance::from(123_000_000_000_u64); + let lock2 = TaoBalance::from(321_000_000_000_u64); + SubnetLocked::::insert(netuid1, lock1); + SubnetLocked::::insert(netuid2, lock2); + + // Add SubnetTAO + let reserve1 = TaoBalance::from(456_000_000_000_u64); + let reserve2 = TaoBalance::from(654_000_000_000_u64); + SubnetTAO::::insert(netuid1, reserve1); + SubnetTAO::::insert(netuid2, reserve2); + + // Run migration + crate::migrations::migrate_subnet_balances::migrate_subnet_balances::(); + + // Test that subnet balances got updated + let subnet_account_1 = SubtensorModule::get_subnet_account_id(netuid1).unwrap(); + let subnet_account_2 = SubtensorModule::get_subnet_account_id(netuid2).unwrap(); + let balance1 = SubtensorModule::get_coldkey_balance(&subnet_account_1); + let balance2 = SubtensorModule::get_coldkey_balance(&subnet_account_2); + let initial_pool_tao = NetworkMinLockCost::::get(); + assert_eq!(balance1, lock1 + reserve1 - initial_pool_tao); + assert_eq!(balance2, lock2 + reserve2 - initial_pool_tao); + + // Check migration has been marked as run + const MIGRATION_NAME: &[u8] = b"migrate_subnet_balances"; + assert!(HasMigrationRun::::get(MIGRATION_NAME.to_vec())); + }); +} diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index fa16b3d0f2..8c553e3ee8 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -8,6 +8,7 @@ use core::num::NonZeroU64; use crate::utils::rate_limiting::TransactionType; use crate::*; +pub use frame_support::traits::Imbalance; use frame_support::traits::{Contains, Everything, InherentBuilder, InsideBoth, InstanceFilter}; use frame_support::weights::Weight; use frame_support::weights::constants::RocksDbWeight; @@ -43,13 +44,14 @@ frame_support::construct_runtime!( Balances: pallet_balances = 2, Shield: pallet_shield = 3, SubtensorModule: crate = 4, - Utility: pallet_utility = 5, - Scheduler: pallet_scheduler = 6, - Preimage: pallet_preimage = 7, - Drand: pallet_drand = 8, - Swap: pallet_subtensor_swap = 9, - Crowdloan: pallet_crowdloan = 10, - Proxy: pallet_subtensor_proxy = 11, + AlphaAssets: pallet_alpha_assets = 5, + Utility: pallet_utility = 6, + Scheduler: pallet_scheduler = 7, + Preimage: pallet_preimage = 8, + Drand: pallet_drand = 9, + Swap: pallet_subtensor_swap = 10, + Crowdloan: pallet_crowdloan = 11, + Proxy: pallet_subtensor_proxy = 12, } ); @@ -106,6 +108,8 @@ impl pallet_shield::Config for Test { type WeightInfo = (); } +impl pallet_alpha_assets::Config for Test {} + pub struct NoNestingCallFilter; impl Contains for NoNestingCallFilter { @@ -246,6 +250,8 @@ parameter_types! { pub const LeaseDividendsDistributionInterval: u32 = 100; pub const MaxImmuneUidsPercentage: Percent = Percent::from_percent(80); pub const EvmKeyAssociateRateLimit: u64 = 10; + pub const SubtensorPalletId: PalletId = PalletId(*b"subtensr"); + pub const BurnAccountId: PalletId = PalletId(*b"burntnsr"); } impl crate::Config for Test { @@ -319,8 +325,11 @@ impl crate::Config for Test { type GetCommitments = (); type MaxImmuneUidsPercentage = MaxImmuneUidsPercentage; type CommitmentsInterface = CommitmentsI; + type AlphaAssets = AlphaAssets; type EvmKeyAssociateRateLimit = EvmKeyAssociateRateLimit; type AuthorshipProvider = MockAuthorshipProvider; + type SubtensorPalletId = SubtensorPalletId; + type BurnAccountId = BurnAccountId; type WeightInfo = (); } @@ -769,7 +778,7 @@ pub fn register_ok_neuron( let bal: TaoBalance = SubtensorModule::get_coldkey_balance(&cold); if bal < min_balance_needed { - SubtensorModule::add_balance_to_coldkey_account(&cold, min_balance_needed - bal); + add_balance_to_coldkey_account(&cold, min_balance_needed - bal); } }; @@ -842,7 +851,7 @@ pub fn add_network_disable_subtoken(netuid: NetUid, tempo: u16, _modality: u16) 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()); + add_balance_to_coldkey_account(coldkey, lock_cost.into()); TotalIssuance::::mutate(|total_issuance| { *total_issuance = total_issuance.saturating_add(lock_cost); }); @@ -866,7 +875,7 @@ pub fn add_dynamic_network(hotkey: &U256, coldkey: &U256) -> NetUid { pub fn add_dynamic_network_without_emission_block(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()); + add_balance_to_coldkey_account(coldkey, lock_cost.into()); TotalIssuance::::mutate(|total_issuance| { *total_issuance = total_issuance.saturating_add(lock_cost); }); @@ -974,6 +983,10 @@ pub fn increase_stake_on_coldkey_hotkey_account( tao_staked: TaoBalance, netuid: NetUid, ) { + // Add TAO balance to coldkey account + add_balance_to_coldkey_account(coldkey, tao_staked.into()); + + // Stake SubtensorModule::stake_into_subnet( hotkey, coldkey, @@ -1107,6 +1120,41 @@ pub fn sf_from_u64(val: u64) -> SafeFloat { } #[allow(dead_code)] +pub fn add_balance_to_coldkey_account(coldkey: &U256, tao: TaoBalance) { + let ed = ExistentialDeposit::get(); + if tao >= ed { + let credit = SubtensorModule::mint_tao(tao); + let _ = SubtensorModule::spend_tao(coldkey, credit, tao).unwrap(); + } +} + +#[allow(dead_code)] +pub fn remove_balance_from_coldkey_account(coldkey: &U256, tao: TaoBalance) { + let _ = SubtensorModule::burn_tao(coldkey, tao); +} + +#[allow(dead_code)] +pub fn mock_increase_stake_for_hotkey_and_coldkey_on_subnet( + hotkey: &U256, + coldkey: &U256, + netuid: NetUid, + alpha: AlphaBalance, +) { + // Record stake in alpha pool + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + hotkey, coldkey, netuid, alpha, + ); + + // Make sure subnet exists, so does it's account + NetworksAdded::::insert(netuid, true); + + // Add TAO balance to subnet account + // For simplicity make it equal to alpha * 100, which is more than needed + let subnet_account = SubtensorModule::get_subnet_account_id(netuid).unwrap(); + let tao_bal = u64::from(alpha) * 100; + add_balance_to_coldkey_account(&subnet_account, tao_bal.into()); +} + pub fn remove_owner_registration_stake(netuid: NetUid) { let owner_hotkey = SubnetOwnerHotkey::::get(netuid); let owner_coldkey = SubnetOwner::::get(netuid); diff --git a/pallets/subtensor/src/tests/mock_high_ed.rs b/pallets/subtensor/src/tests/mock_high_ed.rs new file mode 100644 index 0000000000..0f0d818c38 --- /dev/null +++ b/pallets/subtensor/src/tests/mock_high_ed.rs @@ -0,0 +1,578 @@ +#![allow( + clippy::arithmetic_side_effects, + clippy::expect_used, + clippy::unwrap_used +)] + +use core::num::NonZeroU64; + +use crate::*; +use frame_support::traits::{Everything, InherentBuilder, InstanceFilter}; +use frame_support::weights::Weight; +use frame_support::weights::constants::RocksDbWeight; +use frame_support::{PalletId, derive_impl}; +use frame_support::{parameter_types, traits::PrivilegeCmp}; +use frame_system as system; +use frame_system::{EnsureRoot, limits, offchain::CreateTransactionBase}; +use pallet_subtensor_proxy as pallet_proxy; +use sp_core::{ConstU64, H256, U256, offchain::KeyTypeId}; +use sp_runtime::Perbill; +use sp_runtime::{ + BuildStorage, Percent, + traits::{BlakeTwo256, IdentityLookup}, +}; +use sp_std::{cmp::Ordering, sync::OnceLock}; +use sp_tracing::tracing_subscriber; +use substrate_fixed::types::U64F64; +use subtensor_runtime_common::{AuthorshipInfo, NetUid, TaoBalance}; +use tracing_subscriber::{EnvFilter, layer::SubscriberExt, util::SubscriberInitExt}; +type Block = frame_system::mocking::MockBlock; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system = 1, + Balances: pallet_balances = 2, + Shield: pallet_shield = 3, + SubtensorModule: crate = 4, + AlphaAssets: pallet_alpha_assets = 5, + Scheduler: pallet_scheduler = 6, + Preimage: pallet_preimage = 7, + Drand: pallet_drand = 8, + Swap: pallet_subtensor_swap = 9, + Crowdloan: pallet_crowdloan = 10, + Proxy: pallet_subtensor_proxy = 11, + } +); + +#[allow(dead_code)] +pub type TestRuntimeCall = frame_system::Call; + +pub const KEY_TYPE: KeyTypeId = KeyTypeId(*b"test"); + +#[allow(dead_code)] +pub type AccountId = U256; + +// The address format for describing accounts. +#[allow(dead_code)] +pub type Address = AccountId; + +// Balance of an account. +#[allow(dead_code)] +pub type Balance = TaoBalance; + +// An index to a block. +#[allow(dead_code)] +pub type BlockNumber = u64; + +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] +impl pallet_balances::Config for Test { + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type MaxLocks = (); + type WeightInfo = (); + type MaxReserves = (); + type ReserveIdentifier = (); + type RuntimeHoldReason = (); + type FreezeIdentifier = (); + type MaxFreezes = (); +} + +impl pallet_shield::Config for Test { + type AuthorityId = sp_core::sr25519::Public; + type FindAuthors = (); + type RuntimeCall = RuntimeCall; + type ExtrinsicDecryptor = (); + type WeightInfo = (); +} + +impl pallet_alpha_assets::Config for Test {} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl system::Config for Test { + type BaseCallFilter = Everything; + type BlockWeights = BlockWeights; + type BlockLength = (); + type DbWeight = RocksDbWeight; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = U256; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = SS58Prefix; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; + type Nonce = u64; + type Block = Block; + type DispatchExtension = crate::CheckColdkeySwap; +} + +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const SS58Prefix: u8 = 42; +} + +pub const MOCK_BLOCK_BUILDER: u64 = 12345u64; + +pub struct MockAuthorshipProvider; + +impl AuthorshipInfo for MockAuthorshipProvider { + fn author() -> Option { + Some(U256::from(MOCK_BLOCK_BUILDER)) + } +} + +parameter_types! { + pub const InitialMinAllowedWeights: u16 = 0; + pub const InitialEmissionValue: u16 = 0; + pub BlockWeights: limits::BlockWeights = limits::BlockWeights::with_sensible_defaults( + Weight::from_parts(2_000_000_000_000, u64::MAX), + Perbill::from_percent(75), + ); + pub const ExistentialDeposit: Balance = TaoBalance::new(100); + pub const TransactionByteFee: Balance = TaoBalance::new(100); + pub const SDebug:u64 = 1; + pub const InitialRho: u16 = 30; + pub const InitialAlphaSigmoidSteepness: i16 = 1000; + pub const InitialKappa: u16 = 32_767; + pub const InitialTempo: u16 = 360; + pub const SelfOwnership: u64 = 2; + pub const InitialImmunityPeriod: u16 = 2; + pub const InitialMinAllowedUids: u16 = 2; + pub const InitialMaxAllowedUids: u16 = 256; + pub const InitialBondsMovingAverage: u64 = 900_000; + pub const InitialBondsPenalty:u16 = u16::MAX; + pub const InitialBondsResetOn: bool = false; + pub const InitialStakePruningMin: u16 = 0; + pub const InitialFoundationDistribution: u64 = 0; + pub const InitialDefaultDelegateTake: u16 = 11_796; // 18%, same as in production + pub const InitialMinDelegateTake: u16 = 5_898; // 9%; + pub const InitialDefaultChildKeyTake: u16 = 0 ;// 0 % + pub const InitialMinChildKeyTake: u16 = 0; // 0 %; + pub const InitialMaxChildKeyTake: u16 = 11_796; // 18 %; + pub const InitialWeightsVersionKey: u16 = 0; + pub const InitialServingRateLimit: u64 = 0; // No limit. + pub const InitialTxRateLimit: u64 = 0; // Disable rate limit for testing + pub const InitialTxDelegateTakeRateLimit: u64 = 1; // 1 block take rate limit for testing + pub const InitialTxChildKeyTakeRateLimit: u64 = 1; // 1 block take rate limit for testing + pub const InitialBurn: u64 = 0; + pub const InitialMinBurn: u64 = 500_000; + pub const InitialMaxBurn: u64 = 1_000_000_000; + pub const MinBurnUpperBound: TaoBalance = TaoBalance::new(1_000_000_000); // 1 TAO + pub const MaxBurnLowerBound: TaoBalance = TaoBalance::new(100_000_000); // 0.1 TAO + pub const InitialValidatorPruneLen: u64 = 0; + pub const InitialScalingLawPower: u16 = 50; + pub const InitialMaxAllowedValidators: u16 = 100; + pub const InitialIssuance: u64 = 0; + pub const InitialDifficulty: u64 = 10000; + pub const InitialActivityCutoff: u16 = 5000; + pub const InitialAdjustmentInterval: u16 = 100; + pub const InitialAdjustmentAlpha: u64 = 0; // no weight to previous value. + pub const InitialMaxRegistrationsPerBlock: u16 = 3; + pub const InitialTargetRegistrationsPerInterval: u16 = 2; + pub const InitialPruningScore : u16 = u16::MAX; + pub const InitialRegistrationRequirement: u16 = u16::MAX; // Top 100% + pub const InitialMinDifficulty: u64 = 1; + pub const InitialMaxDifficulty: u64 = u64::MAX; + pub const InitialRAORecycledForRegistration: u64 = 0; + pub const InitialNetworkImmunityPeriod: u64 = 1_296_000; + pub const InitialNetworkMinLockCost: u64 = 100_000_000_000; + pub const InitialSubnetOwnerCut: u16 = 0; // 0%. 100% of rewards go to validators + miners. + pub const InitialNetworkLockReductionInterval: u64 = 2; // 2 blocks. + pub const InitialNetworkRateLimit: u64 = 0; + pub const InitialKeySwapCost: u64 = 1_000_000_000; + pub const InitialAlphaHigh: u16 = 58982; // Represents 0.9 as per the production default + pub const InitialAlphaLow: u16 = 45875; // Represents 0.7 as per the production default + pub const InitialLiquidAlphaOn: bool = false; // Default value for LiquidAlphaOn + pub const InitialYuma3On: bool = false; // Default value for Yuma3On + pub const InitialColdkeySwapAnnouncementDelay: u64 = 50; + pub const InitialColdkeySwapReannouncementDelay: u64 = 10; + pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days + pub const InitialTaoWeight: u64 = 0; // 100% global weight. + pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks + pub const InitialStartCallDelay: u64 = 0; // 0 days + 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; + pub const LeaseDividendsDistributionInterval: u32 = 100; + pub const MaxImmuneUidsPercentage: Percent = Percent::from_percent(80); + pub const EvmKeyAssociateRateLimit: u64 = 10; + pub const SubtensorPalletId: PalletId = PalletId(*b"subtensr"); + pub const BurnAccountId: PalletId = PalletId(*b"burntnsr"); +} + +impl crate::Config for Test { + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type InitialIssuance = InitialIssuance; + type SudoRuntimeCall = TestRuntimeCall; + type Scheduler = Scheduler; + type InitialMinAllowedWeights = InitialMinAllowedWeights; + type InitialEmissionValue = InitialEmissionValue; + type InitialTempo = InitialTempo; + type InitialDifficulty = InitialDifficulty; + type InitialAdjustmentInterval = InitialAdjustmentInterval; + type InitialAdjustmentAlpha = InitialAdjustmentAlpha; + type InitialTargetRegistrationsPerInterval = InitialTargetRegistrationsPerInterval; + type InitialRho = InitialRho; + type InitialAlphaSigmoidSteepness = InitialAlphaSigmoidSteepness; + type InitialKappa = InitialKappa; + type InitialMinAllowedUids = InitialMinAllowedUids; + type InitialMaxAllowedUids = InitialMaxAllowedUids; + type InitialValidatorPruneLen = InitialValidatorPruneLen; + type InitialScalingLawPower = InitialScalingLawPower; + type InitialImmunityPeriod = InitialImmunityPeriod; + type InitialActivityCutoff = InitialActivityCutoff; + type InitialMaxRegistrationsPerBlock = InitialMaxRegistrationsPerBlock; + type InitialPruningScore = InitialPruningScore; + type InitialBondsMovingAverage = InitialBondsMovingAverage; + type InitialBondsPenalty = InitialBondsPenalty; + type InitialBondsResetOn = InitialBondsResetOn; + type InitialMaxAllowedValidators = InitialMaxAllowedValidators; + type InitialDefaultDelegateTake = InitialDefaultDelegateTake; + type InitialMinDelegateTake = InitialMinDelegateTake; + type InitialDefaultChildKeyTake = InitialDefaultChildKeyTake; + type InitialMinChildKeyTake = InitialMinChildKeyTake; + type InitialMaxChildKeyTake = InitialMaxChildKeyTake; + type InitialTxChildKeyTakeRateLimit = InitialTxChildKeyTakeRateLimit; + type InitialWeightsVersionKey = InitialWeightsVersionKey; + type InitialMaxDifficulty = InitialMaxDifficulty; + type InitialMinDifficulty = InitialMinDifficulty; + type InitialServingRateLimit = InitialServingRateLimit; + type InitialTxRateLimit = InitialTxRateLimit; + type InitialTxDelegateTakeRateLimit = InitialTxDelegateTakeRateLimit; + type InitialBurn = InitialBurn; + type InitialMaxBurn = InitialMaxBurn; + type InitialMinBurn = InitialMinBurn; + type MinBurnUpperBound = MinBurnUpperBound; + type MaxBurnLowerBound = MaxBurnLowerBound; + type InitialRAORecycledForRegistration = InitialRAORecycledForRegistration; + type InitialNetworkImmunityPeriod = InitialNetworkImmunityPeriod; + type InitialNetworkMinLockCost = InitialNetworkMinLockCost; + type InitialSubnetOwnerCut = InitialSubnetOwnerCut; + type InitialNetworkLockReductionInterval = InitialNetworkLockReductionInterval; + type InitialNetworkRateLimit = InitialNetworkRateLimit; + type KeySwapCost = InitialKeySwapCost; + type AlphaHigh = InitialAlphaHigh; + type AlphaLow = InitialAlphaLow; + type LiquidAlphaOn = InitialLiquidAlphaOn; + type Yuma3On = InitialYuma3On; + type Preimages = Preimage; + type InitialColdkeySwapAnnouncementDelay = InitialColdkeySwapAnnouncementDelay; + type InitialColdkeySwapReannouncementDelay = InitialColdkeySwapReannouncementDelay; + type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; + type InitialTaoWeight = InitialTaoWeight; + type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; + type InitialStartCallDelay = InitialStartCallDelay; + type SwapInterface = pallet_subtensor_swap::Pallet; + type KeySwapOnSubnetCost = InitialKeySwapOnSubnetCost; + type HotkeySwapOnSubnetInterval = HotkeySwapOnSubnetInterval; + type ProxyInterface = (); + type LeaseDividendsDistributionInterval = LeaseDividendsDistributionInterval; + type GetCommitments = (); + type MaxImmuneUidsPercentage = MaxImmuneUidsPercentage; + type CommitmentsInterface = CommitmentsI; + type AlphaAssets = AlphaAssets; + type EvmKeyAssociateRateLimit = EvmKeyAssociateRateLimit; + type AuthorshipProvider = MockAuthorshipProvider; + type SubtensorPalletId = SubtensorPalletId; + type BurnAccountId = BurnAccountId; + type WeightInfo = (); +} + +// Swap-related parameter types +parameter_types! { + pub const SwapProtocolId: PalletId = PalletId(*b"ten/swap"); + pub const SwapMaxFeeRate: u16 = 10000; // 15.26% + pub const SwapMaxPositions: u32 = 100; + pub const SwapMinimumLiquidity: u64 = 1_000; + pub const SwapMinimumReserve: NonZeroU64 = NonZeroU64::new(100).unwrap(); +} + +impl pallet_subtensor_swap::Config for Test { + type SubnetInfo = SubtensorModule; + type BalanceOps = SubtensorModule; + type ProtocolId = SwapProtocolId; + type TaoReserve = TaoBalanceReserve; + type AlphaReserve = AlphaBalanceReserve; + type MaxFeeRate = SwapMaxFeeRate; + type MaxPositions = SwapMaxPositions; + type MinimumLiquidity = SwapMinimumLiquidity; + type MinimumReserve = SwapMinimumReserve; + type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); +} + +pub struct OriginPrivilegeCmp; + +impl PrivilegeCmp for OriginPrivilegeCmp { + fn cmp_privilege(_left: &OriginCaller, _right: &OriginCaller) -> Option { + Some(Ordering::Less) + } +} + +pub struct CommitmentsI; +impl CommitmentsInterface for CommitmentsI { + fn purge_netuid(_netuid: NetUid) {} +} + +parameter_types! { + pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) * + BlockWeights::get().max_block; + pub const MaxScheduledPerBlock: u32 = 50; + pub const NoPreimagePostponement: Option = Some(10); +} + +impl pallet_scheduler::Config for Test { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeEvent = RuntimeEvent; + type PalletsOrigin = OriginCaller; + type RuntimeCall = RuntimeCall; + type MaximumWeight = MaximumSchedulerWeight; + type ScheduleOrigin = EnsureRoot; + type MaxScheduledPerBlock = MaxScheduledPerBlock; + type WeightInfo = pallet_scheduler::weights::SubstrateWeight; + type OriginPrivilegeCmp = OriginPrivilegeCmp; + type Preimages = Preimage; + type BlockNumberProvider = System; +} + +parameter_types! { + pub const PreimageMaxSize: u32 = 4096 * 1024; + pub const PreimageBaseDeposit: Balance = TaoBalance::new(1); + pub const PreimageByteDeposit: Balance = TaoBalance::new(1); +} + +impl pallet_preimage::Config for Test { + type WeightInfo = pallet_preimage::weights::SubstrateWeight; + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type ManagerOrigin = EnsureRoot; + type Consideration = (); +} + +parameter_types! { + pub const CrowdloanPalletId: PalletId = PalletId(*b"bt/cloan"); + pub const MinimumDeposit: u64 = 50; + pub const AbsoluteMinimumContribution: u64 = 10; + pub const MinimumBlockDuration: u64 = 20; + pub const MaximumBlockDuration: u64 = 100; + pub const RefundContributorsLimit: u32 = 5; + pub const MaxContributors: u32 = 10; +} + +impl pallet_crowdloan::Config for Test { + type PalletId = CrowdloanPalletId; + type Currency = Balances; + type RuntimeCall = RuntimeCall; + type WeightInfo = pallet_crowdloan::weights::SubstrateWeight; + type Preimages = Preimage; + type MinimumDeposit = MinimumDeposit; + type AbsoluteMinimumContribution = AbsoluteMinimumContribution; + type MinimumBlockDuration = MinimumBlockDuration; + type MaximumBlockDuration = MaximumBlockDuration; + type RefundContributorsLimit = RefundContributorsLimit; + type MaxContributors = MaxContributors; +} + +// Proxy Pallet config +parameter_types! { + // Set as 1 for testing purposes + pub const ProxyDepositBase: Balance = TaoBalance::new(1); + // Set as 1 for testing purposes + pub const ProxyDepositFactor: Balance = TaoBalance::new(1); + // Set as 20 for testing purposes + pub const MaxProxies: u32 = 20; // max num proxies per acct + // 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 = TaoBalance::new(1); + // Set as 1 for testing purposes + pub const AnnouncementDepositFactor: Balance = TaoBalance::new(1); +} + +impl pallet_proxy::Config for Test { + type RuntimeCall = RuntimeCall; + 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; +} + +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::{ + U256, + sr25519::{Public as Sr25519Public, Signature as Sr25519Signature}, + }; + use sp_runtime::{ + app_crypto::{app_crypto, sr25519}, + traits::IdentifyAccount, + }; + + app_crypto!(sr25519, KEY_TYPE); + + pub struct TestAuthId; + + impl frame_system::offchain::AppCrypto for TestAuthId { + type RuntimeAppPublic = Public; + type GenericSignature = Sr25519Signature; + type GenericPublic = Sr25519Public; + } + + impl IdentifyAccount for Public { + type AccountId = U256; + + fn into_account(self) -> U256 { + let mut bytes = [0u8; 32]; + bytes.copy_from_slice(self.as_ref()); + U256::from_big_endian(&bytes) + } + } +} + +pub type TestAuthId = test_crypto::TestAuthId; + +impl pallet_drand::Config for Test { + type AuthorityId = TestAuthId; + type Verifier = pallet_drand::verifier::QuicknetVerifier; + type UnsignedPriority = ConstU64<{ 1 << 20 }>; + type HttpFetchTimeout = ConstU64<1_000>; + type WeightInfo = (); +} + +impl frame_system::offchain::SigningTypes for Test { + type Public = test_crypto::Public; + type Signature = test_crypto::Signature; +} + +pub type UncheckedExtrinsic = sp_runtime::testing::TestXt; + +impl frame_system::offchain::CreateTransactionBase for Test +where + RuntimeCall: From, +{ + type Extrinsic = UncheckedExtrinsic; + type RuntimeCall = RuntimeCall; +} + +impl frame_system::offchain::CreateInherent for Test +where + RuntimeCall: From, +{ + fn create_bare(call: Self::RuntimeCall) -> Self::Extrinsic { + UncheckedExtrinsic::new_inherent(call) + } +} + +impl frame_system::offchain::CreateSignedTransaction for Test +where + RuntimeCall: From, +{ + fn create_signed_transaction< + C: frame_system::offchain::AppCrypto, + >( + call: >::RuntimeCall, + _public: Self::Public, + _account: Self::AccountId, + nonce: Self::Nonce, + ) -> Option { + Some(UncheckedExtrinsic::new_signed(call, nonce.into(), (), ())) + } +} + +static TEST_LOGS_INIT: OnceLock<()> = OnceLock::new(); + +pub fn init_logs_for_tests() { + if TEST_LOGS_INIT.get().is_some() { + return; + } + + // RUST_LOG (full syntax) or "off" if unset + let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("off")); + + // Bridge log -> tracing (ok if already set) + let _ = tracing_log::LogTracer::init(); + + // Simple formatter + let fmt_layer = tracing_subscriber::fmt::layer() + .with_ansi(false) + .with_target(true) + .with_level(true) + .without_time(); + + let _ = tracing_subscriber::registry() + .with(filter) + .with(fmt_layer) + .try_init(); + + let _ = TEST_LOGS_INIT.set(()); +} + +#[allow(dead_code)] +// Build genesis storage according to the mock runtime. +pub fn new_test_ext(block_number: BlockNumber) -> sp_io::TestExternalities { + init_logs_for_tests(); + let t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(block_number)); + ext +} + +#[allow(dead_code)] +pub fn add_network(netuid: NetUid, tempo: u16, _modality: u16) { + SubtensorModule::init_new_network(netuid, tempo); + SubtensorModule::set_network_registration_allowed(netuid, true); + FirstEmissionBlockNumber::::insert(netuid, 1); + SubtokenEnabled::::insert(netuid, true); + + // make interval 1 block so tests can register by stepping 1 block. + BurnHalfLife::::insert(netuid, 1); + BurnIncreaseMult::::insert(netuid, U64F64::from_num(1)); +} + +#[allow(dead_code)] +pub fn add_balance_to_coldkey_account(coldkey: &U256, tao: TaoBalance) { + let ed = ExistentialDeposit::get(); + if tao >= ed { + let credit = SubtensorModule::mint_tao(tao); + let _ = SubtensorModule::spend_tao(coldkey, credit, tao).unwrap(); + } +} diff --git a/pallets/subtensor/src/tests/mod.rs b/pallets/subtensor/src/tests/mod.rs index 7e0c477c56..f3d363ec29 100644 --- a/pallets/subtensor/src/tests/mod.rs +++ b/pallets/subtensor/src/tests/mod.rs @@ -11,10 +11,12 @@ mod epoch; mod epoch_logs; mod evm; mod leasing; +mod locks; mod math; mod mechanism; mod migration; pub(crate) mod mock; +pub(crate) mod mock_high_ed; mod move_stake; mod networks; mod neuron_info; @@ -28,6 +30,7 @@ mod subnet_emissions; mod swap_coldkey; mod swap_hotkey; mod swap_hotkey_with_subnet; +mod tao; mod uids; mod voting_power; mod weights; diff --git a/pallets/subtensor/src/tests/move_stake.rs b/pallets/subtensor/src/tests/move_stake.rs index 294dc79661..a991df20a5 100644 --- a/pallets/subtensor/src/tests/move_stake.rs +++ b/pallets/subtensor/src/tests/move_stake.rs @@ -26,8 +26,9 @@ fn test_do_move_success() { let stake_amount = DefaultMinStake::::get() * 10.into(); // Set up initial stake - SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); - SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); + add_balance_to_coldkey_account(&coldkey, stake_amount); SubtensorModule::stake_into_subnet( &origin_hotkey, &coldkey, @@ -78,7 +79,7 @@ fn test_do_move_success() { // 2. test_do_move_different_subnets // Description: Test moving stake between two hotkeys in different subnets -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test move -- test_do_move_different_subnets --exact --nocapture +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::move_stake::test_do_move_different_subnets --exact --show-output --nocapture #[test] fn test_do_move_different_subnets() { new_test_ext(1).execute_with(|| { @@ -103,8 +104,9 @@ fn test_do_move_different_subnets() { ); // Set up initial stake and subnets - SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); - SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); + add_balance_to_coldkey_account(&coldkey, stake_amount.into()); SubtensorModule::stake_into_subnet( &origin_hotkey, &coldkey, @@ -173,6 +175,7 @@ fn test_do_move_nonexistent_subnet() { mock::setup_reserves(origin_netuid, reserve.into(), reserve.into()); // Set up initial stake + add_balance_to_coldkey_account(&coldkey, stake_amount.into()); SubtensorModule::stake_into_subnet( &origin_hotkey, &coldkey, @@ -269,14 +272,17 @@ fn test_do_move_nonexistent_destination_hotkey() { let coldkey = U256::from(1); let origin_hotkey = U256::from(2); let nonexistent_destination_hotkey = U256::from(99); // Assuming this hotkey doesn't exist - let netuid = NetUid::from(1); + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); let stake_amount = 1_000_000; let reserve = stake_amount * 1000; mock::setup_reserves(netuid, reserve.into(), reserve.into()); // Set up initial stake - SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); + add_balance_to_coldkey_account(&coldkey, stake_amount.into()); let alpha = SubtensorModule::stake_into_subnet( &origin_hotkey, &coldkey, @@ -342,6 +348,7 @@ fn test_do_move_partial_stake() { let total_stake = DefaultMinStake::::get().to_u64() * 20; // Set up initial stake + add_balance_to_coldkey_account(&coldkey, total_stake.into()); SubtensorModule::stake_into_subnet( &origin_hotkey, &coldkey, @@ -360,8 +367,9 @@ fn test_do_move_partial_stake() { // Move partial stake let alpha_moved = AlphaBalance::from(alpha.to_u64() * portion_moved / 10); - SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); - SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); + let _ = + SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); assert_ok!(SubtensorModule::do_move_stake( RuntimeOrigin::signed(coldkey), origin_hotkey, @@ -409,8 +417,9 @@ fn test_do_move_multiple_times() { let initial_stake = DefaultMinStake::::get().to_u64() * 10; // Set up initial stake - SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey1); - SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey2); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey1); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey2); + add_balance_to_coldkey_account(&coldkey, initial_stake.into()); SubtensorModule::stake_into_subnet( &hotkey1, &coldkey, @@ -476,13 +485,16 @@ fn test_do_move_wrong_origin() { let wrong_coldkey = U256::from(99); let origin_hotkey = U256::from(2); let destination_hotkey = U256::from(3); - let netuid = NetUid::from(1); + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); let stake_amount = DefaultMinStake::::get().to_u64() * 10; let reserve = stake_amount * 1000; mock::setup_reserves(netuid, reserve.into(), reserve.into()); // Set up initial stake + add_balance_to_coldkey_account(&coldkey, stake_amount.into()); SubtensorModule::stake_into_subnet( &origin_hotkey, &coldkey, @@ -501,8 +513,8 @@ fn test_do_move_wrong_origin() { // Attempt to move stake with wrong origin add_network(netuid, 1, 0); - SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); - SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); assert_err!( SubtensorModule::do_move_stake( RuntimeOrigin::signed(wrong_coldkey), @@ -549,7 +561,8 @@ fn test_do_move_same_hotkey_fails() { let stake_amount = DefaultMinStake::::get().to_u64() * 10; // Set up initial stake - SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + add_balance_to_coldkey_account(&coldkey, stake_amount.into()); SubtensorModule::stake_into_subnet( &hotkey, &coldkey, @@ -599,8 +612,9 @@ fn test_do_move_event_emission() { let stake_amount = DefaultMinStake::::get().to_u64() * 10; // Set up initial stake - SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); - SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); + add_balance_to_coldkey_account(&coldkey, stake_amount.into()); SubtensorModule::stake_into_subnet( &origin_hotkey, &coldkey, @@ -662,6 +676,7 @@ fn test_do_move_storage_updates() { let stake_amount = DefaultMinStake::::get().to_u64() * 10; // Set up initial stake + add_balance_to_coldkey_account(&coldkey, stake_amount.into()); SubtensorModule::stake_into_subnet( &origin_hotkey, &coldkey, @@ -674,8 +689,8 @@ fn test_do_move_storage_updates() { .unwrap(); // Move stake - SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); - SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &origin_hotkey, &coldkey, @@ -725,8 +740,9 @@ fn test_move_full_amount_same_netuid() { let origin_hotkey = U256::from(2); let destination_hotkey = U256::from(3); let stake_amount = DefaultMinStake::::get().to_u64() * 10; - SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); - SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); + add_balance_to_coldkey_account(&coldkey, stake_amount.into()); // Set up initial stake SubtensorModule::stake_into_subnet( @@ -790,8 +806,9 @@ fn test_do_move_max_values() { let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); // Set up initial stake with maximum value - SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); - SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); + add_balance_to_coldkey_account(&coldkey, max_stake.into()); // Add lots of liquidity to bypass low liquidity check let reserve = u64::MAX / 1000; @@ -859,10 +876,7 @@ fn test_moving_too_little_unstakes() { let (_, fee) = mock::swap_tao_to_alpha(netuid, amount); - SubtensorModule::add_balance_to_coldkey_account( - &coldkey_account_id, - amount + (fee * 2).into(), - ); + add_balance_to_coldkey_account(&coldkey_account_id, amount + (fee * 2).into()); assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(coldkey_account_id), @@ -901,8 +915,9 @@ fn test_do_transfer_success() { let stake_amount = DefaultMinStake::::get().to_u64() * 10; // 3. Set up initial stake: (origin_coldkey, hotkey) on netuid. - SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); - SubtensorModule::create_account_if_non_existent(&destination_coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&destination_coldkey, &hotkey); + add_balance_to_coldkey_account(&origin_coldkey, stake_amount.into()); SubtensorModule::stake_into_subnet( &hotkey, &origin_coldkey, @@ -1011,7 +1026,8 @@ fn test_do_transfer_insufficient_stake() { let hotkey = U256::from(3); let stake_amount = DefaultMinStake::::get().to_u64() * 10; - SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); + add_balance_to_coldkey_account(&origin_coldkey, stake_amount.into()); SubtensorModule::stake_into_subnet( &hotkey, &origin_coldkey, @@ -1051,11 +1067,8 @@ fn test_do_transfer_wrong_origin() { let stake_amount = DefaultMinStake::::get().to_u64() * 10; let fee: u64 = 0; // FIXME: DefaultStakingFee is deprecated - SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); - SubtensorModule::add_balance_to_coldkey_account( - &origin_coldkey, - (stake_amount + fee).into(), - ); + let _ = SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); + add_balance_to_coldkey_account(&origin_coldkey, (stake_amount + fee).into()); SubtensorModule::stake_into_subnet( &hotkey, &origin_coldkey, @@ -1093,7 +1106,8 @@ fn test_do_transfer_minimum_stake_check() { let hotkey = U256::from(3); let stake_amount = DefaultMinStake::::get(); - SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); + add_balance_to_coldkey_account(&origin_coldkey, stake_amount.into()); SubtensorModule::stake_into_subnet( &hotkey, &origin_coldkey, @@ -1135,11 +1149,11 @@ fn test_do_transfer_different_subnets() { let stake_amount = DefaultMinStake::::get().to_u64() * 10; // 3. Create accounts if needed. - SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); - SubtensorModule::create_account_if_non_existent(&destination_coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&destination_coldkey, &hotkey); // 4. Deposit free balance so transaction fees do not reduce staked funds. - SubtensorModule::add_balance_to_coldkey_account(&origin_coldkey, 1_000_000_000.into()); + add_balance_to_coldkey_account(&origin_coldkey, 1_000_000_000.into()); // 5. Stake into the origin subnet. SubtensorModule::stake_into_subnet( @@ -1207,7 +1221,8 @@ fn test_do_swap_success() { let hotkey = U256::from(2); let stake_amount = DefaultMinStake::::get().to_u64() * 10; - SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + add_balance_to_coldkey_account(&coldkey, stake_amount.into()); SubtensorModule::stake_into_subnet( &hotkey, &coldkey, @@ -1262,7 +1277,7 @@ fn test_do_swap_nonexistent_subnet() { let nonexistent_netuid2 = NetUid::from(9999); let stake_amount = 1_000_000; - SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); assert_noop!( SubtensorModule::do_swap_stake( @@ -1315,7 +1330,8 @@ fn test_do_swap_insufficient_stake() { let stake_amount = DefaultMinStake::::get().to_u64() * 5; let attempted_swap = stake_amount * 2; - SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + add_balance_to_coldkey_account(&coldkey, stake_amount.into()); SubtensorModule::stake_into_subnet( &hotkey, &coldkey, @@ -1350,7 +1366,8 @@ fn test_do_swap_wrong_origin() { let hotkey = U256::from(3); let stake_amount = 100_000; - SubtensorModule::create_account_if_non_existent(&real_coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&real_coldkey, &hotkey); + add_balance_to_coldkey_account(&real_coldkey, stake_amount.into()); SubtensorModule::stake_into_subnet( &hotkey, &real_coldkey, @@ -1388,7 +1405,8 @@ fn test_do_swap_minimum_stake_check() { let total_stake = DefaultMinStake::::get(); let swap_amount = 1; - SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + add_balance_to_coldkey_account(&coldkey, total_stake); SubtensorModule::stake_into_subnet( &hotkey, &coldkey, @@ -1424,7 +1442,8 @@ fn test_do_swap_same_subnet() { let hotkey = U256::from(2); let stake_amount = DefaultMinStake::::get().to_u64() * 10; - SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + add_balance_to_coldkey_account(&coldkey, stake_amount.into()); SubtensorModule::stake_into_subnet( &hotkey, &coldkey, @@ -1469,7 +1488,8 @@ fn test_do_swap_partial_stake() { let hotkey = U256::from(2); let total_stake_tao = DefaultMinStake::::get().to_u64() * 10; - SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + add_balance_to_coldkey_account(&coldkey, total_stake_tao.into()); SubtensorModule::stake_into_subnet( &hotkey, &coldkey, @@ -1521,7 +1541,8 @@ fn test_do_swap_storage_updates() { let hotkey = U256::from(2); let stake_amount = DefaultMinStake::::get().to_u64() * 10; - SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + add_balance_to_coldkey_account(&coldkey, stake_amount.into()); SubtensorModule::stake_into_subnet( &hotkey, &coldkey, @@ -1581,7 +1602,8 @@ fn test_do_swap_multiple_times() { let hotkey = U256::from(2); let initial_stake = DefaultMinStake::::get().to_u64() * 10; - SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + add_balance_to_coldkey_account(&coldkey, initial_stake.into()); SubtensorModule::stake_into_subnet( &hotkey, &coldkey, @@ -1652,7 +1674,8 @@ fn test_do_swap_allows_non_owned_hotkey() { let foreign_coldkey = U256::from(3); let stake_amount = DefaultMinStake::::get().to_u64() * 10; - SubtensorModule::create_account_if_non_existent(&foreign_coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&foreign_coldkey, &hotkey); + add_balance_to_coldkey_account(&coldkey, stake_amount.into()); SubtensorModule::stake_into_subnet( &hotkey, &coldkey, @@ -1730,10 +1753,7 @@ fn test_move_stake_specific_stake_into_subnet_fail() { SubnetTAO::::insert(netuid, tao_in); // Give TAO balance to coldkey - SubtensorModule::add_balance_to_coldkey_account( - &coldkey_account_id, - (tao_staked + 1_000_000_000).into(), - ); + add_balance_to_coldkey_account(&coldkey_account_id, (tao_staked + 1_000_000_000).into()); // Setup Subnet pool for origin netuid SubnetAlphaIn::::insert(origin_netuid, alpha_in + 10_000_000.into()); @@ -1799,8 +1819,9 @@ fn test_transfer_stake_rate_limited() { let hotkey = U256::from(3); let stake_amount = DefaultMinStake::::get().to_u64() * 10; - SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); - SubtensorModule::create_account_if_non_existent(&destination_coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&destination_coldkey, &hotkey); + add_balance_to_coldkey_account(&origin_coldkey, stake_amount.into()); SubtensorModule::stake_into_subnet( &hotkey, &origin_coldkey, @@ -1844,8 +1865,9 @@ fn test_transfer_stake_doesnt_limit_destination_coldkey() { let hotkey = U256::from(3); let stake_amount = DefaultMinStake::::get().to_u64() * 10; - SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); - SubtensorModule::create_account_if_non_existent(&destination_coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&destination_coldkey, &hotkey); + add_balance_to_coldkey_account(&origin_coldkey, stake_amount.into()); SubtensorModule::stake_into_subnet( &hotkey, &origin_coldkey, @@ -1891,7 +1913,8 @@ fn test_swap_stake_limits_destination_netuid() { let hotkey = U256::from(3); let stake_amount = DefaultMinStake::::get().to_u64() * 10; - SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); + add_balance_to_coldkey_account(&origin_coldkey, stake_amount.into()); SubtensorModule::stake_into_subnet( &hotkey, &origin_coldkey, diff --git a/pallets/subtensor/src/tests/networks.rs b/pallets/subtensor/src/tests/networks.rs index c5107cffc5..c4efc75825 100644 --- a/pallets/subtensor/src/tests/networks.rs +++ b/pallets/subtensor/src/tests/networks.rs @@ -1,4 +1,4 @@ -#![allow(clippy::expect_used, clippy::indexing_slicing)] +#![allow(clippy::expect_used, clippy::indexing_slicing, clippy::unwrap_used)] use super::mock::*; use crate::migrations::migrate_network_immunity_period; @@ -31,14 +31,8 @@ fn test_registration_ok() { ); // registration economics changed. Ensure the coldkey has enough spendable balance - SubtensorModule::add_balance_to_coldkey_account( - &coldkey_account_id, - TaoBalance::from(reserve), - ); - SubtensorModule::add_balance_to_coldkey_account( - &hotkey_account_id, - TaoBalance::from(reserve), - ); + add_balance_to_coldkey_account(&coldkey_account_id, TaoBalance::from(reserve)); + add_balance_to_coldkey_account(&hotkey_account_id, TaoBalance::from(reserve)); let (nonce, work): (u64, Vec) = SubtensorModule::create_work_for_block_number( netuid, @@ -241,7 +235,7 @@ 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); - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &sh, &sc, net, @@ -674,6 +668,10 @@ fn dissolve_decrements_total_networks() { let hot = U256::from(42); let net = add_dynamic_network(&hot, &cold); + // Add 100 TAO to subnet account (lock) + let subnet_account = SubtensorModule::get_subnet_account_id(net).unwrap(); + add_balance_to_coldkey_account(&subnet_account, 100_000_000_000_u64.into()); + // Sanity: adding network increments the counter. assert_eq!(TotalNetworks::::get(), total_before + 1); @@ -748,8 +746,8 @@ fn destroy_alpha_out_multiple_stakers_pro_rata() { let s1: u64 = 3u64 * min_total_u64; let s2: u64 = 7u64 * min_total_u64; - SubtensorModule::add_balance_to_coldkey_account(&c1, (s1 + 50_000).into()); - SubtensorModule::add_balance_to_coldkey_account(&c2, (s2 + 50_000).into()); + add_balance_to_coldkey_account(&c1, (s1 + 50_000).into()); + add_balance_to_coldkey_account(&c2, (s2 + 50_000).into()); assert_ok!(SubtensorModule::do_add_stake( RuntimeOrigin::signed(c1), @@ -860,7 +858,7 @@ fn destroy_alpha_out_many_stakers_complex_distribution() { stake[i] = (i as u64 + 1u64) * min_amount_u64; // multiples of min_amount register_ok_neuron(netuid, hot[i], cold[i], 0); - SubtensorModule::add_balance_to_coldkey_account(&cold[i], (stake[i] + 100_000).into()); + add_balance_to_coldkey_account(&cold[i], (stake[i] + 100_000).into()); assert_ok!(SubtensorModule::do_add_stake( RuntimeOrigin::signed(cold[i]), @@ -987,7 +985,7 @@ fn destroy_alpha_out_refund_gating_by_registration_block() { // 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( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &other_hot, &other_cold, netuid, @@ -1053,7 +1051,7 @@ fn destroy_alpha_out_refund_gating_by_registration_block() { // 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( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &other_hot, &other_cold, netuid, @@ -1224,6 +1222,20 @@ fn prune_selection_complex_state_exhaustive() { System::set_block_number(imm + 6); let n6 = add_dynamic_network(&U256::from(106), &U256::from(206)); // immune at first + // Add 100 TAO to subnet accounts (lock) + let subnet_account1 = SubtensorModule::get_subnet_account_id(n1).unwrap(); + let subnet_account2 = SubtensorModule::get_subnet_account_id(n2).unwrap(); + let subnet_account3 = SubtensorModule::get_subnet_account_id(n3).unwrap(); + let subnet_account4 = SubtensorModule::get_subnet_account_id(n4).unwrap(); + let subnet_account5 = SubtensorModule::get_subnet_account_id(n5).unwrap(); + let subnet_account6 = SubtensorModule::get_subnet_account_id(n6).unwrap(); + add_balance_to_coldkey_account(&subnet_account1, 100_000_000_000_u64.into()); + add_balance_to_coldkey_account(&subnet_account2, 100_000_000_000_u64.into()); + add_balance_to_coldkey_account(&subnet_account3, 100_000_000_000_u64.into()); + add_balance_to_coldkey_account(&subnet_account4, 100_000_000_000_u64.into()); + add_balance_to_coldkey_account(&subnet_account5, 100_000_000_000_u64.into()); + add_balance_to_coldkey_account(&subnet_account6, 100_000_000_000_u64.into()); + // (Root is ignored by the selector.) let root = NetUid::ROOT; @@ -1332,7 +1344,7 @@ fn prune_selection_complex_state_exhaustive() { // Remove n5; now n6 (price=0) should be selected. // This validates robustness to holes / non-contiguous netuids. // --------------------------------------------------------------------- - SubtensorModule::do_dissolve_network(n5).expect("Expected not to panic"); + assert_ok!(SubtensorModule::do_dissolve_network(n5)); assert_eq!( SubtensorModule::get_network_to_prune(), Some(n6), @@ -1397,6 +1409,12 @@ fn register_network_prunes_and_recycles_netuid() { let n2_hot = U256::from(24); let n2 = add_dynamic_network(&n2_hot, &n2_cold); + // Add 100 TAO to subnet accounts (lock) + let subnet_account1 = SubtensorModule::get_subnet_account_id(n1).unwrap(); + add_balance_to_coldkey_account(&subnet_account1, 100_000_000_000_u64.into()); + let subnet_account2 = SubtensorModule::get_subnet_account_id(n2).unwrap(); + add_balance_to_coldkey_account(&subnet_account2, 100_000_000_000_u64.into()); + let imm = SubtensorModule::get_network_immunity_period(); System::set_block_number(imm + 100); @@ -1406,10 +1424,7 @@ fn register_network_prunes_and_recycles_netuid() { let new_cold = U256::from(30); let new_hot = U256::from(31); let needed: u64 = SubtensorModule::get_network_lock_cost().into(); - SubtensorModule::add_balance_to_coldkey_account( - &new_cold, - needed.saturating_mul(10).into(), - ); + add_balance_to_coldkey_account(&new_cold, needed.saturating_mul(10).into()); assert_ok!(SubtensorModule::do_register_network( RuntimeOrigin::signed(new_cold), @@ -1665,7 +1680,7 @@ fn test_migrate_network_immunity_period() { // let coldkey_account_id = U256::from(0); // Neighbour of the beast, har har // let new_network_owner_account_id = U256::from(2); // -// SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, 1000000000000000); +// add_balance_to_coldkey_account(&coldkey_account_id, 1000000000000000); // let (nonce, work): (u64, Vec) = SubtensorModule::create_work_for_block_number( // netuid, @@ -1873,7 +1888,7 @@ fn massive_dissolve_refund_and_reregistration_flow_is_lossless_and_cleans_state( // 3) LPs per net: register each (hot, cold), massive τ prefund, and stake // ──────────────────────────────────────────────────────────────────── for &cold in cold_lps.iter() { - SubtensorModule::add_balance_to_coldkey_account(&cold, u64::MAX.into()); + add_balance_to_coldkey_account(&cold, 1_000_000_000_000_u64.into()); } // τ balances before LP adds (after staking): @@ -2148,6 +2163,10 @@ fn dissolve_clears_all_mechanism_scoped_maps_for_all_mechanisms() { let owner_hot = U256::from(456); let net = add_dynamic_network(&owner_hot, &owner_cold); + // Add 100 TAO to subnet account (lock) + let subnet_account = SubtensorModule::get_subnet_account_id(net).unwrap(); + add_balance_to_coldkey_account(&subnet_account, 100_000_000_000_u64.into()); + // We'll use two mechanisms for this subnet. MechanismCountCurrent::::insert(net, MechId::from(2)); let m0 = MechId::from(0u8); @@ -2405,10 +2424,7 @@ fn register_network_seeds_first_subnet_from_fallback_price_one_and_keeps_lock_in assert_eq!(expected_owner_alpha_u64, owner_alpha_tao_equivalent_u64); assert_eq!(expected_recycled, TaoBalance::ZERO); - SubtensorModule::add_balance_to_coldkey_account( - &new_cold, - lock_cost_u64.saturating_mul(2).into(), - ); + add_balance_to_coldkey_account(&new_cold, lock_cost_u64.saturating_mul(2).into()); assert_ok!(SubtensorModule::do_register_network( RuntimeOrigin::signed(new_cold), @@ -2501,12 +2517,7 @@ fn register_network_seeds_new_subnet_from_even_median_snapshot() { ); let expected_owner_alpha: AlphaBalance = expected_owner_alpha_u64.into(); - let expected_recycled: TaoBalance = lock_cost_u64.saturating_sub(total_pool_tao_u64).into(); - - SubtensorModule::add_balance_to_coldkey_account( - &new_cold, - lock_cost_u64.saturating_mul(2).into(), - ); + add_balance_to_coldkey_account(&new_cold, lock_cost_u64.saturating_mul(2).into()); assert_ok!(SubtensorModule::do_register_network( RuntimeOrigin::signed(new_cold), @@ -2546,10 +2557,6 @@ fn register_network_seeds_new_subnet_from_even_median_snapshot() { TotalHotkeyAlpha::::get(new_hot, new_netuid), expected_owner_alpha ); - assert_eq!( - RAORecycledForRegistration::::get(new_netuid), - expected_recycled - ); // The new subnet is seeded from the pre-registration median snapshot, // so it is no longer initialized at the old 1:1 seed price. @@ -2632,7 +2639,7 @@ fn register_network_non_associated_hotkey_does_not_withdraw_or_write_owner_alpha let would_be_netuid = SubtensorModule::get_next_netuid(); let lock_cost_u64: u64 = SubtensorModule::get_network_lock_cost().into(); - SubtensorModule::add_balance_to_coldkey_account(&attacker_cold, lock_cost_u64.into()); + add_balance_to_coldkey_account(&attacker_cold, lock_cost_u64.into()); let attacker_balance_before = SubtensorModule::get_coldkey_balance(&attacker_cold); assert_err!( diff --git a/pallets/subtensor/src/tests/recycle_alpha.rs b/pallets/subtensor/src/tests/recycle_alpha.rs index 39fcfcaeaa..3da1112972 100644 --- a/pallets/subtensor/src/tests/recycle_alpha.rs +++ b/pallets/subtensor/src/tests/recycle_alpha.rs @@ -23,7 +23,7 @@ fn test_recycle_success() { Balances::make_free_balance_be(&coldkey, initial_balance.into()); // associate coldkey and hotkey - SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); register_ok_neuron(netuid, hotkey, coldkey, 0); assert!(SubtensorModule::if_subnet_exist(netuid)); @@ -79,7 +79,7 @@ fn test_recycle_two_stakers() { Balances::make_free_balance_be(&coldkey, initial_balance.into()); // associate coldkey and hotkey - SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); register_ok_neuron(netuid, hotkey, coldkey, 0); assert!(SubtensorModule::if_subnet_exist(netuid)); @@ -149,7 +149,7 @@ fn test_recycle_staker_is_nominator() { Balances::make_free_balance_be(&coldkey, initial_balance.into()); // associate coldkey and hotkey - SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); register_ok_neuron(netuid, hotkey, coldkey, 0); assert!(SubtensorModule::if_subnet_exist(netuid)); @@ -222,7 +222,7 @@ fn test_burn_success() { Balances::make_free_balance_be(&coldkey, initial_balance.into()); // associate coldkey and hotkey - SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); register_ok_neuron(netuid, hotkey, coldkey, 0); assert!(SubtensorModule::if_subnet_exist(netuid)); @@ -278,7 +278,7 @@ fn test_burn_staker_is_nominator() { Balances::make_free_balance_be(&coldkey, initial_balance.into()); // associate coldkey and hotkey - SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); register_ok_neuron(netuid, hotkey, coldkey, 0); assert!(SubtensorModule::if_subnet_exist(netuid)); @@ -348,7 +348,7 @@ fn test_burn_two_stakers() { Balances::make_free_balance_be(&coldkey, initial_balance.into()); // associate coldkey and hotkey - SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); register_ok_neuron(netuid, hotkey, coldkey, 0); assert!(SubtensorModule::if_subnet_exist(netuid)); @@ -419,7 +419,7 @@ fn test_recycle_errors() { let initial_balance = 1_000_000_000; Balances::make_free_balance_be(&coldkey, initial_balance.into()); - SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); register_ok_neuron(netuid, hotkey, coldkey, 0); let stake_amount = 200_000; @@ -491,7 +491,7 @@ fn test_burn_errors() { let initial_balance = 1_000_000_000; Balances::make_free_balance_be(&coldkey, initial_balance.into()); - SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); register_ok_neuron(netuid, hotkey, coldkey, 0); let stake_amount = 200_000; @@ -654,7 +654,7 @@ fn test_add_stake_burn_success() { (amount * 10_000_000).into(), ); - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount.into()); + add_balance_to_coldkey_account(&coldkey_account_id, amount.into()); // Check we have zero staked before transfer assert_eq!( @@ -724,7 +724,7 @@ fn test_add_stake_burn_with_limit_success() { assert_eq!(current_price, U96F32::from_num(1.0)); // Give coldkey sufficient balance - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount.into()); + add_balance_to_coldkey_account(&coldkey_account_id, amount.into()); let initial_balance = SubtensorModule::get_coldkey_balance(&coldkey_account_id); @@ -774,7 +774,7 @@ fn test_add_stake_burn_with_limit_success() { } #[test] -fn test_add_stake_burn_non_owner_fails() { +fn test_add_stake_burn_non_owner_succeeds() { new_test_ext(1).execute_with(|| { let hotkey_account_id = U256::from(1); let coldkey_account_id = U256::from(2); @@ -791,19 +791,32 @@ fn test_add_stake_burn_non_owner_fails() { ); // Give non-owner some balance - SubtensorModule::add_balance_to_coldkey_account(&non_owner_coldkey, amount.into()); + add_balance_to_coldkey_account(&non_owner_coldkey, amount.into()); - // Non-owner trying to call add_stake_burn should fail with BadOrigin - assert_noop!( - SubtensorModule::add_stake_burn( - RuntimeOrigin::signed(non_owner_coldkey), - hotkey_account_id, - netuid, - amount.into(), - None, + // Any signed origin can atomically stake and burn. + assert_ok!(SubtensorModule::add_stake_burn( + RuntimeOrigin::signed(non_owner_coldkey), + hotkey_account_id, + netuid, + amount.into(), + None, + )); + + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_account_id, + &non_owner_coldkey, + netuid ), - DispatchError::BadOrigin + AlphaBalance::ZERO ); + + assert!(System::events().iter().any(|e| { + matches!( + &e.event, + RuntimeEvent::SubtensorModule(Event::AddStakeBurn { .. }) + ) + })); }); } @@ -815,7 +828,7 @@ fn test_add_stake_burn_nonexistent_subnet_fails() { let amount = DefaultMinStake::::get().to_u64() * 10; // Give some balance - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount.into()); + add_balance_to_coldkey_account(&coldkey_account_id, amount.into()); // Try to call add_stake_burn on non-existent subnet let nonexistent_netuid = NetUid::from(999); @@ -827,7 +840,7 @@ fn test_add_stake_burn_nonexistent_subnet_fails() { amount.into(), None, ), - DispatchError::BadOrigin + Error::::SubnetNotExists ); }); } @@ -861,66 +874,3 @@ fn test_add_stake_burn_insufficient_balance_fails() { ); }); } - -#[test] -fn test_add_stake_burn_rate_limit_exceeded() { - new_test_ext(1).execute_with(|| { - let hotkey_account_id = U256::from(533453); - let coldkey_account_id = U256::from(55453); - let amount: u64 = 10_000_000_000; // 10 TAO - - // Add network - let netuid = add_dynamic_network(&hotkey_account_id, &coldkey_account_id); - - // Setup reserves with large liquidity - let tao_reserve = TaoBalance::from(1_000_000_000_000_u64); - let alpha_in = AlphaBalance::from(1_000_000_000_000_u64); - mock::setup_reserves(netuid, tao_reserve, alpha_in); - - // Give coldkey sufficient balance for multiple "add stake and burn" operations. - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, (amount * 10).into()); - - assert_eq!( - SubtensorModule::get_rate_limited_last_block(&RateLimitKey::AddStakeBurn(netuid)), - 0 - ); - - // First "add stake and burn" should succeed - assert_ok!(SubtensorModule::add_stake_burn( - RuntimeOrigin::signed(coldkey_account_id), - hotkey_account_id, - netuid, - amount.into(), - None, - )); - - assert_eq!( - SubtensorModule::get_rate_limited_last_block(&RateLimitKey::AddStakeBurn(netuid)), - SubtensorModule::get_current_block_as_u64() - ); - - // Second "add stake and burn" immediately after should fail due to rate limit - assert_noop!( - SubtensorModule::add_stake_burn( - RuntimeOrigin::signed(coldkey_account_id), - hotkey_account_id, - netuid, - amount.into(), - None, - ), - Error::::AddStakeBurnRateLimitExceeded - ); - - // After stepping past the rate limit, "add stake and burn" should succeed again - let rate_limit = TransactionType::AddStakeBurn.rate_limit_on_subnet::(netuid); - step_block(rate_limit as u16); - - assert_ok!(SubtensorModule::add_stake_burn( - RuntimeOrigin::signed(coldkey_account_id), - hotkey_account_id, - netuid, - amount.into(), - None, - )); - }); -} diff --git a/pallets/subtensor/src/tests/registration.rs b/pallets/subtensor/src/tests/registration.rs index c3cd3acb3c..cd3d040bae 100644 --- a/pallets/subtensor/src/tests/registration.rs +++ b/pallets/subtensor/src/tests/registration.rs @@ -46,12 +46,13 @@ fn test_registration_ok() { mock::setup_reserves(netuid, DEFAULT_RESERVE.into(), DEFAULT_RESERVE.into()); // Make burn small and stable for this test. - SubtensorModule::set_burn(netuid, 1_000u64.into()); + let burn = 1_000_u64; + SubtensorModule::set_burn(netuid, burn.into()); let hotkey = U256::from(1); let coldkey = U256::from(667); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, 50_000.into()); + add_balance_to_coldkey_account(&coldkey, 50_000.into()); assert_ok!(SubtensorModule::burned_register( <::RuntimeOrigin>::signed(coldkey), @@ -77,6 +78,9 @@ fn test_registration_ok() { SubtensorModule::get_stake_for_uid_and_subnetwork(netuid, uid), AlphaBalance::ZERO ); + + // TAO inflow recorded + assert_eq!(SubnetTaoFlow::::get(netuid), burn as i64); }); } @@ -159,7 +163,7 @@ fn test_registration_not_enough_balance() { let hotkey = U256::from(1); let coldkey = U256::from(667); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, 9_999.into()); + add_balance_to_coldkey_account(&coldkey, 9_999.into()); let result = SubtensorModule::burned_register( <::RuntimeOrigin>::signed(coldkey), @@ -189,7 +193,7 @@ fn test_registration_non_associated_coldkey() { Owner::::insert(hotkey, true_owner); // Attacker has enough funds, but doesn't own the hotkey. - SubtensorModule::add_balance_to_coldkey_account(&attacker, 50_000.into()); + add_balance_to_coldkey_account(&attacker, 50_000.into()); let result = SubtensorModule::burned_register( <::RuntimeOrigin>::signed(attacker), @@ -214,7 +218,7 @@ fn test_registration_without_neuron_slot_doesnt_burn() { let hotkey = U256::from(1); let coldkey = U256::from(667); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, 10_000.into()); + add_balance_to_coldkey_account(&coldkey, 10_000.into()); let before = SubtensorModule::get_coldkey_balance(&coldkey); // No slots => should fail before burning. @@ -245,7 +249,7 @@ fn test_registration_already_active_hotkey_error() { let coldkey = U256::from(667); let hotkey = U256::from(1); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1_000_000.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000.into()); assert_ok!(SubtensorModule::burned_register( <::RuntimeOrigin>::signed(coldkey), @@ -292,7 +296,7 @@ fn test_burn_decay() { let coldkey = U256::from(100); let hotkey = U256::from(200); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1_000_000.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000.into()); // Register in this block. Burn updates immediately now. assert_ok!(SubtensorModule::burned_register( @@ -357,7 +361,7 @@ fn test_burn_min_and_max_clamps_prevent_zero_stuck_and_cap_bump() { // Register now; bump should apply immediately but be capped by max burn. let coldkey = U256::from(1); let hotkey = U256::from(2); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1_000_000u64.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000u64.into()); assert_ok!(SubtensorModule::burned_register( <::RuntimeOrigin>::signed(coldkey), @@ -390,7 +394,7 @@ fn test_registration_increases_recycled_rao_per_subnet() { SubtensorModule::set_burn(netuid, 1_000u64.into()); let coldkey = U256::from(667); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1_000_000.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000.into()); // First registration let burn1 = SubtensorModule::get_burn(netuid); @@ -590,7 +594,7 @@ fn test_registration_get_neuron_metadata() { let hotkey = U256::from(1); let coldkey = U256::from(667); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, 100_000.into()); + add_balance_to_coldkey_account(&coldkey, 100_000.into()); assert_ok!(SubtensorModule::burned_register( <::RuntimeOrigin>::signed(coldkey), @@ -629,7 +633,7 @@ fn test_last_update_correctness() { LastUpdate::::remove(NetUidStorageIndex::from(netuid)); // Give enough balance for the burn path. - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, 10_000.into()); + add_balance_to_coldkey_account(&coldkey_account_id, 10_000.into()); // Register and ensure LastUpdate is expanded correctly. assert_ok!(SubtensorModule::burned_register( @@ -1312,7 +1316,7 @@ fn test_burned_register_immediately_bumps_price_many_multipliers_and_same_block_ let current: u64 = SubtensorModule::get_coldkey_balance(&coldkey).into(); if current < needed { - SubtensorModule::add_balance_to_coldkey_account(&coldkey, (needed - current).into()); + add_balance_to_coldkey_account(&coldkey, (needed - current).into()); } } diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index 7a7c5b69ac..0fe951a29b 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -58,7 +58,7 @@ fn test_add_stake_ok_no_emission() { ); // Give it some $$$ in his coldkey balance - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount.into()); + add_balance_to_coldkey_account(&coldkey_account_id, amount.into()); // Check we have zero staked before transfer assert_eq!( @@ -218,7 +218,7 @@ fn test_add_stake_not_registered_key_pair() { let hotkey_account_id = U256::from(54544); let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); let amount = DefaultMinStake::::get().to_u64() * 10; - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount.into()); + add_balance_to_coldkey_account(&coldkey_account_id, amount.into()); assert_err!( SubtensorModule::add_stake( RuntimeOrigin::signed(coldkey_account_id), @@ -241,7 +241,7 @@ fn test_add_stake_ok_neuron_does_not_belong_to_coldkey() { let stake = DefaultMinStake::::get() * 10.into(); // Give it some $$$ in his coldkey balance - SubtensorModule::add_balance_to_coldkey_account(&other_cold_key, stake.into()); + add_balance_to_coldkey_account(&other_cold_key, stake.into()); // Perform the request which is signed by a different cold key assert_ok!(SubtensorModule::add_stake( @@ -287,10 +287,7 @@ fn test_add_stake_total_issuance_no_change() { // Give it some $$$ in his coldkey balance let initial_balance = 10000; - SubtensorModule::add_balance_to_coldkey_account( - &coldkey_account_id, - initial_balance.into(), - ); + add_balance_to_coldkey_account(&coldkey_account_id, initial_balance.into()); // Check we have zero staked before transfer let initial_stake = SubtensorModule::get_total_stake_for_hotkey(&hotkey_account_id); @@ -551,7 +548,7 @@ fn test_add_stake_partial_below_min_stake_fails() { // Stake TAO amount is above min stake let min_stake = DefaultMinStake::::get(); let amount = min_stake.to_u64() * 2; - SubtensorModule::add_balance_to_coldkey_account( + add_balance_to_coldkey_account( &coldkey_account_id, TaoBalance::from(amount) + ExistentialDeposit::get(), ); @@ -761,8 +758,8 @@ fn test_add_stake_insufficient_liquidity() { let amount_staked = DefaultMinStake::::get().to_u64() * 10; let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); - SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, amount_staked.into()); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + add_balance_to_coldkey_account(&coldkey, amount_staked.into()); // Set the liquidity at lowest possible value so that all staking requests fail let reserve = u64::from(mock::SwapMinimumReserve::get()) - 1; @@ -792,8 +789,8 @@ fn test_add_stake_insufficient_liquidity_one_side_ok() { let amount_staked = DefaultMinStake::::get().to_u64() * 10; let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); - SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, amount_staked.into()); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + add_balance_to_coldkey_account(&coldkey, amount_staked.into()); // Set the liquidity at lowest possible value so that all staking requests fail let reserve_alpha = u64::from(mock::SwapMinimumReserve::get()); @@ -821,8 +818,8 @@ fn test_add_stake_insufficient_liquidity_one_side_fail() { let amount_staked = DefaultMinStake::::get().to_u64() * 10; let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); - SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, amount_staked.into()); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + add_balance_to_coldkey_account(&coldkey, amount_staked.into()); // Set the liquidity at lowest possible value so that all staking requests fail let reserve_alpha = u64::from(mock::SwapMinimumReserve::get()) - 1; @@ -852,8 +849,8 @@ fn test_remove_stake_insufficient_liquidity() { let amount_staked = DefaultMinStake::::get().to_u64() * 10; let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); - SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, amount_staked.into()); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + add_balance_to_coldkey_account(&coldkey, amount_staked.into()); // Simulate stake for hotkey let reserve = u64::MAX / 1000; @@ -908,7 +905,7 @@ fn test_remove_stake_total_issuance_no_change() { pallet_subtensor_swap::FeeRate::::insert(netuid, 0); // Ensure the coldkey has at least 'amount' more balance available for staking - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount.into()); + add_balance_to_coldkey_account(&coldkey_account_id, amount.into()); mock::setup_reserves(netuid, (amount * 100).into(), (amount * 100).into()); @@ -928,11 +925,7 @@ fn test_remove_stake_total_issuance_no_change() { let issuance_after_stake = Balances::total_issuance(); // Staking burns `amount` from balances issuance in this system design. - assert_abs_diff_eq!( - issuance_before, - issuance_after_stake + TaoBalance::from(amount), - epsilon = 1.into() - ); + assert_abs_diff_eq!(issuance_before, issuance_after_stake, epsilon = 1.into()); // Remove all stake let stake_alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -1019,7 +1012,7 @@ fn test_remove_prev_epoch_stake() { register_ok_neuron(netuid, hotkey_account_id, coldkey_account_id, 192213123); // Give it some $$$ in his coldkey balance - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount.into()); + add_balance_to_coldkey_account(&coldkey_account_id, amount.into()); AlphaDividendsPerSubnet::::insert(netuid, hotkey_account_id, alpha_divs); TotalHotkeyAlphaLastEpoch::::insert(hotkey_account_id, netuid, hotkey_alpha); let balance_before = SubtensorModule::get_coldkey_balance(&coldkey_account_id); @@ -1079,7 +1072,7 @@ fn test_staking_sets_div_variables() { register_ok_neuron(netuid, hotkey_account_id, coldkey_account_id, 192213123); // Give it some $$$ in his coldkey balance - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount.into()); + add_balance_to_coldkey_account(&coldkey_account_id, amount.into()); // Verify that divident variables are clear in the beginning assert_eq!( @@ -1151,7 +1144,7 @@ fn test_get_coldkey_balance_with_balance() { let amount = 1337; // Put the balance on the account - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount.into()); + add_balance_to_coldkey_account(&coldkey_account_id, amount.into()); let result = SubtensorModule::get_coldkey_balance(&coldkey_account_id); @@ -1379,7 +1372,7 @@ fn test_add_balance_to_coldkey_account_ok() { new_test_ext(1).execute_with(|| { let coldkey_id = U256::from(4444322); let amount = 50000; - SubtensorModule::add_balance_to_coldkey_account(&coldkey_id, amount.into()); + add_balance_to_coldkey_account(&coldkey_id, amount.into()); assert_eq!( SubtensorModule::get_coldkey_balance(&coldkey_id), amount.into() @@ -1395,17 +1388,17 @@ fn test_remove_balance_from_coldkey_account_ok() { new_test_ext(1).execute_with(|| { let coldkey_account_id = U256::from(434324); // Random let amount = 10000; // Arbitrary + let netuid = NetUid::from(1); // Put some $$ on the bank - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount.into()); + add_balance_to_coldkey_account(&coldkey_account_id, amount.into()); + NetworksAdded::::insert(netuid, true); assert_eq!( SubtensorModule::get_coldkey_balance(&coldkey_account_id), amount.into() ); // Should be able to withdraw without hassle - let result = SubtensorModule::remove_balance_from_coldkey_account( - &coldkey_account_id, - amount.into(), - ); + let result = + SubtensorModule::transfer_tao_to_subnet(netuid, &coldkey_account_id, amount.into()); assert!(result.is_ok()); }); } @@ -1416,13 +1409,14 @@ fn test_remove_balance_from_coldkey_account_failed() { let coldkey_account_id = U256::from(434324); // Random let amount = 10000; // Arbitrary + let netuid = NetUid::from(1); + NetworksAdded::::insert(netuid, true); + // Try to remove stake from the coldkey account. This should fail, // as there is no balance, nor does the account exist - let result = SubtensorModule::remove_balance_from_coldkey_account( - &coldkey_account_id, - amount.into(), - ); - assert_eq!(result, Err(Error::::ZeroBalanceAfterWithdrawn.into())); + let result = + SubtensorModule::transfer_tao_to_subnet(netuid, &coldkey_account_id, amount.into()); + assert_eq!(result, Err(Error::::InsufficientBalance.into())); }); } @@ -1454,7 +1448,7 @@ fn test_can_remove_balane_from_coldkey_account_ok() { let coldkey_id = U256::from(87987984); let initial_amount = 10000; let remove_amount = 5000; - SubtensorModule::add_balance_to_coldkey_account(&coldkey_id, initial_amount.into()); + add_balance_to_coldkey_account(&coldkey_id, initial_amount.into()); assert!(SubtensorModule::can_remove_balance_from_coldkey_account( &coldkey_id, remove_amount.into() @@ -1468,7 +1462,7 @@ fn test_can_remove_balance_from_coldkey_account_err_insufficient_balance() { let coldkey_id = U256::from(87987984); let initial_amount = 10000; let remove_amount = 20000; - SubtensorModule::add_balance_to_coldkey_account(&coldkey_id, initial_amount.into()); + add_balance_to_coldkey_account(&coldkey_id, initial_amount.into()); assert!(!SubtensorModule::can_remove_balance_from_coldkey_account( &coldkey_id, remove_amount.into() @@ -1689,7 +1683,7 @@ fn test_clear_small_nominations() { assert_eq!(SubtensorModule::get_owning_coldkey_for_hotkey(&hot2), cold2); // Add stake cold1 --> hot1 (non delegation.) - SubtensorModule::add_balance_to_coldkey_account(&cold1, init_balance); + add_balance_to_coldkey_account(&cold1, init_balance); assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(cold1), hot1, @@ -1713,7 +1707,7 @@ fn test_clear_small_nominations() { ); // Add stake cold2 --> hot1 (is delegation.) - SubtensorModule::add_balance_to_coldkey_account(&cold2, init_balance); + add_balance_to_coldkey_account(&cold2, init_balance); assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(cold2), hot1, @@ -1794,7 +1788,7 @@ fn test_delegate_take_can_be_decreased() { let coldkey0 = U256::from(3); // Add balance - SubtensorModule::add_balance_to_coldkey_account(&coldkey0, 100000.into()); + add_balance_to_coldkey_account(&coldkey0, 100000.into()); // Register the neuron to a new network let netuid = NetUid::from(1); @@ -1829,7 +1823,7 @@ fn test_can_set_min_take_ok() { let coldkey0 = U256::from(3); // Add balance - SubtensorModule::add_balance_to_coldkey_account(&coldkey0, 100000.into()); + add_balance_to_coldkey_account(&coldkey0, 100000.into()); // Register the neuron to a new network let netuid = NetUid::from(1); @@ -1861,7 +1855,7 @@ fn test_delegate_take_can_not_be_increased_with_decrease_take() { let coldkey0 = U256::from(3); // Add balance - SubtensorModule::add_balance_to_coldkey_account(&coldkey0, 100000.into()); + add_balance_to_coldkey_account(&coldkey0, 100000.into()); // Register the neuron to a new network let netuid = NetUid::from(1); @@ -1896,7 +1890,7 @@ fn test_delegate_take_can_be_increased() { let coldkey0 = U256::from(3); // Add balance - SubtensorModule::add_balance_to_coldkey_account(&coldkey0, 100000.into()); + add_balance_to_coldkey_account(&coldkey0, 100000.into()); // Register the neuron to a new network let netuid = NetUid::from(1); @@ -1931,7 +1925,7 @@ fn test_delegate_take_can_not_be_decreased_with_increase_take() { let coldkey0 = U256::from(3); // Add balance - SubtensorModule::add_balance_to_coldkey_account(&coldkey0, 100000.into()); + add_balance_to_coldkey_account(&coldkey0, 100000.into()); // Register the neuron to a new network let netuid = NetUid::from(1); @@ -1970,7 +1964,7 @@ fn test_delegate_take_can_be_increased_to_limit() { let coldkey0 = U256::from(3); // Add balance - SubtensorModule::add_balance_to_coldkey_account(&coldkey0, 100000.into()); + add_balance_to_coldkey_account(&coldkey0, 100000.into()); // Register the neuron to a new network let netuid = NetUid::from(1); @@ -2008,7 +2002,7 @@ fn test_delegate_take_can_not_be_increased_beyond_limit() { let coldkey0 = U256::from(3); // Add balance - SubtensorModule::add_balance_to_coldkey_account(&coldkey0, 100000.into()); + add_balance_to_coldkey_account(&coldkey0, 100000.into()); // Register the neuron to a new network let netuid = NetUid::from(1); @@ -2050,7 +2044,7 @@ fn test_rate_limits_enforced_on_increase_take() { let coldkey0 = U256::from(3); // Add balance - SubtensorModule::add_balance_to_coldkey_account(&coldkey0, 100000.into()); + add_balance_to_coldkey_account(&coldkey0, 100000.into()); // Register the neuron to a new network let netuid = NetUid::from(1); @@ -2110,7 +2104,7 @@ fn test_rate_limits_enforced_on_decrease_before_increase_take() { let coldkey0 = U256::from(3); // Add balance - SubtensorModule::add_balance_to_coldkey_account(&coldkey0, 100000.into()); + add_balance_to_coldkey_account(&coldkey0, 100000.into()); // Register the neuron to a new network let netuid = NetUid::from(1); @@ -2180,7 +2174,7 @@ fn test_get_total_delegated_stake_after_unstaking() { register_ok_neuron(netuid, delegate_hotkey, delegate_coldkey, 0); // Add balance to delegator - SubtensorModule::add_balance_to_coldkey_account(&delegator, initial_stake.into()); + add_balance_to_coldkey_account(&delegator, initial_stake.into()); // Delegate stake let (_, fee) = mock::swap_tao_to_alpha(netuid, initial_stake.into()); @@ -2283,7 +2277,7 @@ fn test_get_total_delegated_stake_single_delegator() { register_ok_neuron(netuid, delegate_hotkey, delegate_coldkey, 0); // Add stake from delegator - SubtensorModule::add_balance_to_coldkey_account(&delegator, stake_amount.into()); + add_balance_to_coldkey_account(&delegator, stake_amount.into()); let (_, fee) = mock::swap_tao_to_alpha(netuid, stake_amount.into()); @@ -2345,7 +2339,7 @@ fn test_get_alpha_share_stake_multiple_delegators() { register_ok_neuron(netuid, hotkey2, coldkey2, 0); // Add stake from delegator1 - SubtensorModule::add_balance_to_coldkey_account(&coldkey1, stake1 + existential_deposit); + add_balance_to_coldkey_account(&coldkey1, stake1 + existential_deposit); assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(coldkey1), hotkey1, @@ -2354,7 +2348,7 @@ fn test_get_alpha_share_stake_multiple_delegators() { )); // Add stake from delegator2 - SubtensorModule::add_balance_to_coldkey_account(&coldkey2, stake2 + existential_deposit); + add_balance_to_coldkey_account(&coldkey2, stake2 + existential_deposit); assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(coldkey2), hotkey2, @@ -2396,7 +2390,7 @@ fn test_get_total_delegated_stake_exclude_owner_stake() { remove_owner_registration_stake(netuid); // Add owner stake - SubtensorModule::add_balance_to_coldkey_account(&delegate_coldkey, owner_stake.into()); + add_balance_to_coldkey_account(&delegate_coldkey, owner_stake.into()); assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(delegate_coldkey), delegate_hotkey, @@ -2405,7 +2399,7 @@ fn test_get_total_delegated_stake_exclude_owner_stake() { )); // Add delegator stake - SubtensorModule::add_balance_to_coldkey_account(&delegator, delegator_stake.into()); + add_balance_to_coldkey_account(&delegator, delegator_stake.into()); let (_, fee) = mock::swap_tao_to_alpha(netuid, delegator_stake.into()); assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(delegator), @@ -2447,18 +2441,9 @@ fn test_mining_emission_distribution_validator_valiminer_miner() { 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(), - ); + add_balance_to_coldkey_account(&validator_coldkey, stake + ExistentialDeposit::get()); + add_balance_to_coldkey_account(&validator_miner_coldkey, stake + ExistentialDeposit::get()); + add_balance_to_coldkey_account(&miner_coldkey, stake + ExistentialDeposit::get()); SubtensorModule::set_weights_set_rate_limit(netuid, 0); step_block(subnet_tempo); SubnetOwnerCut::::set(0); @@ -2546,7 +2531,7 @@ fn test_staking_too_little_fails() { let netuid = add_dynamic_network(&hotkey_account_id, &coldkey_account_id); // Give it some $$$ in his coldkey balance - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount.into()); + add_balance_to_coldkey_account(&coldkey_account_id, amount.into()); // Coldkey / hotkey 0 decreases take to 5%. This should fail as the minimum take is 9% assert_err!( @@ -2574,11 +2559,11 @@ fn test_add_stake_fee_goes_to_subnet_tao() { let tao_to_stake = DefaultMinStake::::get() * 10.into(); let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); - SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); let subnet_tao_before = SubnetTAO::::get(netuid); // Add stake - SubtensorModule::add_balance_to_coldkey_account(&coldkey, tao_to_stake); + add_balance_to_coldkey_account(&coldkey, tao_to_stake); assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(coldkey), hotkey, @@ -2620,11 +2605,11 @@ fn test_remove_stake_fee_goes_to_subnet_tao() { let tao_to_stake = DefaultMinStake::::get() * 10.into(); let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); - SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); let subnet_tao_before = SubnetTAO::::get(netuid); // Add stake - SubtensorModule::add_balance_to_coldkey_account(&coldkey, tao_to_stake.into()); + add_balance_to_coldkey_account(&coldkey, tao_to_stake.into()); assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(coldkey), hotkey, @@ -2673,7 +2658,7 @@ fn test_remove_stake_fee_realistic_values() { let alpha_divs = AlphaBalance::from(2_816_190); let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); - SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); // Mock a realistic scenario: // Subnet 1 has 3896 TAO and 128_011 Alpha in reserves, which @@ -2729,15 +2714,15 @@ fn test_stake_overflow() { let coldkey_account_id = U256::from(435445); let hotkey_account_id = U256::from(54544); let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); - let amount: u64 = 21_000_000_000_000_000_u64; // Max TAO supply (test context) + let ed = u64::from(ExistentialDeposit::get()); + // Maximum possible: Max TAO supply less locked balance less ED (that's on owner's coldkey) + let amount = + 21_000_000_000_000_000_u64 - u64::from(SubtensorModule::get_network_last_lock()) - ed; register_ok_neuron(netuid, hotkey_account_id, coldkey_account_id, 192213123); - // Give it some $$$ in his coldkey balance (+ED buffer to avoid reaping-related edge cases) - SubtensorModule::add_balance_to_coldkey_account( - &coldkey_account_id, - TaoBalance::from(amount) + ExistentialDeposit::get(), - ); + // Give it some $$$ in his coldkey balance + add_balance_to_coldkey_account(&coldkey_account_id, amount.into()); // Setup liquidity with 21M TAO values mock::setup_reserves(netuid, amount.into(), amount.into()); @@ -2754,9 +2739,10 @@ fn test_stake_overflow() { )); // Check if stake has increased properly - assert_eq!( + assert_abs_diff_eq!( SubtensorModule::get_stake_for_hotkey_on_subnet(&hotkey_account_id, netuid), - expected_alpha + expected_alpha, + epsilon = 1.into() ); // Check if total stake has increased accordingly. @@ -3786,7 +3772,7 @@ fn test_add_stake_limit_ok() { assert_eq!(current_price, U96F32::from_num(1.5)); // Give it some $$$ in his coldkey balance - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount.into()); + add_balance_to_coldkey_account(&coldkey_account_id, amount.into()); // Setup limit price so that it doesn't peak above 4x of current price // The amount that can be executed at this price is 450 TAO only @@ -3861,7 +3847,7 @@ fn test_add_stake_limit_fill_or_kill() { assert_eq!(current_price, U96F32::from_num(1.5)); // Give it some $$$ in his coldkey balance - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount.into()); + add_balance_to_coldkey_account(&coldkey_account_id, amount.into()); // Setup limit price so that it doesn't peak above 4x of current price // The amount that can be executed at this price is 450 TAO only @@ -3911,7 +3897,7 @@ fn test_add_stake_limit_partial_zero_max_stake_amount_error() { SubnetTAO::::insert(netuid, tao_reserve); SubnetAlphaIn::::insert(netuid, alpha_in); - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount.into()); + add_balance_to_coldkey_account(&coldkey_account_id, amount.into()); assert_noop!( SubtensorModule::add_stake_limit( @@ -3936,7 +3922,7 @@ fn test_remove_stake_limit_ok() { // add network let netuid = add_dynamic_network(&hotkey_account_id, &coldkey_account_id); - SubtensorModule::add_balance_to_coldkey_account( + add_balance_to_coldkey_account( &coldkey_account_id, stake_amount + ExistentialDeposit::get(), ); @@ -4096,10 +4082,7 @@ fn test_add_stake_specific_stake_into_subnet_fail() { SubnetTAO::::insert(netuid, tao_in); // Give TAO balance to coldkey - SubtensorModule::add_balance_to_coldkey_account( - &coldkey_account_id, - tao_staked + 1_000_000_000.into(), - ); + add_balance_to_coldkey_account(&coldkey_account_id, tao_staked + 1_000_000_000.into()); // Add stake as new hotkey let order = GetAlphaForTao::::with_amount(tao_staked); @@ -4149,7 +4132,7 @@ fn test_remove_99_9991_per_cent_stake_works_precisely() { pallet_subtensor_swap::FeeRate::::insert(netuid, 0); // Give it some $$$ in his coldkey balance (in addition to any leftover buffer from registration) - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount.into()); + add_balance_to_coldkey_account(&coldkey_account_id, amount.into()); // Stake to hotkey account. assert_ok!(SubtensorModule::add_stake( @@ -4228,7 +4211,7 @@ fn test_remove_99_9989_per_cent_stake_leaves_a_little() { pallet_subtensor_swap::FeeRate::::insert(netuid, 0); // Give it some $$$ in his coldkey balance - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount.into()); + add_balance_to_coldkey_account(&coldkey_account_id, amount.into()); // Stake to hotkey account, and check if the result is ok let (_, fee) = mock::swap_tao_to_alpha(netuid, amount.into()); @@ -4419,10 +4402,7 @@ fn test_unstake_all_alpha_hits_liquidity_min() { let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); register_ok_neuron(netuid, hotkey, coldkey, 192213123); - SubtensorModule::add_balance_to_coldkey_account( - &coldkey, - stake_amount + ExistentialDeposit::get(), - ); + add_balance_to_coldkey_account(&coldkey, stake_amount + ExistentialDeposit::get()); // Give the neuron some stake to remove assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(coldkey), @@ -4474,10 +4454,7 @@ fn test_unstake_all_alpha_works() { let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); register_ok_neuron(netuid, hotkey, coldkey, 192213123); - SubtensorModule::add_balance_to_coldkey_account( - &coldkey, - stake_amount + ExistentialDeposit::get(), - ); + add_balance_to_coldkey_account(&coldkey, stake_amount + ExistentialDeposit::get()); // Give the neuron some stake to remove assert_ok!(SubtensorModule::add_stake( @@ -4526,10 +4503,7 @@ fn test_unstake_all_works() { let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); register_ok_neuron(netuid, hotkey, coldkey, 192213123); - SubtensorModule::add_balance_to_coldkey_account( - &coldkey, - stake_amount + ExistentialDeposit::get(), - ); + add_balance_to_coldkey_account(&coldkey, stake_amount + ExistentialDeposit::get()); // Give the neuron some stake to remove assert_ok!(SubtensorModule::add_stake( @@ -4592,6 +4566,7 @@ fn test_stake_into_subnet_ok() { )); // Add stake with slippage safety and check if the result is ok + add_balance_to_coldkey_account(&coldkey, TaoBalance::MAX); assert_ok!(SubtensorModule::stake_into_subnet( &hotkey, &coldkey, @@ -4646,6 +4621,7 @@ fn test_stake_into_subnet_low_amount() { )); // Add stake with slippage safety and check if the result is ok + add_balance_to_coldkey_account(&coldkey, TaoBalance::MAX); assert_ok!(SubtensorModule::stake_into_subnet( &hotkey, &coldkey, @@ -4694,6 +4670,7 @@ fn test_unstake_from_subnet_low_amount() { )); // Add stake and check if the result is ok + add_balance_to_coldkey_account(&coldkey, TaoBalance::MAX); assert_ok!(SubtensorModule::stake_into_subnet( &hotkey, &coldkey, @@ -4710,6 +4687,7 @@ fn test_unstake_from_subnet_low_amount() { assert_ok!(SubtensorModule::unstake_from_subnet( &hotkey, &coldkey, + &coldkey, netuid, alpha, TaoBalance::ZERO, @@ -4734,7 +4712,7 @@ fn test_stake_into_subnet_prohibitive_limit() { // add network let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, amount.into()); + add_balance_to_coldkey_account(&coldkey, amount.into()); // Forse-set alpha in and tao reserve to make price equal 0.01 let tao_reserve = TaoBalance::from(100_000_000_000_u64); @@ -4793,7 +4771,7 @@ fn test_unstake_from_subnet_prohibitive_limit() { // add network let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, amount.into()); + add_balance_to_coldkey_account(&coldkey, amount.into()); // Forse-set alpha in and tao reserve to make price equal 0.01 let tao_reserve = TaoBalance::from(100_000_000_000_u64); @@ -4869,7 +4847,7 @@ fn test_unstake_full_amount() { // add network let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, amount.into()); + add_balance_to_coldkey_account(&coldkey, amount.into()); // Forse-set alpha in and tao reserve to make price equal 0.01 let tao_reserve = TaoBalance::from(100_000_000_000_u64); @@ -4977,8 +4955,8 @@ fn test_swap_fees_tao_correctness() { // add network let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); - SubtensorModule::add_balance_to_coldkey_account(&owner_coldkey, owner_balance_before); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, user_balance_before); + add_balance_to_coldkey_account(&owner_coldkey, owner_balance_before); + add_balance_to_coldkey_account(&coldkey, user_balance_before); pallet_subtensor_swap::EnabledUserLiquidity::::insert(NetUid::from(netuid), true); // Forse-set alpha in and tao reserve to make price equal 0.25 @@ -5273,8 +5251,8 @@ fn test_default_min_stake_sufficiency() { // add network let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); - SubtensorModule::add_balance_to_coldkey_account(&owner_coldkey, owner_balance_before); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, user_balance_before); + add_balance_to_coldkey_account(&owner_coldkey, owner_balance_before); + add_balance_to_coldkey_account(&coldkey, user_balance_before); let fee_rate = pallet_subtensor_swap::FeeRate::::get(NetUid::from(netuid)) as f64 / u16::MAX as f64; @@ -5320,143 +5298,6 @@ fn test_default_min_stake_sufficiency() { }); } -/// Test that modify_position always credits fees -/// -/// cargo test --package pallet-subtensor --lib -- tests::staking::test_update_position_fees --exact --show-output -#[test] -fn test_update_position_fees() { - // Test cases: add or remove liquidity during modification - [false, true].into_iter().for_each(|add| { - new_test_ext(1).execute_with(|| { - let owner_hotkey = U256::from(1); - let owner_coldkey = U256::from(2); - let coldkey = U256::from(4); - let amount = 1_000_000_000; - - // add network - let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); - SubtensorModule::add_balance_to_coldkey_account(&owner_coldkey, (amount * 10).into()); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, (amount * 100).into()); - pallet_subtensor_swap::EnabledUserLiquidity::::insert(NetUid::from(netuid), true); - - // Forse-set alpha in and tao reserve to make price equal 0.25 - let tao_reserve = TaoBalance::from(100_000_000_000_u64); - let alpha_in = AlphaBalance::from(400_000_000_000_u64); - mock::setup_reserves(netuid, tao_reserve, alpha_in); - - // Get the block builder balance - let block_builder = U256::from(MOCK_BLOCK_BUILDER); - let block_builder_balance_before = Balances::free_balance(block_builder); - - // Get alpha for owner - assert_ok!(SubtensorModule::add_stake( - RuntimeOrigin::signed(owner_coldkey), - owner_hotkey, - netuid, - amount.into(), - )); - - // Add owner coldkey Alpha as concentrated liquidity - // between current price current price + 0.01 - let current_price = - ::SwapInterface::current_alpha_price(netuid.into()) - .to_num::() - + 0.0001; - let limit_price = current_price + 0.001; - let tick_low = price_to_tick(current_price); - let tick_high = price_to_tick(limit_price); - let liquidity = amount; - - let (position_id, _, _) = ::SwapInterface::do_add_liquidity( - NetUid::from(netuid), - &owner_coldkey, - &owner_hotkey, - tick_low, - tick_high, - liquidity, - ) - .unwrap(); - - // Buy and then sell all alpha for user to hit owner liquidity - assert_ok!(SubtensorModule::add_stake( - RuntimeOrigin::signed(coldkey), - owner_hotkey, - netuid, - amount.into(), - )); - - remove_stake_rate_limit_for_tests(&owner_hotkey, &coldkey, netuid); - - let user_alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &owner_hotkey, - &coldkey, - netuid, - ); - assert_ok!(SubtensorModule::remove_stake( - RuntimeOrigin::signed(coldkey), - owner_hotkey, - netuid, - user_alpha, - )); - - // Modify position - fees should be collected and paid to the owner (block builder is already paid by now) - let owner_tao_before = SubtensorModule::get_coldkey_balance(&owner_coldkey); - - // Make small modification - let delta = - ::MinimumLiquidity::get() - as i64 - * (if add { 1 } else { -1 }); - assert_ok!(Swap::modify_position( - RuntimeOrigin::signed(owner_coldkey), - owner_hotkey, - netuid.into(), - position_id.into(), - delta, - )); - - // Check ending owner TAO and alpha - let block_builder_balance_after_add = Balances::free_balance(block_builder); - let owner_tao_after_add = SubtensorModule::get_coldkey_balance(&owner_coldkey); - let owner_alpha_after_add = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &owner_hotkey, - &owner_coldkey, - netuid, - ); - - assert!( - owner_tao_after_add + block_builder_balance_after_add - > owner_tao_before + block_builder_balance_before - ); - - // Make small modification again - should not claim more fees - assert_ok!(Swap::modify_position( - RuntimeOrigin::signed(owner_coldkey), - owner_hotkey, - netuid.into(), - position_id.into(), - delta, - )); - - // Check ending owner TAO and alpha - let owner_tao_after_repeat = SubtensorModule::get_coldkey_balance(&owner_coldkey); - let owner_alpha_after_repeat = - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &owner_hotkey, - &owner_coldkey, - netuid, - ); - - assert!(owner_tao_after_add == owner_tao_after_repeat); - if add { - assert!(owner_alpha_after_add > owner_alpha_after_repeat); - } else { - assert!(owner_alpha_after_add < owner_alpha_after_repeat); - } - }); - }); -} - #[test] fn test_stake_rate_limits() { new_test_ext(0).execute_with(|| { @@ -5474,7 +5315,7 @@ fn test_stake_rate_limits() { Delegates::::insert(hot1, SubtensorModule::get_min_delegate_take()); assert_eq!(SubtensorModule::get_owning_coldkey_for_hotkey(&hot1), cold1); - SubtensorModule::add_balance_to_coldkey_account(&cold1, init_balance); + add_balance_to_coldkey_account(&cold1, init_balance); assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(cold1), hot1, @@ -5520,7 +5361,7 @@ fn test_add_root_updates_counters() { // Give it some $$$ in his coldkey balance let initial_balance = stake_amount + ExistentialDeposit::get(); - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, initial_balance); + add_balance_to_coldkey_account(&coldkey_account_id, initial_balance); // Setup SubnetAlphaIn (because we are going to stake) SubnetAlphaIn::::insert(NetUid::ROOT, AlphaBalance::from(stake_amount.to_u64())); @@ -5575,10 +5416,10 @@ fn test_remove_root_updates_counters() { // Give it some $$$ in his coldkey balance let initial_balance = stake_amount + ExistentialDeposit::get(); - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, initial_balance); + add_balance_to_coldkey_account(&coldkey_account_id, initial_balance); // Setup existing stake - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey_account_id, &coldkey_account_id, NetUid::ROOT, @@ -5653,6 +5494,7 @@ fn test_staking_records_flow() { .unwrap(); // Add stake with slippage safety and check if the result is ok + add_balance_to_coldkey_account(&coldkey, TaoBalance::MAX); assert_ok!(SubtensorModule::stake_into_subnet( &hotkey, &coldkey, @@ -5679,6 +5521,7 @@ fn test_staking_records_flow() { assert_ok!(SubtensorModule::unstake_from_subnet( &hotkey, &coldkey, + &coldkey, netuid, alpha, TaoBalance::ZERO, diff --git a/pallets/subtensor/src/tests/subnet.rs b/pallets/subtensor/src/tests/subnet.rs index 9d734b992a..12b23c74e4 100644 --- a/pallets/subtensor/src/tests/subnet.rs +++ b/pallets/subtensor/src/tests/subnet.rs @@ -1,10 +1,11 @@ -#![allow(clippy::unwrap_used)] +#![allow(clippy::expect_used, clippy::unwrap_used)] use super::mock::*; use crate::subnets::symbols::{DEFAULT_SYMBOL, SYMBOLS}; use crate::*; use frame_support::{assert_err, assert_noop, assert_ok}; use frame_system::Config; use sp_core::U256; +use std::collections::BTreeSet; use subtensor_runtime_common::{AlphaBalance, TaoBalance}; use super::mock; @@ -70,7 +71,7 @@ fn test_do_start_call_fail_not_owner() { add_network_without_emission_block(netuid, tempo, 0); mock::setup_reserves(netuid, 1_000_000_000.into(), 1_000_000_000.into()); // Give it some $$$ in his coldkey balance - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, 10000.into()); + add_balance_to_coldkey_account(&coldkey_account_id, 10000.into()); add_network_without_emission_block(netuid, tempo, 0); @@ -100,7 +101,7 @@ fn test_do_start_call_can_start_now() { add_network_without_emission_block(netuid, tempo, 0); mock::setup_reserves(netuid, 1_000_000_000.into(), 1_000_000_000.into()); // Give it some $$$ in his coldkey balance - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, 10000.into()); + add_balance_to_coldkey_account(&coldkey_account_id, 10000.into()); add_network_without_emission_block(netuid, tempo, 0); @@ -132,7 +133,7 @@ fn test_do_start_call_fail_for_set_again() { // Fund coldkey based on the actual burn. let burn_u64 = SubtensorModule::get_burn(netuid); - SubtensorModule::add_balance_to_coldkey_account( + add_balance_to_coldkey_account( &coldkey_account_id, burn_u64 .saturating_add(ExistentialDeposit::get()) @@ -210,7 +211,7 @@ fn test_register_network_min_burn_at_default() { let cost = SubtensorModule::get_network_lock_cost(); // Give coldkey enough for lock - SubtensorModule::add_balance_to_coldkey_account(&sn_owner_coldkey, cost.into()); + add_balance_to_coldkey_account(&sn_owner_coldkey, ExistentialDeposit::get() + cost.into()); // Register network assert_ok!(SubtensorModule::register_network( @@ -241,7 +242,7 @@ fn test_register_network_use_symbol_for_subnet_if_available() { let coldkey = U256::from(1_000_000 + i); let hotkey = U256::from(2_000_000 + i); let cost = SubtensorModule::get_network_lock_cost(); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, cost.into()); + add_balance_to_coldkey_account(&coldkey, ExistentialDeposit::get() + cost.into()); assert_ok!(SubtensorModule::register_network( <::RuntimeOrigin>::signed(coldkey), @@ -260,6 +261,9 @@ fn test_register_network_use_symbol_for_subnet_if_available() { // Check registration allowed assert!(NetworkRegistrationAllowed::::get(netuid)); assert!(NetworkPowRegistrationAllowed::::get(netuid)); + + // Reduce lock cost to avoid exponential cost growth + NetworkLastLockCost::::set(1_000.into()); } }); } @@ -272,7 +276,7 @@ fn test_register_network_use_next_available_symbol_if_symbol_for_subnet_is_taken let coldkey = U256::from(1_000_000 + i); let hotkey = U256::from(2_000_000 + i); let cost = SubtensorModule::get_network_lock_cost(); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, cost.into()); + add_balance_to_coldkey_account(&coldkey, ExistentialDeposit::get() + cost.into()); assert_ok!(SubtensorModule::register_network( <::RuntimeOrigin>::signed(coldkey), @@ -291,6 +295,9 @@ fn test_register_network_use_next_available_symbol_if_symbol_for_subnet_is_taken // Check registration allowed assert!(NetworkRegistrationAllowed::::get(netuid)); assert!(NetworkPowRegistrationAllowed::::get(netuid)); + + // Reduce lock cost to avoid exponential cost growth + NetworkLastLockCost::::set(1_000.into()); } // Swap some of the network symbol for the network 25 to network 51 symbol (not registered yet) @@ -300,7 +307,7 @@ fn test_register_network_use_next_available_symbol_if_symbol_for_subnet_is_taken let coldkey = U256::from(1_000_000 + 50); let hotkey = U256::from(2_000_000 + 50); let cost = SubtensorModule::get_network_lock_cost(); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, cost.into()); + add_balance_to_coldkey_account(&coldkey, ExistentialDeposit::get() + cost.into()); assert_ok!(SubtensorModule::register_network( <::RuntimeOrigin>::signed(coldkey), @@ -328,19 +335,22 @@ fn test_register_network_use_default_symbol_if_all_symbols_are_taken() { let coldkey = U256::from(1_000_000 + i); let hotkey = U256::from(2_000_000 + i); let cost = SubtensorModule::get_network_lock_cost(); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, cost.into()); + add_balance_to_coldkey_account(&coldkey, ExistentialDeposit::get() + cost.into()); assert_ok!(SubtensorModule::register_network( <::RuntimeOrigin>::signed(coldkey), hotkey )); + + // Reduce lock cost to avoid exponential cost growth + NetworkLastLockCost::::set(1_000.into()); } // Register a new network let coldkey = U256::from(1_000_000 + 50); let hotkey = U256::from(2_000_000 + 50); let cost = SubtensorModule::get_network_lock_cost(); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, cost.into()); + add_balance_to_coldkey_account(&coldkey, ExistentialDeposit::get() + cost.into()); assert_ok!(SubtensorModule::register_network( <::RuntimeOrigin>::signed(coldkey), @@ -362,6 +372,7 @@ fn test_register_network_use_default_symbol_if_all_symbols_are_taken() { assert!(NetworkPowRegistrationAllowed::::get(netuid)); }); } + // cargo test --package pallet-subtensor --lib -- tests::subnet::test_subtoken_enable --exact --show-output #[test] fn test_subtoken_enable() { @@ -385,8 +396,7 @@ fn test_subtoken_enable() { }); } -// cargo test --package pallet-subtensor --lib -- -// tests::subnet::test_subtoken_enable_reject_trading_before_enable --exact --show-output +// cargo test --package pallet-subtensor --lib -- tests::subnet::test_subtoken_enable_reject_trading_before_enable --exact --show-output #[allow(clippy::unwrap_used)] #[test] fn test_subtoken_enable_reject_trading_before_enable() { @@ -419,10 +429,10 @@ fn test_subtoken_enable_reject_trading_before_enable() { register_ok_neuron(netuid, hotkey_account_2_id, coldkey_account_id, 0); register_ok_neuron(netuid2, hotkey_account_2_id, coldkey_account_id, 100); - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, 10_000.into()); + add_balance_to_coldkey_account(&coldkey_account_id, 10_000.into()); // Give some stake - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + mock_increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey_account_id, &coldkey_account_id, netuid, @@ -592,10 +602,7 @@ fn test_subtoken_enable_trading_ok_with_enable() { register_ok_neuron(netuid, hotkey_account_2_id, coldkey_account_id, 0); register_ok_neuron(netuid2, hotkey_account_2_id, coldkey_account_id, 100); - SubtensorModule::add_balance_to_coldkey_account( - &coldkey_account_id, - stake_amount * 10.into(), - ); + add_balance_to_coldkey_account(&coldkey_account_id, stake_amount * 10.into()); // all trading extrinsic should be possible now that subtoken is enabled. assert_ok!(SubtensorModule::add_stake( @@ -712,7 +719,7 @@ fn test_subtoken_enable_ok_for_burn_register_before_enable() { // Fund enough to burned-register twice + keep-alive buffer. let burn_1 = SubtensorModule::get_burn(netuid); let burn_2 = SubtensorModule::get_burn(netuid2); - SubtensorModule::add_balance_to_coldkey_account( + add_balance_to_coldkey_account( &coldkey_account_id, burn_1 .saturating_add(burn_2) @@ -909,3 +916,137 @@ fn test_update_symbol_fails_if_symbol_already_in_use() { ); }); } + +// cargo test --package pallet-subtensor --lib -- tests::subnet::test_get_subnet_account_id_exists_and_is_distinct_for_257_consecutive_subnets --exact --nocapture +#[test] +fn test_get_subnet_account_id_exists_and_is_distinct_for_257_consecutive_subnets() { + new_test_ext(1).execute_with(|| { + let mut account_ids = BTreeSet::new(); + + for raw_netuid in 0u16..=256u16 { + let netuid = NetUid::from(raw_netuid); + add_network(netuid, 10, 0); + + let account_id = SubtensorModule::get_subnet_account_id(netuid); + assert!( + account_ids.insert(account_id), + "duplicate subnet account id for netuid {:?}", + netuid + ); + } + + assert_eq!(account_ids.len(), 257); + }); +} + +// cargo test --package pallet-subtensor --lib -- tests::subnet::test_is_subnet_account_id --exact --nocapture +#[test] +fn test_is_subnet_account_id() { + new_test_ext(1).execute_with(|| { + for raw_netuid in 0u16..=2048u16 { + let netuid = NetUid::from(raw_netuid); + add_network(netuid, 10, 0); + + let account_id = SubtensorModule::get_subnet_account_id(netuid).unwrap(); + let roudtrip_netuid = SubtensorModule::is_subnet_account_id(&account_id); + assert_eq!(netuid, roudtrip_netuid.unwrap()); + } + + // Not a subnet account + let not_subnet_account_id = U256::from(1); + assert!(SubtensorModule::is_subnet_account_id(¬_subnet_account_id).is_none()); + }); +} + +// cargo test --package pallet-subtensor --lib -- tests::subnet::test_cannot_register_system_hotkey --exact --nocapture +#[test] +fn test_cannot_register_system_hotkey() { + new_test_ext(1).execute_with(|| { + for raw_netuid in 0u16..=2048u16 { + let coldkey = U256::from(1); + let netuid = NetUid::from(raw_netuid); + add_network(netuid, 10, 0); + + let account_id = SubtensorModule::get_subnet_account_id(netuid).unwrap(); + assert_err!( + SubtensorModule::create_account_if_non_existent(&coldkey, &account_id), + Error::::CannotUseSystemAccount + ); + assert!(!SubtensorModule::coldkey_owns_hotkey(&coldkey, &account_id),); + } + }); +} + +#[test] +fn test_burned_register_increases_subnet_tao_flow() { + new_test_ext(1).execute_with(|| { + let netuid = NetUid::from(1); + let coldkey = U256::from(77); + let hotkey = U256::from(88); + + add_network(netuid, 13, 0); + mock::setup_reserves(netuid, DEFAULT_RESERVE.into(), DEFAULT_RESERVE.into()); + + let burn = 1_000u64; + SubtensorModule::set_burn(netuid, burn.into()); + let flow_before = SubnetTaoFlow::::get(netuid); + + add_balance_to_coldkey_account( + &coldkey, + ExistentialDeposit::get() + burn.into() + 10u64.into(), + ); + + assert_ok!(SubtensorModule::burned_register( + <::RuntimeOrigin>::signed(coldkey), + netuid, + hotkey + )); + + assert_eq!( + SubnetTaoFlow::::get(netuid), + flow_before + burn as i64 + ); + }); +} + +#[test] +fn test_register_network_gives_owner_no_initial_alpha_distribution() { + new_test_ext(1).execute_with(|| { + let owner_coldkey = U256::from(5001); + let owner_hotkey = U256::from(5002); + let lock_cost = SubtensorModule::get_network_lock_cost(); + let netuids_before = SubtensorModule::get_all_subnet_netuids(); + + add_balance_to_coldkey_account( + &owner_coldkey, + ExistentialDeposit::get() + lock_cost.into(), + ); + + assert_ok!(SubtensorModule::register_network( + <::RuntimeOrigin>::signed(owner_coldkey), + owner_hotkey + )); + + let netuid = SubtensorModule::get_all_subnet_netuids() + .into_iter() + .find(|netuid| !netuids_before.contains(netuid)) + .expect("new subnet should be added"); + + assert_eq!(SubnetOwner::::get(netuid), owner_coldkey); + assert_eq!(SubnetOwnerHotkey::::get(netuid), owner_hotkey); + assert_eq!(SubnetAlphaOut::::get(netuid), AlphaBalance::ZERO); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &owner_hotkey, + &owner_coldkey, + netuid + ), + AlphaBalance::ZERO + ); + assert!( + Lock::::iter_prefix((&owner_coldkey, netuid)) + .next() + .is_none() + ); + }); +} diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index e756429b8d..fd0281ad35 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -48,7 +48,7 @@ fn test_announce_coldkey_swap_works() { assert_eq!(ColdkeySwapAnnouncements::::iter().count(), 0); let swap_cost = SubtensorModule::get_key_swap_cost(); - SubtensorModule::add_balance_to_coldkey_account(&who, swap_cost + ed); + add_balance_to_coldkey_account(&who, swap_cost + ed); assert_eq!(SubtensorModule::get_coldkey_balance(&who), swap_cost + ed); assert_ok!(SubtensorModule::announce_coldkey_swap( @@ -85,7 +85,7 @@ fn test_announce_coldkey_swap_with_existing_announcement_past_delay_works() { assert_eq!(ColdkeySwapAnnouncements::::iter().count(), 0); let swap_cost = SubtensorModule::get_key_swap_cost(); - SubtensorModule::add_balance_to_coldkey_account(&who, swap_cost * 2.into()); + add_balance_to_coldkey_account(&who, swap_cost * 2.into()); assert_ok!(SubtensorModule::announce_coldkey_swap( RuntimeOrigin::signed(who), @@ -126,7 +126,7 @@ fn test_announce_coldkey_swap_only_pays_swap_cost_if_no_announcement_exists() { let ed = ExistentialDeposit::get(); let swap_cost = SubtensorModule::get_key_swap_cost(); - SubtensorModule::add_balance_to_coldkey_account(&who, swap_cost + ed); + add_balance_to_coldkey_account(&who, swap_cost + ed); assert_eq!(SubtensorModule::get_coldkey_balance(&who), swap_cost + ed); assert_ok!(SubtensorModule::announce_coldkey_swap( @@ -179,7 +179,7 @@ fn test_announce_coldkey_swap_with_existing_announcement_not_past_delay_fails() let swap_cost = SubtensorModule::get_key_swap_cost(); let ed = ExistentialDeposit::get(); - SubtensorModule::add_balance_to_coldkey_account(&who, swap_cost + ed); + add_balance_to_coldkey_account(&who, swap_cost + ed); assert_ok!(SubtensorModule::announce_coldkey_swap( RuntimeOrigin::signed(who), @@ -222,7 +222,7 @@ fn test_swap_coldkey_announced_works() { let delay = ColdkeySwapAnnouncementDelay::::get() + 1; run_to_block(now + delay); - SubtensorModule::add_balance_to_coldkey_account(&who, stake1 + stake2 + stake3 + ed); + add_balance_to_coldkey_account(&who, stake1 + stake2 + stake3 + ed); let expected_remaining: u64 = ed.to_u64(); @@ -368,7 +368,7 @@ fn test_swap_coldkey_announced_with_already_associated_coldkey_fails() { let swap_cost = SubtensorModule::get_key_swap_cost(); let ed = ExistentialDeposit::get(); - SubtensorModule::add_balance_to_coldkey_account(&who, swap_cost + ed); + add_balance_to_coldkey_account(&who, swap_cost + ed); assert_ok!(SubtensorModule::announce_coldkey_swap( RuntimeOrigin::signed(who), @@ -435,10 +435,7 @@ fn test_swap_coldkey_works() { let stake3 = min_stake * 30.into(); // Fund: stake_total + (swap_cost + ED). - SubtensorModule::add_balance_to_coldkey_account( - &old_coldkey, - swap_cost + stake1 + stake2 + stake3 + ed, - ); + add_balance_to_coldkey_account(&old_coldkey, swap_cost + stake1 + stake2 + stake3 + ed); // Some old announcement and dispute that will be cleared let now = System::block_number() - 100; @@ -519,10 +516,7 @@ fn test_swap_coldkey_works_with_zero_cost() { let stake2 = min_stake * 20.into(); let stake3 = min_stake * 30.into(); - SubtensorModule::add_balance_to_coldkey_account( - &old_coldkey, - stake1 + stake2 + stake3 + ed, - ); + add_balance_to_coldkey_account(&old_coldkey, stake1 + stake2 + stake3 + ed); let expected_remaining = ed; let ( @@ -607,6 +601,7 @@ fn test_swap_coldkey_with_bad_origin_fails() { }); } +// cargo test --package pallet-subtensor --lib -- tests::swap_coldkey::test_swap_coldkey_with_not_enough_balance_to_pay_swap_cost_fails --exact --nocapture #[test] fn test_swap_coldkey_with_not_enough_balance_to_pay_swap_cost_fails() { new_test_ext(1).execute_with(|| { @@ -627,7 +622,8 @@ fn test_swap_coldkey_with_not_enough_balance_to_pay_swap_cost_fails() { // Needs to preserve ED let balance = SubtensorModule::get_key_swap_cost() + ExistentialDeposit::get() - 1.into(); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, balance); + add_balance_to_coldkey_account(&old_coldkey, balance); + assert_noop!( SubtensorModule::swap_coldkey( RuntimeOrigin::root(), @@ -681,7 +677,7 @@ fn test_announce_coldkey_swap_with_not_enough_balance_to_pay_swap_cost_fails() { // Needs to preserve ED let balance = SubtensorModule::get_key_swap_cost() + ExistentialDeposit::get() - 1.into(); - SubtensorModule::add_balance_to_coldkey_account(&who, balance); + add_balance_to_coldkey_account(&who, balance); assert_noop!( SubtensorModule::announce_coldkey_swap(RuntimeOrigin::signed(who), new_coldkey_hash), Error::::NotEnoughBalanceToPaySwapColdKey @@ -720,7 +716,10 @@ fn test_do_swap_coldkey_with_max_values() { let other_coldkey = U256::from(7); let netuid = NetUid::from(1); let netuid2 = NetUid::from(2); - let max_stake = TaoBalance::from(21_000_000_000_000_000_u64); // 21 Million TAO; max possible balance. + // Max possible balance: (21M - EDs - ...) / 2 + let ed = u64::from(ExistentialDeposit::get()); + let locks = 200_000_000_000_u64; + let max_stake = (21_000_000_000_000_000_u64 - 2 * ed - 100) / 2; // Add a network add_network(netuid, 1, 0); @@ -732,19 +731,18 @@ fn test_do_swap_coldkey_with_max_values() { register_ok_neuron(netuid2, hotkey2, other_coldkey, 1001000); // Give balance to old_coldkey and old_coldkey2. - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, max_stake + 1_000.into()); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey2, max_stake + 1_000.into()); + add_balance_to_coldkey_account(&old_coldkey, max_stake.into()); + add_balance_to_coldkey_account(&old_coldkey2, max_stake.into()); - let reserve = u64::from(max_stake) * 10; - mock::setup_reserves(netuid, reserve.into(), reserve.into()); - mock::setup_reserves(netuid2, reserve.into(), reserve.into()); + mock::setup_reserves(netuid, max_stake.into(), max_stake.into()); + mock::setup_reserves(netuid2, max_stake.into(), max_stake.into()); // Stake to hotkey on each subnet. assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(old_coldkey), hotkey, netuid, - max_stake + max_stake.into() )); let expected_stake1 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, @@ -756,7 +754,7 @@ fn test_do_swap_coldkey_with_max_values() { <::RuntimeOrigin>::signed(old_coldkey2), hotkey2, netuid2, - max_stake + max_stake.into() )); let expected_stake2 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey2, @@ -807,8 +805,8 @@ fn test_do_swap_coldkey_effect_on_delegated_stake() { StakingHotkeys::::insert(old_coldkey, vec![hotkey]); StakingHotkeys::::insert(delegator, vec![hotkey]); SubtensorModule::create_account_if_non_existent(&old_coldkey, &hotkey); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, stake); - SubtensorModule::add_balance_to_coldkey_account(&delegator, stake); + add_balance_to_coldkey_account(&old_coldkey, stake); + add_balance_to_coldkey_account(&delegator, stake); assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(old_coldkey), @@ -868,7 +866,7 @@ fn test_swap_delegated_stake_for_coldkey() { // Notice hotkey1 and hotkey2 are Owned by other_coldkey // old_coldkey and new_coldkey therefore delegates stake to them // === Give old_coldkey some balance === - SubtensorModule::add_balance_to_coldkey_account( + add_balance_to_coldkey_account( &old_coldkey, stake_amount1 + stake_amount2 + 1_000_000.into(), ); @@ -1008,13 +1006,13 @@ fn test_coldkey_swap_total() { let stake = DefaultMinStake::::get() * 10.into(); // Initial funding. Burns will reduce these balances. - SubtensorModule::add_balance_to_coldkey_account(&coldkey, stake * 6.into() + ed.into()); - SubtensorModule::add_balance_to_coldkey_account(&delegate1, stake * 2.into() + ed.into()); - SubtensorModule::add_balance_to_coldkey_account(&delegate2, stake * 2.into() + ed.into()); - SubtensorModule::add_balance_to_coldkey_account(&delegate3, stake * 2.into() + ed.into()); - SubtensorModule::add_balance_to_coldkey_account(&nominator1, stake * 2.into() + ed.into()); - SubtensorModule::add_balance_to_coldkey_account(&nominator2, stake * 2.into() + ed.into()); - SubtensorModule::add_balance_to_coldkey_account(&nominator3, stake * 2.into() + ed.into()); + add_balance_to_coldkey_account(&coldkey, stake * 6.into() + ed.into()); + add_balance_to_coldkey_account(&delegate1, stake * 2.into() + ed.into()); + add_balance_to_coldkey_account(&delegate2, stake * 2.into() + ed.into()); + add_balance_to_coldkey_account(&delegate3, stake * 2.into() + ed.into()); + add_balance_to_coldkey_account(&nominator1, stake * 2.into() + ed.into()); + add_balance_to_coldkey_account(&nominator2, stake * 2.into() + ed.into()); + add_balance_to_coldkey_account(&nominator3, stake * 2.into() + ed.into()); let reserve = u64::from(stake) * 10; mock::setup_reserves(netuid1, reserve.into(), reserve.into()); @@ -1044,7 +1042,7 @@ fn test_coldkey_swap_total() { let ensure_min_balance = |account: &U256, required: TaoBalance| { let bal = SubtensorModule::get_coldkey_balance(account); if bal < required { - SubtensorModule::add_balance_to_coldkey_account(account, required - bal); + add_balance_to_coldkey_account(account, required - bal); } }; @@ -1357,7 +1355,7 @@ fn test_do_swap_coldkey_effect_on_delegations() { delegate )); // register on root register_ok_neuron(netuid2, delegate, owner, 0); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, stake * 10.into()); + add_balance_to_coldkey_account(&coldkey, stake * 10.into()); // since the reserves are equal and we stake the same amount to both networks, we can reuse // this values for different networks. but you should take it into account in case of tests @@ -1697,7 +1695,7 @@ macro_rules! comprehensive_setup { let current_free = SubtensorModule::get_coldkey_balance(&$who); if current_free < required_free { - SubtensorModule::add_balance_to_coldkey_account(&$who, required_free - current_free); + add_balance_to_coldkey_account(&$who, required_free - current_free); } // Now staking will succeed and leave exactly expected_remaining behind. @@ -1900,7 +1898,7 @@ fn coldkey_hash_of(coldkey: U256) -> H256 { fn announce_coldkey_swap(who: U256, new_coldkey: U256) { let ed = ExistentialDeposit::get(); let swap_cost = SubtensorModule::get_key_swap_cost(); - SubtensorModule::add_balance_to_coldkey_account(&who, ed + swap_cost); + add_balance_to_coldkey_account(&who, ed + swap_cost); assert_ok!(SubtensorModule::announce_coldkey_swap( RuntimeOrigin::signed(who), diff --git a/pallets/subtensor/src/tests/swap_hotkey.rs b/pallets/subtensor/src/tests/swap_hotkey.rs index c54c0507e0..3fdacf23be 100644 --- a/pallets/subtensor/src/tests/swap_hotkey.rs +++ b/pallets/subtensor/src/tests/swap_hotkey.rs @@ -81,7 +81,7 @@ fn test_swap_total_hotkey_stake() { mock::setup_reserves(netuid, reserve.into(), reserve.into()); // Give it some $$$ in his coldkey balance - SubtensorModule::add_balance_to_coldkey_account(&coldkey, amount); + add_balance_to_coldkey_account(&coldkey, amount); // Add stake let (expected_alpha, _) = mock::swap_tao_to_alpha(netuid, amount); @@ -420,14 +420,8 @@ fn test_swap_hotkey_with_multiple_coldkeys() { StakingHotkeys::::insert(coldkey1, vec![old_hotkey]); StakingHotkeys::::insert(coldkey2, vec![old_hotkey]); SubtensorModule::create_account_if_non_existent(&coldkey1, &old_hotkey); - SubtensorModule::add_balance_to_coldkey_account( - &coldkey1, - stake + ExistentialDeposit::get(), - ); - SubtensorModule::add_balance_to_coldkey_account( - &coldkey2, - stake + ExistentialDeposit::get(), - ); + add_balance_to_coldkey_account(&coldkey1, stake + ExistentialDeposit::get()); + add_balance_to_coldkey_account(&coldkey2, stake + ExistentialDeposit::get()); assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(coldkey1), @@ -519,14 +513,8 @@ fn test_swap_staking_hotkeys_multiple_coldkeys() { Alpha::::insert((old_hotkey, coldkey2, netuid), U64F64::from_num(100)); SubtensorModule::create_account_if_non_existent(&coldkey1, &old_hotkey); - SubtensorModule::add_balance_to_coldkey_account( - &coldkey1, - stake + ExistentialDeposit::get(), - ); - SubtensorModule::add_balance_to_coldkey_account( - &coldkey2, - stake + ExistentialDeposit::get(), - ); + add_balance_to_coldkey_account(&coldkey1, stake + ExistentialDeposit::get()); + add_balance_to_coldkey_account(&coldkey2, stake + ExistentialDeposit::get()); assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(coldkey1), old_hotkey, @@ -618,8 +606,8 @@ fn test_swap_hotkey_with_multiple_coldkeys_and_subnets() { mock::setup_reserves(netuid2, reserve.into(), reserve.into()); // Add balance to both coldkeys - SubtensorModule::add_balance_to_coldkey_account(&coldkey1, stake + 1_000.into()); - SubtensorModule::add_balance_to_coldkey_account(&coldkey2, stake + 1_000.into()); + add_balance_to_coldkey_account(&coldkey1, stake + 1_000.into()); + add_balance_to_coldkey_account(&coldkey2, stake + 1_000.into()); // Stake with coldkey1 assert_ok!(SubtensorModule::add_stake( @@ -741,7 +729,7 @@ fn test_swap_hotkey_tx_rate_limit_exceeded() { let new_hotkey_1 = U256::from(2); let new_hotkey_2 = U256::from(4); let coldkey = U256::from(3); - let swap_cost = TaoBalance::from(1_000_000_000u64 * 2); + let swap_cost = SubtensorModule::get_key_swap_cost() * 2.into(); let tx_rate_limit = 1; @@ -757,7 +745,7 @@ fn test_swap_hotkey_tx_rate_limit_exceeded() { // Setup initial state add_network(netuid, tempo, 0); register_ok_neuron(netuid, old_hotkey, coldkey, 0); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, swap_cost); + add_balance_to_coldkey_account(&coldkey, swap_cost + ExistentialDeposit::get()); // Perform the first swap assert_ok!(SubtensorModule::do_swap_hotkey( @@ -807,7 +795,7 @@ fn test_do_swap_hotkey_err_not_owner() { // Setup initial state add_network(netuid, tempo, 0); register_ok_neuron(netuid, old_hotkey, coldkey, 0); - SubtensorModule::add_balance_to_coldkey_account(¬_owner_coldkey, swap_cost); + add_balance_to_coldkey_account(¬_owner_coldkey, swap_cost); // Attempt the swap with a non-owner coldkey assert_err!( @@ -1140,7 +1128,7 @@ fn test_swap_hotkey_error_cases() { ); let initial_balance = SubtensorModule::get_key_swap_cost() + 1000.into(); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, initial_balance); + add_balance_to_coldkey_account(&coldkey, initial_balance); // Test new hotkey same as old assert_noop!( @@ -1208,7 +1196,7 @@ fn test_do_swap_hotkey_err_new_hotkey_not_clean_for_root() { SubtensorModule::set_last_tx_block(&coldkey, 0); let initial_balance = SubtensorModule::get_key_swap_cost() + 1000.into(); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, initial_balance); + add_balance_to_coldkey_account(&coldkey, initial_balance); // new_hotkey is NOT registered on any network, but some other coldkey // has staked to it on root. This must block a root-touching swap. @@ -1577,7 +1565,7 @@ fn test_swap_hotkey_swap_rate_limits() { let new_hotkey = U256::from(2); let coldkey = U256::from(3); let netuid = add_dynamic_network(&old_hotkey, &coldkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into()); let last_tx_block = 123; let delegate_take_block = 4567; diff --git a/pallets/subtensor/src/tests/swap_hotkey_with_subnet.rs b/pallets/subtensor/src/tests/swap_hotkey_with_subnet.rs index 73594ff7d6..48a4442acd 100644 --- a/pallets/subtensor/src/tests/swap_hotkey_with_subnet.rs +++ b/pallets/subtensor/src/tests/swap_hotkey_with_subnet.rs @@ -24,7 +24,7 @@ fn test_swap_owner() { let coldkey = U256::from(3); let netuid = add_dynamic_network(&old_hotkey, &coldkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into()); Owner::::insert(old_hotkey, coldkey); System::set_block_number(System::block_number() + HotkeySwapOnSubnetInterval::get()); assert_ok!(SubtensorModule::do_swap_hotkey( @@ -49,7 +49,7 @@ fn test_swap_owned_hotkeys() { let coldkey = U256::from(3); let netuid = add_dynamic_network(&old_hotkey, &coldkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into()); OwnedHotkeys::::insert(coldkey, vec![old_hotkey]); System::set_block_number(System::block_number() + HotkeySwapOnSubnetInterval::get()); @@ -83,7 +83,7 @@ fn test_swap_total_hotkey_stake() { remove_owner_registration_stake(netuid); // Give it some $$$ in his coldkey balance - SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into()); // Add stake assert_ok!(SubtensorModule::add_stake( @@ -138,7 +138,7 @@ fn test_swap_delegates() { let coldkey = U256::from(3); let netuid = add_dynamic_network(&old_hotkey, &coldkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into()); Delegates::::insert(old_hotkey, 100); System::set_block_number(System::block_number() + HotkeySwapOnSubnetInterval::get()); @@ -164,7 +164,7 @@ fn test_swap_subnet_membership() { let coldkey = U256::from(3); let netuid = add_dynamic_network(&old_hotkey, &coldkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into()); IsNetworkMember::::insert(old_hotkey, netuid, true); System::set_block_number(System::block_number() + HotkeySwapOnSubnetInterval::get()); @@ -191,7 +191,7 @@ fn test_swap_uids_and_keys() { let coldkey = U256::from(3); let netuid = add_dynamic_network(&old_hotkey, &coldkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into()); IsNetworkMember::::insert(old_hotkey, netuid, true); Uids::::insert(netuid, old_hotkey, uid); @@ -224,7 +224,7 @@ fn test_swap_prometheus() { let prometheus_info = PrometheusInfo::default(); let netuid = add_dynamic_network(&old_hotkey, &coldkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into()); IsNetworkMember::::insert(old_hotkey, netuid, true); Prometheus::::insert(netuid, old_hotkey, prometheus_info.clone()); @@ -258,7 +258,7 @@ fn test_swap_axons() { let axon_info = AxonInfo::default(); let netuid = add_dynamic_network(&old_hotkey, &coldkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into()); IsNetworkMember::::insert(old_hotkey, netuid, true); Axons::::insert(netuid, old_hotkey, axon_info.clone()); @@ -289,7 +289,7 @@ fn test_swap_certificates() { let certificate = NeuronCertificate::try_from(vec![1, 2, 3]).unwrap(); let netuid = add_dynamic_network(&old_hotkey, &coldkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into()); IsNetworkMember::::insert(old_hotkey, netuid, true); NeuronCertificates::::insert(netuid, old_hotkey, certificate.clone()); @@ -326,7 +326,7 @@ fn test_swap_weight_commits() { weight_commits.push_back((H256::from_low_u64_be(100), 200, 1, 1)); let netuid = add_dynamic_network(&old_hotkey, &coldkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into()); IsNetworkMember::::insert(old_hotkey, netuid, true); WeightCommits::::insert( @@ -368,7 +368,7 @@ fn test_swap_loaded_emission() { let validator_emission = 1000u64; let netuid = add_dynamic_network(&old_hotkey, &coldkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into()); IsNetworkMember::::insert(old_hotkey, netuid, true); LoadedEmission::::insert( @@ -401,7 +401,7 @@ fn test_swap_staking_hotkeys() { let new_hotkey = U256::from(2); let coldkey = U256::from(3); let netuid = add_dynamic_network(&old_hotkey, &coldkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into()); StakingHotkeys::::insert(coldkey, vec![old_hotkey]); Alpha::::insert((old_hotkey, coldkey, netuid), U64F64::from_num(100)); @@ -439,8 +439,8 @@ fn test_swap_hotkey_with_multiple_coldkeys() { StakingHotkeys::::insert(coldkey1, vec![old_hotkey]); StakingHotkeys::::insert(coldkey2, vec![old_hotkey]); SubtensorModule::create_account_if_non_existent(&coldkey1, &old_hotkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey1, u64::MAX.into()); - SubtensorModule::add_balance_to_coldkey_account(&coldkey2, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey1, 1_000_000_000_000_u64.into()); + add_balance_to_coldkey_account(&coldkey2, 1_000_000_000_000_u64.into()); assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(coldkey1), @@ -498,7 +498,7 @@ fn test_swap_hotkey_with_multiple_subnets() { let new_hotkey_2 = U256::from(3); let coldkey = U256::from(4); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into()); let netuid1 = add_dynamic_network(&old_hotkey, &coldkey); let netuid2 = add_dynamic_network(&old_hotkey, &coldkey); @@ -546,8 +546,8 @@ fn test_swap_staking_hotkeys_multiple_coldkeys() { let staker5 = U256::from(5); let stake = 1_000_000_000; - SubtensorModule::add_balance_to_coldkey_account(&coldkey1, u64::MAX.into()); - SubtensorModule::add_balance_to_coldkey_account(&coldkey2, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey1, 1_000_000_000_000_u64.into()); + add_balance_to_coldkey_account(&coldkey2, 1_000_000_000_000_u64.into()); // Set up initial state StakingHotkeys::::insert(coldkey1, vec![old_hotkey]); @@ -601,7 +601,7 @@ fn test_swap_hotkey_with_no_stake() { let new_hotkey = U256::from(2); let coldkey = U256::from(3); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into()); // Set up initial state with no stake Owner::::insert(old_hotkey, coldkey); @@ -645,8 +645,8 @@ fn test_swap_hotkey_with_multiple_coldkeys_and_subnets() { register_ok_neuron(netuid2, old_hotkey, coldkey1, 1234); // Add balance to both coldkeys - SubtensorModule::add_balance_to_coldkey_account(&coldkey1, u64::MAX.into()); - SubtensorModule::add_balance_to_coldkey_account(&coldkey2, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey1, 1_000_000_000_000_u64.into()); + add_balance_to_coldkey_account(&coldkey2, 1_000_000_000_000_u64.into()); // Stake with coldkey1 assert_ok!(SubtensorModule::add_stake( @@ -800,7 +800,7 @@ fn test_swap_hotkey_tx_rate_limit_exceeded() { // Setup initial state add_network(netuid, tempo, 0); register_ok_neuron(netuid, old_hotkey, coldkey, 0); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, swap_cost); + add_balance_to_coldkey_account(&coldkey, swap_cost); // Perform the first swap System::set_block_number(System::block_number() + HotkeySwapOnSubnetInterval::get()); @@ -852,7 +852,7 @@ fn test_do_swap_hotkey_err_not_owner() { // Setup initial state add_network(netuid, tempo, 0); register_ok_neuron(netuid, old_hotkey, coldkey, 0); - SubtensorModule::add_balance_to_coldkey_account(¬_owner_coldkey, swap_cost); + add_balance_to_coldkey_account(¬_owner_coldkey, swap_cost); // Attempt the swap with a non-owner coldkey assert_err!( @@ -877,7 +877,7 @@ fn test_swap_owner_old_hotkey_not_exist() { let coldkey = U256::from(3); let netuid = add_dynamic_network(&new_hotkey, &coldkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into()); // Ensure old_hotkey does not exist assert!(!Owner::::contains_key(old_hotkey)); @@ -910,7 +910,7 @@ fn test_swap_owner_new_hotkey_already_exists() { let another_coldkey = U256::from(4); let netuid = add_dynamic_network(&new_hotkey, &coldkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into()); // Initialize Owner for old_hotkey and new_hotkey Owner::::insert(old_hotkey, coldkey); @@ -946,7 +946,7 @@ fn test_swap_stake_success() { let subnet_owner_hotkey = U256::from(1002); let netuid = add_dynamic_network(&old_hotkey, &coldkey); remove_owner_registration_stake(netuid); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into()); let amount = 10_000; let shares = U64F64::from_num(10_000); @@ -1033,7 +1033,7 @@ fn test_swap_stake_v2_success() { let subnet_owner_coldkey = U256::from(1001); let subnet_owner_hotkey = U256::from(1002); let netuid = add_dynamic_network(&old_hotkey, &coldkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into()); let amount = 10_000; let shares = U64F64::from_num(10_000); @@ -1136,7 +1136,7 @@ fn test_swap_hotkey_error_cases() { ); let initial_balance = SubtensorModule::get_key_swap_cost() + 1000.into(); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, initial_balance); + add_balance_to_coldkey_account(&coldkey, initial_balance); // Test new hotkey same as old System::set_block_number(System::block_number() + HotkeySwapOnSubnetInterval::get()); @@ -1198,7 +1198,7 @@ fn test_swap_child_keys() { let new_hotkey = U256::from(2); let coldkey = U256::from(3); let netuid = add_dynamic_network(&old_hotkey, &coldkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into()); let children = vec![(100u64, U256::from(4)), (200u64, U256::from(5))]; @@ -1231,7 +1231,7 @@ fn test_swap_child_keys_self_loop() { let coldkey = U256::from(3); let netuid = add_dynamic_network(&old_hotkey, &coldkey); let amount = AlphaBalance::from(12345); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into()); // Only for checking TotalHotkeyAlpha::::insert(old_hotkey, netuid, AlphaBalance::from(amount)); @@ -1272,7 +1272,7 @@ fn test_swap_parent_keys() { let new_hotkey = U256::from(2); let coldkey = U256::from(3); let netuid = add_dynamic_network(&old_hotkey, &coldkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into()); let parents = vec![(100u64, U256::from(4)), (200u64, U256::from(5))]; // Initialize ParentKeys for old_hotkey @@ -1319,7 +1319,7 @@ fn test_swap_multiple_subnets() { let netuid1 = add_dynamic_network(&old_hotkey, &coldkey); let netuid2 = add_dynamic_network(&old_hotkey, &coldkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into()); let children1 = vec![(100u64, U256::from(4)), (200u64, U256::from(5))]; let children2 = vec![(300u64, U256::from(6))]; @@ -1363,7 +1363,7 @@ fn test_swap_complex_parent_child_structure() { let new_hotkey = U256::from(2); let coldkey = U256::from(3); let netuid = add_dynamic_network(&old_hotkey, &coldkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into()); let parent1 = U256::from(4); let parent2 = U256::from(5); let child1 = U256::from(6); @@ -1429,7 +1429,7 @@ fn test_swap_parent_hotkey_childkey_maps() { let parent_new = U256::from(5); let netuid = add_dynamic_network(&parent_old, &coldkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into()); SubtensorModule::create_account_if_non_existent(&coldkey, &parent_old); @@ -1486,7 +1486,7 @@ fn test_swap_child_hotkey_childkey_maps() { let child_old = U256::from(3); let child_new = U256::from(4); let netuid = add_dynamic_network(&child_old, &coldkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into()); SubtensorModule::create_account_if_non_existent(&coldkey, &child_old); SubtensorModule::create_account_if_non_existent(&coldkey, &parent); @@ -1546,7 +1546,7 @@ fn test_swap_hotkey_is_sn_owner_hotkey() { // Create dynamic network let netuid = add_dynamic_network(&old_hotkey, &coldkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into()); // Check for SubnetOwnerHotkey assert_eq!(SubnetOwnerHotkey::::get(netuid), old_hotkey); @@ -1579,7 +1579,7 @@ fn test_swap_hotkey_swap_rate_limits() { let child_key_take_block = 8910; let netuid = add_dynamic_network(&old_hotkey, &coldkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into()); // Set the last tx block for the old hotkey SubtensorModule::set_last_tx_block(&old_hotkey, last_tx_block); @@ -1622,7 +1622,7 @@ fn test_swap_owner_failed_interval_not_passed() { let coldkey = U256::from(3); let netuid = add_dynamic_network(&old_hotkey, &coldkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into()); Owner::::insert(old_hotkey, coldkey); assert_err!( SubtensorModule::do_swap_hotkey( @@ -1645,7 +1645,7 @@ fn test_swap_owner_check_swap_block_set() { let coldkey = U256::from(3); let netuid = add_dynamic_network(&old_hotkey, &coldkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into()); Owner::::insert(old_hotkey, coldkey); let new_block_number = System::block_number() + HotkeySwapOnSubnetInterval::get(); System::set_block_number(new_block_number); @@ -1671,7 +1671,7 @@ fn test_swap_owner_check_swap_record_clean_up() { let new_hotkey = U256::from(2); let coldkey = U256::from(3); let netuid = add_dynamic_network(&old_hotkey, &coldkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into()); Owner::::insert(old_hotkey, coldkey); let new_block_number = System::block_number() + HotkeySwapOnSubnetInterval::get(); System::set_block_number(new_block_number); @@ -1713,7 +1713,7 @@ fn test_revert_hotkey_swap_stake_is_not_lost() { add_network(netuid2, tempo, 0); register_ok_neuron(netuid, hk1, coldkey, 0); register_ok_neuron(netuid2, hk1, coldkey, 0); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, swap_cost.into()); + add_balance_to_coldkey_account(&coldkey, swap_cost.into()); let hk1_stake_before_increase = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hk1, &coldkey, netuid); @@ -1808,7 +1808,7 @@ fn test_hotkey_swap_keep_stake() { // Setup add_network(netuid, tempo, 0); register_ok_neuron(netuid, old_hotkey, coldkey, 0); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, swap_cost.into()); + add_balance_to_coldkey_account(&coldkey, swap_cost.into()); VotingPower::::insert(netuid, old_hotkey, voting_power_value); assert_eq!( @@ -1942,7 +1942,7 @@ fn test_revert_hotkey_swap() { add_network(netuid2, tempo, 0); register_ok_neuron(netuid, old_hotkey, coldkey, 0); register_ok_neuron(netuid2, old_hotkey, coldkey, 0); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, swap_cost.into()); + add_balance_to_coldkey_account(&coldkey, swap_cost.into()); step_block(20); // Perform the first swap (only on netuid) @@ -1982,7 +1982,7 @@ fn test_revert_hotkey_swap_parent_hotkey_childkey_maps() { let netuid = add_dynamic_network(&hk1, &coldkey); let netuid2 = add_dynamic_network(&hk1, &coldkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into()); SubtensorModule::create_account_if_non_existent(&coldkey, &hk1); mock_set_children(&coldkey, &hk1, netuid, &[(u64::MAX, child)]); @@ -2065,7 +2065,7 @@ fn test_revert_hotkey_swap_uids_and_keys() { let netuid = add_dynamic_network(&hk1, &coldkey); let netuid2 = add_dynamic_network(&hk1, &coldkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into()); IsNetworkMember::::insert(hk1, netuid, true); Uids::::insert(netuid, hk1, uid); @@ -2129,7 +2129,7 @@ fn test_revert_hotkey_swap_auto_stake_destination() { add_network(netuid2, 1, 0); register_ok_neuron(netuid, hk1, coldkey, 0); register_ok_neuron(netuid2, hk1, coldkey, 0); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into()); AutoStakeDestinationColdkeys::::insert(hk1, netuid, coldkeys.clone()); AutoStakeDestination::::insert(coldkey, netuid, hk1); @@ -2210,7 +2210,7 @@ fn test_revert_hotkey_swap_subnet_owner() { let netuid = add_dynamic_network(&hk1, &coldkey); let netuid2 = add_dynamic_network(&hk1, &coldkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into()); assert_eq!(SubnetOwnerHotkey::::get(netuid), hk1); @@ -2259,7 +2259,7 @@ fn test_revert_hotkey_swap_dividends() { remove_owner_registration_stake(netuid); let netuid2 = add_dynamic_network(&hk1, &coldkey); remove_owner_registration_stake(netuid2); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into()); let amount = 10_000; let shares = U64F64::from_num(10_000); @@ -2448,7 +2448,7 @@ fn test_revert_claim_root_with_swap_hotkey() { let netuid = add_dynamic_network(&hk1, &owner_coldkey); let netuid2 = add_dynamic_network(&hk1, &owner_coldkey); - SubtensorModule::add_balance_to_coldkey_account(&owner_coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&owner_coldkey, 1_000_000_000_000_u64.into()); SubtensorModule::set_tao_weight(u64::MAX); let root_stake = 2_000_000u64; @@ -2569,9 +2569,9 @@ fn test_swap_hotkey_with_existing_stake() { register_ok_neuron(netuid, new_hotkey, coldkey, 1234); // Add balance to coldkeys - SubtensorModule::add_balance_to_coldkey_account(&coldkey, 10_000_000_000_u64.into()); - SubtensorModule::add_balance_to_coldkey_account(&staker1, 10_000_000_000_u64.into()); - SubtensorModule::add_balance_to_coldkey_account(&staker2, 10_000_000_000_u64.into()); + add_balance_to_coldkey_account(&coldkey, 10_000_000_000_u64.into()); + add_balance_to_coldkey_account(&staker1, 10_000_000_000_u64.into()); + add_balance_to_coldkey_account(&staker2, 10_000_000_000_u64.into()); // Stake with staker1 coldkey on old_hotkey assert_ok!(SubtensorModule::add_stake( @@ -2741,9 +2741,9 @@ fn test_revert_hotkey_swap_with_revert_stake_the_same() { register_ok_neuron(netuid_1, hk1, coldkey, 0); register_ok_neuron(netuid_2, hk1, coldkey, 0); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, initial_balance.into()); - SubtensorModule::add_balance_to_coldkey_account(&coldkey_4, initial_balance.into()); - SubtensorModule::add_balance_to_coldkey_account(&random_coldkey, initial_balance.into()); + add_balance_to_coldkey_account(&coldkey, initial_balance.into()); + add_balance_to_coldkey_account(&coldkey_4, initial_balance.into()); + add_balance_to_coldkey_account(&random_coldkey, initial_balance.into()); step_block(20); // Waiting interval to be able to swap later // Checking stake for hk1 on both networks @@ -2927,7 +2927,7 @@ fn test_swap_hotkey_root_claims_unchanged_if_not_root() { let netuid = add_dynamic_network(&neuron_hotkey, &owner_coldkey); let new_hotkey = U256::from(10030); - SubtensorModule::add_balance_to_coldkey_account(&owner_coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&owner_coldkey, u64::MAX.into()); SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 let root_stake = 2_000_000_000u64; @@ -3013,7 +3013,7 @@ fn test_swap_hotkey_root_claims_changed_if_root() { // Use neuron_hotkey as subnet creator so it receives root dividends let netuid_1 = add_dynamic_network(&neuron_hotkey, &owner_coldkey); - SubtensorModule::add_balance_to_coldkey_account(&owner_coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&owner_coldkey, u64::MAX.into()); SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 let root_stake = 2_000_000_000u64; @@ -3102,7 +3102,7 @@ fn test_swap_hotkey_root_claims_changed_if_all_subnets() { // Use neuron_hotkey as subnet creator so it receives root dividends let netuid_1 = add_dynamic_network(&neuron_hotkey, &owner_coldkey); - SubtensorModule::add_balance_to_coldkey_account(&owner_coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&owner_coldkey, u64::MAX.into()); SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 let root_stake = 2_000_000_000u64; @@ -3183,7 +3183,7 @@ fn test_swap_hotkey_auto_parent_delegation_transferred_on_root() { let new_hotkey = U256::from(1005); let _ = add_dynamic_network(&old_hotkey, &owner_coldkey); - SubtensorModule::add_balance_to_coldkey_account(&owner_coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&owner_coldkey, u64::MAX.into()); // Opt out of auto parent delegation on the old hotkey. AutoParentDelegationEnabled::::insert(old_hotkey, false); @@ -3224,7 +3224,7 @@ fn test_swap_hotkey_auto_parent_delegation_transferred_on_all_subnets() { NetworksAdded::::insert(NetUid::ROOT, true); let _ = add_dynamic_network(&old_hotkey, &owner_coldkey); - SubtensorModule::add_balance_to_coldkey_account(&owner_coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&owner_coldkey, u64::MAX.into()); AutoParentDelegationEnabled::::insert(old_hotkey, false); @@ -3256,7 +3256,7 @@ fn test_swap_hotkey_auto_parent_delegation_not_transferred_on_non_root() { let new_hotkey = U256::from(1005); let netuid = add_dynamic_network(&old_hotkey, &owner_coldkey); - SubtensorModule::add_balance_to_coldkey_account(&owner_coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&owner_coldkey, u64::MAX.into()); AutoParentDelegationEnabled::::insert(old_hotkey, false); diff --git a/pallets/subtensor/src/tests/tao.rs b/pallets/subtensor/src/tests/tao.rs new file mode 100644 index 0000000000..b79b80e3f3 --- /dev/null +++ b/pallets/subtensor/src/tests/tao.rs @@ -0,0 +1,516 @@ +#![allow( + unused, + clippy::indexing_slicing, + clippy::panic, + clippy::unwrap_used, + clippy::expect_used +)] + +use super::mock_high_ed::*; +use crate::tests::mock_high_ed; +use crate::*; +use frame_support::{ + assert_noop, assert_ok, + traits::{ + Imbalance, + tokens::{Fortitude, Preservation, fungible::Inspect as _}, + }, +}; +use sp_core::U256; +use sp_runtime::traits::{AccountIdConversion, Zero}; +use subtensor_runtime_common::TaoBalance; + +const MAX_TAO_ISSUANCE: u64 = 21_000_000_000_000_000_u64; + +/// Helper: balances-pallet total issuance. +fn balances_total_issuance() -> TaoBalance { + ::Currency::total_issuance() +} + +/// Helper: subtensor-pallet total issuance. +fn subtensor_total_issuance() -> TaoBalance { + TotalIssuance::::get() +} + +/// Helper: free/reducible balance view used by tao.rs. +fn reducible_balance(account: &U256) -> TaoBalance { + SubtensorModule::get_coldkey_balance(account) +} + +/// Helper: total balance as seen by the currency implementation. +fn total_balance(account: &U256) -> TaoBalance { + Balances::total_balance(account) +} + +// ---------------------------------------------------- +// transfer_tao +// ---------------------------------------------------- + +#[test] +fn test_transfer_tao_normal_case() { + new_test_ext(1).execute_with(|| { + let origin = U256::from(1); + let dest = U256::from(2); + + let amount = TaoBalance::from(200); + add_balance_to_coldkey_account(&origin, ExistentialDeposit::get() * 10.into() + amount); + let origin_before = total_balance(&origin); + let dest_before = total_balance(&dest); + + assert!(origin_before >= amount); + + assert_ok!(SubtensorModule::transfer_tao(&origin, &dest, amount)); + + assert_eq!(total_balance(&origin), origin_before - amount); + assert_eq!(total_balance(&dest), dest_before + amount); + assert_eq!(balances_total_issuance(), subtensor_total_issuance()); + }); +} + +#[test] +fn test_transfer_tao_zero_balance_zero_amount() { + new_test_ext(1).execute_with(|| { + let origin = U256::from(10_001); + let dest = U256::from(10_002); + + assert_eq!(total_balance(&origin), 0.into()); + assert_eq!(total_balance(&dest), 0.into()); + + assert_ok!(SubtensorModule::transfer_tao(&origin, &dest, 0.into())); + + assert_eq!(total_balance(&origin), 0.into()); + assert_eq!(total_balance(&dest), 0.into()); + assert_eq!(balances_total_issuance(), subtensor_total_issuance()); + }); +} + +#[test] +fn test_transfer_tao_zero_balance_non_zero_amount_fails() { + new_test_ext(1).execute_with(|| { + let origin = U256::from(10_011); + let dest = U256::from(10_012); + + assert_eq!(total_balance(&origin), 0.into()); + + assert_noop!( + SubtensorModule::transfer_tao(&origin, &dest, 1u64.into()), + Error::::InsufficientBalance + ); + + assert_eq!(total_balance(&origin), 0.into()); + assert_eq!(total_balance(&dest), 0.into()); + }); +} + +#[test] +fn test_transfer_tao_amount_greater_than_transferrable_fails() { + new_test_ext(1).execute_with(|| { + let origin = U256::from(1); + let dest = U256::from(2); + + let max_transferrable = reducible_balance(&origin); + let amount = max_transferrable + 1.into(); + + assert_noop!( + SubtensorModule::transfer_tao(&origin, &dest, amount.into()), + Error::::InsufficientBalance + ); + }); +} + +#[test] +fn test_transfer_tao_transfer_exactly_transferrable_succeeds() { + new_test_ext(1).execute_with(|| { + let origin = U256::from(1); + let dest = U256::from(2); + + let amount = reducible_balance(&origin); + let origin_before = total_balance(&origin); + let dest_before = total_balance(&dest); + + assert_ok!(SubtensorModule::transfer_tao(&origin, &dest, amount.into())); + + assert_eq!(total_balance(&origin), origin_before - amount); + assert_eq!(total_balance(&dest), dest_before + amount); + }); +} + +#[test] +fn test_transfer_tao_can_reap_origin_when_amount_brings_it_below_ed() { + new_test_ext(1).execute_with(|| { + let origin = U256::from(1); + let dest = U256::from(2); + + let ed = ExistentialDeposit::get(); + let amount = TaoBalance::from(100); + let balance_origin = amount + ed - 1.into(); + let balance_dest = ed + 1234.into(); + add_balance_to_coldkey_account(&origin, balance_origin); + add_balance_to_coldkey_account(&dest, balance_dest); + + assert_ok!(SubtensorModule::transfer_tao(&origin, &dest, amount)); + + // With Preservation::Expendable, origin may be reaped. + assert!(total_balance(&origin).is_zero()); + assert_eq!(total_balance(&dest), amount + balance_dest); + + // Issuance should not change on plain transfer. + assert_eq!(balances_total_issuance(), subtensor_total_issuance()); + }); +} + +#[test] +fn test_transfer_tao_to_self_is_ok_and_no_net_balance_change() { + new_test_ext(1).execute_with(|| { + let who = U256::from(1); + let before = total_balance(&who); + let amount = reducible_balance(&who).min(10.into()); + + assert_ok!(SubtensorModule::transfer_tao(&who, &who, amount)); + + assert_eq!(total_balance(&who), before); + }); +} + +// ---------------------------------------------------- +// transfer_all_tao_and_kill +// ---------------------------------------------------- + +#[test] +fn test_transfer_all_tao_and_kill_normal_case() { + new_test_ext(1).execute_with(|| { + let origin = U256::from(1); + let dest = U256::from(2); + + let ed = ExistentialDeposit::get(); + let amount = TaoBalance::from(100); + let balance_origin = amount + ed; + add_balance_to_coldkey_account(&origin, balance_origin); + + let transferable = reducible_balance(&origin); + let origin_before = total_balance(&origin); + let dest_before = total_balance(&dest); + + assert!(!transferable.is_zero()); + + assert_ok!(SubtensorModule::transfer_all_tao_and_kill(&origin, &dest)); + + assert_eq!(total_balance(&dest), dest_before + transferable); + assert_eq!( + total_balance(&origin), + origin_before.saturating_sub(transferable) + ); + assert_eq!(reducible_balance(&origin), 0.into()); + }); +} + +#[test] +fn test_transfer_all_tao_and_kill_non_existing_origin_is_noop() { + new_test_ext(1).execute_with(|| { + let origin = U256::from(20_001); + let dest = U256::from(20_002); + + assert_eq!(total_balance(&origin), 0.into()); + assert_eq!(reducible_balance(&origin), 0.into()); + + let dest_before = total_balance(&dest); + + assert_ok!(SubtensorModule::transfer_all_tao_and_kill(&origin, &dest)); + + assert_eq!(total_balance(&origin), 0.into()); + assert_eq!(total_balance(&dest), dest_before); + }); +} + +#[test] +fn test_transfer_all_tao_and_kill_preexisting_destination() { + new_test_ext(1).execute_with(|| { + let origin = U256::from(1); + let dest = U256::from(2); + + let amount_o = TaoBalance::from(200) + ExistentialDeposit::get(); + let amount_d = TaoBalance::from(1000) + ExistentialDeposit::get(); + add_balance_to_coldkey_account(&origin, amount_o); + add_balance_to_coldkey_account(&dest, amount_d); + + let transferable = reducible_balance(&origin); + let dest_before = total_balance(&dest); + + assert!(dest_before > 0.into()); + + assert_ok!(SubtensorModule::transfer_all_tao_and_kill(&origin, &dest)); + + assert_eq!(total_balance(&dest), dest_before + transferable); + assert_eq!(reducible_balance(&origin), 0.into()); + }); +} + +#[test] +fn test_transfer_all_tao_and_kill_to_self_is_noop() { + new_test_ext(1).execute_with(|| { + let who = U256::from(1); + let before_total = total_balance(&who); + let before_reducible = reducible_balance(&who); + + assert_ok!(SubtensorModule::transfer_all_tao_and_kill(&who, &who)); + + assert_eq!(total_balance(&who), before_total); + assert_eq!(reducible_balance(&who), before_reducible); + }); +} + +// ---------------------------------------------------- +// burn_tao +// ---------------------------------------------------- + +#[test] +fn test_burn_tao_increases_burn_address_balance() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let burn_address: U256 = ::BurnAccountId::get().into_account_truncating(); + + let amount = reducible_balance(&coldkey).min(10.into()); + let burn_before = total_balance(&burn_address); + let coldkey_before = total_balance(&coldkey); + + assert_ok!(SubtensorModule::burn_tao(&coldkey, amount)); + + assert_eq!(total_balance(&burn_address), burn_before + amount); + assert_eq!(total_balance(&coldkey), coldkey_before - amount); + + // burn_tao is just a transfer to burn address, not issuance reduction. + assert_eq!(balances_total_issuance(), subtensor_total_issuance()); + }); +} + +#[test] +fn test_burn_tao_zero_amount_is_ok() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let burn_address: U256 = ::BurnAccountId::get().into_account_truncating(); + + let burn_before = total_balance(&burn_address); + let coldkey_before = total_balance(&coldkey); + + assert_ok!(SubtensorModule::burn_tao(&coldkey, 0u64.into())); + + assert_eq!(total_balance(&burn_address), burn_before); + assert_eq!(total_balance(&coldkey), coldkey_before); + }); +} + +#[test] +fn test_burn_tao_insufficient_balance_fails() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(30_001); + + assert_noop!( + SubtensorModule::burn_tao(&coldkey, 1u64.into()), + Error::::InsufficientBalance + ); + }); +} + +// ---------------------------------------------------- +// recycle_tao / issuance consistency +// ---------------------------------------------------- + +#[test] +fn test_recycle_tao_reduces_both_balances_and_subtensor_total_issuance() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let max_preserving = SubtensorModule::get_coldkey_balance(&coldkey); + let amount = max_preserving.min(10.into()); + + let coldkey_before = total_balance(&coldkey); + let balances_ti_before = balances_total_issuance(); + let subtensor_ti_before = subtensor_total_issuance(); + + assert_ok!(SubtensorModule::recycle_tao(&coldkey, amount)); + + assert_eq!(total_balance(&coldkey), coldkey_before - amount); + + // Balances-pallet withdraw burns supply. + assert_eq!(balances_total_issuance(), balances_ti_before - amount); + + // Subtensor TI is reduced explicitly in recycle_tao. + assert_eq!(subtensor_total_issuance(), subtensor_ti_before - amount); + + // End state still aligned. + assert_eq!(balances_total_issuance(), subtensor_total_issuance()); + }); +} + +#[test] +fn test_recycle_tao_amount_greater_than_max_preserving_fails() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let max_preserving: u64 = ::Currency::reducible_balance( + &coldkey, + frame_support::traits::tokens::Preservation::Preserve, + frame_support::traits::tokens::Fortitude::Polite, + ) + .into(); + + let too_much = max_preserving.saturating_add(1); + + assert_noop!( + SubtensorModule::recycle_tao(&coldkey, too_much.into()), + Error::::InsufficientBalance + ); + + assert_eq!(balances_total_issuance(), subtensor_total_issuance()); + }); +} + +#[test] +fn test_recycle_tao_zero_amount_keeps_issuance_equal() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let balances_before = balances_total_issuance(); + let subtensor_before = subtensor_total_issuance(); + let balance_before = total_balance(&coldkey); + + assert_ok!(SubtensorModule::recycle_tao(&coldkey, 0u64.into())); + + assert_eq!(total_balance(&coldkey), balance_before); + assert_eq!(balances_total_issuance(), balances_before); + assert_eq!(subtensor_total_issuance(), subtensor_before); + assert_eq!(balances_total_issuance(), subtensor_total_issuance()); + }); +} + +/// This is the invariant you asked for in normal ED=1 test runtime: +/// plain transfers and burns should keep both issuance trackers aligned, +/// and recycle should reduce both by the same amount. +#[test] +fn test_total_issuance_subtensor_matches_balances_across_tao_operations() { + new_test_ext(1).execute_with(|| { + let a = U256::from(1); + let b = U256::from(2); + + let ed = ExistentialDeposit::get(); + let balance = TaoBalance::from(1_000_000) + ed - 1.into(); + add_balance_to_coldkey_account(&a, balance); + add_balance_to_coldkey_account(&b, balance); + + assert_eq!(balances_total_issuance(), subtensor_total_issuance()); + + assert_ok!(SubtensorModule::transfer_tao(&a, &b, 1000.into())); + assert_eq!(balances_total_issuance(), subtensor_total_issuance()); + + assert_ok!(SubtensorModule::burn_tao(&a, 1000.into())); + assert_eq!(balances_total_issuance(), subtensor_total_issuance()); + + let max_preserving: u64 = ::Currency::reducible_balance( + &a, + frame_support::traits::tokens::Preservation::Preserve, + frame_support::traits::tokens::Fortitude::Polite, + ) + .into(); + let recycle_amount = max_preserving.min(1); + assert_ok!(SubtensorModule::recycle_tao(&a, recycle_amount.into())); + assert_eq!(balances_total_issuance(), subtensor_total_issuance()); + }); +} + +// ---------------------------------------------------- +// mint_tao +// ---------------------------------------------------- + +/// This is expected to fail with the current implementation: +/// mint_tao issues into the balances pallet but does not update +/// SubtensorModule::TotalIssuance. +#[test] +fn test_mint_tao_increases_total_issuance_in_balances_and_subtensor() { + new_test_ext(1).execute_with(|| { + let amount = TaoBalance::from(123); + + let balances_before = balances_total_issuance(); + let subtensor_before = subtensor_total_issuance(); + + let credit = SubtensorModule::mint_tao(amount); + + assert_eq!(credit.peek(), amount); + + // This one should pass. + assert_eq!(balances_total_issuance(), balances_before + amount); + + // This one is expected to fail until mint_tao updates TotalIssuance::. + assert_eq!(subtensor_total_issuance(), subtensor_before + amount); + }); +} + +#[test] +fn test_mint_tao_zero_amount() { + new_test_ext(1).execute_with(|| { + let balances_before = balances_total_issuance(); + let subtensor_before = subtensor_total_issuance(); + + let credit = SubtensorModule::mint_tao(0u64.into()); + + assert_eq!(u64::from(credit.peek()), 0); + assert_eq!(balances_total_issuance(), balances_before); + assert_eq!(subtensor_total_issuance(), subtensor_before); + }); +} + +#[test] +fn test_mint_tao_respects_max_issuance_cap_in_balances() { + new_test_ext(1).execute_with(|| { + // We cannot directly force balances-pallet issuance above the cap in every mock, + // but we *can* set subtensor's mirror and still verify that mint_tao uses the + // balances-pallet total issuance as its source of truth. + // + // This test is mostly a guard that the returned credit is capped by + // MAX_TAO_ISSUANCE - Currency::total_issuance(). + let balances_before = balances_total_issuance(); + let remaining = TaoBalance::from(MAX_TAO_ISSUANCE) - balances_before; + let request = remaining + 1000.into(); + + let credit = SubtensorModule::mint_tao(request.into()); + + assert_eq!(credit.peek(), remaining); + assert_eq!(balances_total_issuance(), MAX_TAO_ISSUANCE.into()); + }); +} + +#[test] +fn test_transfer_tao_reaps_origin() { + new_test_ext(1).execute_with(|| { + let origin = U256::from(1); + let dest = U256::from(2); + + let ed = ExistentialDeposit::get(); + let balance_origin = TaoBalance::from(3) + ed; + let amount = TaoBalance::from(2) + ed; + add_balance_to_coldkey_account(&origin, balance_origin); + let subtensor_ti_before = subtensor_total_issuance(); + let balances_ti_before = balances_total_issuance(); + + assert_ok!(SubtensorModule::transfer_tao(&origin, &dest, amount)); + + let subtensor_ti_after = subtensor_total_issuance(); + let balances_ti_after = balances_total_issuance(); + + assert_eq!(Balances::total_balance(&origin), 0.into()); + assert_eq!(Balances::total_balance(&dest), amount); + assert_eq!(balances_ti_before - balances_ti_after, 1.into()); + assert_eq!(subtensor_ti_before - subtensor_ti_after, 1.into()); + }); +} + +#[test] +fn test_recycle_tao_cannot_cross_preserve_threshold_in_high_ed_runtime() { + new_test_ext(1).execute_with(|| { + let origin = U256::from(1); + + let max_preserving = + Balances::reducible_balance(&origin, Preservation::Preserve, Fortitude::Polite); + + assert_noop!( + SubtensorModule::recycle_tao(&origin, max_preserving + 1u64.into()), + Error::::InsufficientBalance + ); + }); +} diff --git a/pallets/subtensor/src/tests/voting_power.rs b/pallets/subtensor/src/tests/voting_power.rs index 3ffb1a6611..9af3639b99 100644 --- a/pallets/subtensor/src/tests/voting_power.rs +++ b/pallets/subtensor/src/tests/voting_power.rs @@ -77,7 +77,7 @@ impl VotingPowerTestFixture { #[allow(clippy::arithmetic_side_effects)] fn setup_for_staking_with_amount(&self, amount: u64) { mock::setup_reserves(self.netuid, (amount * 100).into(), (amount * 100).into()); - SubtensorModule::add_balance_to_coldkey_account(&self.coldkey, (amount * 10).into()); + add_balance_to_coldkey_account(&self.coldkey, (amount * 10).into()); } /// Enable voting power tracking for the subnet @@ -401,10 +401,7 @@ fn test_only_validators_get_voting_power() { (DEFAULT_STAKE_AMOUNT * 100).into(), (DEFAULT_STAKE_AMOUNT * 100).into(), ); - SubtensorModule::add_balance_to_coldkey_account( - &coldkey, - (DEFAULT_STAKE_AMOUNT * 20).into(), - ); + add_balance_to_coldkey_account(&coldkey, (DEFAULT_STAKE_AMOUNT * 20).into()); // Register miner register_ok_neuron(netuid, miner_hotkey, coldkey, 0); diff --git a/pallets/subtensor/src/tests/weights.rs b/pallets/subtensor/src/tests/weights.rs index 5237cca131..36cf17bfd8 100644 --- a/pallets/subtensor/src/tests/weights.rs +++ b/pallets/subtensor/src/tests/weights.rs @@ -119,7 +119,7 @@ fn test_commit_weights_validate() { SubtensorModule::append_neuron(netuid, &hotkey, 0); crate::Owner::::insert(hotkey, coldkey); - SubtensorModule::add_balance_to_coldkey_account(&hotkey, u64::MAX.into()); + add_balance_to_coldkey_account(&hotkey, u64::MAX.into()); let min_stake = 500_000_000_000_u64; let reserve = min_stake * 1000; @@ -255,7 +255,7 @@ fn test_set_weights_validate() { SubtensorModule::append_neuron(netuid, &hotkey, 0); crate::Owner::::insert(hotkey, coldkey); - SubtensorModule::add_balance_to_coldkey_account(&hotkey, u64::MAX.into()); + add_balance_to_coldkey_account(&hotkey, u64::MAX.into()); let min_stake = TaoBalance::from(500_000_000_000_u64); @@ -361,7 +361,7 @@ fn test_reveal_weights_validate() { SubtensorModule::append_neuron(netuid, &hotkey2, 0); crate::Owner::::insert(hotkey, coldkey); crate::Owner::::insert(hotkey2, coldkey); - SubtensorModule::add_balance_to_coldkey_account(&hotkey, u64::MAX.into()); + add_balance_to_coldkey_account(&hotkey, u64::MAX.into()); let min_stake = TaoBalance::from(500_000_000_000_u64); // Set the minimum stake @@ -544,7 +544,7 @@ fn test_batch_reveal_weights_validate() { SubtensorModule::append_neuron(netuid, &hotkey2, 0); crate::Owner::::insert(hotkey, coldkey); crate::Owner::::insert(hotkey2, coldkey); - SubtensorModule::add_balance_to_coldkey_account(&hotkey, u64::MAX.into()); + add_balance_to_coldkey_account(&hotkey, u64::MAX.into()); SubtensorModule::set_commit_reveal_weights_enabled(netuid, true); let min_stake = TaoBalance::from(500_000_000_000_u64); @@ -782,7 +782,7 @@ fn test_set_stake_threshold_failed() { add_network_disable_commit_reveal(netuid, 1, 0); register_ok_neuron(netuid, hotkey, coldkey, 2143124); SubtensorModule::set_stake_threshold(20_000_000_000_000); - SubtensorModule::add_balance_to_coldkey_account(&hotkey, u64::MAX.into()); + add_balance_to_coldkey_account(&hotkey, u64::MAX.into()); // Check the signed extension function. assert_eq!(SubtensorModule::get_stake_threshold(), 20_000_000_000_000); @@ -928,7 +928,7 @@ fn test_weights_err_setting_weights_too_fast() { SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey_account_id) .expect("Not registered."); SubtensorModule::set_validator_permit_for_uid(netuid, neuron_uid, true); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(66), 1.into()); + add_balance_to_coldkey_account(&U256::from(66), 1.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey_account_id, &(U256::from(66)), @@ -1021,7 +1021,7 @@ fn test_weights_err_has_duplicate_ids() { SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey_account_id) .expect("Not registered."); SubtensorModule::set_validator_permit_for_uid(netuid, neuron_uid, true); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(77), 1.into()); + add_balance_to_coldkey_account(&U256::from(77), 1.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey_account_id, &(U256::from(77)), @@ -1124,7 +1124,7 @@ fn test_set_weights_err_invalid_uid() { .expect("Not registered."); SubtensorModule::set_stake_threshold(0); SubtensorModule::set_validator_permit_for_uid(netuid, neuron_uid, true); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(66), 1.into()); + add_balance_to_coldkey_account(&U256::from(66), 1.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey_account_id, &(U256::from(66)), @@ -1160,7 +1160,7 @@ fn test_set_weight_not_enough_values() { let neuron_uid: u16 = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &U256::from(1)) .expect("Not registered."); SubtensorModule::set_validator_permit_for_uid(netuid, neuron_uid, true); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(2), 1.into()); + add_balance_to_coldkey_account(&U256::from(2), 1.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &account_id, &(U256::from(2)), @@ -1268,7 +1268,7 @@ fn test_set_weights_sum_larger_than_u16_max() { .expect("Not registered."); SubtensorModule::set_stake_threshold(0); SubtensorModule::set_validator_permit_for_uid(netuid, neuron_uid, true); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(2), 1.into()); + add_balance_to_coldkey_account(&U256::from(2), 1.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &(U256::from(1)), &(U256::from(2)), @@ -1731,8 +1731,8 @@ fn test_commit_reveal_weights_ok() { SubtensorModule::set_validator_permit_for_uid(netuid, 0, true); SubtensorModule::set_validator_permit_for_uid(netuid, 1, true); SubtensorModule::set_commit_reveal_weights_enabled(netuid, true); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(0), 1.into()); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(1), 1.into()); + add_balance_to_coldkey_account(&U256::from(0), 1.into()); + add_balance_to_coldkey_account(&U256::from(1), 1.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &(U256::from(0)), &(U256::from(0)), @@ -1799,8 +1799,8 @@ fn test_commit_reveal_tempo_interval() { SubtensorModule::set_validator_permit_for_uid(netuid, 0, true); SubtensorModule::set_validator_permit_for_uid(netuid, 1, true); SubtensorModule::set_commit_reveal_weights_enabled(netuid, true); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(0), 1.into()); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(1), 1.into()); + add_balance_to_coldkey_account(&U256::from(0), 1.into()); + add_balance_to_coldkey_account(&U256::from(1), 1.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &(U256::from(0)), &(U256::from(0)), @@ -1934,8 +1934,8 @@ fn test_commit_reveal_hash() { SubtensorModule::set_weights_set_rate_limit(netuid, 5); SubtensorModule::set_validator_permit_for_uid(netuid, 0, true); SubtensorModule::set_validator_permit_for_uid(netuid, 1, true); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(0), 1.into()); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(1), 1.into()); + add_balance_to_coldkey_account(&U256::from(0), 1.into()); + add_balance_to_coldkey_account(&U256::from(1), 1.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &(U256::from(0)), &(U256::from(0)), @@ -2034,8 +2034,8 @@ fn test_commit_reveal_disabled_or_enabled() { SubtensorModule::set_weights_set_rate_limit(netuid, 5); SubtensorModule::set_validator_permit_for_uid(netuid, 0, true); SubtensorModule::set_validator_permit_for_uid(netuid, 1, true); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(0), 1.into()); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(1), 1.into()); + add_balance_to_coldkey_account(&U256::from(0), 1.into()); + add_balance_to_coldkey_account(&U256::from(1), 1.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &(U256::from(0)), &(U256::from(0)), @@ -2111,8 +2111,8 @@ fn test_toggle_commit_reveal_weights_and_set_weights() { SubtensorModule::set_validator_permit_for_uid(netuid, 0, true); SubtensorModule::set_validator_permit_for_uid(netuid, 1, true); SubtensorModule::set_weights_set_rate_limit(netuid, 5); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(0), 1.into()); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(1), 1.into()); + add_balance_to_coldkey_account(&U256::from(0), 1.into()); + add_balance_to_coldkey_account(&U256::from(1), 1.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &(U256::from(0)), &(U256::from(0)), @@ -2197,8 +2197,8 @@ fn test_tempo_change_during_commit_reveal_process() { SubtensorModule::set_validator_permit_for_uid(netuid, 0, true); SubtensorModule::set_validator_permit_for_uid(netuid, 1, true); SubtensorModule::set_commit_reveal_weights_enabled(netuid, true); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(0), 1.into()); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(1), 1.into()); + add_balance_to_coldkey_account(&U256::from(0), 1.into()); + add_balance_to_coldkey_account(&U256::from(1), 1.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &(U256::from(0)), &(U256::from(0)), @@ -2346,8 +2346,8 @@ fn test_commit_reveal_multiple_commits() { SubtensorModule::set_validator_permit_for_uid(netuid, 0, true); SubtensorModule::set_validator_permit_for_uid(netuid, 1, true); SubtensorModule::set_commit_reveal_weights_enabled(netuid, true); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(0), 1.into()); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(1), 1.into()); + add_balance_to_coldkey_account(&U256::from(0), 1.into()); + add_balance_to_coldkey_account(&U256::from(1), 1.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &(U256::from(0)), &(U256::from(0)), @@ -2752,8 +2752,8 @@ fn test_expired_commits_handling_in_commit_and_reveal() { SubtensorModule::set_stake_threshold(0); SubtensorModule::set_validator_permit_for_uid(netuid, 0, true); SubtensorModule::set_validator_permit_for_uid(netuid, 1, true); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(0), 1.into()); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(1), 1.into()); + add_balance_to_coldkey_account(&U256::from(0), 1.into()); + add_balance_to_coldkey_account(&U256::from(1), 1.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &(U256::from(0)), &(U256::from(0)), @@ -2951,8 +2951,8 @@ fn test_reveal_at_exact_epoch() { SubtensorModule::set_stake_threshold(0); SubtensorModule::set_validator_permit_for_uid(netuid, 0, true); SubtensorModule::set_validator_permit_for_uid(netuid, 1, true); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(0), 1.into()); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(1), 1.into()); + add_balance_to_coldkey_account(&U256::from(0), 1.into()); + add_balance_to_coldkey_account(&U256::from(1), 1.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &(U256::from(0)), &(U256::from(0)), @@ -3115,8 +3115,8 @@ fn test_tempo_and_reveal_period_change_during_commit_reveal_process() { SubtensorModule::set_stake_threshold(0); SubtensorModule::set_validator_permit_for_uid(netuid, 0, true); SubtensorModule::set_validator_permit_for_uid(netuid, 1, true); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(0), 1.into()); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(1), 1.into()); + add_balance_to_coldkey_account(&U256::from(0), 1.into()); + add_balance_to_coldkey_account(&U256::from(1), 1.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &(U256::from(0)), &(U256::from(0)), @@ -3302,8 +3302,8 @@ fn test_commit_reveal_order_enforcement() { SubtensorModule::set_stake_threshold(0); SubtensorModule::set_validator_permit_for_uid(netuid, 0, true); SubtensorModule::set_validator_permit_for_uid(netuid, 1, true); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(0), 1.into()); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(1), 1.into()); + add_balance_to_coldkey_account(&U256::from(0), 1.into()); + add_balance_to_coldkey_account(&U256::from(1), 1.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &(U256::from(0)), &(U256::from(0)), @@ -3561,8 +3561,8 @@ fn test_successful_batch_reveal() { SubtensorModule::set_stake_threshold(0); SubtensorModule::set_validator_permit_for_uid(netuid, 0, true); SubtensorModule::set_validator_permit_for_uid(netuid, 1, true); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(0), 1.into()); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(1), 1.into()); + add_balance_to_coldkey_account(&U256::from(0), 1.into()); + add_balance_to_coldkey_account(&U256::from(1), 1.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &(U256::from(0)), &(U256::from(0)), @@ -3639,8 +3639,8 @@ fn test_batch_reveal_with_expired_commits() { SubtensorModule::set_stake_threshold(0); SubtensorModule::set_validator_permit_for_uid(netuid, 0, true); SubtensorModule::set_validator_permit_for_uid(netuid, 1, true); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(0), 1.into()); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(1), 1.into()); + add_balance_to_coldkey_account(&U256::from(0), 1.into()); + add_balance_to_coldkey_account(&U256::from(1), 1.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &(U256::from(0)), &(U256::from(0)), @@ -4056,8 +4056,8 @@ fn test_batch_reveal_with_out_of_order_commits() { SubtensorModule::set_stake_threshold(0); SubtensorModule::set_validator_permit_for_uid(netuid, 0, true); SubtensorModule::set_validator_permit_for_uid(netuid, 1, true); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(0), 1.into()); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(1), 1.into()); + add_balance_to_coldkey_account(&U256::from(0), 1.into()); + add_balance_to_coldkey_account(&U256::from(1), 1.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &(U256::from(0)), &(U256::from(0)), @@ -4457,8 +4457,8 @@ fn test_get_reveal_blocks() { SubtensorModule::set_validator_permit_for_uid(netuid, 0, true); SubtensorModule::set_validator_permit_for_uid(netuid, 1, true); SubtensorModule::set_commit_reveal_weights_enabled(netuid, true); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(0), 1.into()); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(1), 1.into()); + add_balance_to_coldkey_account(&U256::from(0), 1.into()); + add_balance_to_coldkey_account(&U256::from(1), 1.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &(U256::from(0)), &(U256::from(0)), @@ -4591,8 +4591,8 @@ fn test_commit_weights_rate_limit() { SubtensorModule::set_validator_permit_for_uid(netuid, 0, true); SubtensorModule::set_validator_permit_for_uid(netuid, 1, true); SubtensorModule::set_commit_reveal_weights_enabled(netuid, true); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(0), 1.into()); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(1), 1.into()); + add_balance_to_coldkey_account(&U256::from(0), 1.into()); + add_balance_to_coldkey_account(&U256::from(1), 1.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &(U256::from(0)), &(U256::from(0)), @@ -4779,8 +4779,8 @@ fn test_reveal_crv3_commits_success() { SubtensorModule::set_validator_permit_for_uid(netuid, neuron_uid1, true); SubtensorModule::set_validator_permit_for_uid(netuid, neuron_uid2, true); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(3), 1.into()); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(4), 1.into()); + add_balance_to_coldkey_account(&U256::from(3), 1.into()); + add_balance_to_coldkey_account(&U256::from(4), 1.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey1, &(U256::from(3)), @@ -6038,7 +6038,7 @@ fn test_reveal_crv3_commits_multiple_valid_commits_all_processed() { SubtensorModule::set_validator_permit_for_uid(netuid, i as u16, true); // add minimal stake so `do_set_weights` will succeed - SubtensorModule::add_balance_to_coldkey_account(&cold, 1.into()); + add_balance_to_coldkey_account(&cold, 1.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( hk, &cold, @@ -6136,7 +6136,7 @@ fn test_reveal_crv3_commits_max_neurons() { SubtensorModule::set_validator_permit_for_uid(netuid, i, true); // give each neuron a nominal stake (safe even if not needed) - SubtensorModule::add_balance_to_coldkey_account(&cold, 1.into()); + add_balance_to_coldkey_account(&cold, 1.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &hk, &cold, @@ -6358,8 +6358,8 @@ fn test_reveal_crv3_commits_hotkey_check() { SubtensorModule::set_validator_permit_for_uid(netuid, neuron_uid1, true); SubtensorModule::set_validator_permit_for_uid(netuid, neuron_uid2, true); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(3), 1.into()); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(4), 1.into()); + add_balance_to_coldkey_account(&U256::from(3), 1.into()); + add_balance_to_coldkey_account(&U256::from(4), 1.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey1, &(U256::from(3)), @@ -6475,8 +6475,8 @@ fn test_reveal_crv3_commits_hotkey_check() { SubtensorModule::set_validator_permit_for_uid(netuid, neuron_uid1, true); SubtensorModule::set_validator_permit_for_uid(netuid, neuron_uid2, true); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(3), 1.into()); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(4), 1.into()); + add_balance_to_coldkey_account(&U256::from(3), 1.into()); + add_balance_to_coldkey_account(&U256::from(4), 1.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey1, &(U256::from(3)), @@ -6741,8 +6741,8 @@ fn test_reveal_crv3_commits_legacy_payload_success() { SubtensorModule::set_validator_permit_for_uid(netuid, uid1, true); SubtensorModule::set_validator_permit_for_uid(netuid, uid2, true); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(3), 1.into()); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(4), 1.into()); + add_balance_to_coldkey_account(&U256::from(3), 1.into()); + add_balance_to_coldkey_account(&U256::from(4), 1.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey1, &U256::from(3), @@ -6874,7 +6874,7 @@ fn test_subnet_owner_can_validate_without_stake_or_manual_permit() { // Add one non-owner neuron with deterministic subnet stake. register_ok_neuron(netuid, other_hotkey, other_coldkey, 0); - SubtensorModule::add_balance_to_coldkey_account(&other_coldkey, 1.into()); + add_balance_to_coldkey_account(&other_coldkey, 1.into()); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &other_hotkey, &other_coldkey, diff --git a/pallets/subtensor/src/utils/misc.rs b/pallets/subtensor/src/utils/misc.rs index 1319eeb817..f6b24db36b 100644 --- a/pallets/subtensor/src/utils/misc.rs +++ b/pallets/subtensor/src/utils/misc.rs @@ -137,9 +137,6 @@ impl Pallet { // ======================== // ==== Global Getters ==== // ======================== - pub fn get_total_issuance() -> TaoBalance { - TotalIssuance::::get() - } pub fn get_current_block_as_u64() -> u64 { TryInto::try_into(>::block_number()) .ok() @@ -340,13 +337,6 @@ impl Pallet { // ======================== // === Token Management === // ======================== - pub fn recycle_tao(amount: TaoBalance) { - TotalIssuance::::put(TotalIssuance::::get().saturating_sub(amount)); - } - pub fn increase_issuance(amount: TaoBalance) { - TotalIssuance::::put(TotalIssuance::::get().saturating_add(amount)); - } - pub fn set_subnet_locked_balance(netuid: NetUid, amount: TaoBalance) { SubnetLocked::::insert(netuid, amount); } @@ -712,10 +702,6 @@ impl Pallet { StakingHotkeys::::get(coldkey) } - pub fn set_total_issuance(total_issuance: TaoBalance) { - TotalIssuance::::put(total_issuance); - } - pub fn get_rao_recycled(netuid: NetUid) -> TaoBalance { RAORecycledForRegistration::::get(netuid) } @@ -847,9 +833,16 @@ impl Pallet { /// /// * Update the SubnetOwnerHotkey storage. /// * Emits a SubnetOwnerHotkeySet event. - pub fn set_subnet_owner_hotkey(netuid: NetUid, hotkey: &T::AccountId) { + pub fn set_subnet_owner_hotkey(netuid: NetUid, hotkey: &T::AccountId) -> DispatchResult { + // Ensure that hotkey is not a special account + ensure!( + Self::is_subnet_account_id(hotkey).is_none(), + Error::::CannotUseSystemAccount + ); + SubnetOwnerHotkey::::insert(netuid, hotkey.clone()); Self::deposit_event(Event::SubnetOwnerHotkeySet(netuid, hotkey.clone())); + Ok(()) } // Get the uid of the Owner Hotkey for a subnet. @@ -915,6 +908,11 @@ impl Pallet { FlowEmaSmoothingFactor::::set(smoothing_factor); } + /// Enables or disables net TAO flow (protocol cost deduction from emission shares). + pub fn set_net_tao_flow_enabled(enabled: bool) { + NetTaoFlowEnabled::::set(enabled); + } + /// Multiply an integer `value` by a Q32 fixed-point factor. /// /// Q32 means: diff --git a/pallets/subtensor/src/utils/try_state.rs b/pallets/subtensor/src/utils/try_state.rs index 576c3c7951..8f43148d9f 100644 --- a/pallets/subtensor/src/utils/try_state.rs +++ b/pallets/subtensor/src/utils/try_state.rs @@ -1,25 +1,47 @@ use frame_support::traits::fungible::Inspect; +use frame_system::pallet_prelude::BlockNumberFor; use super::*; impl Pallet { /// Checks [`TotalIssuance`] equals the sum of currency issuance, total stake, and total subnet /// locked. - #[allow(clippy::expect_used)] + #[allow(clippy::arithmetic_side_effects, clippy::expect_used)] pub(crate) fn check_total_issuance() -> Result<(), sp_runtime::TryRuntimeError> { // Get the total currency issuance let currency_issuance = ::Currency::total_issuance(); + let total_issuance = TotalIssuance::::get(); + + log::info!("=== Try runtime check_total_issuance ==="); + log::info!(" currency_issuance: {}", currency_issuance); + log::info!(" total_issuance: {}", total_issuance); + + // If balances total issuance is greater than 21M, we're on devnet or testnet, ignore + // this check, TI is off for multiple reasons. + if currency_issuance > 21_000_000_000_000_000_u64.into() { + return Ok(()); + } + + // If there's an exact match, it means we are past imbalances upgrade + if currency_issuance == total_issuance { + return Ok(()); + } // Calculate the expected total issuance let expected_total_issuance = currency_issuance.saturating_add(TotalStake::::get().into()); // Verify the diff between calculated TI and actual TI is less than delta - // - // These values can be off slightly due to float rounding errors. - // They are corrected every runtime upgrade. - let delta = TaoBalance::from(1000); - let total_issuance = TotalIssuance::::get(); + // Allow greater tolerance for non-mainnet + let genesis_hash = frame_system::Pallet::::block_hash(BlockNumberFor::::zero()); + let genesis_bytes = genesis_hash.as_ref(); + let mainnet_genesis = + hex_literal::hex!("2f0555cc76fc2840a25a6ea3b9637146806f1f44b090c175ffde2a7e5ab36c03"); + let delta = if genesis_bytes == mainnet_genesis { + TaoBalance::from(1000) + } else { + TaoBalance::from(1_000_000_000_000_u64) + }; let diff = if total_issuance > expected_total_issuance { total_issuance.checked_sub(&expected_total_issuance) diff --git a/pallets/subtensor/src/weights.rs b/pallets/subtensor/src/weights.rs index d6c63175f0..4e759e12e0 100644 --- a/pallets/subtensor/src/weights.rs +++ b/pallets/subtensor/src/weights.rs @@ -2,9 +2,9 @@ //! Autogenerated weights for `pallet_subtensor` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 49.1.0 -//! DATE: 2026-04-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2026-05-04, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runnervm727z3`, CPU: `AMD EPYC 9V74 80-Core Processor` +//! HOSTNAME: `runnervmeorf1`, CPU: `AMD EPYC 7763 64-Core Processor` //! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` // Executed Command: @@ -22,7 +22,7 @@ // --no-storage-info // --no-min-squares // --no-median-slopes -// --output=/tmp/tmp.caw6C0JGm3 +// --output=/tmp/tmp.bgeSTyDtzW // --template=/home/runner/work/subtensor/subtensor/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -60,6 +60,7 @@ pub trait WeightInfo { fn start_call() -> Weight; fn add_stake_limit() -> Weight; fn move_stake() -> Weight; + fn remove_stake() -> Weight; fn remove_stake_limit() -> Weight; fn swap_stake_limit() -> Weight; fn transfer_stake() -> Weight; @@ -86,9 +87,12 @@ pub trait WeightInfo { fn claim_root() -> Weight; fn sudo_set_num_root_claims() -> Weight; fn sudo_set_root_claim_threshold() -> Weight; + fn set_auto_parent_delegation_enabled() -> Weight; fn add_stake_burn() -> Weight; fn set_pending_childkey_cooldown() -> Weight; - fn set_auto_parent_delegation_enabled() -> Weight; + fn lock_stake() -> Weight; + fn unlock_stake() -> Weight; + fn move_lock() -> Weight; } /// Weights for `pallet_subtensor` using the Substrate node and recommended hardware. @@ -102,7 +106,7 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::Uids` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Burn` (r:1 w:1) /// Proof: `SubtensorModule::Burn` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `System::Account` (r:1 w:1) + /// Storage: `System::Account` (r:2 w:2) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::Owner` (r:1 w:1) /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -188,12 +192,12 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Swap::CurrentTick` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) fn register() -> Weight { // Proof Size summary in bytes: - // Measured: `1629` + // Measured: `1706` // Estimated: `13600` - // Minimum execution time: 348_026_000 picoseconds. - Weight::from_parts(354_034_000, 13600) - .saturating_add(T::DbWeight::get().reads(46_u64)) - .saturating_add(T::DbWeight::get().writes(38_u64)) + // Minimum execution time: 355_490_000 picoseconds. + Weight::from_parts(364_739_000, 13600) + .saturating_add(T::DbWeight::get().reads(47_u64)) + .saturating_add(T::DbWeight::get().writes(39_u64)) } /// Storage: `SubtensorModule::CommitRevealWeightsEnabled` (r:1 w:0) /// Proof: `SubtensorModule::CommitRevealWeightsEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -233,15 +237,15 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `188782` // Estimated: `10327372` - // Minimum execution time: 16_089_221_000 picoseconds. - Weight::from_parts(16_473_771_000, 10327372) + // Minimum execution time: 14_846_685_000 picoseconds. + Weight::from_parts(15_166_549_000, 10327372) .saturating_add(T::DbWeight::get().reads(4112_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: `SubtensorModule::SubtokenEnabled` (r:1 w:0) - /// Proof: `SubtensorModule::SubtokenEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubtokenEnabled` (r:1 w:0) + /// Proof: `SubtensorModule::SubtokenEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetMechanism` (r:1 w:0) /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) @@ -260,7 +264,7 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Swap::FeeRate` (`max_values`: None, `max_size`: Some(12), added: 2487, mode: `MaxEncodedLen`) /// Storage: `Swap::CurrentLiquidity` (r:1 w:0) /// Proof: `Swap::CurrentLiquidity` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) + /// Storage: `System::Account` (r:3 w:3) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::Owner` (r:1 w:0) /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -284,22 +288,28 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AlphaV2` (r:1 w:1) /// Proof: `SubtensorModule::AlphaV2` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalIssuance` (r:1 w:1) - /// Proof: `SubtensorModule::TotalIssuance` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetTaoFlow` (r:1 w:1) /// Proof: `SubtensorModule::SubnetTaoFlow` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Lock` (r:2 w:1) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::MaturityRate` (r:1 w:0) + /// Proof: `SubtensorModule::MaturityRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::UnlockRate` (r:1 w:0) + /// Proof: `SubtensorModule::UnlockRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::HotkeyLock` (r:1 w:1) + /// Proof: `SubtensorModule::HotkeyLock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::StakingOperationRateLimiter` (r:0 w:1) /// Proof: `SubtensorModule::StakingOperationRateLimiter` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (r:0 w:1) /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn add_stake() -> Weight { // Proof Size summary in bytes: - // Measured: `2307` - // Estimated: `8556` - // Minimum execution time: 338_691_000 picoseconds. - Weight::from_parts(346_814_000, 8556) - .saturating_add(T::DbWeight::get().reads(27_u64)) - .saturating_add(T::DbWeight::get().writes(15_u64)) + // Measured: `2560` + // Estimated: `8727` + // Minimum execution time: 431_592_000 picoseconds. + Weight::from_parts(453_283_000, 8727) + .saturating_add(T::DbWeight::get().reads(33_u64)) + .saturating_add(T::DbWeight::get().writes(18_u64)) } /// Storage: `SubtensorModule::IsNetworkMember` (r:2 w:0) /// Proof: `SubtensorModule::IsNetworkMember` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -311,8 +321,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `791` // Estimated: `6731` - // Minimum execution time: 32_479_000 picoseconds. - Weight::from_parts(33_721_000, 6731) + // Minimum execution time: 34_354_000 picoseconds. + Weight::from_parts(34_836_000, 6731) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -326,8 +336,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `764` // Estimated: `6704` - // Minimum execution time: 29_264_000 picoseconds. - Weight::from_parts(30_095_000, 6704) + // Minimum execution time: 30_417_000 picoseconds. + Weight::from_parts(31_620_000, 6704) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -339,7 +349,7 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::Uids` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Burn` (r:1 w:1) /// Proof: `SubtensorModule::Burn` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `System::Account` (r:1 w:1) + /// Storage: `System::Account` (r:2 w:2) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::Owner` (r:1 w:1) /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -427,10 +437,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1639` // Estimated: `13600` - // Minimum execution time: 341_145_000 picoseconds. - Weight::from_parts(345_863_000, 13600) - .saturating_add(T::DbWeight::get().reads(46_u64)) - .saturating_add(T::DbWeight::get().writes(38_u64)) + // Minimum execution time: 364_917_000 picoseconds. + Weight::from_parts(368_714_000, 13600) + .saturating_add(T::DbWeight::get().reads(47_u64)) + .saturating_add(T::DbWeight::get().writes(39_u64)) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -480,8 +490,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1415` // Estimated: `4880` - // Minimum execution time: 100_752_000 picoseconds. - Weight::from_parts(102_565_000, 4880) + // Minimum execution time: 100_830_000 picoseconds. + Weight::from_parts(102_322_000, 4880) .saturating_add(T::DbWeight::get().reads(19_u64)) .saturating_add(T::DbWeight::get().writes(16_u64)) } @@ -507,7 +517,7 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::TotalIssuance` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::BlockEmission` (r:1 w:0) /// Proof: `SubtensorModule::BlockEmission` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `System::Account` (r:1 w:1) + /// Storage: `System::Account` (r:2 w:2) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::SubnetMechanism` (r:1 w:1) /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -551,26 +561,16 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::ValidatorTrust` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::ValidatorPermit` (r:1 w:1) /// Proof: `SubtensorModule::ValidatorPermit` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::RegisteredSubnetCounter` (r:1 w:1) + /// Proof: `SubtensorModule::RegisteredSubnetCounter` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TokenSymbol` (r:3 w:1) /// Proof: `SubtensorModule::TokenSymbol` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalHotkeyAlpha` (r:1 w:1) - /// Proof: `SubtensorModule::TotalHotkeyAlpha` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::Alpha` (r:1 w:0) - /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::AlphaV2` (r:1 w:1) - /// Proof: `SubtensorModule::AlphaV2` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalHotkeyShares` (r:1 w:0) - /// Proof: `SubtensorModule::TotalHotkeyShares` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalHotkeySharesV2` (r:1 w:1) - /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalStake` (r:1 w:1) /// Proof: `SubtensorModule::TotalStake` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Keys` (r:1 w:1) /// Proof: `SubtensorModule::Keys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Burn` (r:0 w:1) /// Proof: `SubtensorModule::Burn` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::RAORecycledForRegistration` (r:0 w:1) - /// Proof: `SubtensorModule::RAORecycledForRegistration` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetLocked` (r:0 w:1) /// Proof: `SubtensorModule::SubnetLocked` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworkRegisteredAt` (r:0 w:1) @@ -607,12 +607,12 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::MaxAllowedUids` (`max_values`: None, `max_size`: None, mode: `Measured`) fn register_network() -> Weight { // Proof Size summary in bytes: - // Measured: `1676` - // Estimated: `10091` - // Minimum execution time: 289_917_000 picoseconds. - Weight::from_parts(293_954_000, 10091) - .saturating_add(T::DbWeight::get().reads(45_u64)) - .saturating_add(T::DbWeight::get().writes(49_u64)) + // Measured: `1459` + // Estimated: `9874` + // Minimum execution time: 268_496_000 picoseconds. + Weight::from_parts(273_143_000, 9874) + .saturating_add(T::DbWeight::get().reads(42_u64)) + .saturating_add(T::DbWeight::get().writes(47_u64)) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -638,8 +638,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1061` // Estimated: `4526` - // Minimum execution time: 59_199_000 picoseconds. - Weight::from_parts(60_772_000, 4526) + // Minimum execution time: 60_835_000 picoseconds. + Weight::from_parts(62_007_000, 4526) .saturating_add(T::DbWeight::get().reads(10_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -683,8 +683,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1579` // Estimated: `7519` - // Minimum execution time: 107_763_000 picoseconds. - Weight::from_parts(109_746_000, 7519) + // Minimum execution time: 107_622_000 picoseconds. + Weight::from_parts(109_516_000, 7519) .saturating_add(T::DbWeight::get().reads(18_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -694,8 +694,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_126_000 picoseconds. - Weight::from_parts(4_407_000, 0) + // Minimum execution time: 5_260_000 picoseconds. + Weight::from_parts(5_611_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Owner` (r:1 w:0) @@ -712,8 +712,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `938` // Estimated: `4403` - // Minimum execution time: 45_358_000 picoseconds. - Weight::from_parts(46_140_000, 4403) + // Minimum execution time: 46_848_000 picoseconds. + Weight::from_parts(47_770_000, 4403) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -729,8 +729,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `694` // Estimated: `4159` - // Minimum execution time: 39_469_000 picoseconds. - Weight::from_parts(40_962_000, 4159) + // Minimum execution time: 45_235_000 picoseconds. + Weight::from_parts(46_999_000, 4159) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -760,17 +760,19 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::OwnedHotkeys` (r:2 w:2) /// Proof: `SubtensorModule::OwnedHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Lock` (r:2 w:0) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `System::Account` (r:2 w:2) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::LastRateLimitedBlock` (r:0 w:1) /// Proof: `SubtensorModule::LastRateLimitedBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn swap_coldkey_announced() -> Weight { // Proof Size summary in bytes: - // Measured: `1815` - // Estimated: `12705` - // Minimum execution time: 260_764_000 picoseconds. - Weight::from_parts(265_261_000, 12705) - .saturating_add(T::DbWeight::get().reads(31_u64)) + // Measured: `2117` + // Estimated: `13007` + // Minimum execution time: 267_614_000 picoseconds. + Weight::from_parts(273_394_000, 13007) + .saturating_add(T::DbWeight::get().reads(33_u64)) .saturating_add(T::DbWeight::get().writes(15_u64)) } /// Storage: `System::Account` (r:2 w:2) @@ -801,6 +803,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::OwnedHotkeys` (r:2 w:2) /// Proof: `SubtensorModule::OwnedHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Lock` (r:2 w:0) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::ColdkeySwapAnnouncements` (r:0 w:1) /// Proof: `SubtensorModule::ColdkeySwapAnnouncements` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::ColdkeySwapDisputes` (r:0 w:1) @@ -809,11 +813,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::LastRateLimitedBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn swap_coldkey() -> Weight { // Proof Size summary in bytes: - // Measured: `1908` - // Estimated: `12798` - // Minimum execution time: 281_736_000 picoseconds. - Weight::from_parts(286_753_000, 12798) - .saturating_add(T::DbWeight::get().reads(31_u64)) + // Measured: `2210` + // Estimated: `13100` + // Minimum execution time: 289_935_000 picoseconds. + Weight::from_parts(294_274_000, 13100) + .saturating_add(T::DbWeight::get().reads(33_u64)) .saturating_add(T::DbWeight::get().writes(19_u64)) } /// Storage: `SubtensorModule::ColdkeySwapAnnouncements` (r:1 w:0) @@ -824,8 +828,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `665` // Estimated: `4130` - // Minimum execution time: 19_950_000 picoseconds. - Weight::from_parts(20_701_000, 4130) + // Minimum execution time: 22_412_000 picoseconds. + Weight::from_parts(23_364_000, 4130) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -837,8 +841,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `613` // Estimated: `4078` - // Minimum execution time: 16_415_000 picoseconds. - Weight::from_parts(17_096_000, 4078) + // Minimum execution time: 18_325_000 picoseconds. + Weight::from_parts(19_206_000, 4078) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -850,8 +854,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_790_000 picoseconds. - Weight::from_parts(7_151_000, 0) + // Minimum execution time: 8_376_000 picoseconds. + Weight::from_parts(8_697_000, 0) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: `SubtensorModule::CommitRevealWeightsEnabled` (r:1 w:0) @@ -894,8 +898,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `2084` // Estimated: `8024` - // Minimum execution time: 426_724_000 picoseconds. - Weight::from_parts(431_712_000, 8024) + // Minimum execution time: 396_345_000 picoseconds. + Weight::from_parts(408_599_000, 8024) .saturating_add(T::DbWeight::get().reads(18_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -917,13 +921,17 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:1 w:1) /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:0) + /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Lock` (r:1 w:0) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn recycle_alpha() -> Weight { // Proof Size summary in bytes: - // Measured: `1424` - // Estimated: `4889` - // Minimum execution time: 128_484_000 picoseconds. - Weight::from_parts(130_548_000, 4889) - .saturating_add(T::DbWeight::get().reads(9_u64)) + // Measured: `1860` + // Estimated: `5325` + // Minimum execution time: 166_603_000 picoseconds. + Weight::from_parts(168_788_000, 5325) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -944,13 +952,17 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:1 w:0) /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:0) + /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Lock` (r:1 w:0) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn burn_alpha() -> Weight { // Proof Size summary in bytes: - // Measured: `1424` - // Estimated: `4889` - // Minimum execution time: 126_171_000 picoseconds. - Weight::from_parts(128_965_000, 4889) - .saturating_add(T::DbWeight::get().reads(9_u64)) + // Measured: `1860` + // Estimated: `5325` + // Minimum execution time: 164_650_000 picoseconds. + Weight::from_parts(166_603_000, 5325) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -969,8 +981,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1079` // Estimated: `4544` - // Minimum execution time: 37_957_000 picoseconds. - Weight::from_parts(38_939_000, 4544) + // Minimum execution time: 38_783_000 picoseconds. + Weight::from_parts(40_136_000, 4544) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -996,7 +1008,7 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubtokenEnabled` (r:1 w:0) /// Proof: `SubtensorModule::SubtokenEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `System::Account` (r:1 w:1) + /// Storage: `System::Account` (r:3 w:3) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::Owner` (r:1 w:0) /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -1020,22 +1032,28 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AlphaV2` (r:1 w:1) /// Proof: `SubtensorModule::AlphaV2` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalIssuance` (r:1 w:1) - /// Proof: `SubtensorModule::TotalIssuance` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetTaoFlow` (r:1 w:1) /// Proof: `SubtensorModule::SubnetTaoFlow` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Lock` (r:2 w:1) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::MaturityRate` (r:1 w:0) + /// Proof: `SubtensorModule::MaturityRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::UnlockRate` (r:1 w:0) + /// Proof: `SubtensorModule::UnlockRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::HotkeyLock` (r:1 w:1) + /// Proof: `SubtensorModule::HotkeyLock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::StakingOperationRateLimiter` (r:0 w:1) /// Proof: `SubtensorModule::StakingOperationRateLimiter` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (r:0 w:1) /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn add_stake_limit() -> Weight { // Proof Size summary in bytes: - // Measured: `2307` - // Estimated: `8556` - // Minimum execution time: 376_539_000 picoseconds. - Weight::from_parts(383_750_000, 8556) - .saturating_add(T::DbWeight::get().reads(27_u64)) - .saturating_add(T::DbWeight::get().writes(15_u64)) + // Measured: `2560` + // Estimated: `8727` + // Minimum execution time: 465_225_000 picoseconds. + Weight::from_parts(485_933_000, 8727) + .saturating_add(T::DbWeight::get().reads(33_u64)) + .saturating_add(T::DbWeight::get().writes(18_u64)) } /// Storage: `SubtensorModule::Alpha` (r:2 w:0) /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -1069,11 +1087,76 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `2002` // Estimated: `7942` - // Minimum execution time: 222_486_000 picoseconds. - Weight::from_parts(223_918_000, 7942) + // Minimum execution time: 205_998_000 picoseconds. + Weight::from_parts(208_783_000, 7942) .saturating_add(T::DbWeight::get().reads(19_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } + /// Storage: `SubtensorModule::SubtokenEnabled` (r:1 w:0) + /// Proof: `SubtensorModule::SubtokenEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Alpha` (r:1 w:0) + /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::AlphaV2` (r:1 w:1) + /// Proof: `SubtensorModule::AlphaV2` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TotalHotkeyAlpha` (r:2 w:1) + /// Proof: `SubtensorModule::TotalHotkeyAlpha` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TotalHotkeyShares` (r:1 w:0) + /// Proof: `SubtensorModule::TotalHotkeyShares` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TotalHotkeySharesV2` (r:1 w:1) + /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::NetworksAdded` (r:3 w:0) + /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::StakingOperationRateLimiter` (r:1 w:0) + /// Proof: `SubtensorModule::StakingOperationRateLimiter` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetMechanism` (r:2 w:0) + /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetTaoProvided` (r:1 w:0) + /// Proof: `SubtensorModule::SubnetTaoProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Swap::SwapV3Initialized` (r:1 w:0) + /// Proof: `Swap::SwapV3Initialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) + /// Storage: `Swap::AlphaSqrtPrice` (r:1 w:1) + /// Proof: `Swap::AlphaSqrtPrice` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) + /// Storage: `Swap::CurrentTick` (r:1 w:1) + /// Proof: `Swap::CurrentTick` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Swap::TickIndexBitmapWords` (r:3 w:0) + /// Proof: `Swap::TickIndexBitmapWords` (`max_values`: None, `max_size`: Some(47), added: 2522, mode: `MaxEncodedLen`) + /// Storage: `Swap::FeeRate` (r:1 w:0) + /// Proof: `Swap::FeeRate` (`max_values`: None, `max_size`: Some(12), added: 2487, mode: `MaxEncodedLen`) + /// Storage: `Swap::CurrentLiquidity` (r:1 w:0) + /// Proof: `Swap::CurrentLiquidity` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::Owner` (r:1 w:0) + /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:0) + /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Lock` (r:1 w:0) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetAlphaOut` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TotalStake` (r:1 w:1) + /// Proof: `SubtensorModule::TotalStake` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetVolume` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetVolume` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::SubnetTaoFlow` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetTaoFlow` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::StakeThreshold` (r:1 w:0) + /// Proof: `SubtensorModule::StakeThreshold` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (r:0 w:1) + /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn remove_stake() -> Weight { + // Proof Size summary in bytes: + // Measured: `2536` + // Estimated: `10951` + // Minimum execution time: 412_326_000 picoseconds. + Weight::from_parts(433_846_000, 10951) + .saturating_add(T::DbWeight::get().reads(34_u64)) + .saturating_add(T::DbWeight::get().writes(14_u64)) + } /// Storage: `SubtensorModule::SubnetMechanism` (r:2 w:0) /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) @@ -1108,6 +1191,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Owner` (r:1 w:0) /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:0) + /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Lock` (r:1 w:0) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:1 w:1) @@ -1116,22 +1203,22 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::TotalStake` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetVolume` (r:1 w:1) /// Proof: `SubtensorModule::SubnetVolume` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::SubnetTaoFlow` (r:1 w:1) /// Proof: `SubtensorModule::SubnetTaoFlow` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::StakeThreshold` (r:1 w:0) /// Proof: `SubtensorModule::StakeThreshold` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (r:0 w:1) /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn remove_stake_limit() -> Weight { // Proof Size summary in bytes: - // Measured: `2211` - // Estimated: `10626` - // Minimum execution time: 387_646_000 picoseconds. - Weight::from_parts(403_169_000, 10626) - .saturating_add(T::DbWeight::get().reads(30_u64)) - .saturating_add(T::DbWeight::get().writes(13_u64)) + // Measured: `2536` + // Estimated: `10951` + // Minimum execution time: 445_979_000 picoseconds. + Weight::from_parts(451_159_000, 10951) + .saturating_add(T::DbWeight::get().reads(33_u64)) + .saturating_add(T::DbWeight::get().writes(14_u64)) } /// Storage: `SubtensorModule::Alpha` (r:2 w:0) /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -1169,6 +1256,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::SubtokenEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Owner` (r:1 w:0) /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:0) + /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Lock` (r:3 w:1) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaIn` (r:2 w:2) /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:2 w:2) @@ -1177,22 +1268,26 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::TotalStake` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetVolume` (r:2 w:2) /// Proof: `SubtensorModule::SubnetVolume` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:3 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::SubnetTaoFlow` (r:2 w:2) /// Proof: `SubtensorModule::SubnetTaoFlow` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:0) - /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalIssuance` (r:1 w:1) - /// Proof: `SubtensorModule::TotalIssuance` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::MaturityRate` (r:1 w:0) + /// Proof: `SubtensorModule::MaturityRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::UnlockRate` (r:1 w:0) + /// Proof: `SubtensorModule::UnlockRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::HotkeyLock` (r:1 w:1) + /// Proof: `SubtensorModule::HotkeyLock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (r:0 w:1) /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn swap_stake_limit() -> Weight { // Proof Size summary in bytes: - // Measured: `2494` - // Estimated: `8556` - // Minimum execution time: 461_377_000 picoseconds. - Weight::from_parts(477_951_000, 8556) - .saturating_add(T::DbWeight::get().reads(40_u64)) - .saturating_add(T::DbWeight::get().writes(22_u64)) + // Measured: `2923` + // Estimated: `11338` + // Minimum execution time: 641_075_000 picoseconds. + Weight::from_parts(664_801_000, 11338) + .saturating_add(T::DbWeight::get().reads(48_u64)) + .saturating_add(T::DbWeight::get().writes(25_u64)) } /// Storage: `SubtensorModule::Alpha` (r:2 w:0) /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -1214,8 +1309,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TransferToggle` (r:1 w:0) /// Proof: `SubtensorModule::TransferToggle` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:1) + /// Storage: `SubtensorModule::StakingHotkeys` (r:2 w:1) /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Lock` (r:1 w:0) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetMechanism` (r:1 w:0) /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Swap::SwapV3Initialized` (r:1 w:0) @@ -1226,11 +1323,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn transfer_stake() -> Weight { // Proof Size summary in bytes: - // Measured: `1829` - // Estimated: `7769` - // Minimum execution time: 215_726_000 picoseconds. - Weight::from_parts(219_552_000, 7769) - .saturating_add(T::DbWeight::get().reads(16_u64)) + // Measured: `1996` + // Estimated: `7936` + // Minimum execution time: 240_382_000 picoseconds. + Weight::from_parts(243_919_000, 7936) + .saturating_add(T::DbWeight::get().reads(18_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } /// Storage: `SubtensorModule::Alpha` (r:2 w:0) @@ -1269,6 +1366,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Swap::FeeRate` (`max_values`: None, `max_size`: Some(12), added: 2487, mode: `MaxEncodedLen`) /// Storage: `Swap::CurrentLiquidity` (r:1 w:0) /// Proof: `Swap::CurrentLiquidity` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:0) + /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Lock` (r:3 w:1) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaIn` (r:2 w:2) /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:2 w:2) @@ -1277,22 +1378,26 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::TotalStake` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetVolume` (r:2 w:2) /// Proof: `SubtensorModule::SubnetVolume` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:3 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::SubnetTaoFlow` (r:2 w:2) /// Proof: `SubtensorModule::SubnetTaoFlow` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:0) - /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalIssuance` (r:1 w:1) - /// Proof: `SubtensorModule::TotalIssuance` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::MaturityRate` (r:1 w:0) + /// Proof: `SubtensorModule::MaturityRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::UnlockRate` (r:1 w:0) + /// Proof: `SubtensorModule::UnlockRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::HotkeyLock` (r:1 w:1) + /// Proof: `SubtensorModule::HotkeyLock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (r:0 w:1) /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn swap_stake() -> Weight { // Proof Size summary in bytes: - // Measured: `2421` - // Estimated: `8556` - // Minimum execution time: 402_808_000 picoseconds. - Weight::from_parts(420_035_000, 8556) - .saturating_add(T::DbWeight::get().reads(40_u64)) - .saturating_add(T::DbWeight::get().writes(22_u64)) + // Measured: `2785` + // Estimated: `11200` + // Minimum execution time: 591_602_000 picoseconds. + Weight::from_parts(613_634_000, 11200) + .saturating_add(T::DbWeight::get().reads(48_u64)) + .saturating_add(T::DbWeight::get().writes(25_u64)) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -1320,8 +1425,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1084` // Estimated: `4549` - // Minimum execution time: 125_589_000 picoseconds. - Weight::from_parts(141_484_000, 4549) + // Minimum execution time: 122_971_000 picoseconds. + Weight::from_parts(124_314_000, 4549) .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -1361,8 +1466,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1416` // Estimated: `7356` - // Minimum execution time: 99_310_000 picoseconds. - Weight::from_parts(101_193_000, 7356) + // Minimum execution time: 100_659_000 picoseconds. + Weight::from_parts(101_972_000, 7356) .saturating_add(T::DbWeight::get().reads(16_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -1378,8 +1483,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `793` // Estimated: `4258` - // Minimum execution time: 25_499_000 picoseconds. - Weight::from_parts(26_330_000, 4258) + // Minimum execution time: 27_622_000 picoseconds. + Weight::from_parts(29_025_000, 4258) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -1397,8 +1502,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `886` // Estimated: `4351` - // Minimum execution time: 32_540_000 picoseconds. - Weight::from_parts(33_501_000, 4351) + // Minimum execution time: 34_876_000 picoseconds. + Weight::from_parts(35_297_000, 4351) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -1444,6 +1549,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::ActivityCutoff` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::RegistrationsThisInterval` (r:1 w:1) /// Proof: `SubtensorModule::RegistrationsThisInterval` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::OwnedHotkeys` (r:1 w:1) /// Proof: `SubtensorModule::OwnedHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:1) @@ -1466,26 +1573,16 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::ValidatorTrust` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::ValidatorPermit` (r:1 w:1) /// Proof: `SubtensorModule::ValidatorPermit` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::RegisteredSubnetCounter` (r:1 w:1) + /// Proof: `SubtensorModule::RegisteredSubnetCounter` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TokenSymbol` (r:3 w:1) /// Proof: `SubtensorModule::TokenSymbol` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalHotkeyAlpha` (r:1 w:1) - /// Proof: `SubtensorModule::TotalHotkeyAlpha` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::Alpha` (r:1 w:0) - /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::AlphaV2` (r:1 w:1) - /// Proof: `SubtensorModule::AlphaV2` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalHotkeyShares` (r:1 w:0) - /// Proof: `SubtensorModule::TotalHotkeyShares` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalHotkeySharesV2` (r:1 w:1) - /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalStake` (r:1 w:1) /// Proof: `SubtensorModule::TotalStake` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Keys` (r:1 w:1) /// Proof: `SubtensorModule::Keys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Burn` (r:0 w:1) /// Proof: `SubtensorModule::Burn` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::RAORecycledForRegistration` (r:0 w:1) - /// Proof: `SubtensorModule::RAORecycledForRegistration` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetLocked` (r:0 w:1) /// Proof: `SubtensorModule::SubnetLocked` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworkRegisteredAt` (r:0 w:1) @@ -1522,12 +1619,12 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::MaxAllowedUids` (`max_values`: None, `max_size`: None, mode: `Measured`) fn register_network_with_identity() -> Weight { // Proof Size summary in bytes: - // Measured: `1560` - // Estimated: `9975` - // Minimum execution time: 279_983_000 picoseconds. - Weight::from_parts(284_690_000, 9975) - .saturating_add(T::DbWeight::get().reads(44_u64)) - .saturating_add(T::DbWeight::get().writes(48_u64)) + // Measured: `1343` + // Estimated: `9758` + // Minimum execution time: 263_716_000 picoseconds. + Weight::from_parts(267_293_000, 9758) + .saturating_add(T::DbWeight::get().reads(41_u64)) + .saturating_add(T::DbWeight::get().writes(46_u64)) } /// Storage: `SubtensorModule::IsNetworkMember` (r:2 w:0) /// Proof: `SubtensorModule::IsNetworkMember` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -1539,8 +1636,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `762` // Estimated: `6702` - // Minimum execution time: 31_257_000 picoseconds. - Weight::from_parts(32_769_000, 6702) + // Minimum execution time: 33_633_000 picoseconds. + Weight::from_parts(34_445_000, 6702) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -1554,8 +1651,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `842` // Estimated: `6782` - // Minimum execution time: 28_703_000 picoseconds. - Weight::from_parts(30_106_000, 6782) + // Minimum execution time: 30_758_000 picoseconds. + Weight::from_parts(31_870_000, 6782) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -1567,8 +1664,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `595` // Estimated: `4060` - // Minimum execution time: 15_634_000 picoseconds. - Weight::from_parts(16_254_000, 4060) + // Minimum execution time: 17_412_000 picoseconds. + Weight::from_parts(17_964_000, 4060) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -1580,16 +1677,22 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::TxRateLimit` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::IsNetworkMember` (r:6 w:10) /// Proof: `SubtensorModule::IsNetworkMember` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::RootClaimable` (r:2 w:2) + /// Proof: `SubtensorModule::RootClaimable` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TotalHotkeyAlpha` (r:9 w:8) + /// Proof: `SubtensorModule::TotalHotkeyAlpha` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalIssuance` (r:1 w:1) /// Proof: `SubtensorModule::TotalIssuance` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Alpha` (r:9 w:0) /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AlphaV2` (r:9 w:8) /// Proof: `SubtensorModule::AlphaV2` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::OwnedHotkeys` (r:1 w:1) - /// Proof: `SubtensorModule::OwnedHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:6 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::HotkeyLock` (r:5 w:0) + /// Proof: `SubtensorModule::HotkeyLock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::OwnedHotkeys` (r:1 w:1) + /// Proof: `SubtensorModule::OwnedHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::ChildKeys` (r:10 w:10) /// Proof: `SubtensorModule::ChildKeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::ParentKeys` (r:10 w:10) @@ -1606,8 +1709,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::AlphaDividendsPerSubnet` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::VotingPower` (r:5 w:0) /// Proof: `SubtensorModule::VotingPower` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::RootClaimable` (r:2 w:2) - /// Proof: `SubtensorModule::RootClaimable` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::AutoParentDelegationEnabled` (r:1 w:0) + /// Proof: `SubtensorModule::AutoParentDelegationEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Uids` (r:4 w:8) /// Proof: `SubtensorModule::Uids` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Prometheus` (r:4 w:0) @@ -1622,8 +1725,6 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::LoadedEmission` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NeuronCertificates` (r:4 w:0) /// Proof: `SubtensorModule::NeuronCertificates` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalHotkeyAlpha` (r:8 w:8) - /// Proof: `SubtensorModule::TotalHotkeyAlpha` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalHotkeyShares` (r:8 w:0) /// Proof: `SubtensorModule::TotalHotkeyShares` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalHotkeySharesV2` (r:8 w:8) @@ -1638,9 +1739,9 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `3026` // Estimated: `28766` - // Minimum execution time: 1_148_985_000 picoseconds. - Weight::from_parts(1_154_584_000, 28766) - .saturating_add(T::DbWeight::get().reads(159_u64)) + // Minimum execution time: 1_118_497_000 picoseconds. + Weight::from_parts(1_127_995_000, 28766) + .saturating_add(T::DbWeight::get().reads(166_u64)) .saturating_add(T::DbWeight::get().writes(95_u64)) } /// Storage: `SubtensorModule::Owner` (r:1 w:1) @@ -1653,8 +1754,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `745` // Estimated: `4210` - // Minimum execution time: 21_963_000 picoseconds. - Weight::from_parts(22_504_000, 4210) + // Minimum execution time: 23_785_000 picoseconds. + Weight::from_parts(24_536_000, 4210) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1668,8 +1769,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `740` // Estimated: `9155` - // Minimum execution time: 24_397_000 picoseconds. - Weight::from_parts(25_138_000, 9155) + // Minimum execution time: 27_242_000 picoseconds. + Weight::from_parts(27_693_000, 9155) .saturating_add(T::DbWeight::get().reads(6_u64)) } /// Storage: `SubtensorModule::Owner` (r:1 w:0) @@ -1708,6 +1809,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Swap::FeeRate` (`max_values`: None, `max_size`: Some(12), added: 2487, mode: `MaxEncodedLen`) /// Storage: `Swap::CurrentLiquidity` (r:1 w:0) /// Proof: `Swap::CurrentLiquidity` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:0) + /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Lock` (r:2 w:0) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaIn` (r:2 w:2) /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:2 w:2) @@ -1716,12 +1821,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::TotalStake` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetVolume` (r:2 w:2) /// Proof: `SubtensorModule::SubnetVolume` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:4 w:3) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::SubnetTaoFlow` (r:2 w:2) /// Proof: `SubtensorModule::SubnetTaoFlow` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:0) - /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalIssuance` (r:1 w:1) - /// Proof: `SubtensorModule::TotalIssuance` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::RootClaimable` (r:1 w:0) /// Proof: `SubtensorModule::RootClaimable` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::StakingColdkeys` (r:1 w:1) @@ -1734,12 +1837,12 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn unstake_all_alpha() -> Weight { // Proof Size summary in bytes: - // Measured: `2372` - // Estimated: `10787` - // Minimum execution time: 414_015_000 picoseconds. - Weight::from_parts(427_445_000, 10787) - .saturating_add(T::DbWeight::get().reads(44_u64)) - .saturating_add(T::DbWeight::get().writes(24_u64)) + // Measured: `2614` + // Estimated: `11306` + // Minimum execution time: 545_167_000 picoseconds. + Weight::from_parts(569_493_000, 11306) + .saturating_add(T::DbWeight::get().reads(49_u64)) + .saturating_add(T::DbWeight::get().writes(26_u64)) } /// Storage: `SubtensorModule::Alpha` (r:1 w:0) /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -1775,6 +1878,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::StakingOperationRateLimiter` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Owner` (r:1 w:0) /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:0) + /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Lock` (r:1 w:0) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:1 w:1) @@ -1783,22 +1890,22 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::TotalStake` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetVolume` (r:1 w:1) /// Proof: `SubtensorModule::SubnetVolume` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::SubnetTaoFlow` (r:1 w:1) /// Proof: `SubtensorModule::SubnetTaoFlow` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::StakeThreshold` (r:1 w:0) /// Proof: `SubtensorModule::StakeThreshold` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (r:0 w:1) /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn remove_stake_full_limit() -> Weight { // Proof Size summary in bytes: - // Measured: `2211` - // Estimated: `10626` - // Minimum execution time: 412_223_000 picoseconds. - Weight::from_parts(430_190_000, 10626) - .saturating_add(T::DbWeight::get().reads(30_u64)) - .saturating_add(T::DbWeight::get().writes(13_u64)) + // Measured: `2536` + // Estimated: `10951` + // Minimum execution time: 468_592_000 picoseconds. + Weight::from_parts(490_254_000, 10951) + .saturating_add(T::DbWeight::get().reads(33_u64)) + .saturating_add(T::DbWeight::get().writes(14_u64)) } /// Storage: `Crowdloan::CurrentCrowdloanId` (r:1 w:0) /// Proof: `Crowdloan::CurrentCrowdloanId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -1806,7 +1913,7 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Crowdloan::Crowdloans` (`max_values`: None, `max_size`: Some(282), added: 2757, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::NextSubnetLeaseId` (r:1 w:1) /// Proof: `SubtensorModule::NextSubnetLeaseId` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `System::Account` (r:502 w:502) + /// Storage: `System::Account` (r:503 w:503) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::Owner` (r:1 w:1) /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -1872,18 +1979,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::ValidatorTrust` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::ValidatorPermit` (r:1 w:1) /// Proof: `SubtensorModule::ValidatorPermit` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::RegisteredSubnetCounter` (r:1 w:1) + /// Proof: `SubtensorModule::RegisteredSubnetCounter` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TokenSymbol` (r:3 w:1) /// Proof: `SubtensorModule::TokenSymbol` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalHotkeyAlpha` (r:1 w:1) - /// Proof: `SubtensorModule::TotalHotkeyAlpha` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::Alpha` (r:1 w:0) - /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::AlphaV2` (r:1 w:1) - /// Proof: `SubtensorModule::AlphaV2` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalHotkeyShares` (r:1 w:0) - /// Proof: `SubtensorModule::TotalHotkeyShares` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalHotkeySharesV2` (r:1 w:1) - /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalStake` (r:1 w:1) /// Proof: `SubtensorModule::TotalStake` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Keys` (r:1 w:1) @@ -1896,8 +1995,6 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Crowdloan::Contributions` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::Burn` (r:0 w:1) /// Proof: `SubtensorModule::Burn` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::RAORecycledForRegistration` (r:0 w:1) - /// Proof: `SubtensorModule::RAORecycledForRegistration` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetUidToLeaseId` (r:0 w:1) /// Proof: `SubtensorModule::SubnetUidToLeaseId` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetLocked` (r:0 w:1) @@ -1941,15 +2038,15 @@ impl WeightInfo for SubstrateWeight { /// The range of component `k` is `[2, 500]`. fn register_leased_network(k: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1979 + k * (44 ±0)` - // Estimated: `10400 + k * (2579 ±0)` - // Minimum execution time: 488_338_000 picoseconds. - Weight::from_parts(286_320_370, 10400) - // Standard Error: 33_372 - .saturating_add(Weight::from_parts(47_145_967, 0).saturating_mul(k.into())) - .saturating_add(T::DbWeight::get().reads(54_u64)) + // Measured: `1762 + k * (44 ±0)` + // Estimated: `10183 + k * (2579 ±0)` + // Minimum execution time: 468_092_000 picoseconds. + Weight::from_parts(285_158_564, 10183) + // Standard Error: 22_583 + .saturating_add(Weight::from_parts(45_494_972, 0).saturating_mul(k.into())) + .saturating_add(T::DbWeight::get().reads(51_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(k.into()))) - .saturating_add(T::DbWeight::get().writes(54_u64)) + .saturating_add(T::DbWeight::get().writes(52_u64)) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(k.into()))) .saturating_add(Weight::from_parts(0, 2579).saturating_mul(k.into())) } @@ -1976,10 +2073,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1447 + k * (53 ±0)` // Estimated: `6148 + k * (2514 ±0)` - // Minimum execution time: 112_219_000 picoseconds. - Weight::from_parts(130_541_041, 6148) - // Standard Error: 7_186 - .saturating_add(Weight::from_parts(1_496_294, 0).saturating_mul(k.into())) + // Minimum execution time: 92_805_000 picoseconds. + Weight::from_parts(131_135_086, 6148) + // Standard Error: 6_682 + .saturating_add(Weight::from_parts(1_630_520, 0).saturating_mul(k.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(T::DbWeight::get().writes(7_u64)) @@ -1994,8 +2091,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `649` // Estimated: `9064` - // Minimum execution time: 24_617_000 picoseconds. - Weight::from_parts(25_379_000, 9064) + // Minimum execution time: 27_632_000 picoseconds. + Weight::from_parts(29_085_000, 9064) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -2023,8 +2120,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1060` // Estimated: `4525` - // Minimum execution time: 72_058_000 picoseconds. - Weight::from_parts(73_902_000, 4525) + // Minimum execution time: 73_969_000 picoseconds. + Weight::from_parts(76_133_000, 4525) .saturating_add(T::DbWeight::get().reads(10_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -2040,8 +2137,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `799` // Estimated: `4264` - // Minimum execution time: 31_788_000 picoseconds. - Weight::from_parts(32_469_000, 4264) + // Minimum execution time: 33_843_000 picoseconds. + Weight::from_parts(34_686_000, 4264) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -2057,8 +2154,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `476` // Estimated: `3941` - // Minimum execution time: 15_574_000 picoseconds. - Weight::from_parts(15_894_000, 3941) + // Minimum execution time: 17_483_000 picoseconds. + Weight::from_parts(17_994_000, 3941) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -2088,8 +2185,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1908` // Estimated: `7848` - // Minimum execution time: 137_608_000 picoseconds. - Weight::from_parts(140_011_000, 7848) + // Minimum execution time: 132_329_000 picoseconds. + Weight::from_parts(134_352_000, 7848) .saturating_add(T::DbWeight::get().reads(16_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -2099,8 +2196,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_983_000 picoseconds. - Weight::from_parts(2_173_000, 0) + // Minimum execution time: 2_595_000 picoseconds. + Weight::from_parts(2_805_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::RootClaimableThreshold` (r:0 w:1) @@ -2109,8 +2206,23 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_336_000 picoseconds. - Weight::from_parts(4_737_000, 0) + // Minimum execution time: 5_180_000 picoseconds. + Weight::from_parts(5_821_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `SubtensorModule::Owner` (r:1 w:0) + /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Uids` (r:1 w:0) + /// Proof: `SubtensorModule::Uids` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::AutoParentDelegationEnabled` (r:0 w:1) + /// Proof: `SubtensorModule::AutoParentDelegationEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn set_auto_parent_delegation_enabled() -> Weight { + // Proof Size summary in bytes: + // Measured: `852` + // Estimated: `4317` + // Minimum execution time: 27_352_000 picoseconds. + Weight::from_parts(28_503_000, 4317) + .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::SubnetOwner` (r:1 w:0) @@ -2141,7 +2253,7 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubtokenEnabled` (r:1 w:0) /// Proof: `SubtensorModule::SubtokenEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `System::Account` (r:1 w:1) + /// Storage: `System::Account` (r:3 w:3) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::Owner` (r:1 w:0) /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -2165,22 +2277,28 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AlphaV2` (r:1 w:1) /// Proof: `SubtensorModule::AlphaV2` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalIssuance` (r:1 w:1) - /// Proof: `SubtensorModule::TotalIssuance` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetTaoFlow` (r:1 w:1) /// Proof: `SubtensorModule::SubnetTaoFlow` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Lock` (r:2 w:1) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::MaturityRate` (r:1 w:0) + /// Proof: `SubtensorModule::MaturityRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::UnlockRate` (r:1 w:0) + /// Proof: `SubtensorModule::UnlockRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::HotkeyLock` (r:1 w:1) + /// Proof: `SubtensorModule::HotkeyLock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::StakingOperationRateLimiter` (r:0 w:1) /// Proof: `SubtensorModule::StakingOperationRateLimiter` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (r:0 w:1) /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn add_stake_burn() -> Weight { // Proof Size summary in bytes: - // Measured: `2365` - // Estimated: `8556` - // Minimum execution time: 471_702_000 picoseconds. - Weight::from_parts(484_481_000, 8556) - .saturating_add(T::DbWeight::get().reads(30_u64)) - .saturating_add(T::DbWeight::get().writes(16_u64)) + // Measured: `2617` + // Estimated: `8727` + // Minimum execution time: 595_879_000 picoseconds. + Weight::from_parts(616_657_000, 8727) + .saturating_add(T::DbWeight::get().reads(36_u64)) + .saturating_add(T::DbWeight::get().writes(19_u64)) } /// Storage: `SubtensorModule::PendingChildKeyCooldown` (r:0 w:1) /// Proof: `SubtensorModule::PendingChildKeyCooldown` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) @@ -2188,26 +2306,71 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_013_000 picoseconds. - Weight::from_parts(2_243_000, 0) + // Minimum execution time: 2_575_000 picoseconds. + Weight::from_parts(2_725_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } - - /// Storage: `SubtensorModule::Owner` (r:1 w:0) - /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::Uids` (r:1 w:0) - /// Proof: `SubtensorModule::Uids` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::AutoParentDelegationEnabled` (r:0 w:1) - /// Proof: `SubtensorModule::AutoParentDelegationEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) - fn set_auto_parent_delegation_enabled() -> Weight { - // Proof Size summary in bytes: - // Measured: `852` - // Estimated: `4317` - // Minimum execution time: 19_000_000 picoseconds. - Weight::from_parts(20_000_000, 4317) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } + /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:0) + /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Alpha` (r:1 w:0) + /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::AlphaV2` (r:1 w:0) + /// Proof: `SubtensorModule::AlphaV2` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TotalHotkeyAlpha` (r:1 w:0) + /// Proof: `SubtensorModule::TotalHotkeyAlpha` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TotalHotkeyShares` (r:1 w:0) + /// Proof: `SubtensorModule::TotalHotkeyShares` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TotalHotkeySharesV2` (r:1 w:0) + /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Lock` (r:1 w:1) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::HotkeyLock` (r:1 w:1) + /// Proof: `SubtensorModule::HotkeyLock` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn lock_stake() -> Weight { + // Proof Size summary in bytes: + // Measured: `1463` + // Estimated: `4928` + // Minimum execution time: 90_731_000 picoseconds. + Weight::from_parts(92_755_000, 4928) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `SubtensorModule::Lock` (r:2 w:1) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::MaturityRate` (r:1 w:0) + /// Proof: `SubtensorModule::MaturityRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::UnlockRate` (r:1 w:0) + /// Proof: `SubtensorModule::UnlockRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::HotkeyLock` (r:1 w:1) + /// Proof: `SubtensorModule::HotkeyLock` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn unlock_stake() -> Weight { + // Proof Size summary in bytes: + // Measured: `978` + // Estimated: `6918` + // Minimum execution time: 70_652_000 picoseconds. + Weight::from_parts(72_135_000, 6918) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `SubtensorModule::Lock` (r:2 w:2) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Owner` (r:2 w:0) + /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::MaturityRate` (r:1 w:0) + /// Proof: `SubtensorModule::MaturityRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::UnlockRate` (r:1 w:0) + /// Proof: `SubtensorModule::UnlockRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::HotkeyLock` (r:2 w:2) + /// Proof: `SubtensorModule::HotkeyLock` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn move_lock() -> Weight { + // Proof Size summary in bytes: + // Measured: `1302` + // Estimated: `7242` + // Minimum execution time: 93_155_000 picoseconds. + Weight::from_parts(94_457_000, 7242) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } } // For backwards compatibility and tests. @@ -2220,7 +2383,7 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::Uids` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Burn` (r:1 w:1) /// Proof: `SubtensorModule::Burn` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `System::Account` (r:1 w:1) + /// Storage: `System::Account` (r:2 w:2) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::Owner` (r:1 w:1) /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -2306,12 +2469,12 @@ impl WeightInfo for () { /// Proof: `Swap::CurrentTick` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) fn register() -> Weight { // Proof Size summary in bytes: - // Measured: `1629` + // Measured: `1706` // Estimated: `13600` - // Minimum execution time: 348_026_000 picoseconds. - Weight::from_parts(354_034_000, 13600) - .saturating_add(RocksDbWeight::get().reads(46_u64)) - .saturating_add(RocksDbWeight::get().writes(38_u64)) + // Minimum execution time: 355_490_000 picoseconds. + Weight::from_parts(364_739_000, 13600) + .saturating_add(RocksDbWeight::get().reads(47_u64)) + .saturating_add(RocksDbWeight::get().writes(39_u64)) } /// Storage: `SubtensorModule::CommitRevealWeightsEnabled` (r:1 w:0) /// Proof: `SubtensorModule::CommitRevealWeightsEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -2351,15 +2514,15 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `188782` // Estimated: `10327372` - // Minimum execution time: 16_089_221_000 picoseconds. - Weight::from_parts(16_473_771_000, 10327372) + // Minimum execution time: 14_846_685_000 picoseconds. + Weight::from_parts(15_166_549_000, 10327372) .saturating_add(RocksDbWeight::get().reads(4112_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: `SubtensorModule::SubtokenEnabled` (r:1 w:0) - /// Proof: `SubtensorModule::SubtokenEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubtokenEnabled` (r:1 w:0) + /// Proof: `SubtensorModule::SubtokenEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetMechanism` (r:1 w:0) /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) @@ -2378,7 +2541,7 @@ impl WeightInfo for () { /// Proof: `Swap::FeeRate` (`max_values`: None, `max_size`: Some(12), added: 2487, mode: `MaxEncodedLen`) /// Storage: `Swap::CurrentLiquidity` (r:1 w:0) /// Proof: `Swap::CurrentLiquidity` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) + /// Storage: `System::Account` (r:3 w:3) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::Owner` (r:1 w:0) /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -2402,22 +2565,28 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AlphaV2` (r:1 w:1) /// Proof: `SubtensorModule::AlphaV2` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalIssuance` (r:1 w:1) - /// Proof: `SubtensorModule::TotalIssuance` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetTaoFlow` (r:1 w:1) /// Proof: `SubtensorModule::SubnetTaoFlow` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Lock` (r:2 w:1) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::MaturityRate` (r:1 w:0) + /// Proof: `SubtensorModule::MaturityRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::UnlockRate` (r:1 w:0) + /// Proof: `SubtensorModule::UnlockRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::HotkeyLock` (r:1 w:1) + /// Proof: `SubtensorModule::HotkeyLock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::StakingOperationRateLimiter` (r:0 w:1) /// Proof: `SubtensorModule::StakingOperationRateLimiter` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (r:0 w:1) /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn add_stake() -> Weight { // Proof Size summary in bytes: - // Measured: `2307` - // Estimated: `8556` - // Minimum execution time: 338_691_000 picoseconds. - Weight::from_parts(346_814_000, 8556) - .saturating_add(RocksDbWeight::get().reads(27_u64)) - .saturating_add(RocksDbWeight::get().writes(15_u64)) + // Measured: `2560` + // Estimated: `8727` + // Minimum execution time: 431_592_000 picoseconds. + Weight::from_parts(453_283_000, 8727) + .saturating_add(RocksDbWeight::get().reads(33_u64)) + .saturating_add(RocksDbWeight::get().writes(18_u64)) } /// Storage: `SubtensorModule::IsNetworkMember` (r:2 w:0) /// Proof: `SubtensorModule::IsNetworkMember` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -2429,8 +2598,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `791` // Estimated: `6731` - // Minimum execution time: 32_479_000 picoseconds. - Weight::from_parts(33_721_000, 6731) + // Minimum execution time: 34_354_000 picoseconds. + Weight::from_parts(34_836_000, 6731) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -2444,8 +2613,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `764` // Estimated: `6704` - // Minimum execution time: 29_264_000 picoseconds. - Weight::from_parts(30_095_000, 6704) + // Minimum execution time: 30_417_000 picoseconds. + Weight::from_parts(31_620_000, 6704) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -2457,7 +2626,7 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::Uids` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Burn` (r:1 w:1) /// Proof: `SubtensorModule::Burn` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `System::Account` (r:1 w:1) + /// Storage: `System::Account` (r:2 w:2) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::Owner` (r:1 w:1) /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -2545,10 +2714,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1639` // Estimated: `13600` - // Minimum execution time: 341_145_000 picoseconds. - Weight::from_parts(345_863_000, 13600) - .saturating_add(RocksDbWeight::get().reads(46_u64)) - .saturating_add(RocksDbWeight::get().writes(38_u64)) + // Minimum execution time: 364_917_000 picoseconds. + Weight::from_parts(368_714_000, 13600) + .saturating_add(RocksDbWeight::get().reads(47_u64)) + .saturating_add(RocksDbWeight::get().writes(39_u64)) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -2598,8 +2767,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1415` // Estimated: `4880` - // Minimum execution time: 100_752_000 picoseconds. - Weight::from_parts(102_565_000, 4880) + // Minimum execution time: 100_830_000 picoseconds. + Weight::from_parts(102_322_000, 4880) .saturating_add(RocksDbWeight::get().reads(19_u64)) .saturating_add(RocksDbWeight::get().writes(16_u64)) } @@ -2625,7 +2794,7 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::TotalIssuance` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::BlockEmission` (r:1 w:0) /// Proof: `SubtensorModule::BlockEmission` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `System::Account` (r:1 w:1) + /// Storage: `System::Account` (r:2 w:2) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::SubnetMechanism` (r:1 w:1) /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -2669,26 +2838,16 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::ValidatorTrust` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::ValidatorPermit` (r:1 w:1) /// Proof: `SubtensorModule::ValidatorPermit` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::RegisteredSubnetCounter` (r:1 w:1) + /// Proof: `SubtensorModule::RegisteredSubnetCounter` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TokenSymbol` (r:3 w:1) /// Proof: `SubtensorModule::TokenSymbol` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalHotkeyAlpha` (r:1 w:1) - /// Proof: `SubtensorModule::TotalHotkeyAlpha` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::Alpha` (r:1 w:0) - /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::AlphaV2` (r:1 w:1) - /// Proof: `SubtensorModule::AlphaV2` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalHotkeyShares` (r:1 w:0) - /// Proof: `SubtensorModule::TotalHotkeyShares` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalHotkeySharesV2` (r:1 w:1) - /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalStake` (r:1 w:1) /// Proof: `SubtensorModule::TotalStake` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Keys` (r:1 w:1) /// Proof: `SubtensorModule::Keys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Burn` (r:0 w:1) /// Proof: `SubtensorModule::Burn` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::RAORecycledForRegistration` (r:0 w:1) - /// Proof: `SubtensorModule::RAORecycledForRegistration` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetLocked` (r:0 w:1) /// Proof: `SubtensorModule::SubnetLocked` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworkRegisteredAt` (r:0 w:1) @@ -2725,12 +2884,12 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::MaxAllowedUids` (`max_values`: None, `max_size`: None, mode: `Measured`) fn register_network() -> Weight { // Proof Size summary in bytes: - // Measured: `1676` - // Estimated: `10091` - // Minimum execution time: 289_917_000 picoseconds. - Weight::from_parts(293_954_000, 10091) - .saturating_add(RocksDbWeight::get().reads(45_u64)) - .saturating_add(RocksDbWeight::get().writes(49_u64)) + // Measured: `1459` + // Estimated: `9874` + // Minimum execution time: 268_496_000 picoseconds. + Weight::from_parts(273_143_000, 9874) + .saturating_add(RocksDbWeight::get().reads(42_u64)) + .saturating_add(RocksDbWeight::get().writes(47_u64)) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -2756,8 +2915,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1061` // Estimated: `4526` - // Minimum execution time: 59_199_000 picoseconds. - Weight::from_parts(60_772_000, 4526) + // Minimum execution time: 60_835_000 picoseconds. + Weight::from_parts(62_007_000, 4526) .saturating_add(RocksDbWeight::get().reads(10_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -2801,8 +2960,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1579` // Estimated: `7519` - // Minimum execution time: 107_763_000 picoseconds. - Weight::from_parts(109_746_000, 7519) + // Minimum execution time: 107_622_000 picoseconds. + Weight::from_parts(109_516_000, 7519) .saturating_add(RocksDbWeight::get().reads(18_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -2812,8 +2971,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_126_000 picoseconds. - Weight::from_parts(4_407_000, 0) + // Minimum execution time: 5_260_000 picoseconds. + Weight::from_parts(5_611_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Owner` (r:1 w:0) @@ -2830,8 +2989,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `938` // Estimated: `4403` - // Minimum execution time: 45_358_000 picoseconds. - Weight::from_parts(46_140_000, 4403) + // Minimum execution time: 46_848_000 picoseconds. + Weight::from_parts(47_770_000, 4403) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -2847,8 +3006,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `694` // Estimated: `4159` - // Minimum execution time: 39_469_000 picoseconds. - Weight::from_parts(40_962_000, 4159) + // Minimum execution time: 45_235_000 picoseconds. + Weight::from_parts(46_999_000, 4159) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -2878,17 +3037,19 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::OwnedHotkeys` (r:2 w:2) /// Proof: `SubtensorModule::OwnedHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Lock` (r:2 w:0) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `System::Account` (r:2 w:2) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::LastRateLimitedBlock` (r:0 w:1) /// Proof: `SubtensorModule::LastRateLimitedBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn swap_coldkey_announced() -> Weight { // Proof Size summary in bytes: - // Measured: `1815` - // Estimated: `12705` - // Minimum execution time: 260_764_000 picoseconds. - Weight::from_parts(265_261_000, 12705) - .saturating_add(RocksDbWeight::get().reads(31_u64)) + // Measured: `2117` + // Estimated: `13007` + // Minimum execution time: 267_614_000 picoseconds. + Weight::from_parts(273_394_000, 13007) + .saturating_add(RocksDbWeight::get().reads(33_u64)) .saturating_add(RocksDbWeight::get().writes(15_u64)) } /// Storage: `System::Account` (r:2 w:2) @@ -2919,6 +3080,8 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::OwnedHotkeys` (r:2 w:2) /// Proof: `SubtensorModule::OwnedHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Lock` (r:2 w:0) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::ColdkeySwapAnnouncements` (r:0 w:1) /// Proof: `SubtensorModule::ColdkeySwapAnnouncements` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::ColdkeySwapDisputes` (r:0 w:1) @@ -2927,11 +3090,11 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::LastRateLimitedBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn swap_coldkey() -> Weight { // Proof Size summary in bytes: - // Measured: `1908` - // Estimated: `12798` - // Minimum execution time: 281_736_000 picoseconds. - Weight::from_parts(286_753_000, 12798) - .saturating_add(RocksDbWeight::get().reads(31_u64)) + // Measured: `2210` + // Estimated: `13100` + // Minimum execution time: 289_935_000 picoseconds. + Weight::from_parts(294_274_000, 13100) + .saturating_add(RocksDbWeight::get().reads(33_u64)) .saturating_add(RocksDbWeight::get().writes(19_u64)) } /// Storage: `SubtensorModule::ColdkeySwapAnnouncements` (r:1 w:0) @@ -2942,8 +3105,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `665` // Estimated: `4130` - // Minimum execution time: 19_950_000 picoseconds. - Weight::from_parts(20_701_000, 4130) + // Minimum execution time: 22_412_000 picoseconds. + Weight::from_parts(23_364_000, 4130) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -2955,8 +3118,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `613` // Estimated: `4078` - // Minimum execution time: 16_415_000 picoseconds. - Weight::from_parts(17_096_000, 4078) + // Minimum execution time: 18_325_000 picoseconds. + Weight::from_parts(19_206_000, 4078) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -2968,8 +3131,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_790_000 picoseconds. - Weight::from_parts(7_151_000, 0) + // Minimum execution time: 8_376_000 picoseconds. + Weight::from_parts(8_697_000, 0) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: `SubtensorModule::CommitRevealWeightsEnabled` (r:1 w:0) @@ -3012,8 +3175,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `2084` // Estimated: `8024` - // Minimum execution time: 426_724_000 picoseconds. - Weight::from_parts(431_712_000, 8024) + // Minimum execution time: 396_345_000 picoseconds. + Weight::from_parts(408_599_000, 8024) .saturating_add(RocksDbWeight::get().reads(18_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -3035,13 +3198,17 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:1 w:1) /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:0) + /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Lock` (r:1 w:0) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn recycle_alpha() -> Weight { // Proof Size summary in bytes: - // Measured: `1424` - // Estimated: `4889` - // Minimum execution time: 128_484_000 picoseconds. - Weight::from_parts(130_548_000, 4889) - .saturating_add(RocksDbWeight::get().reads(9_u64)) + // Measured: `1860` + // Estimated: `5325` + // Minimum execution time: 166_603_000 picoseconds. + Weight::from_parts(168_788_000, 5325) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -3062,13 +3229,17 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:1 w:0) /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:0) + /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Lock` (r:1 w:0) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn burn_alpha() -> Weight { // Proof Size summary in bytes: - // Measured: `1424` - // Estimated: `4889` - // Minimum execution time: 126_171_000 picoseconds. - Weight::from_parts(128_965_000, 4889) - .saturating_add(RocksDbWeight::get().reads(9_u64)) + // Measured: `1860` + // Estimated: `5325` + // Minimum execution time: 164_650_000 picoseconds. + Weight::from_parts(166_603_000, 5325) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -3087,8 +3258,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1079` // Estimated: `4544` - // Minimum execution time: 37_957_000 picoseconds. - Weight::from_parts(38_939_000, 4544) + // Minimum execution time: 38_783_000 picoseconds. + Weight::from_parts(40_136_000, 4544) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -3114,7 +3285,7 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubtokenEnabled` (r:1 w:0) /// Proof: `SubtensorModule::SubtokenEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `System::Account` (r:1 w:1) + /// Storage: `System::Account` (r:3 w:3) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::Owner` (r:1 w:0) /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -3138,22 +3309,28 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AlphaV2` (r:1 w:1) /// Proof: `SubtensorModule::AlphaV2` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalIssuance` (r:1 w:1) - /// Proof: `SubtensorModule::TotalIssuance` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetTaoFlow` (r:1 w:1) /// Proof: `SubtensorModule::SubnetTaoFlow` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Lock` (r:2 w:1) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::MaturityRate` (r:1 w:0) + /// Proof: `SubtensorModule::MaturityRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::UnlockRate` (r:1 w:0) + /// Proof: `SubtensorModule::UnlockRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::HotkeyLock` (r:1 w:1) + /// Proof: `SubtensorModule::HotkeyLock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::StakingOperationRateLimiter` (r:0 w:1) /// Proof: `SubtensorModule::StakingOperationRateLimiter` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (r:0 w:1) /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn add_stake_limit() -> Weight { // Proof Size summary in bytes: - // Measured: `2307` - // Estimated: `8556` - // Minimum execution time: 376_539_000 picoseconds. - Weight::from_parts(383_750_000, 8556) - .saturating_add(RocksDbWeight::get().reads(27_u64)) - .saturating_add(RocksDbWeight::get().writes(15_u64)) + // Measured: `2560` + // Estimated: `8727` + // Minimum execution time: 465_225_000 picoseconds. + Weight::from_parts(485_933_000, 8727) + .saturating_add(RocksDbWeight::get().reads(33_u64)) + .saturating_add(RocksDbWeight::get().writes(18_u64)) } /// Storage: `SubtensorModule::Alpha` (r:2 w:0) /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -3187,11 +3364,76 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `2002` // Estimated: `7942` - // Minimum execution time: 222_486_000 picoseconds. - Weight::from_parts(223_918_000, 7942) + // Minimum execution time: 205_998_000 picoseconds. + Weight::from_parts(208_783_000, 7942) .saturating_add(RocksDbWeight::get().reads(19_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } + /// Storage: `SubtensorModule::SubtokenEnabled` (r:1 w:0) + /// Proof: `SubtensorModule::SubtokenEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Alpha` (r:1 w:0) + /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::AlphaV2` (r:1 w:1) + /// Proof: `SubtensorModule::AlphaV2` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TotalHotkeyAlpha` (r:2 w:1) + /// Proof: `SubtensorModule::TotalHotkeyAlpha` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TotalHotkeyShares` (r:1 w:0) + /// Proof: `SubtensorModule::TotalHotkeyShares` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TotalHotkeySharesV2` (r:1 w:1) + /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::NetworksAdded` (r:3 w:0) + /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::StakingOperationRateLimiter` (r:1 w:0) + /// Proof: `SubtensorModule::StakingOperationRateLimiter` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetMechanism` (r:2 w:0) + /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetTaoProvided` (r:1 w:0) + /// Proof: `SubtensorModule::SubnetTaoProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Swap::SwapV3Initialized` (r:1 w:0) + /// Proof: `Swap::SwapV3Initialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) + /// Storage: `Swap::AlphaSqrtPrice` (r:1 w:1) + /// Proof: `Swap::AlphaSqrtPrice` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) + /// Storage: `Swap::CurrentTick` (r:1 w:1) + /// Proof: `Swap::CurrentTick` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Swap::TickIndexBitmapWords` (r:3 w:0) + /// Proof: `Swap::TickIndexBitmapWords` (`max_values`: None, `max_size`: Some(47), added: 2522, mode: `MaxEncodedLen`) + /// Storage: `Swap::FeeRate` (r:1 w:0) + /// Proof: `Swap::FeeRate` (`max_values`: None, `max_size`: Some(12), added: 2487, mode: `MaxEncodedLen`) + /// Storage: `Swap::CurrentLiquidity` (r:1 w:0) + /// Proof: `Swap::CurrentLiquidity` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::Owner` (r:1 w:0) + /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:0) + /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Lock` (r:1 w:0) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetAlphaOut` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TotalStake` (r:1 w:1) + /// Proof: `SubtensorModule::TotalStake` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetVolume` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetVolume` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::SubnetTaoFlow` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetTaoFlow` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::StakeThreshold` (r:1 w:0) + /// Proof: `SubtensorModule::StakeThreshold` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (r:0 w:1) + /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn remove_stake() -> Weight { + // Proof Size summary in bytes: + // Measured: `2536` + // Estimated: `10951` + // Minimum execution time: 412_326_000 picoseconds. + Weight::from_parts(433_846_000, 10951) + .saturating_add(RocksDbWeight::get().reads(34_u64)) + .saturating_add(RocksDbWeight::get().writes(14_u64)) + } /// Storage: `SubtensorModule::SubnetMechanism` (r:2 w:0) /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) @@ -3226,6 +3468,10 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Owner` (r:1 w:0) /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:0) + /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Lock` (r:1 w:0) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:1 w:1) @@ -3234,22 +3480,22 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::TotalStake` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetVolume` (r:1 w:1) /// Proof: `SubtensorModule::SubnetVolume` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::SubnetTaoFlow` (r:1 w:1) /// Proof: `SubtensorModule::SubnetTaoFlow` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::StakeThreshold` (r:1 w:0) /// Proof: `SubtensorModule::StakeThreshold` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (r:0 w:1) /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn remove_stake_limit() -> Weight { // Proof Size summary in bytes: - // Measured: `2211` - // Estimated: `10626` - // Minimum execution time: 387_646_000 picoseconds. - Weight::from_parts(403_169_000, 10626) - .saturating_add(RocksDbWeight::get().reads(30_u64)) - .saturating_add(RocksDbWeight::get().writes(13_u64)) + // Measured: `2536` + // Estimated: `10951` + // Minimum execution time: 445_979_000 picoseconds. + Weight::from_parts(451_159_000, 10951) + .saturating_add(RocksDbWeight::get().reads(33_u64)) + .saturating_add(RocksDbWeight::get().writes(14_u64)) } /// Storage: `SubtensorModule::Alpha` (r:2 w:0) /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -3287,6 +3533,10 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::SubtokenEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Owner` (r:1 w:0) /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:0) + /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Lock` (r:3 w:1) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaIn` (r:2 w:2) /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:2 w:2) @@ -3295,22 +3545,26 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::TotalStake` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetVolume` (r:2 w:2) /// Proof: `SubtensorModule::SubnetVolume` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:3 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::SubnetTaoFlow` (r:2 w:2) /// Proof: `SubtensorModule::SubnetTaoFlow` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:0) - /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalIssuance` (r:1 w:1) - /// Proof: `SubtensorModule::TotalIssuance` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::MaturityRate` (r:1 w:0) + /// Proof: `SubtensorModule::MaturityRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::UnlockRate` (r:1 w:0) + /// Proof: `SubtensorModule::UnlockRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::HotkeyLock` (r:1 w:1) + /// Proof: `SubtensorModule::HotkeyLock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (r:0 w:1) /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn swap_stake_limit() -> Weight { // Proof Size summary in bytes: - // Measured: `2494` - // Estimated: `8556` - // Minimum execution time: 461_377_000 picoseconds. - Weight::from_parts(477_951_000, 8556) - .saturating_add(RocksDbWeight::get().reads(40_u64)) - .saturating_add(RocksDbWeight::get().writes(22_u64)) + // Measured: `2923` + // Estimated: `11338` + // Minimum execution time: 641_075_000 picoseconds. + Weight::from_parts(664_801_000, 11338) + .saturating_add(RocksDbWeight::get().reads(48_u64)) + .saturating_add(RocksDbWeight::get().writes(25_u64)) } /// Storage: `SubtensorModule::Alpha` (r:2 w:0) /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -3332,8 +3586,10 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TransferToggle` (r:1 w:0) /// Proof: `SubtensorModule::TransferToggle` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:1) + /// Storage: `SubtensorModule::StakingHotkeys` (r:2 w:1) /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Lock` (r:1 w:0) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetMechanism` (r:1 w:0) /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Swap::SwapV3Initialized` (r:1 w:0) @@ -3344,11 +3600,11 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn transfer_stake() -> Weight { // Proof Size summary in bytes: - // Measured: `1829` - // Estimated: `7769` - // Minimum execution time: 215_726_000 picoseconds. - Weight::from_parts(219_552_000, 7769) - .saturating_add(RocksDbWeight::get().reads(16_u64)) + // Measured: `1996` + // Estimated: `7936` + // Minimum execution time: 240_382_000 picoseconds. + Weight::from_parts(243_919_000, 7936) + .saturating_add(RocksDbWeight::get().reads(18_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } /// Storage: `SubtensorModule::Alpha` (r:2 w:0) @@ -3387,6 +3643,10 @@ impl WeightInfo for () { /// Proof: `Swap::FeeRate` (`max_values`: None, `max_size`: Some(12), added: 2487, mode: `MaxEncodedLen`) /// Storage: `Swap::CurrentLiquidity` (r:1 w:0) /// Proof: `Swap::CurrentLiquidity` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:0) + /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Lock` (r:3 w:1) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaIn` (r:2 w:2) /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:2 w:2) @@ -3395,22 +3655,26 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::TotalStake` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetVolume` (r:2 w:2) /// Proof: `SubtensorModule::SubnetVolume` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:3 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::SubnetTaoFlow` (r:2 w:2) /// Proof: `SubtensorModule::SubnetTaoFlow` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:0) - /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalIssuance` (r:1 w:1) - /// Proof: `SubtensorModule::TotalIssuance` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::MaturityRate` (r:1 w:0) + /// Proof: `SubtensorModule::MaturityRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::UnlockRate` (r:1 w:0) + /// Proof: `SubtensorModule::UnlockRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::HotkeyLock` (r:1 w:1) + /// Proof: `SubtensorModule::HotkeyLock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (r:0 w:1) /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn swap_stake() -> Weight { // Proof Size summary in bytes: - // Measured: `2421` - // Estimated: `8556` - // Minimum execution time: 402_808_000 picoseconds. - Weight::from_parts(420_035_000, 8556) - .saturating_add(RocksDbWeight::get().reads(40_u64)) - .saturating_add(RocksDbWeight::get().writes(22_u64)) + // Measured: `2785` + // Estimated: `11200` + // Minimum execution time: 591_602_000 picoseconds. + Weight::from_parts(613_634_000, 11200) + .saturating_add(RocksDbWeight::get().reads(48_u64)) + .saturating_add(RocksDbWeight::get().writes(25_u64)) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -3438,8 +3702,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1084` // Estimated: `4549` - // Minimum execution time: 125_589_000 picoseconds. - Weight::from_parts(141_484_000, 4549) + // Minimum execution time: 122_971_000 picoseconds. + Weight::from_parts(124_314_000, 4549) .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -3479,8 +3743,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1416` // Estimated: `7356` - // Minimum execution time: 99_310_000 picoseconds. - Weight::from_parts(101_193_000, 7356) + // Minimum execution time: 100_659_000 picoseconds. + Weight::from_parts(101_972_000, 7356) .saturating_add(RocksDbWeight::get().reads(16_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -3496,8 +3760,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `793` // Estimated: `4258` - // Minimum execution time: 25_499_000 picoseconds. - Weight::from_parts(26_330_000, 4258) + // Minimum execution time: 27_622_000 picoseconds. + Weight::from_parts(29_025_000, 4258) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -3515,8 +3779,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `886` // Estimated: `4351` - // Minimum execution time: 32_540_000 picoseconds. - Weight::from_parts(33_501_000, 4351) + // Minimum execution time: 34_876_000 picoseconds. + Weight::from_parts(35_297_000, 4351) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -3562,6 +3826,8 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::ActivityCutoff` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::RegistrationsThisInterval` (r:1 w:1) /// Proof: `SubtensorModule::RegistrationsThisInterval` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::OwnedHotkeys` (r:1 w:1) /// Proof: `SubtensorModule::OwnedHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:1) @@ -3584,26 +3850,16 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::ValidatorTrust` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::ValidatorPermit` (r:1 w:1) /// Proof: `SubtensorModule::ValidatorPermit` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::RegisteredSubnetCounter` (r:1 w:1) + /// Proof: `SubtensorModule::RegisteredSubnetCounter` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TokenSymbol` (r:3 w:1) /// Proof: `SubtensorModule::TokenSymbol` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalHotkeyAlpha` (r:1 w:1) - /// Proof: `SubtensorModule::TotalHotkeyAlpha` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::Alpha` (r:1 w:0) - /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::AlphaV2` (r:1 w:1) - /// Proof: `SubtensorModule::AlphaV2` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalHotkeyShares` (r:1 w:0) - /// Proof: `SubtensorModule::TotalHotkeyShares` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalHotkeySharesV2` (r:1 w:1) - /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalStake` (r:1 w:1) /// Proof: `SubtensorModule::TotalStake` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Keys` (r:1 w:1) /// Proof: `SubtensorModule::Keys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Burn` (r:0 w:1) /// Proof: `SubtensorModule::Burn` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::RAORecycledForRegistration` (r:0 w:1) - /// Proof: `SubtensorModule::RAORecycledForRegistration` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetLocked` (r:0 w:1) /// Proof: `SubtensorModule::SubnetLocked` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworkRegisteredAt` (r:0 w:1) @@ -3640,12 +3896,12 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::MaxAllowedUids` (`max_values`: None, `max_size`: None, mode: `Measured`) fn register_network_with_identity() -> Weight { // Proof Size summary in bytes: - // Measured: `1560` - // Estimated: `9975` - // Minimum execution time: 279_983_000 picoseconds. - Weight::from_parts(284_690_000, 9975) - .saturating_add(RocksDbWeight::get().reads(44_u64)) - .saturating_add(RocksDbWeight::get().writes(48_u64)) + // Measured: `1343` + // Estimated: `9758` + // Minimum execution time: 263_716_000 picoseconds. + Weight::from_parts(267_293_000, 9758) + .saturating_add(RocksDbWeight::get().reads(41_u64)) + .saturating_add(RocksDbWeight::get().writes(46_u64)) } /// Storage: `SubtensorModule::IsNetworkMember` (r:2 w:0) /// Proof: `SubtensorModule::IsNetworkMember` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -3657,8 +3913,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `762` // Estimated: `6702` - // Minimum execution time: 31_257_000 picoseconds. - Weight::from_parts(32_769_000, 6702) + // Minimum execution time: 33_633_000 picoseconds. + Weight::from_parts(34_445_000, 6702) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -3672,8 +3928,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `842` // Estimated: `6782` - // Minimum execution time: 28_703_000 picoseconds. - Weight::from_parts(30_106_000, 6782) + // Minimum execution time: 30_758_000 picoseconds. + Weight::from_parts(31_870_000, 6782) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -3685,8 +3941,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `595` // Estimated: `4060` - // Minimum execution time: 15_634_000 picoseconds. - Weight::from_parts(16_254_000, 4060) + // Minimum execution time: 17_412_000 picoseconds. + Weight::from_parts(17_964_000, 4060) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -3698,16 +3954,22 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::TxRateLimit` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::IsNetworkMember` (r:6 w:10) /// Proof: `SubtensorModule::IsNetworkMember` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::RootClaimable` (r:2 w:2) + /// Proof: `SubtensorModule::RootClaimable` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TotalHotkeyAlpha` (r:9 w:8) + /// Proof: `SubtensorModule::TotalHotkeyAlpha` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalIssuance` (r:1 w:1) /// Proof: `SubtensorModule::TotalIssuance` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Alpha` (r:9 w:0) /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AlphaV2` (r:9 w:8) /// Proof: `SubtensorModule::AlphaV2` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::OwnedHotkeys` (r:1 w:1) - /// Proof: `SubtensorModule::OwnedHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:6 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::HotkeyLock` (r:5 w:0) + /// Proof: `SubtensorModule::HotkeyLock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::OwnedHotkeys` (r:1 w:1) + /// Proof: `SubtensorModule::OwnedHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::ChildKeys` (r:10 w:10) /// Proof: `SubtensorModule::ChildKeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::ParentKeys` (r:10 w:10) @@ -3724,8 +3986,8 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::AlphaDividendsPerSubnet` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::VotingPower` (r:5 w:0) /// Proof: `SubtensorModule::VotingPower` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::RootClaimable` (r:2 w:2) - /// Proof: `SubtensorModule::RootClaimable` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::AutoParentDelegationEnabled` (r:1 w:0) + /// Proof: `SubtensorModule::AutoParentDelegationEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Uids` (r:4 w:8) /// Proof: `SubtensorModule::Uids` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Prometheus` (r:4 w:0) @@ -3740,8 +4002,6 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::LoadedEmission` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NeuronCertificates` (r:4 w:0) /// Proof: `SubtensorModule::NeuronCertificates` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalHotkeyAlpha` (r:8 w:8) - /// Proof: `SubtensorModule::TotalHotkeyAlpha` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalHotkeyShares` (r:8 w:0) /// Proof: `SubtensorModule::TotalHotkeyShares` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalHotkeySharesV2` (r:8 w:8) @@ -3756,9 +4016,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `3026` // Estimated: `28766` - // Minimum execution time: 1_148_985_000 picoseconds. - Weight::from_parts(1_154_584_000, 28766) - .saturating_add(RocksDbWeight::get().reads(159_u64)) + // Minimum execution time: 1_118_497_000 picoseconds. + Weight::from_parts(1_127_995_000, 28766) + .saturating_add(RocksDbWeight::get().reads(166_u64)) .saturating_add(RocksDbWeight::get().writes(95_u64)) } /// Storage: `SubtensorModule::Owner` (r:1 w:1) @@ -3771,8 +4031,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `745` // Estimated: `4210` - // Minimum execution time: 21_963_000 picoseconds. - Weight::from_parts(22_504_000, 4210) + // Minimum execution time: 23_785_000 picoseconds. + Weight::from_parts(24_536_000, 4210) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3786,8 +4046,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `740` // Estimated: `9155` - // Minimum execution time: 24_397_000 picoseconds. - Weight::from_parts(25_138_000, 9155) + // Minimum execution time: 27_242_000 picoseconds. + Weight::from_parts(27_693_000, 9155) .saturating_add(RocksDbWeight::get().reads(6_u64)) } /// Storage: `SubtensorModule::Owner` (r:1 w:0) @@ -3826,6 +4086,10 @@ impl WeightInfo for () { /// Proof: `Swap::FeeRate` (`max_values`: None, `max_size`: Some(12), added: 2487, mode: `MaxEncodedLen`) /// Storage: `Swap::CurrentLiquidity` (r:1 w:0) /// Proof: `Swap::CurrentLiquidity` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:0) + /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Lock` (r:2 w:0) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaIn` (r:2 w:2) /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:2 w:2) @@ -3834,12 +4098,10 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::TotalStake` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetVolume` (r:2 w:2) /// Proof: `SubtensorModule::SubnetVolume` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:4 w:3) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::SubnetTaoFlow` (r:2 w:2) /// Proof: `SubtensorModule::SubnetTaoFlow` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:0) - /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalIssuance` (r:1 w:1) - /// Proof: `SubtensorModule::TotalIssuance` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::RootClaimable` (r:1 w:0) /// Proof: `SubtensorModule::RootClaimable` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::StakingColdkeys` (r:1 w:1) @@ -3852,12 +4114,12 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn unstake_all_alpha() -> Weight { // Proof Size summary in bytes: - // Measured: `2372` - // Estimated: `10787` - // Minimum execution time: 414_015_000 picoseconds. - Weight::from_parts(427_445_000, 10787) - .saturating_add(RocksDbWeight::get().reads(44_u64)) - .saturating_add(RocksDbWeight::get().writes(24_u64)) + // Measured: `2614` + // Estimated: `11306` + // Minimum execution time: 545_167_000 picoseconds. + Weight::from_parts(569_493_000, 11306) + .saturating_add(RocksDbWeight::get().reads(49_u64)) + .saturating_add(RocksDbWeight::get().writes(26_u64)) } /// Storage: `SubtensorModule::Alpha` (r:1 w:0) /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -3893,6 +4155,10 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::StakingOperationRateLimiter` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Owner` (r:1 w:0) /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:0) + /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Lock` (r:1 w:0) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:1 w:1) @@ -3901,22 +4167,22 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::TotalStake` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetVolume` (r:1 w:1) /// Proof: `SubtensorModule::SubnetVolume` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::SubnetTaoFlow` (r:1 w:1) /// Proof: `SubtensorModule::SubnetTaoFlow` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::StakeThreshold` (r:1 w:0) /// Proof: `SubtensorModule::StakeThreshold` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (r:0 w:1) /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn remove_stake_full_limit() -> Weight { // Proof Size summary in bytes: - // Measured: `2211` - // Estimated: `10626` - // Minimum execution time: 412_223_000 picoseconds. - Weight::from_parts(430_190_000, 10626) - .saturating_add(RocksDbWeight::get().reads(30_u64)) - .saturating_add(RocksDbWeight::get().writes(13_u64)) + // Measured: `2536` + // Estimated: `10951` + // Minimum execution time: 468_592_000 picoseconds. + Weight::from_parts(490_254_000, 10951) + .saturating_add(RocksDbWeight::get().reads(33_u64)) + .saturating_add(RocksDbWeight::get().writes(14_u64)) } /// Storage: `Crowdloan::CurrentCrowdloanId` (r:1 w:0) /// Proof: `Crowdloan::CurrentCrowdloanId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -3924,7 +4190,7 @@ impl WeightInfo for () { /// Proof: `Crowdloan::Crowdloans` (`max_values`: None, `max_size`: Some(282), added: 2757, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::NextSubnetLeaseId` (r:1 w:1) /// Proof: `SubtensorModule::NextSubnetLeaseId` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `System::Account` (r:502 w:502) + /// Storage: `System::Account` (r:503 w:503) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::Owner` (r:1 w:1) /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -3990,18 +4256,10 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::ValidatorTrust` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::ValidatorPermit` (r:1 w:1) /// Proof: `SubtensorModule::ValidatorPermit` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::RegisteredSubnetCounter` (r:1 w:1) + /// Proof: `SubtensorModule::RegisteredSubnetCounter` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TokenSymbol` (r:3 w:1) /// Proof: `SubtensorModule::TokenSymbol` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalHotkeyAlpha` (r:1 w:1) - /// Proof: `SubtensorModule::TotalHotkeyAlpha` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::Alpha` (r:1 w:0) - /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::AlphaV2` (r:1 w:1) - /// Proof: `SubtensorModule::AlphaV2` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalHotkeyShares` (r:1 w:0) - /// Proof: `SubtensorModule::TotalHotkeyShares` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalHotkeySharesV2` (r:1 w:1) - /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalStake` (r:1 w:1) /// Proof: `SubtensorModule::TotalStake` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Keys` (r:1 w:1) @@ -4014,8 +4272,6 @@ impl WeightInfo for () { /// Proof: `Crowdloan::Contributions` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::Burn` (r:0 w:1) /// Proof: `SubtensorModule::Burn` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::RAORecycledForRegistration` (r:0 w:1) - /// Proof: `SubtensorModule::RAORecycledForRegistration` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetUidToLeaseId` (r:0 w:1) /// Proof: `SubtensorModule::SubnetUidToLeaseId` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetLocked` (r:0 w:1) @@ -4059,15 +4315,15 @@ impl WeightInfo for () { /// The range of component `k` is `[2, 500]`. fn register_leased_network(k: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1979 + k * (44 ±0)` - // Estimated: `10400 + k * (2579 ±0)` - // Minimum execution time: 488_338_000 picoseconds. - Weight::from_parts(286_320_370, 10400) - // Standard Error: 33_372 - .saturating_add(Weight::from_parts(47_145_967, 0).saturating_mul(k.into())) - .saturating_add(RocksDbWeight::get().reads(54_u64)) + // Measured: `1762 + k * (44 ±0)` + // Estimated: `10183 + k * (2579 ±0)` + // Minimum execution time: 468_092_000 picoseconds. + Weight::from_parts(285_158_564, 10183) + // Standard Error: 22_583 + .saturating_add(Weight::from_parts(45_494_972, 0).saturating_mul(k.into())) + .saturating_add(RocksDbWeight::get().reads(51_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(k.into()))) - .saturating_add(RocksDbWeight::get().writes(54_u64)) + .saturating_add(RocksDbWeight::get().writes(52_u64)) .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(k.into()))) .saturating_add(Weight::from_parts(0, 2579).saturating_mul(k.into())) } @@ -4094,10 +4350,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1447 + k * (53 ±0)` // Estimated: `6148 + k * (2514 ±0)` - // Minimum execution time: 112_219_000 picoseconds. - Weight::from_parts(130_541_041, 6148) - // Standard Error: 7_186 - .saturating_add(Weight::from_parts(1_496_294, 0).saturating_mul(k.into())) + // Minimum execution time: 92_805_000 picoseconds. + Weight::from_parts(131_135_086, 6148) + // Standard Error: 6_682 + .saturating_add(Weight::from_parts(1_630_520, 0).saturating_mul(k.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(RocksDbWeight::get().writes(7_u64)) @@ -4112,8 +4368,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `649` // Estimated: `9064` - // Minimum execution time: 24_617_000 picoseconds. - Weight::from_parts(25_379_000, 9064) + // Minimum execution time: 27_632_000 picoseconds. + Weight::from_parts(29_085_000, 9064) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -4141,8 +4397,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1060` // Estimated: `4525` - // Minimum execution time: 72_058_000 picoseconds. - Weight::from_parts(73_902_000, 4525) + // Minimum execution time: 73_969_000 picoseconds. + Weight::from_parts(76_133_000, 4525) .saturating_add(RocksDbWeight::get().reads(10_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -4158,8 +4414,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `799` // Estimated: `4264` - // Minimum execution time: 31_788_000 picoseconds. - Weight::from_parts(32_469_000, 4264) + // Minimum execution time: 33_843_000 picoseconds. + Weight::from_parts(34_686_000, 4264) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -4175,8 +4431,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `476` // Estimated: `3941` - // Minimum execution time: 15_574_000 picoseconds. - Weight::from_parts(15_894_000, 3941) + // Minimum execution time: 17_483_000 picoseconds. + Weight::from_parts(17_994_000, 3941) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -4206,8 +4462,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1908` // Estimated: `7848` - // Minimum execution time: 137_608_000 picoseconds. - Weight::from_parts(140_011_000, 7848) + // Minimum execution time: 132_329_000 picoseconds. + Weight::from_parts(134_352_000, 7848) .saturating_add(RocksDbWeight::get().reads(16_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -4217,8 +4473,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_983_000 picoseconds. - Weight::from_parts(2_173_000, 0) + // Minimum execution time: 2_595_000 picoseconds. + Weight::from_parts(2_805_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::RootClaimableThreshold` (r:0 w:1) @@ -4227,8 +4483,23 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_336_000 picoseconds. - Weight::from_parts(4_737_000, 0) + // Minimum execution time: 5_180_000 picoseconds. + Weight::from_parts(5_821_000, 0) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `SubtensorModule::Owner` (r:1 w:0) + /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Uids` (r:1 w:0) + /// Proof: `SubtensorModule::Uids` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::AutoParentDelegationEnabled` (r:0 w:1) + /// Proof: `SubtensorModule::AutoParentDelegationEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn set_auto_parent_delegation_enabled() -> Weight { + // Proof Size summary in bytes: + // Measured: `852` + // Estimated: `4317` + // Minimum execution time: 27_352_000 picoseconds. + Weight::from_parts(28_503_000, 4317) + .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::SubnetOwner` (r:1 w:0) @@ -4259,7 +4530,7 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubtokenEnabled` (r:1 w:0) /// Proof: `SubtensorModule::SubtokenEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `System::Account` (r:1 w:1) + /// Storage: `System::Account` (r:3 w:3) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::Owner` (r:1 w:0) /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -4283,22 +4554,28 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AlphaV2` (r:1 w:1) /// Proof: `SubtensorModule::AlphaV2` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalIssuance` (r:1 w:1) - /// Proof: `SubtensorModule::TotalIssuance` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetTaoFlow` (r:1 w:1) /// Proof: `SubtensorModule::SubnetTaoFlow` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Lock` (r:2 w:1) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::MaturityRate` (r:1 w:0) + /// Proof: `SubtensorModule::MaturityRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::UnlockRate` (r:1 w:0) + /// Proof: `SubtensorModule::UnlockRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::HotkeyLock` (r:1 w:1) + /// Proof: `SubtensorModule::HotkeyLock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::StakingOperationRateLimiter` (r:0 w:1) /// Proof: `SubtensorModule::StakingOperationRateLimiter` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (r:0 w:1) /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn add_stake_burn() -> Weight { // Proof Size summary in bytes: - // Measured: `2365` - // Estimated: `8556` - // Minimum execution time: 471_702_000 picoseconds. - Weight::from_parts(484_481_000, 8556) - .saturating_add(RocksDbWeight::get().reads(30_u64)) - .saturating_add(RocksDbWeight::get().writes(16_u64)) + // Measured: `2617` + // Estimated: `8727` + // Minimum execution time: 595_879_000 picoseconds. + Weight::from_parts(616_657_000, 8727) + .saturating_add(RocksDbWeight::get().reads(36_u64)) + .saturating_add(RocksDbWeight::get().writes(19_u64)) } /// Storage: `SubtensorModule::PendingChildKeyCooldown` (r:0 w:1) /// Proof: `SubtensorModule::PendingChildKeyCooldown` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) @@ -4306,24 +4583,69 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_013_000 picoseconds. - Weight::from_parts(2_243_000, 0) + // Minimum execution time: 2_575_000 picoseconds. + Weight::from_parts(2_725_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - - /// Storage: `SubtensorModule::Owner` (r:1 w:0) - /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::Uids` (r:1 w:0) - /// Proof: `SubtensorModule::Uids` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::AutoParentDelegationEnabled` (r:0 w:1) - /// Proof: `SubtensorModule::AutoParentDelegationEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) - fn set_auto_parent_delegation_enabled() -> Weight { - // Proof Size summary in bytes: - // Measured: `852` - // Estimated: `4317` - // Minimum execution time: 19_000_000 picoseconds. - Weight::from_parts(20_000_000, 4317) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } + /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:0) + /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Alpha` (r:1 w:0) + /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::AlphaV2` (r:1 w:0) + /// Proof: `SubtensorModule::AlphaV2` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TotalHotkeyAlpha` (r:1 w:0) + /// Proof: `SubtensorModule::TotalHotkeyAlpha` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TotalHotkeyShares` (r:1 w:0) + /// Proof: `SubtensorModule::TotalHotkeyShares` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TotalHotkeySharesV2` (r:1 w:0) + /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Lock` (r:1 w:1) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::HotkeyLock` (r:1 w:1) + /// Proof: `SubtensorModule::HotkeyLock` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn lock_stake() -> Weight { + // Proof Size summary in bytes: + // Measured: `1463` + // Estimated: `4928` + // Minimum execution time: 90_731_000 picoseconds. + Weight::from_parts(92_755_000, 4928) + .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: `SubtensorModule::Lock` (r:2 w:1) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::MaturityRate` (r:1 w:0) + /// Proof: `SubtensorModule::MaturityRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::UnlockRate` (r:1 w:0) + /// Proof: `SubtensorModule::UnlockRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::HotkeyLock` (r:1 w:1) + /// Proof: `SubtensorModule::HotkeyLock` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn unlock_stake() -> Weight { + // Proof Size summary in bytes: + // Measured: `978` + // Estimated: `6918` + // Minimum execution time: 70_652_000 picoseconds. + Weight::from_parts(72_135_000, 6918) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: `SubtensorModule::Lock` (r:2 w:2) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Owner` (r:2 w:0) + /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::MaturityRate` (r:1 w:0) + /// Proof: `SubtensorModule::MaturityRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::UnlockRate` (r:1 w:0) + /// Proof: `SubtensorModule::UnlockRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::HotkeyLock` (r:2 w:2) + /// Proof: `SubtensorModule::HotkeyLock` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn move_lock() -> Weight { + // Proof Size summary in bytes: + // Measured: `1302` + // Estimated: `7242` + // Minimum execution time: 93_155_000 picoseconds. + Weight::from_parts(94_457_000, 7242) + .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } } diff --git a/pallets/swap/src/mock.rs b/pallets/swap/src/mock.rs index 6df0c3584f..65dcc676a2 100644 --- a/pallets/swap/src/mock.rs +++ b/pallets/swap/src/mock.rs @@ -267,15 +267,6 @@ impl BalanceOps for MockBalanceOps { .into() } - fn increase_balance(_coldkey: &AccountId, _tao: TaoBalance) {} - - fn decrease_balance( - _coldkey: &AccountId, - tao: TaoBalance, - ) -> Result { - Ok(tao) - } - fn increase_stake( _coldkey: &AccountId, _hotkey: &AccountId, diff --git a/pallets/swap/src/pallet/impls.rs b/pallets/swap/src/pallet/impls.rs index 4386eae4bb..7be087c0f0 100644 --- a/pallets/swap/src/pallet/impls.rs +++ b/pallets/swap/src/pallet/impls.rs @@ -5,7 +5,7 @@ use frame_support::storage::{TransactionOutcome, transactional}; use frame_support::{ensure, pallet_prelude::DispatchError, traits::Get, weights::Weight}; use safe_math::*; use sp_arithmetic::{helpers_128bit, traits::Zero}; -use sp_runtime::{DispatchResult, Vec, traits::AccountIdConversion}; +use sp_runtime::{DispatchResult, traits::AccountIdConversion}; use substrate_fixed::types::{I64F64, U64F64, U96F32}; use subtensor_runtime_common::{ AlphaBalance, BalanceOps, NetUid, SubnetInfo, TaoBalance, Token, TokenReserve, @@ -829,133 +829,10 @@ impl Pallet { } /// Dissolve all LPs and clean state. - pub fn do_dissolve_all_liquidity_providers(netuid: NetUid) -> DispatchResultWithPostInfo { - let mut weight = Weight::default(); - if SwapV3Initialized::::get(netuid) { - weight.saturating_accrue(T::DbWeight::get().reads(1)); - // 1) Snapshot only *non‑protocol* positions: (owner, position_id). - struct CloseItem { - owner: A, - pos_id: PositionId, - } - let protocol_account = Self::protocol_account_id(); - weight.saturating_accrue(T::DbWeight::get().reads(1)); - - let mut to_close: sp_std::vec::Vec> = sp_std::vec::Vec::new(); - for ((owner, pos_id), _pos) in Positions::::iter_prefix((netuid,)) { - weight.saturating_accrue(T::DbWeight::get().reads(1)); - if owner != protocol_account { - to_close.push(CloseItem { owner, pos_id }); - } - } - - if to_close.is_empty() { - log::debug!( - "dissolve_all_lp: no user positions; netuid={netuid:?}, protocol liquidity untouched" - ); - return Ok(Some(weight).into()); - } - - let mut user_refunded_tao = TaoBalance::ZERO; - let mut user_staked_alpha = AlphaBalance::ZERO; - - let trust: Vec = T::SubnetInfo::get_validator_trust(netuid.into()); - weight.saturating_accrue(T::DbWeight::get().reads(1)); - let permit: Vec = T::SubnetInfo::get_validator_permit(netuid.into()); - weight.saturating_accrue(T::DbWeight::get().reads(1)); - - // Helper: pick target validator uid, only among permitted validators, by highest trust. - let pick_target_uid = |trust: &Vec, permit: &Vec| -> Option { - let mut best_uid: Option = None; - let mut best_trust: u16 = 0; - for (i, (&t, &p)) in trust.iter().zip(permit.iter()).enumerate() { - if p && (best_uid.is_none() || t > best_trust) { - best_uid = Some(i); - best_trust = t; - } - } - best_uid.map(|i| i as u16) - }; - - for CloseItem { owner, pos_id } in to_close.into_iter() { - match Self::do_remove_liquidity(netuid, &owner, pos_id) { - Ok(rm) => { - weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 6)); - // α withdrawn from the pool = principal + accrued fees - let alpha_total_from_pool: AlphaBalance = - rm.alpha.saturating_add(rm.fee_alpha); - - // ---------------- USER: refund τ and convert α → stake ---------------- - - // 1) Refund τ principal directly. - let tao_total_from_pool: TaoBalance = rm.tao.saturating_add(rm.fee_tao); - if tao_total_from_pool > TaoBalance::ZERO { - T::BalanceOps::increase_balance(&owner, tao_total_from_pool); - weight.saturating_accrue(T::DbWeight::get().writes(1)); - user_refunded_tao = - user_refunded_tao.saturating_add(tao_total_from_pool); - T::TaoReserve::decrease_provided(netuid, tao_total_from_pool); - weight.saturating_accrue(T::DbWeight::get().writes(1)); - } - - // 2) Stake ALL withdrawn α (principal + fees) to the best permitted validator. - if alpha_total_from_pool > AlphaBalance::ZERO { - if let Some(target_uid) = pick_target_uid(&trust, &permit) { - let validator_hotkey: T::AccountId = - T::SubnetInfo::hotkey_of_uid(netuid.into(), target_uid).ok_or( - sp_runtime::DispatchError::Other( - "validator_hotkey_missing", - ), - )?; - weight.saturating_accrue(T::DbWeight::get().reads(1)); - - // Stake α from LP owner (coldkey) to chosen validator (hotkey). - T::BalanceOps::increase_stake( - &owner, - &validator_hotkey, - netuid, - alpha_total_from_pool, - )?; - weight.saturating_accrue(T::DbWeight::get().writes(1)); - user_staked_alpha = - user_staked_alpha.saturating_add(alpha_total_from_pool); - - log::debug!( - "dissolve_all_lp: user dissolved & staked α: netuid={netuid:?}, owner={owner:?}, pos_id={pos_id:?}, α_staked={alpha_total_from_pool:?}, target_uid={target_uid}" - ); - } else { - // No permitted validators; burn to avoid balance drift. - log::debug!( - "dissolve_all_lp: no permitted validators; α burned: netuid={netuid:?}, owner={owner:?}, pos_id={pos_id:?}, α_total={alpha_total_from_pool:?}" - ); - } - - T::AlphaReserve::decrease_provided(netuid, alpha_total_from_pool); - weight.saturating_accrue(T::DbWeight::get().writes(1)); - } - } - Err(e) => { - log::debug!( - "dissolve_all_lp: force-close failed: netuid={netuid:?}, owner={owner:?}, pos_id={pos_id:?}, err={e:?}" - ); - weight.saturating_accrue(T::DbWeight::get().reads(1)); - continue; - } - } - } - - log::debug!( - "dissolve_all_liquidity_providers (users-only): netuid={netuid:?}, users_refunded_total_τ={user_refunded_tao:?}, users_staked_total_α={user_staked_alpha:?}; protocol liquidity untouched" - ); - - return Ok(Some(weight).into()); - } - - log::debug!( - "dissolve_all_liquidity_providers: netuid={netuid:?}, mode=V2-or-nonV3, leaving all liquidity/state intact" - ); - - Ok(Some(weight).into()) + pub fn do_dissolve_all_liquidity_providers(_netuid: NetUid) -> DispatchResultWithPostInfo { + // Deprecated in balancer, also we do not have any active liquidity providers + // or any ways to provide liquidity. + Ok(Some(Weight::default()).into()) } /// Clear **protocol-owned** liquidity and wipe all swap state for `netuid`. diff --git a/pallets/swap/src/pallet/mod.rs b/pallets/swap/src/pallet/mod.rs index 95a82a1c08..763d2150b2 100644 --- a/pallets/swap/src/pallet/mod.rs +++ b/pallets/swap/src/pallet/mod.rs @@ -1,12 +1,11 @@ use core::num::NonZeroU64; -use core::ops::Neg; use frame_support::{PalletId, pallet_prelude::*, traits::Get}; use frame_system::pallet_prelude::*; use sp_arithmetic::Perbill; use substrate_fixed::types::U64F64; use subtensor_runtime_common::{ - AlphaBalance, BalanceOps, NetUid, SubnetInfo, TaoBalance, Token, TokenReserve, + AlphaBalance, BalanceOps, NetUid, SubnetInfo, TaoBalance, TokenReserve, }; use crate::{ @@ -463,50 +462,12 @@ mod pallet { #[pallet::call_index(2)] #[pallet::weight(::WeightInfo::remove_liquidity())] pub fn remove_liquidity( - origin: OriginFor, - hotkey: T::AccountId, - netuid: NetUid, - position_id: PositionId, + _origin: OriginFor, + _hotkey: T::AccountId, + _netuid: NetUid, + _position_id: PositionId, ) -> DispatchResult { - let coldkey = ensure_signed(origin)?; - - // Ensure that the subnet exists. - ensure!( - T::SubnetInfo::exists(netuid.into()), - Error::::MechanismDoesNotExist - ); - - // Remove liquidity - let result = Self::do_remove_liquidity(netuid, &coldkey, position_id)?; - - // Credit the returned tao and alpha to the account - T::BalanceOps::increase_balance(&coldkey, result.tao.saturating_add(result.fee_tao)); - T::BalanceOps::increase_stake( - &coldkey, - &hotkey, - netuid.into(), - result.alpha.saturating_add(result.fee_alpha), - )?; - - // Remove withdrawn liquidity from user-provided reserves - T::TaoReserve::decrease_provided(netuid.into(), result.tao); - T::AlphaReserve::decrease_provided(netuid.into(), result.alpha); - - // Emit an event - Self::deposit_event(Event::LiquidityRemoved { - coldkey, - hotkey, - netuid: netuid.into(), - position_id, - liquidity: result.liquidity, - tao: result.tao, - alpha: result.alpha, - fee_tao: result.fee_tao, - fee_alpha: result.fee_alpha, - tick_low: result.tick_low.into(), - tick_high: result.tick_high.into(), - }); - + // Deprecated by balancer. We don't have any active liquidity providers either. Ok(()) } @@ -522,100 +483,13 @@ mod pallet { #[pallet::call_index(3)] #[pallet::weight(::WeightInfo::modify_position())] pub fn modify_position( - origin: OriginFor, - hotkey: T::AccountId, - netuid: NetUid, - position_id: PositionId, - liquidity_delta: i64, + _origin: OriginFor, + _hotkey: T::AccountId, + _netuid: NetUid, + _position_id: PositionId, + _liquidity_delta: i64, ) -> DispatchResult { - let coldkey = ensure_signed(origin)?; - - // Ensure that the subnet exists. - ensure!( - T::SubnetInfo::exists(netuid.into()), - Error::::MechanismDoesNotExist - ); - - ensure!( - T::SubnetInfo::is_subtoken_enabled(netuid.into()), - Error::::SubtokenDisabled - ); - - // Add or remove liquidity - let result = - Self::do_modify_position(netuid, &coldkey, &hotkey, position_id, liquidity_delta)?; - - if liquidity_delta > 0 { - // Remove TAO and Alpha balances or fail transaction if they can't be removed exactly - let tao_provided = T::BalanceOps::decrease_balance(&coldkey, result.tao)?; - ensure!(tao_provided == result.tao, Error::::InsufficientBalance); - - T::BalanceOps::decrease_stake(&coldkey, &hotkey, netuid.into(), result.alpha)?; - - // Emit an event - Self::deposit_event(Event::LiquidityModified { - coldkey: coldkey.clone(), - hotkey: hotkey.clone(), - netuid, - position_id, - liquidity: liquidity_delta, - tao: result.tao.to_u64() as i64, - alpha: result.alpha.to_u64() as i64, - fee_tao: result.fee_tao, - fee_alpha: result.fee_alpha, - tick_low: result.tick_low, - tick_high: result.tick_high, - }); - } else { - // Credit the returned tao and alpha to the account - T::BalanceOps::increase_balance(&coldkey, result.tao); - T::BalanceOps::increase_stake(&coldkey, &hotkey, netuid.into(), result.alpha)?; - - // Emit an event - if result.removed { - Self::deposit_event(Event::LiquidityRemoved { - coldkey: coldkey.clone(), - hotkey: hotkey.clone(), - netuid, - position_id, - liquidity: liquidity_delta.unsigned_abs(), - tao: result.tao, - alpha: result.alpha, - fee_tao: result.fee_tao, - fee_alpha: result.fee_alpha, - tick_low: result.tick_low, - tick_high: result.tick_high, - }); - } else { - Self::deposit_event(Event::LiquidityModified { - coldkey: coldkey.clone(), - hotkey: hotkey.clone(), - netuid, - position_id, - liquidity: liquidity_delta, - tao: (result.tao.to_u64() as i64).neg(), - alpha: (result.alpha.to_u64() as i64).neg(), - fee_tao: result.fee_tao, - fee_alpha: result.fee_alpha, - tick_low: result.tick_low, - tick_high: result.tick_high, - }); - } - } - - // Credit accrued fees to user account (no matter if liquidity is added or removed) - if result.fee_tao > TaoBalance::ZERO { - T::BalanceOps::increase_balance(&coldkey, result.fee_tao); - } - if !result.fee_alpha.is_zero() { - T::BalanceOps::increase_stake( - &coldkey, - &hotkey.clone(), - netuid.into(), - result.fee_alpha, - )?; - } - + // Deprecated by balancer. We don't have any active liquidity providers either. Ok(()) } diff --git a/pallets/swap/src/pallet/tests.rs b/pallets/swap/src/pallet/tests.rs index e2b53c5142..5d75c7da27 100644 --- a/pallets/swap/src/pallet/tests.rs +++ b/pallets/swap/src/pallet/tests.rs @@ -2191,78 +2191,6 @@ fn test_liquidate_idempotent() { }); } -#[test] -fn liquidate_v3_refunds_user_funds_and_clears_state() { - new_test_ext().execute_with(|| { - let netuid = NetUid::from(1); - - // Enable V3 path & initialize price/ticks (also creates a protocol position). - assert_ok!(Pallet::::toggle_user_liquidity( - RuntimeOrigin::root(), - netuid, - true - )); - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - // Use distinct cold/hot to demonstrate alpha refund/stake accounting. - let cold = OK_COLDKEY_ACCOUNT_ID; - let hot = OK_HOTKEY_ACCOUNT_ID; - - // Tight in‑range band around current tick. - let ct = CurrentTick::::get(netuid); - let tick_low = ct.saturating_sub(10); - let tick_high = ct.saturating_add(10); - let liquidity: u64 = 1_000_000; - - // Snapshot balances BEFORE. - let tao_before = ::BalanceOps::tao_balance(&cold); - let alpha_before_hot = - ::BalanceOps::alpha_balance(netuid.into(), &cold, &hot); - let alpha_before_owner = - ::BalanceOps::alpha_balance(netuid.into(), &cold, &cold); - let alpha_before_total = alpha_before_hot + alpha_before_owner; - - // Create the user position (storage & v3 state only; no balances moved yet). - let (_pos_id, need_tao, need_alpha) = - Pallet::::do_add_liquidity(netuid, &cold, &hot, tick_low, tick_high, liquidity) - .expect("add liquidity"); - - // Mirror extrinsic bookkeeping: withdraw funds & bump provided‑reserve counters. - let tao_taken = ::BalanceOps::decrease_balance(&cold, need_tao.into()) - .expect("decrease TAO"); - ::BalanceOps::decrease_stake(&cold, &hot, netuid.into(), need_alpha.into()) - .expect("decrease ALPHA"); - TaoReserve::increase_provided(netuid.into(), tao_taken); - AlphaReserve::increase_provided(netuid.into(), need_alpha.into()); - - // Users‑only liquidation. - assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - - // Expect balances restored to BEFORE snapshots (no swaps ran -> zero fees). - let tao_after = ::BalanceOps::tao_balance(&cold); - assert_eq!(tao_after, tao_before, "TAO principal must be refunded"); - - // ALPHA totals conserved to owner (distribution may differ). - let alpha_after_hot = - ::BalanceOps::alpha_balance(netuid.into(), &cold, &hot); - let alpha_after_owner = - ::BalanceOps::alpha_balance(netuid.into(), &cold, &cold); - let alpha_after_total = alpha_after_hot + alpha_after_owner; - assert_eq!( - alpha_after_total, alpha_before_total, - "ALPHA principal must be refunded/staked for the account (check totals)" - ); - - // Clear protocol liquidity and V3 state now. - assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); - - // User position(s) are gone and all V3 state cleared. - assert_eq!(Pallet::::count_positions(netuid, &cold), 0); - assert!(Ticks::::iter_prefix(netuid).next().is_none()); - assert!(!SwapV3Initialized::::contains_key(netuid)); - }); -} - #[test] fn refund_alpha_single_provider_exact() { new_test_ext().execute_with(|| { @@ -2451,171 +2379,6 @@ fn refund_alpha_same_cold_multiple_hotkeys_conserved_to_owner() { }); } -#[test] -fn test_dissolve_v3_green_path_refund_tao_stake_alpha_and_clear_state() { - new_test_ext().execute_with(|| { - // --- Setup --- - let netuid = NetUid::from(42); - let cold = OK_COLDKEY_ACCOUNT_ID; - let hot = OK_HOTKEY_ACCOUNT_ID; - - assert_ok!(Swap::toggle_user_liquidity( - RuntimeOrigin::root(), - netuid.into(), - true - )); - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - assert!(SwapV3Initialized::::get(netuid)); - - // Tight in‑range band so BOTH τ and α are required. - let ct = CurrentTick::::get(netuid); - let tick_low = ct.saturating_sub(10); - let tick_high = ct.saturating_add(10); - let liquidity: u64 = 1_250_000; - - // Add liquidity and capture required τ/α. - let (_pos_id, tao_needed, alpha_needed) = - Pallet::::do_add_liquidity(netuid, &cold, &hot, tick_low, tick_high, liquidity) - .expect("add in-range liquidity"); - assert!(tao_needed > 0, "in-range pos must require TAO"); - assert!(alpha_needed > 0, "in-range pos must require ALPHA"); - - // Determine the permitted validator with the highest trust (green path). - let trust = ::SubnetInfo::get_validator_trust(netuid.into()); - let permit = ::SubnetInfo::get_validator_permit(netuid.into()); - assert_eq!(trust.len(), permit.len(), "trust/permit must align"); - let target_uid: u16 = trust - .iter() - .zip(permit.iter()) - .enumerate() - .filter(|(_, (_t, p))| **p) - .max_by_key(|(_, (t, _))| *t) - .map(|(i, _)| i as u16) - .expect("at least one permitted validator"); - let validator_hotkey: ::AccountId = - ::SubnetInfo::hotkey_of_uid(netuid.into(), target_uid) - .expect("uid -> hotkey mapping must exist"); - - // --- Snapshot BEFORE we withdraw τ/α to fund the position --- - let tao_before = ::BalanceOps::tao_balance(&cold); - - let alpha_before_hot = - ::BalanceOps::alpha_balance(netuid.into(), &cold, &hot); - let alpha_before_owner = - ::BalanceOps::alpha_balance(netuid.into(), &cold, &cold); - let alpha_before_val = - ::BalanceOps::alpha_balance(netuid.into(), &cold, &validator_hotkey); - - let alpha_before_total = if validator_hotkey == hot { - alpha_before_hot + alpha_before_owner - } else { - alpha_before_hot + alpha_before_owner + alpha_before_val - }; - - // --- Mirror extrinsic bookkeeping: withdraw τ & α; bump provided reserves --- - let tao_taken = ::BalanceOps::decrease_balance(&cold, tao_needed.into()) - .expect("decrease TAO"); - ::BalanceOps::decrease_stake( - &cold, - &hot, - netuid.into(), - alpha_needed.into(), - ) - .expect("decrease ALPHA"); - - TaoReserve::increase_provided(netuid.into(), tao_taken); - AlphaReserve::increase_provided(netuid.into(), alpha_needed.into()); - - // --- Act: dissolve (GREEN PATH: permitted validators exist) --- - assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - - // --- Assert: τ principal refunded to user --- - let tao_after = ::BalanceOps::tao_balance(&cold); - assert_eq!(tao_after, tao_before, "TAO principal must be refunded"); - - // --- α ledger assertions --- - let alpha_after_hot = - ::BalanceOps::alpha_balance(netuid.into(), &cold, &hot); - let alpha_after_owner = - ::BalanceOps::alpha_balance(netuid.into(), &cold, &cold); - let alpha_after_val = - ::BalanceOps::alpha_balance(netuid.into(), &cold, &validator_hotkey); - - // Owner ledger must be unchanged in the green path. - assert_eq!( - alpha_after_owner, alpha_before_owner, - "Owner α ledger must be unchanged (staked to validator, not refunded)" - ); - - if validator_hotkey == hot { - assert_eq!( - alpha_after_hot, alpha_before_hot, - "When validator == hotkey, user's hot ledger must net back to its original balance" - ); - let alpha_after_total = alpha_after_hot + alpha_after_owner; - assert_eq!( - alpha_after_total, alpha_before_total, - "Total α for the coldkey must be conserved (validator==hotkey)" - ); - } else { - assert!( - alpha_before_hot >= alpha_after_hot, - "hot ledger should not increase" - ); - assert!( - alpha_after_val >= alpha_before_val, - "validator ledger should not decrease" - ); - - let hot_loss = alpha_before_hot - alpha_after_hot; - let val_gain = alpha_after_val - alpha_before_val; - assert_eq!( - val_gain, hot_loss, - "α that left the user's hot ledger must equal α credited to the validator ledger" - ); - - let alpha_after_total = alpha_after_hot + alpha_after_owner + alpha_after_val; - assert_eq!( - alpha_after_total, alpha_before_total, - "Total α for the coldkey must be conserved" - ); - } - - // Now clear protocol liquidity & state and assert full reset. - assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); - - let protocol_id = Pallet::::protocol_account_id(); - assert_eq!(Pallet::::count_positions(netuid, &cold), 0); - let prot_positions_after = - Positions::::iter_prefix_values((netuid, protocol_id)).collect::>(); - assert!( - prot_positions_after.is_empty(), - "protocol positions must be removed" - ); - - assert!(Ticks::::iter_prefix(netuid).next().is_none()); - assert!(Ticks::::get(netuid, TickIndex::MIN).is_none()); - assert!(Ticks::::get(netuid, TickIndex::MAX).is_none()); - assert!(!CurrentLiquidity::::contains_key(netuid)); - assert!(!CurrentTick::::contains_key(netuid)); - assert!(!AlphaSqrtPrice::::contains_key(netuid)); - assert!(!SwapV3Initialized::::contains_key(netuid)); - - assert!(!FeeGlobalTao::::contains_key(netuid)); - assert!(!FeeGlobalAlpha::::contains_key(netuid)); - - assert!( - TickIndexBitmapWords::::iter_prefix((netuid,)) - .next() - .is_none(), - "active tick bitmap words must be cleared" - ); - - assert!(!FeeRate::::contains_key(netuid)); - assert!(!EnabledUserLiquidity::::contains_key(netuid)); - }); -} - #[test] fn test_clear_protocol_liquidity_green_path() { new_test_ext().execute_with(|| { diff --git a/pallets/swap/src/weights.rs b/pallets/swap/src/weights.rs index e802d58320..70a87eff3d 100644 --- a/pallets/swap/src/weights.rs +++ b/pallets/swap/src/weights.rs @@ -107,10 +107,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1600` // Estimated: `6096` - // Minimum execution time: 131_446_000 picoseconds. - Weight::from_parts(134_572_000, 6096) - .saturating_add(T::DbWeight::get().reads(19_u64)) - .saturating_add(T::DbWeight::get().writes(11_u64)) + // Minimum execution time: 2_535_000 picoseconds. + Weight::from_parts(2_535_000, 6096) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -152,10 +150,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1645` // Estimated: `6096` - // Minimum execution time: 149_791_000 picoseconds. - Weight::from_parts(154_219_000, 6096) - .saturating_add(T::DbWeight::get().reads(19_u64)) - .saturating_add(T::DbWeight::get().writes(9_u64)) + // Minimum execution time: 2_484_000 picoseconds. + Weight::from_parts(2_484_000, 6096) } /// Storage: `Swap::EnabledUserLiquidity` (r:128 w:128) /// Proof: `Swap::EnabledUserLiquidity` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) @@ -187,10 +183,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `32696` // Estimated: `670430` - // Minimum execution time: 9_917_169_000 picoseconds. - Weight::from_parts(9_951_573_000, 670430) - .saturating_add(T::DbWeight::get().reads(1920_u64)) - .saturating_add(T::DbWeight::get().writes(896_u64)) + // Minimum execution time: 795_151_000 picoseconds. + Weight::from_parts(795_151_000, 670430) + .saturating_add(T::DbWeight::get().reads(128_u64)) + .saturating_add(T::DbWeight::get().writes(128_u64)) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -266,10 +262,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1600` // Estimated: `6096` - // Minimum execution time: 131_446_000 picoseconds. - Weight::from_parts(134_572_000, 6096) - .saturating_add(RocksDbWeight::get().reads(19_u64)) - .saturating_add(RocksDbWeight::get().writes(11_u64)) + // Minimum execution time: 2_535_000 picoseconds. + Weight::from_parts(2_535_000, 6096) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -311,10 +305,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1645` // Estimated: `6096` - // Minimum execution time: 149_791_000 picoseconds. - Weight::from_parts(154_219_000, 6096) - .saturating_add(RocksDbWeight::get().reads(19_u64)) - .saturating_add(RocksDbWeight::get().writes(9_u64)) + // Minimum execution time: 2_484_000 picoseconds. + Weight::from_parts(2_484_000, 6096) } /// Storage: `Swap::EnabledUserLiquidity` (r:128 w:128) /// Proof: `Swap::EnabledUserLiquidity` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) @@ -346,10 +338,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `32696` // Estimated: `670430` - // Minimum execution time: 9_917_169_000 picoseconds. - Weight::from_parts(9_951_573_000, 670430) - .saturating_add(RocksDbWeight::get().reads(1920_u64)) - .saturating_add(RocksDbWeight::get().writes(896_u64)) + // Minimum execution time: 795_151_000 picoseconds. + Weight::from_parts(795_151_000, 670430) + .saturating_add(RocksDbWeight::get().reads(128_u64)) + .saturating_add(RocksDbWeight::get().writes(128_u64)) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) diff --git a/pallets/transaction-fee/Cargo.toml b/pallets/transaction-fee/Cargo.toml index d5a5c2f418..272faf3198 100644 --- a/pallets/transaction-fee/Cargo.toml +++ b/pallets/transaction-fee/Cargo.toml @@ -9,6 +9,7 @@ frame-support.workspace = true frame-system.workspace = true log.workspace = true pallet-balances = { workspace = true, default-features = false } +pallet-alpha-assets = { workspace = true, default-features = false } pallet-subtensor.workspace = true pallet-subtensor-swap.workspace = true pallet-transaction-payment.workspace = true @@ -47,6 +48,7 @@ std = [ "frame-support/std", "frame-system/std", "log/std", + "pallet-alpha-assets/std", "pallet-balances/std", "pallet-crowdloan/std", "pallet-drand/std", diff --git a/pallets/transaction-fee/src/lib.rs b/pallets/transaction-fee/src/lib.rs index 3923c8f8e1..72b27cdcfb 100644 --- a/pallets/transaction-fee/src/lib.rs +++ b/pallets/transaction-fee/src/lib.rs @@ -111,6 +111,7 @@ where /// Handle Alpha fees impl AlphaFeeHandler for TransactionFeeHandler where + T: AuthorshipInfo>, T: frame_system::Config, T: pallet_subtensor::Config, T: pallet_subtensor_swap::Config, @@ -174,18 +175,23 @@ where let alpha_fee = alpha_equivalent.min(alpha_balance); // Sell alpha_fee and burn received tao (ignore unstake_from_subnet return). - let swap_result = pallet_subtensor::Pallet::::unstake_from_subnet( - hotkey, - coldkey, - *netuid, - alpha_fee, - 0.into(), - true, - ); - - if let Ok(tao_amount) = swap_result { - (alpha_fee, tao_amount, *netuid) + if let Some(author) = T::author() { + let swap_result = pallet_subtensor::Pallet::::unstake_from_subnet( + hotkey, + coldkey, + &author, + *netuid, + alpha_fee, + 0.into(), + true, + ); + if let Ok(tao_amount) = swap_result { + (alpha_fee, tao_amount, *netuid) + } else { + (0.into(), 0.into(), NetUid::ROOT) + } } else { + // Fallback: no author => no fees (do nothing) (0.into(), 0.into(), NetUid::ROOT) } } else { @@ -406,13 +412,7 @@ where OU::on_unbalanceds(Some(fee).into_iter().chain(Some(tip))); } WithdrawnFee::Alpha((alpha_fee, tao_amount, netuid)) => { - if let Some(author) = T::author() { - // Pay block author - let _ = F::deposit(&author, tao_amount.into(), Precision::BestEffort) - .unwrap_or_else(|_| Debt::::zero()); - } else { - // Fallback: no author => do nothing - } + // Block author already received the fee in withdraw_in_alpha, nothing to do here. frame_system::Pallet::::deposit_event( pallet_subtensor::Event::::TransactionFeePaidWithAlpha { who: who.clone(), diff --git a/pallets/transaction-fee/src/tests/mock.rs b/pallets/transaction-fee/src/tests/mock.rs index a897e82cb6..3607fd3dfa 100644 --- a/pallets/transaction-fee/src/tests/mock.rs +++ b/pallets/transaction-fee/src/tests/mock.rs @@ -39,6 +39,7 @@ frame_support::construct_runtime!( pub enum Test { System: frame_system = 1, Balances: pallet_balances = 2, + AlphaAssets: pallet_alpha_assets = 3, SubtensorModule: pallet_subtensor::{Pallet, Call, Storage, Event, Error} = 4, Scheduler: pallet_scheduler::{Pallet, Call, Storage, Event} = 5, Drand: pallet_drand::{Pallet, Call, Storage, Event} = 6, @@ -229,6 +230,8 @@ parameter_types! { pub const LeaseDividendsDistributionInterval: u32 = 100; // 100 blocks pub const MaxImmuneUidsPercentage: Percent = Percent::from_percent(80); pub const EvmKeyAssociateRateLimit: u64 = 0; + pub const SubtensorPalletId: PalletId = PalletId(*b"subtensr"); + pub const BurnAccountId: PalletId = PalletId(*b"burntnsr"); } impl pallet_subtensor::Config for Test { @@ -288,6 +291,7 @@ impl pallet_subtensor::Config for Test { type LiquidAlphaOn = InitialLiquidAlphaOn; type Yuma3On = InitialYuma3On; type Preimages = (); + type AlphaAssets = AlphaAssets; type InitialColdkeySwapAnnouncementDelay = InitialColdkeySwapAnnouncementDelay; type InitialColdkeySwapReannouncementDelay = InitialColdkeySwapReannouncementDelay; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; @@ -304,6 +308,8 @@ impl pallet_subtensor::Config for Test { type CommitmentsInterface = CommitmentsI; type EvmKeyAssociateRateLimit = EvmKeyAssociateRateLimit; type AuthorshipProvider = MockAuthorshipProvider; + type SubtensorPalletId = SubtensorPalletId; + type BurnAccountId = BurnAccountId; type WeightInfo = (); } @@ -401,6 +407,8 @@ impl pallet_balances::Config for Test { type RuntimeHoldReason = (); } +impl pallet_alpha_assets::Config for Test {} + // Swap-related parameter types parameter_types! { pub const SwapProtocolId: PalletId = PalletId(*b"ten/swap"); @@ -605,7 +613,7 @@ pub fn register_ok_neuron( let bal: TaoBalance = SubtensorModule::get_coldkey_balance(&cold); if bal < min_balance_needed { - SubtensorModule::add_balance_to_coldkey_account(&cold, min_balance_needed - bal); + add_balance_to_coldkey_account(&cold, min_balance_needed - bal); } }; @@ -642,11 +650,22 @@ pub fn register_ok_neuron( ); } +#[allow(dead_code)] +pub fn add_balance_to_coldkey_account(coldkey: &U256, tao: TaoBalance) { + let credit = SubtensorModule::mint_tao(tao); + let _ = SubtensorModule::spend_tao(coldkey, credit, tao).unwrap(); +} + +#[allow(dead_code)] +pub fn remove_balance_from_coldkey_account(coldkey: &U256, tao: TaoBalance) { + let _ = SubtensorModule::burn_tao(coldkey, tao); +} + #[allow(dead_code)] 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()); + add_balance_to_coldkey_account(coldkey, lock_cost.into()); assert_ok!(SubtensorModule::register_network( RawOrigin::Signed(*coldkey).into(), @@ -813,7 +832,7 @@ pub fn setup_stake( stake_amount: u64, ) { // Fund enough to stake while keeping the coldkey account alive (KeepAlive / ED). - SubtensorModule::add_balance_to_coldkey_account( + add_balance_to_coldkey_account( coldkey, TaoBalance::from(stake_amount) + ExistentialDeposit::get(), ); @@ -868,6 +887,7 @@ pub(crate) fn quote_remove_stake_after_alpha_fee( assert_ok!(SubtensorModule::unstake_from_subnet( hotkey, coldkey, + coldkey, netuid, alpha_fee, 0.into(), diff --git a/pallets/transaction-fee/src/tests/mod.rs b/pallets/transaction-fee/src/tests/mod.rs index 6644e014d8..d54c63a63b 100644 --- a/pallets/transaction-fee/src/tests/mod.rs +++ b/pallets/transaction-fee/src/tests/mod.rs @@ -53,8 +53,8 @@ fn test_remove_stake_fees_tao() { let register_prefund = stake_amount .saturating_mul(10_000.into()) // generous buffer .saturating_add(ExistentialDeposit::get()); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(10000), register_prefund); - SubtensorModule::add_balance_to_coldkey_account(&U256::from(20001), register_prefund); + add_balance_to_coldkey_account(&U256::from(10000), register_prefund); + add_balance_to_coldkey_account(&U256::from(20001), register_prefund); let sn = setup_subnets(1, 1); @@ -67,7 +67,7 @@ fn test_remove_stake_fees_tao() { &sn.hotkeys[0], stake_amount.into(), ); - SubtensorModule::add_balance_to_coldkey_account(&sn.coldkey, TaoBalance::from(TAO)); + add_balance_to_coldkey_account(&sn.coldkey, TaoBalance::from(TAO)); // Avoid staking-op rate limit between add_stake and remove_stake. jump_blocks(1_000_001); @@ -230,7 +230,7 @@ fn test_remove_stake_fees_alpha() { // Forse-set signer balance to ED let current_balance = Balances::free_balance(sn.coldkey); - let _ = SubtensorModule::remove_balance_from_coldkey_account( + remove_balance_from_coldkey_account( &sn.coldkey, current_balance - ExistentialDeposit::get(), ); @@ -347,10 +347,7 @@ fn test_remove_stake_root() { // Forse-set signer balance to ED let current_balance = Balances::free_balance(coldkey); - let _ = SubtensorModule::remove_balance_from_coldkey_account( - &coldkey, - current_balance - ExistentialDeposit::get(), - ); + remove_balance_from_coldkey_account(&coldkey, current_balance - ExistentialDeposit::get()); // Remove stake let balance_before = Balances::free_balance(coldkey); @@ -405,10 +402,7 @@ fn test_remove_stake_completely_root() { // Forse-set signer balance to ED let current_balance = Balances::free_balance(coldkey); - let _ = SubtensorModule::remove_balance_from_coldkey_account( - &coldkey, - current_balance - ExistentialDeposit::get(), - ); + remove_balance_from_coldkey_account(&coldkey, current_balance - ExistentialDeposit::get()); // Remove stake let balance_before = Balances::free_balance(coldkey); @@ -462,7 +456,7 @@ fn test_remove_stake_completely_fees_alpha() { // Forse-set signer balance to ED let current_balance = Balances::free_balance(sn.coldkey); - let _ = SubtensorModule::remove_balance_from_coldkey_account( + remove_balance_from_coldkey_account( &sn.coldkey, current_balance - ExistentialDeposit::get(), ); @@ -509,7 +503,7 @@ fn test_remove_stake_not_enough_balance_for_fees() { let stake_amount = TaoBalance::from(TAO); let sn = setup_subnets(1, 1); - SubtensorModule::add_balance_to_coldkey_account( + add_balance_to_coldkey_account( &sn.coldkey, stake_amount .saturating_mul(2.into()) // buffer so staking doesn't attempt to drain the account @@ -531,7 +525,7 @@ fn test_remove_stake_not_enough_balance_for_fees() { // Forse-set signer balance to ED let current_balance = Balances::free_balance(sn.coldkey); - let _ = SubtensorModule::remove_balance_from_coldkey_account( + remove_balance_from_coldkey_account( &sn.coldkey, current_balance - ExistentialDeposit::get(), ); @@ -597,13 +591,13 @@ fn test_remove_stake_edge_alpha() { // Forse-set signer balance to ED let current_balance = Balances::free_balance(sn.coldkey); - let _ = SubtensorModule::remove_balance_from_coldkey_account( + remove_balance_from_coldkey_account( &sn.coldkey, current_balance - ExistentialDeposit::get(), ); // For-set Alpha balance to low, but enough to pay tx fees at the current Alpha price - let new_current_stake = AlphaBalance::from(1_000_000); + let new_current_stake = AlphaBalance::from(2_000_000); SubtensorModule::decrease_stake_for_hotkey_and_coldkey_on_subnet( &sn.hotkeys[0], &sn.coldkey, @@ -662,7 +656,7 @@ fn test_remove_stake_failing_transaction_tao_fees() { let unstake_amount = AlphaBalance::from(TAO / 50); let sn = setup_subnets(1, 1); - SubtensorModule::add_balance_to_coldkey_account( + add_balance_to_coldkey_account( &sn.coldkey, stake_amount .saturating_mul(2.into()) // buffer so staking doesn't attempt to drain the account @@ -675,7 +669,7 @@ fn test_remove_stake_failing_transaction_tao_fees() { stake_amount.into(), )); - SubtensorModule::add_balance_to_coldkey_account(&sn.coldkey, TAO.into()); + add_balance_to_coldkey_account(&sn.coldkey, TAO.into()); // Make unstaking fail by reducing liquidity to critical SubnetTAO::::insert(sn.subnets[0].netuid, TaoBalance::from(1)); @@ -744,7 +738,7 @@ fn test_remove_stake_failing_transaction_alpha_fees() { // Forse-set signer balance to ED let current_balance = Balances::free_balance(sn.coldkey); - let _ = SubtensorModule::remove_balance_from_coldkey_account( + remove_balance_from_coldkey_account( &sn.coldkey, current_balance - ExistentialDeposit::get(), ); @@ -809,7 +803,7 @@ fn test_remove_stake_limit_fees_alpha() { // Forse-set signer balance to ED let current_balance = Balances::free_balance(sn.coldkey); - let _ = SubtensorModule::remove_balance_from_coldkey_account( + remove_balance_from_coldkey_account( &sn.coldkey, current_balance - ExistentialDeposit::get(), ); @@ -911,10 +905,7 @@ fn test_unstake_all_fees_alpha() { // Forse-set signer balance to ED let current_balance = Balances::free_balance(coldkey); - let _ = SubtensorModule::remove_balance_from_coldkey_account( - &coldkey, - current_balance - ExistentialDeposit::get(), - ); + remove_balance_from_coldkey_account(&coldkey, current_balance - ExistentialDeposit::get()); // Unstake all let balance_before = Balances::free_balance(sn.coldkey); @@ -938,7 +929,7 @@ fn test_unstake_all_fees_alpha() { ); // Give the coldkey TAO balance - now should unstake ok - SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1_000_000_000_u64.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_u64.into()); assert_ok!(ext.dispatch_transaction( RuntimeOrigin::signed(coldkey).into(), call, @@ -992,10 +983,7 @@ fn test_unstake_all_alpha_fees_alpha() { // Forse-set signer balance to ED let current_balance = Balances::free_balance(coldkey); - let _ = SubtensorModule::remove_balance_from_coldkey_account( - &coldkey, - current_balance - ExistentialDeposit::get(), - ); + remove_balance_from_coldkey_account(&coldkey, current_balance - ExistentialDeposit::get()); // Unstake all let balance_before = Balances::free_balance(sn.coldkey); @@ -1019,7 +1007,7 @@ fn test_unstake_all_alpha_fees_alpha() { ); // Give the coldkey TAO balance - now should unstake ok - SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1_000_000_000_u64.into()); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_u64.into()); assert_ok!(ext.dispatch_transaction( RuntimeOrigin::signed(coldkey).into(), call, @@ -1063,7 +1051,7 @@ fn test_move_stake_fees_alpha() { // Forse-set signer balance to ED let current_balance = Balances::free_balance(sn.coldkey); - let _ = SubtensorModule::remove_balance_from_coldkey_account( + remove_balance_from_coldkey_account( &sn.coldkey, current_balance - ExistentialDeposit::get(), ); @@ -1135,7 +1123,7 @@ fn test_transfer_stake_fees_alpha() { // Forse-set signer balance to ED let current_balance = Balances::free_balance(sn.coldkey); - let _ = SubtensorModule::remove_balance_from_coldkey_account( + remove_balance_from_coldkey_account( &sn.coldkey, current_balance - ExistentialDeposit::get(), ); @@ -1206,7 +1194,7 @@ fn test_swap_stake_fees_alpha() { // Forse-set signer balance to ED let current_balance = Balances::free_balance(sn.coldkey); - let _ = SubtensorModule::remove_balance_from_coldkey_account( + remove_balance_from_coldkey_account( &sn.coldkey, current_balance - ExistentialDeposit::get(), ); @@ -1276,7 +1264,7 @@ fn test_swap_stake_limit_fees_alpha() { // Forse-set signer balance to ED let current_balance = Balances::free_balance(sn.coldkey); - let _ = SubtensorModule::remove_balance_from_coldkey_account( + remove_balance_from_coldkey_account( &sn.coldkey, current_balance - ExistentialDeposit::get(), ); @@ -1348,7 +1336,7 @@ fn test_burn_alpha_fees_alpha() { // Forse-set signer balance to ED let current_balance = Balances::free_balance(sn.coldkey); - let _ = SubtensorModule::remove_balance_from_coldkey_account( + remove_balance_from_coldkey_account( &sn.coldkey, current_balance - ExistentialDeposit::get(), ); @@ -1409,7 +1397,7 @@ fn test_recycle_alpha_fees_alpha() { // Forse-set signer balance to ED let current_balance = Balances::free_balance(sn.coldkey); - let _ = SubtensorModule::remove_balance_from_coldkey_account( + remove_balance_from_coldkey_account( &sn.coldkey, current_balance - ExistentialDeposit::get(), ); @@ -1471,7 +1459,7 @@ fn test_add_stake_fees_go_to_block_builder() { // Simulate add stake to get the expected TAO fee let (_, swap_fee) = mock::swap_tao_to_alpha(sn.subnets[0].netuid, stake_amount.into()); - SubtensorModule::add_balance_to_coldkey_account(&sn.coldkey, (stake_amount * 10).into()); + add_balance_to_coldkey_account(&sn.coldkey, (stake_amount * 10).into()); remove_stake_rate_limit_for_tests(&sn.hotkeys[0], &sn.coldkey, sn.subnets[0].netuid); // Stake diff --git a/pallets/utility/src/weights.rs b/pallets/utility/src/weights.rs index f87d58b55e..462804199f 100644 --- a/pallets/utility/src/weights.rs +++ b/pallets/utility/src/weights.rs @@ -2,9 +2,9 @@ //! Autogenerated weights for `pallet_subtensor_utility` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 49.1.0 -//! DATE: 2026-04-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2026-05-05, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runnervm35a4x`, CPU: `AMD EPYC 7763 64-Core Processor` +//! HOSTNAME: `runnervmeorf1`, CPU: `AMD EPYC 7763 64-Core Processor` //! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` // Executed Command: @@ -22,7 +22,7 @@ // --no-storage-info // --no-min-squares // --no-median-slopes -// --output=/tmp/tmp.NYSHYuTETx +// --output=/tmp/tmp.4nwfKx4NPm // --template=/home/runner/work/subtensor/subtensor/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -57,10 +57,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `518` // Estimated: `3983` - // Minimum execution time: 4_808_000 picoseconds. - Weight::from_parts(17_572_558, 3983) - // Standard Error: 2_156 - .saturating_add(Weight::from_parts(5_233_749, 0).saturating_mul(c.into())) + // Minimum execution time: 4_859_000 picoseconds. + Weight::from_parts(28_407_150, 3983) + // Standard Error: 6_395 + .saturating_add(Weight::from_parts(5_254_263, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) @@ -71,8 +71,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `518` // Estimated: `3983` - // Minimum execution time: 14_547_000 picoseconds. - Weight::from_parts(15_328_000, 3983) + // Minimum execution time: 14_828_000 picoseconds. + Weight::from_parts(15_318_000, 3983) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) @@ -84,18 +84,18 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `518` // Estimated: `3983` - // Minimum execution time: 4_718_000 picoseconds. - Weight::from_parts(19_169_854, 3983) - // Standard Error: 2_362 - .saturating_add(Weight::from_parts(5_450_969, 0).saturating_mul(c.into())) + // Minimum execution time: 4_528_000 picoseconds. + Weight::from_parts(18_871_700, 3983) + // Standard Error: 1_818 + .saturating_add(Weight::from_parts(5_525_521, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) } fn dispatch_as() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_512_000 picoseconds. - Weight::from_parts(6_762_000, 0) + // Minimum execution time: 6_562_000 picoseconds. + Weight::from_parts(6_823_000, 0) } /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -106,18 +106,18 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `518` // Estimated: `3983` - // Minimum execution time: 4_829_000 picoseconds. - Weight::from_parts(18_257_650, 3983) - // Standard Error: 1_968 - .saturating_add(Weight::from_parts(5_226_981, 0).saturating_mul(c.into())) + // Minimum execution time: 4_939_000 picoseconds. + Weight::from_parts(12_544_904, 3983) + // Standard Error: 3_006 + .saturating_add(Weight::from_parts(5_296_996, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) } fn dispatch_as_fallible() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_372_000 picoseconds. - Weight::from_parts(6_753_000, 0) + // Minimum execution time: 6_472_000 picoseconds. + Weight::from_parts(6_862_000, 0) } /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -127,8 +127,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `518` // Estimated: `3983` - // Minimum execution time: 20_689_000 picoseconds. - Weight::from_parts(21_119_000, 3983) + // Minimum execution time: 21_039_000 picoseconds. + Weight::from_parts(21_600_000, 3983) .saturating_add(T::DbWeight::get().reads(2_u64)) } } @@ -144,10 +144,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `518` // Estimated: `3983` - // Minimum execution time: 4_808_000 picoseconds. - Weight::from_parts(17_572_558, 3983) - // Standard Error: 2_156 - .saturating_add(Weight::from_parts(5_233_749, 0).saturating_mul(c.into())) + // Minimum execution time: 4_859_000 picoseconds. + Weight::from_parts(28_407_150, 3983) + // Standard Error: 6_395 + .saturating_add(Weight::from_parts(5_254_263, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) } /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) @@ -158,8 +158,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `518` // Estimated: `3983` - // Minimum execution time: 14_547_000 picoseconds. - Weight::from_parts(15_328_000, 3983) + // Minimum execution time: 14_828_000 picoseconds. + Weight::from_parts(15_318_000, 3983) .saturating_add(RocksDbWeight::get().reads(2_u64)) } /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) @@ -171,18 +171,18 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `518` // Estimated: `3983` - // Minimum execution time: 4_718_000 picoseconds. - Weight::from_parts(19_169_854, 3983) - // Standard Error: 2_362 - .saturating_add(Weight::from_parts(5_450_969, 0).saturating_mul(c.into())) + // Minimum execution time: 4_528_000 picoseconds. + Weight::from_parts(18_871_700, 3983) + // Standard Error: 1_818 + .saturating_add(Weight::from_parts(5_525_521, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) } fn dispatch_as() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_512_000 picoseconds. - Weight::from_parts(6_762_000, 0) + // Minimum execution time: 6_562_000 picoseconds. + Weight::from_parts(6_823_000, 0) } /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -193,18 +193,18 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `518` // Estimated: `3983` - // Minimum execution time: 4_829_000 picoseconds. - Weight::from_parts(18_257_650, 3983) - // Standard Error: 1_968 - .saturating_add(Weight::from_parts(5_226_981, 0).saturating_mul(c.into())) + // Minimum execution time: 4_939_000 picoseconds. + Weight::from_parts(12_544_904, 3983) + // Standard Error: 3_006 + .saturating_add(Weight::from_parts(5_296_996, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) } fn dispatch_as_fallible() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_372_000 picoseconds. - Weight::from_parts(6_753_000, 0) + // Minimum execution time: 6_472_000 picoseconds. + Weight::from_parts(6_862_000, 0) } /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -214,8 +214,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `518` // Estimated: `3983` - // Minimum execution time: 20_689_000 picoseconds. - Weight::from_parts(21_119_000, 3983) + // Minimum execution time: 21_039_000 picoseconds. + Weight::from_parts(21_600_000, 3983) .saturating_add(RocksDbWeight::get().reads(2_u64)) } } diff --git a/precompiles/Cargo.toml b/precompiles/Cargo.toml index 68f617176d..c896ecb731 100644 --- a/precompiles/Cargo.toml +++ b/precompiles/Cargo.toml @@ -17,6 +17,7 @@ fp-evm.workspace = true frame-support.workspace = true frame-system.workspace = true log.workspace = true +pallet-alpha-assets.workspace = true pallet-balances.workspace = true pallet-evm.workspace = true pallet-evm-precompile-dispatch.workspace = true @@ -53,10 +54,12 @@ std = [ "frame-system/std", "log/std", "pallet-admin-utils/std", + "pallet-alpha-assets/std", "pallet-balances/std", "pallet-crowdloan/std", "pallet-drand/std", "pallet-evm-precompile-bn128/std", + "pallet-evm-chain-id/std", "pallet-evm-precompile-dispatch/std", "pallet-evm-precompile-modexp/std", "pallet-evm-precompile-sha3fips/std", @@ -100,6 +103,7 @@ runtime-benchmarks = [ [dev-dependencies] pallet-drand = { workspace = true, features = ["std"] } +pallet-evm-chain-id = { workspace = true, features = ["std"] } pallet-preimage = { workspace = true, features = ["std"] } pallet-scheduler = { workspace = true, features = ["std"] } pallet-timestamp = { workspace = true, features = ["std"] } diff --git a/precompiles/src/crowdloan.rs b/precompiles/src/crowdloan.rs index 8a4042e7d1..d32dd4ab35 100644 --- a/precompiles/src/crowdloan.rs +++ b/precompiles/src/crowdloan.rs @@ -258,3 +258,409 @@ struct CrowdloanInfo { finalized: bool, contributors_count: u32, } + +#[cfg(test)] +mod tests { + #![allow(clippy::expect_used, clippy::arithmetic_side_effects)] + + use super::*; + use crate::PrecompileExt; + use crate::mock::{ + AccountId, Runtime, RuntimeOrigin, System, addr_from_index, fund_account, mapped_account, + new_test_ext, precompiles, selector_u32, + }; + use precompile_utils::solidity::{codec::Address, encode_return_value, encode_with_selector}; + use precompile_utils::testing::PrecompileTesterExt; + use sp_core::H160; + + const CREATOR_DEPOSIT: u64 = 50; + const MIN_CONTRIBUTION: u64 = 10; + const CAP: u64 = 300; + const END: u32 = 50; + const ACCOUNT_BALANCE: u64 = 1_000; + + fn get_crowdloan(caller: H160, crowdloan_id: u32, expected: CrowdloanInfo) { + let precompile_addr = addr_from_index(CrowdloanPrecompile::::INDEX); + + precompiles::>() + .prepare_test( + caller, + precompile_addr, + encode_with_selector(selector_u32("getCrowdloan(uint32)"), (crowdloan_id,)), + ) + .with_static_call(true) + .execute_returns_raw(encode_return_value(expected)); + } + + fn expected_crowdloan_info(crowdloan_id: u32) -> CrowdloanInfo { + let crowdloan = pallet_crowdloan::Crowdloans::::get(crowdloan_id) + .expect("crowdloan should exist"); + + CrowdloanInfo { + creator: H256::from_slice(crowdloan.creator.as_slice()), + deposit: u64::from(crowdloan.deposit), + min_contribution: u64::from(crowdloan.min_contribution), + end: crowdloan.end as u32, + cap: u64::from(crowdloan.cap), + funds_account: H256::from_slice(crowdloan.funds_account.as_slice()), + raised: u64::from(crowdloan.raised), + has_target_address: crowdloan.target_address.is_some(), + target_address: crowdloan + .target_address + .map(|account| H256::from_slice(account.as_slice())) + .unwrap_or_else(H256::zero), + finalized: crowdloan.finalized, + contributors_count: crowdloan.contributors_count, + } + } + + fn create_crowdloan(caller: H160, target: H160) -> u32 { + let crowdloan_id = pallet_crowdloan::NextCrowdloanId::::get(); + let precompile_addr = addr_from_index(CrowdloanPrecompile::::INDEX); + + precompiles::>() + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("create(uint64,uint64,uint64,uint32,address)"), + (CREATOR_DEPOSIT, MIN_CONTRIBUTION, CAP, END, Address(target)), + ), + ) + .execute_returns(()); + crowdloan_id + } + + fn contribute(caller: H160, crowdloan_id: u32, amount: u64) { + let precompile_addr = addr_from_index(CrowdloanPrecompile::::INDEX); + + precompiles::>() + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("contribute(uint32,uint64)"), + (crowdloan_id, amount), + ), + ) + .execute_returns(()); + } + + fn withdraw(caller: H160, crowdloan_id: u32) { + let precompile_addr = addr_from_index(CrowdloanPrecompile::::INDEX); + + precompiles::>() + .prepare_test( + caller, + precompile_addr, + encode_with_selector(selector_u32("withdraw(uint32)"), (crowdloan_id,)), + ) + .execute_returns(()); + } + + fn finalize(caller: H160, crowdloan_id: u32) { + let precompile_addr = addr_from_index(CrowdloanPrecompile::::INDEX); + + precompiles::>() + .prepare_test( + caller, + precompile_addr, + encode_with_selector(selector_u32("finalize(uint32)"), (crowdloan_id,)), + ) + .execute_returns(()); + } + + #[test] + fn crowdloan_precompile_reads_existing_pallet_crowdloan() { + new_test_ext().execute_with(|| { + let creator = AccountId::from([0x11; 32]); + let caller = addr_from_index(0x7001); + let crowdloan_id = pallet_crowdloan::NextCrowdloanId::::get(); + + fund_account(&creator, ACCOUNT_BALANCE); + pallet_crowdloan::Pallet::::create( + RuntimeOrigin::signed(creator), + CREATOR_DEPOSIT.into(), + MIN_CONTRIBUTION.into(), + CAP.into(), + END.into(), + None, + None, + ) + .expect("direct crowdloan create should work"); + + get_crowdloan(caller, crowdloan_id, expected_crowdloan_info(crowdloan_id)); + }); + } + + #[test] + fn crowdloan_precompile_creates_and_reads_crowdloan() { + new_test_ext().execute_with(|| { + let creator = addr_from_index(0x7002); + let target = addr_from_index(0x7003); + let creator_account = mapped_account(creator); + let target_account = mapped_account(target); + + fund_account(&creator_account, ACCOUNT_BALANCE); + + let crowdloan_id = create_crowdloan(creator, target); + let crowdloan = pallet_crowdloan::Crowdloans::::get(crowdloan_id) + .expect("crowdloan should exist"); + + assert_eq!(crowdloan.creator, creator_account); + assert_eq!(u64::from(crowdloan.deposit), CREATOR_DEPOSIT); + assert_eq!(u64::from(crowdloan.min_contribution), MIN_CONTRIBUTION); + assert_eq!(u64::from(crowdloan.cap), CAP); + assert_eq!(crowdloan.end, END as u64); + assert_eq!(u64::from(crowdloan.raised), CREATOR_DEPOSIT); + assert_eq!(crowdloan.target_address, Some(target_account)); + assert!(!crowdloan.finalized); + assert_eq!(crowdloan.contributors_count, 1); + get_crowdloan(creator, crowdloan_id, expected_crowdloan_info(crowdloan_id)); + }); + } + + #[test] + fn crowdloan_precompile_contributes_and_withdraws() { + new_test_ext().execute_with(|| { + let creator = addr_from_index(0x7004); + let contributor = addr_from_index(0x7005); + let target = addr_from_index(0x7006); + let creator_account = mapped_account(creator); + let contributor_account = mapped_account(contributor); + let contribution = 30_u64; + + fund_account(&creator_account, ACCOUNT_BALANCE); + fund_account(&contributor_account, ACCOUNT_BALANCE); + + let crowdloan_id = create_crowdloan(creator, target); + contribute(contributor, crowdloan_id, contribution); + + let crowdloan = pallet_crowdloan::Crowdloans::::get(crowdloan_id) + .expect("crowdloan should exist"); + assert_eq!(u64::from(crowdloan.raised), CREATOR_DEPOSIT + contribution); + assert_eq!(crowdloan.contributors_count, 2); + assert_eq!( + pallet_crowdloan::Contributions::::get( + crowdloan_id, + &contributor_account, + ), + Some(contribution.into()), + ); + get_crowdloan(creator, crowdloan_id, expected_crowdloan_info(crowdloan_id)); + + withdraw(contributor, crowdloan_id); + + let crowdloan = pallet_crowdloan::Crowdloans::::get(crowdloan_id) + .expect("crowdloan should exist"); + assert_eq!(u64::from(crowdloan.raised), CREATOR_DEPOSIT); + assert_eq!(crowdloan.contributors_count, 1); + assert_eq!( + pallet_crowdloan::Contributions::::get( + crowdloan_id, + &contributor_account, + ), + None, + ); + get_crowdloan(creator, crowdloan_id, expected_crowdloan_info(crowdloan_id)); + }); + } + + #[test] + fn crowdloan_precompile_contributes_and_withdraws_from_pallet_crowdloan() { + new_test_ext().execute_with(|| { + let creator = AccountId::from([0x22; 32]); + let contributor = addr_from_index(0x7016); + let contributor_account = mapped_account(contributor); + let crowdloan_id = pallet_crowdloan::NextCrowdloanId::::get(); + let contribution = 30_u64; + + fund_account(&creator, ACCOUNT_BALANCE); + fund_account(&contributor_account, ACCOUNT_BALANCE); + pallet_crowdloan::Pallet::::create( + RuntimeOrigin::signed(creator), + CREATOR_DEPOSIT.into(), + MIN_CONTRIBUTION.into(), + CAP.into(), + END.into(), + None, + None, + ) + .expect("direct crowdloan create should work"); + + contribute(contributor, crowdloan_id, contribution); + + let crowdloan = pallet_crowdloan::Crowdloans::::get(crowdloan_id) + .expect("crowdloan should exist"); + assert_eq!(u64::from(crowdloan.raised), CREATOR_DEPOSIT + contribution); + assert_eq!(crowdloan.contributors_count, 2); + get_crowdloan(contributor, crowdloan_id, expected_crowdloan_info(crowdloan_id)); + + withdraw(contributor, crowdloan_id); + + let crowdloan = pallet_crowdloan::Crowdloans::::get(crowdloan_id) + .expect("crowdloan should exist"); + assert_eq!(u64::from(crowdloan.raised), CREATOR_DEPOSIT); + assert_eq!(crowdloan.contributors_count, 1); + assert_eq!( + pallet_crowdloan::Contributions::::get( + crowdloan_id, + &contributor_account, + ), + None, + ); + get_crowdloan(contributor, crowdloan_id, expected_crowdloan_info(crowdloan_id)); + }); + } + + #[test] + fn crowdloan_precompile_finalizes_capped_crowdloan() { + new_test_ext().execute_with(|| { + let creator = addr_from_index(0x7007); + let contributor = addr_from_index(0x7008); + let target = addr_from_index(0x7009); + let creator_account = mapped_account(creator); + let contributor_account = mapped_account(contributor); + let target_account = mapped_account(target); + + fund_account(&creator_account, ACCOUNT_BALANCE); + fund_account(&contributor_account, ACCOUNT_BALANCE); + + let crowdloan_id = create_crowdloan(creator, target); + contribute(contributor, crowdloan_id, CAP - CREATOR_DEPOSIT); + System::set_block_number(END.into()); + finalize(creator, crowdloan_id); + + let crowdloan = pallet_crowdloan::Crowdloans::::get(crowdloan_id) + .expect("crowdloan should exist"); + assert!(crowdloan.finalized); + assert_eq!( + pallet_balances::Pallet::::free_balance(&target_account), + CAP.into(), + ); + get_crowdloan(creator, crowdloan_id, expected_crowdloan_info(crowdloan_id)); + }); + } + + #[test] + fn crowdloan_precompile_refunds_and_dissolves_crowdloan() { + new_test_ext().execute_with(|| { + let creator = addr_from_index(0x7010); + let first = addr_from_index(0x7011); + let second = addr_from_index(0x7012); + let target = addr_from_index(0x7013); + let creator_account = mapped_account(creator); + let first_account = mapped_account(first); + let second_account = mapped_account(second); + let contribution = 30_u64; + + fund_account(&creator_account, ACCOUNT_BALANCE); + fund_account(&first_account, ACCOUNT_BALANCE); + fund_account(&second_account, ACCOUNT_BALANCE); + + let crowdloan_id = create_crowdloan(creator, target); + contribute(first, crowdloan_id, contribution); + contribute(second, crowdloan_id, contribution); + System::set_block_number(END.into()); + let precompile_addr = addr_from_index(CrowdloanPrecompile::::INDEX); + + precompiles::>() + .prepare_test( + creator, + precompile_addr, + encode_with_selector(selector_u32("refund(uint32)"), (crowdloan_id,)), + ) + .execute_returns(()); + + let crowdloan = pallet_crowdloan::Crowdloans::::get(crowdloan_id) + .expect("crowdloan should exist"); + assert_eq!(u64::from(crowdloan.raised), CREATOR_DEPOSIT); + assert_eq!(crowdloan.contributors_count, 1); + get_crowdloan(creator, crowdloan_id, expected_crowdloan_info(crowdloan_id)); + + precompiles::>() + .prepare_test( + creator, + precompile_addr, + encode_with_selector(selector_u32("dissolve(uint32)"), (crowdloan_id,)), + ) + .execute_returns(()); + + assert!(pallet_crowdloan::Crowdloans::::get(crowdloan_id).is_none()); + }); + } + + #[test] + fn crowdloan_precompile_updates_crowdloan_terms() { + new_test_ext().execute_with(|| { + let creator = addr_from_index(0x7014); + let target = addr_from_index(0x7015); + let creator_account = mapped_account(creator); + let new_min_contribution = 20_u64; + let new_end = 80_u32; + let new_cap = 400_u64; + + fund_account(&creator_account, ACCOUNT_BALANCE); + + let crowdloan_id = create_crowdloan(creator, target); + let precompiles = precompiles::>(); + let precompile_addr = addr_from_index(CrowdloanPrecompile::::INDEX); + + precompiles + .prepare_test( + creator, + precompile_addr, + encode_with_selector( + selector_u32("updateMinContribution(uint32,uint64)"), + (crowdloan_id, new_min_contribution), + ), + ) + .execute_returns(()); + assert_eq!( + u64::from( + pallet_crowdloan::Crowdloans::::get(crowdloan_id) + .expect("crowdloan should exist") + .min_contribution, + ), + new_min_contribution, + ); + + precompiles + .prepare_test( + creator, + precompile_addr, + encode_with_selector( + selector_u32("updateEnd(uint32,uint32)"), + (crowdloan_id, new_end), + ), + ) + .execute_returns(()); + assert_eq!( + pallet_crowdloan::Crowdloans::::get(crowdloan_id) + .expect("crowdloan should exist") + .end, + new_end as u64, + ); + + precompiles + .prepare_test( + creator, + precompile_addr, + encode_with_selector( + selector_u32("updateCap(uint32,uint64)"), + (crowdloan_id, new_cap), + ), + ) + .execute_returns(()); + assert_eq!( + u64::from( + pallet_crowdloan::Crowdloans::::get(crowdloan_id) + .expect("crowdloan should exist") + .cap, + ), + new_cap, + ); + get_crowdloan(creator, crowdloan_id, expected_crowdloan_info(crowdloan_id)); + }); + } +} diff --git a/precompiles/src/leasing.rs b/precompiles/src/leasing.rs index e3e11759a2..005782c776 100644 --- a/precompiles/src/leasing.rs +++ b/precompiles/src/leasing.rs @@ -187,3 +187,273 @@ struct LeaseInfo { netuid: u16, cost: u64, } + +#[cfg(test)] +mod tests { + #![allow(clippy::expect_used, clippy::arithmetic_side_effects)] + + use super::*; + use crate::PrecompileExt; + use crate::mock::{ + AccountId, Runtime, RuntimeCall, RuntimeOrigin, System, addr_from_index, fund_account, + mapped_account, new_test_ext, precompiles, selector_u32, + }; + use frame_support::StorageDoubleMap; + use precompile_utils::solidity::{encode_return_value, encode_with_selector}; + use precompile_utils::testing::PrecompileTesterExt; + use sp_core::H160; + use subtensor_runtime_common::TaoBalance; + + const CROWDLOAN_DEPOSIT: u64 = 50; + const CROWDLOAN_MIN_CONTRIBUTION: u64 = 10; + const NETWORK_LOCK_COST: u64 = 100; + const CROWDLOAN_CAP: u64 = 200; + const CROWDLOAN_END: u32 = 50; + const LEASING_EMISSIONS_SHARE: u8 = 15; + const LEASING_END_BLOCK: u32 = 80; + const ACCOUNT_BALANCE: u64 = 1_000; + + fn expected_lease_info(lease_id: u32) -> LeaseInfo { + let lease = + pallet_subtensor::SubnetLeases::::get(lease_id).expect("lease should exist"); + + LeaseInfo { + beneficiary: H256::from_slice(lease.beneficiary.as_slice()), + coldkey: H256::from_slice(lease.coldkey.as_slice()), + hotkey: H256::from_slice(lease.hotkey.as_slice()), + emissions_share: lease.emissions_share.deconstruct(), + has_end_block: lease.end_block.is_some(), + end_block: lease.end_block.unwrap_or_default() as u32, + netuid: lease.netuid.into(), + cost: u64::from(lease.cost), + } + } + + fn get_lease(caller: H160, lease_id: u32, expected: LeaseInfo) { + let precompile_addr = addr_from_index(LeasingPrecompile::::INDEX); + + precompiles::>() + .prepare_test( + caller, + precompile_addr, + encode_with_selector(selector_u32("getLease(uint32)"), (lease_id,)), + ) + .with_static_call(true) + .execute_returns_raw(encode_return_value(expected)); + } + + fn create_lease_crowdloan(caller: H160) -> u32 { + let crowdloan_id = pallet_crowdloan::NextCrowdloanId::::get(); + let precompile_addr = addr_from_index(LeasingPrecompile::::INDEX); + + precompiles::>() + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32( + "createLeaseCrowdloan(uint64,uint64,uint64,uint32,uint8,bool,uint32)", + ), + ( + CROWDLOAN_DEPOSIT, + CROWDLOAN_MIN_CONTRIBUTION, + CROWDLOAN_CAP, + CROWDLOAN_END, + LEASING_EMISSIONS_SHARE, + true, + LEASING_END_BLOCK, + ), + ), + ) + .execute_returns(()); + + crowdloan_id + } + + fn contribute_and_finalize(crowdloan_id: u32, creator: AccountId, contributor: AccountId) { + pallet_crowdloan::Pallet::::contribute( + RuntimeOrigin::signed(contributor), + crowdloan_id, + (CROWDLOAN_CAP - CROWDLOAN_DEPOSIT).into(), + ) + .expect("contribute should work"); + + System::set_block_number(CROWDLOAN_END.into()); + pallet_crowdloan::Pallet::::finalize(RuntimeOrigin::signed(creator), crowdloan_id) + .expect("finalize should work"); + } + + fn set_leasing_fixture() { + pallet_subtensor::NetworkMinLockCost::::set(TaoBalance::from(NETWORK_LOCK_COST)); + pallet_subtensor::NetworkLastLockCost::::set(TaoBalance::from(NETWORK_LOCK_COST)); + } + + #[test] + fn leasing_precompile_reads_existing_pallet_lease_and_contributor_shares() { + new_test_ext().execute_with(|| { + set_leasing_fixture(); + + let creator = AccountId::from([0x11; 32]); + let contributor = AccountId::from([0x22; 32]); + let caller = addr_from_index(0x8001); + let crowdloan_id = pallet_crowdloan::NextCrowdloanId::::get(); + let lease_id = pallet_subtensor::NextSubnetLeaseId::::get(); + let leasing_call = pallet_subtensor::Call::::register_leased_network { + emissions_share: Percent::from_percent(LEASING_EMISSIONS_SHARE), + end_block: Some(LEASING_END_BLOCK.into()), + }; + + fund_account(&creator, ACCOUNT_BALANCE); + fund_account(&contributor, ACCOUNT_BALANCE); + pallet_crowdloan::Pallet::::create( + RuntimeOrigin::signed(creator.clone()), + CROWDLOAN_DEPOSIT.into(), + CROWDLOAN_MIN_CONTRIBUTION.into(), + CROWDLOAN_CAP.into(), + CROWDLOAN_END.into(), + Some(Box::new(RuntimeCall::from(leasing_call))), + None, + ) + .expect("direct crowdloan create should work"); + contribute_and_finalize(crowdloan_id, creator.clone(), contributor.clone()); + + let lease = pallet_subtensor::SubnetLeases::::get(lease_id) + .expect("lease should exist"); + get_lease(caller, lease_id, expected_lease_info(lease_id)); + + let precompile_addr = addr_from_index(LeasingPrecompile::::INDEX); + precompiles::>() + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("getLeaseIdForSubnet(uint16)"), + (u16::from(lease.netuid),), + ), + ) + .with_static_call(true) + .execute_returns(lease_id); + + precompiles::>() + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("getContributorShare(uint32,bytes32)"), + (lease_id, H256::from_slice(creator.as_slice())), + ), + ) + .with_static_call(true) + .execute_returns((0_u128, 0_u128)); + + let contributor_share = + pallet_subtensor::SubnetLeaseShares::::get(lease_id, &contributor); + precompiles::>() + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("getContributorShare(uint32,bytes32)"), + (lease_id, H256::from_slice(contributor.as_slice())), + ), + ) + .with_static_call(true) + .execute_returns(( + contributor_share.int().to_bits(), + contributor_share.frac().to_bits(), + )); + }); + } + + #[test] + fn leasing_precompile_creates_lease_crowdloan_and_reads_created_lease() { + new_test_ext().execute_with(|| { + set_leasing_fixture(); + + let creator = addr_from_index(0x8002); + let contributor = addr_from_index(0x8003); + let creator_account = mapped_account(creator); + let contributor_account = mapped_account(contributor); + let lease_id = pallet_subtensor::NextSubnetLeaseId::::get(); + + fund_account(&creator_account, ACCOUNT_BALANCE); + fund_account(&contributor_account, ACCOUNT_BALANCE); + let crowdloan_id = create_lease_crowdloan(creator); + contribute_and_finalize( + crowdloan_id, + creator_account.clone(), + contributor_account.clone(), + ); + + let lease = pallet_subtensor::SubnetLeases::::get(lease_id) + .expect("lease should exist"); + assert_eq!(lease.beneficiary, creator_account); + assert_eq!( + lease.emissions_share, + Percent::from_percent(LEASING_EMISSIONS_SHARE) + ); + assert_eq!(lease.end_block, Some(LEASING_END_BLOCK.into())); + + get_lease(creator, lease_id, expected_lease_info(lease_id)); + let contributor_share = + pallet_subtensor::SubnetLeaseShares::::get(lease_id, &contributor_account); + assert_ne!( + ( + contributor_share.int().to_bits(), + contributor_share.frac().to_bits() + ), + (0_u128, 0_u128), + ); + }); + } + + #[test] + fn leasing_precompile_terminates_ended_lease_and_transfers_subnet_ownership() { + new_test_ext().execute_with(|| { + set_leasing_fixture(); + + let beneficiary = addr_from_index(0x8004); + let contributor = addr_from_index(0x8005); + let beneficiary_account = mapped_account(beneficiary); + let contributor_account = mapped_account(contributor); + let new_hotkey = AccountId::from([0x33; 32]); + let lease_id = pallet_subtensor::NextSubnetLeaseId::::get(); + + fund_account(&beneficiary_account, ACCOUNT_BALANCE); + fund_account(&contributor_account, ACCOUNT_BALANCE); + let crowdloan_id = create_lease_crowdloan(beneficiary); + contribute_and_finalize( + crowdloan_id, + beneficiary_account.clone(), + contributor_account.clone(), + ); + + let lease = pallet_subtensor::SubnetLeases::::get(lease_id) + .expect("lease should exist"); + pallet_subtensor::Owner::::insert(&new_hotkey, &beneficiary_account); + System::set_block_number(LEASING_END_BLOCK.into()); + + precompiles::>() + .prepare_test( + beneficiary, + addr_from_index(LeasingPrecompile::::INDEX), + encode_with_selector( + selector_u32("terminateLease(uint32,bytes32)"), + (lease_id, H256::from_slice(new_hotkey.as_slice())), + ), + ) + .execute_returns(()); + + assert!(pallet_subtensor::SubnetLeases::::get(lease_id).is_none()); + assert!(!pallet_subtensor::SubnetLeaseShares::::contains_prefix(lease_id)); + assert_eq!( + pallet_subtensor::SubnetOwner::::get(lease.netuid), + beneficiary_account, + ); + assert_eq!( + pallet_subtensor::SubnetOwnerHotkey::::get(lease.netuid), + new_hotkey, + ); + }); + } +} diff --git a/precompiles/src/mock.rs b/precompiles/src/mock.rs index 452d8cf6a7..d82422bf51 100644 --- a/precompiles/src/mock.rs +++ b/precompiles/src/mock.rs @@ -12,8 +12,8 @@ use frame_support::{ }; use frame_system::{EnsureRoot, limits, offchain::CreateTransactionBase}; use pallet_evm::{ - BalanceConverter, EnsureAddressNever, EnsureAddressRoot, EvmBalance, PrecompileHandle, - PrecompileSet, SubstrateBalance, + AddressMapping, BalanceConverter, EnsureAddressNever, EnsureAddressRoot, EvmBalance, + PrecompileHandle, PrecompileSet, SubstrateBalance, }; use precompile_utils::testing::MockHandle; use sp_core::{ConstU64, H160, H256, U256, crypto::AccountId32}; @@ -35,6 +35,7 @@ frame_support::construct_runtime!( pub enum Runtime { System: frame_system = 1, Balances: pallet_balances = 2, + AlphaAssets: pallet_alpha_assets = 15, Timestamp: pallet_timestamp = 3, Shield: pallet_shield = 4, SubtensorModule: pallet_subtensor::{Pallet, Call, Storage, Event} = 5, @@ -45,6 +46,8 @@ frame_support::construct_runtime!( Crowdloan: pallet_crowdloan::{Pallet, Call, Storage, Event} = 10, Proxy: pallet_subtensor_proxy = 11, Evm: pallet_evm = 12, + AdminUtils: pallet_admin_utils = 13, + EVMChainId: pallet_evm_chain_id = 14, } ); @@ -148,6 +151,8 @@ parameter_types! { pub const LeaseDividendsDistributionInterval: u32 = 100; pub const MaxImmuneUidsPercentage: Percent = Percent::from_percent(80); pub const EvmKeyAssociateRateLimit: u64 = 0; + pub const SubtensorPalletId: PalletId = PalletId(*b"subtensr"); + pub const BurnAccountId: PalletId = PalletId(*b"burntnsr"); } #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] @@ -182,6 +187,8 @@ impl pallet_balances::Config for Runtime { type RuntimeHoldReason = (); } +impl pallet_alpha_assets::Config for Runtime {} + #[derive_impl(pallet_timestamp::config_preludes::TestDefaultConfig)] impl pallet_timestamp::Config for Runtime { type MinimumPeriod = MinimumPeriod; @@ -338,6 +345,17 @@ mod test_crypto { } } +impl pallet_evm_chain_id::Config for Runtime {} + +impl pallet_admin_utils::Config for Runtime { + type Aura = (); + type Grandpa = (); + type AuthorityId = test_crypto::Public; + type MaxAuthorities = MaxAuthorities; + type Balance = TaoBalance; + type WeightInfo = (); +} + impl pallet_drand::Config for Runtime { type AuthorityId = test_crypto::TestAuthId; type Verifier = pallet_drand::verifier::QuicknetVerifier; @@ -453,6 +471,7 @@ impl pallet_subtensor::Config for Runtime { type LiquidAlphaOn = InitialLiquidAlphaOn; type Yuma3On = InitialYuma3On; type Preimages = Preimage; + type AlphaAssets = AlphaAssets; type InitialColdkeySwapAnnouncementDelay = InitialColdkeySwapAnnouncementDelay; type InitialColdkeySwapReannouncementDelay = InitialColdkeySwapReannouncementDelay; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; @@ -469,6 +488,8 @@ impl pallet_subtensor::Config for Runtime { type CommitmentsInterface = CommitmentsI; type EvmKeyAssociateRateLimit = EvmKeyAssociateRateLimit; type AuthorshipProvider = MockAuthorshipProvider; + type SubtensorPalletId = SubtensorPalletId; + type BurnAccountId = BurnAccountId; type WeightInfo = (); } @@ -564,6 +585,17 @@ pub(crate) fn addr_from_index(index: u64) -> H160 { H160::from_low_u64_be(index) } +pub(crate) fn mapped_account(address: H160) -> AccountId { + ::AddressMapping::into_account_id(address) +} + +pub(crate) fn fund_account(account: &AccountId, amount: u64) { + let amount = TaoBalance::from(amount); + let credit = pallet_subtensor::Pallet::::mint_tao(amount); + let _ = pallet_subtensor::Pallet::::spend_tao(account, credit, amount) + .expect("test account funding should work"); +} + pub(crate) fn abi_word(value: U256) -> Vec { value.to_big_endian().to_vec() } @@ -594,3 +626,9 @@ pub(crate) fn alpha_price_to_evm(price: U96F32) -> U256 { .expect("runtime balance conversion should work for alpha price") .into_u256() } + +pub(crate) fn substrate_to_evm(amount: u64) -> U256 { + ::BalanceConverter::into_evm_balance(amount.into()) + .expect("runtime balance conversion should work") + .into_u256() +} diff --git a/precompiles/src/neuron.rs b/precompiles/src/neuron.rs index 1b66ea902c..1397baf272 100644 --- a/precompiles/src/neuron.rs +++ b/precompiles/src/neuron.rs @@ -255,15 +255,14 @@ where #[cfg(test)] mod tests { - #![allow(clippy::expect_used, clippy::indexing_slicing)] + #![allow(clippy::expect_used, clippy::indexing_slicing, clippy::unwrap_used)] use super::*; use crate::PrecompileExt; use crate::mock::{ - AccountId, Runtime, System, addr_from_index, execute_precompile, new_test_ext, precompiles, - selector_u32, + AccountId, Runtime, System, addr_from_index, execute_precompile, mapped_account, + new_test_ext, precompiles, selector_u32, }; - use pallet_evm::AddressMapping; use precompile_utils::solidity::encode_with_selector; use precompile_utils::testing::PrecompileTesterExt; use sp_core::{H160, H256, U256}; @@ -289,10 +288,14 @@ mod tests { const SERVE_PLACEHOLDER1: u8 = 8; const SERVE_PLACEHOLDER2: u8 = 9; + fn add_balance_to_coldkey_account(coldkey: &sp_core::crypto::AccountId32, tao: TaoBalance) { + let credit = pallet_subtensor::Pallet::::mint_tao(tao); + let _ = pallet_subtensor::Pallet::::spend_tao(coldkey, credit, tao).unwrap(); + } + fn setup_registered_caller(caller: H160) -> (NetUid, AccountId) { let netuid = NetUid::from(TEST_NETUID_U16); - let caller_account = - ::AddressMapping::into_account_id(caller); + let caller_account = mapped_account(caller); let caller_hotkey = H256::from_slice(caller_account.as_ref()); pallet_subtensor::Pallet::::init_new_network(netuid, TEMPO); @@ -306,10 +309,7 @@ mod tests { .expect("reveal period setup should succeed"); pallet_subtensor::SubnetTAO::::insert(netuid, TaoBalance::from(RESERVE)); pallet_subtensor::SubnetAlphaIn::::insert(netuid, AlphaBalance::from(RESERVE)); - pallet_subtensor::Pallet::::add_balance_to_coldkey_account( - &caller_account, - COLDKEY_BALANCE.into(), - ); + add_balance_to_coldkey_account(&caller_account, COLDKEY_BALANCE.into()); precompiles::>() .prepare_test( @@ -348,8 +348,7 @@ mod tests { new_test_ext().execute_with(|| { let netuid = NetUid::from(TEST_NETUID_U16); let caller = addr_from_index(0x1234); - let caller_account = - ::AddressMapping::into_account_id(caller); + let caller_account = mapped_account(caller); let hotkey_account = AccountId::from([0x42; 32]); let hotkey = H256::from_slice(hotkey_account.as_ref()); @@ -359,10 +358,7 @@ mod tests { pallet_subtensor::Pallet::::set_max_allowed_uids(netuid, 4096); pallet_subtensor::SubnetTAO::::insert(netuid, TaoBalance::from(RESERVE)); pallet_subtensor::SubnetAlphaIn::::insert(netuid, AlphaBalance::from(RESERVE)); - pallet_subtensor::Pallet::::add_balance_to_coldkey_account( - &caller_account, - COLDKEY_BALANCE.into(), - ); + add_balance_to_coldkey_account(&caller_account, COLDKEY_BALANCE.into()); let uid_before = pallet_subtensor::SubnetworkN::::get(netuid); let balance_before = diff --git a/precompiles/src/sr25519.rs b/precompiles/src/sr25519.rs index 054948d524..324bd7abca 100644 --- a/precompiles/src/sr25519.rs +++ b/precompiles/src/sr25519.rs @@ -55,3 +55,82 @@ where Ok((ExitSucceed::Returned, buf.to_vec())) } } + +#[cfg(test)] +mod tests { + #![allow(clippy::expect_used)] + + use super::*; + use crate::mock::{ + AccountId, abi_word, addr_from_index, new_test_ext, precompiles, selector_u32, + }; + use precompile_utils::solidity::encode_with_selector; + use precompile_utils::testing::PrecompileTesterExt; + use sp_core::{H256, Pair, U256, sr25519}; + + #[test] + fn sr25519_precompile_verifies_valid_and_invalid_signatures() { + new_test_ext().execute_with(|| { + let caller = addr_from_index(1); + let precompile_addr = addr_from_index(Sr25519Verify::::INDEX); + + let pair = sr25519::Pair::from_seed(&[1u8; 32]); + let message = [7u8; 32]; + let signature = pair.sign(&message); + let public_key = pair.public(); + let broken_message = [8u8; 32]; + let mut broken_signature = signature.0; + broken_signature[0] ^= 1; + let broken_signature = sr25519::Signature::from_raw(broken_signature); + + precompiles::>() + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("verify(bytes32,bytes32,bytes32,bytes32)"), + ( + H256::from(message), + H256::from(public_key.0), + H256::from_slice(&signature.0[..32]), + H256::from_slice(&signature.0[32..]), + ), + ), + ) + .with_static_call(true) + .execute_returns_raw(abi_word(U256::one())); + precompiles::>() + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("verify(bytes32,bytes32,bytes32,bytes32)"), + ( + H256::from(broken_message), + H256::from(public_key.0), + H256::from_slice(&signature.0[..32]), + H256::from_slice(&signature.0[32..]), + ), + ), + ) + .with_static_call(true) + .execute_returns_raw(abi_word(U256::zero())); + precompiles::>() + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("verify(bytes32,bytes32,bytes32,bytes32)"), + ( + H256::from(message), + H256::from(public_key.0), + H256::from_slice(&broken_signature.0[..32]), + H256::from_slice(&broken_signature.0[32..]), + ), + ), + ) + .with_static_call(true) + .execute_returns_raw(abi_word(U256::zero())); + }); + } +} diff --git a/precompiles/src/staking.rs b/precompiles/src/staking.rs index f64f9ca319..28e043f07b 100644 --- a/precompiles/src/staking.rs +++ b/precompiles/src/staking.rs @@ -917,3 +917,1137 @@ fn try_u64_from_u256(value: U256) -> Result { exit_status: ExitError::Other("the value is outside of u64 bounds".into()), }) } + +#[cfg(test)] +mod tests { + #![allow( + clippy::arithmetic_side_effects, + clippy::expect_used, + clippy::unwrap_used, + clippy::indexing_slicing + )] + + use super::*; + use crate::PrecompileExt; + use crate::mock::{ + AccountId, Proxy, Runtime, RuntimeCall, RuntimeOrigin, addr_from_index, assert_static_call, + execute_precompile, fund_account, mapped_account, new_test_ext, precompiles, selector_u32, + substrate_to_evm, + }; + use precompile_utils::solidity::{encode_return_value, encode_with_selector}; + use precompile_utils::testing::PrecompileTesterExt; + use sp_core::{H160, H256}; + use substrate_fixed::types::U64F64; + use subtensor_runtime_common::{AlphaBalance, TaoBalance}; + + const TEST_NETUID_U16: u16 = 1; + const INVALID_NETUID_U16: u16 = 12_345; + const TEMPO: u16 = 100; + const RESERVE_TAO: u64 = 200_000_000_000; + const RESERVE_ALPHA: u64 = 100_000_000_000; + const INITIAL_STAKE_RAO: u64 = 20_000_000_000; + const REMOVE_STAKE_RAO: u64 = 10_000_000_000; + const PROXY_STAKE_RAO: u64 = 1_000_000_000; + const COLDKEY_BALANCE: u64 = 100_000_000_000; + const APPROVED_ALLOWANCE_RAO: u64 = 10_000_000_000; + const TRANSFERRED_ALLOWANCE_RAO: u64 = 5_000_000_000; + const ALLOWANCE_DECREASE_RAO: u64 = 2_000_000_000; + + fn setup_staking_subnet() -> NetUid { + let netuid = NetUid::from(TEST_NETUID_U16); + pallet_subtensor::Pallet::::init_new_network(netuid, TEMPO); + pallet_subtensor::Pallet::::set_network_registration_allowed(netuid, true); + pallet_subtensor::Pallet::::set_max_allowed_uids(netuid, 4096); + pallet_subtensor::FirstEmissionBlockNumber::::insert(netuid, 0); + pallet_subtensor::SubtokenEnabled::::insert(netuid, true); + pallet_subtensor::BurnHalfLife::::insert(netuid, 1); + pallet_subtensor::BurnIncreaseMult::::insert(netuid, U64F64::from_num(1)); + pallet_subtensor::SubnetTAO::::insert(netuid, TaoBalance::from(RESERVE_TAO)); + pallet_subtensor::SubnetAlphaIn::::insert( + netuid, + AlphaBalance::from(RESERVE_ALPHA), + ); + netuid + } + + fn hotkey() -> AccountId { + AccountId::from([0x11; 32]) + } + + fn delegate() -> AccountId { + AccountId::from([0x22; 32]) + } + + fn ensure_hotkey_exists(hotkey: &AccountId) { + pallet_subtensor::Owner::::insert(hotkey, hotkey.clone()); + } + + fn stake_for(hotkey: &AccountId, coldkey: &AccountId, netuid: NetUid) -> u64 { + pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( + hotkey, coldkey, netuid, + ) + .into() + } + + fn total_coldkey_stake_on_subnet(coldkey: &AccountId, netuid: NetUid) -> u64 { + pallet_subtensor::Pallet::::get_total_stake_for_coldkey_on_subnet(coldkey, netuid) + .into() + } + + fn add_stake_v1(caller: H160, hotkey: &AccountId, netuid: u16, amount_rao: u64) { + ensure_hotkey_exists(hotkey); + fund_account(&StakingPrecompile::::account_id(), amount_rao); + + let result = execute_precompile( + &precompiles::>(), + addr_from_index(StakingPrecompile::::INDEX), + caller, + encode_with_selector( + selector_u32("addStake(bytes32,uint256)"), + (H256::from_slice(hotkey.as_ref()), U256::from(netuid)), + ), + substrate_to_evm(amount_rao), + ) + .expect("staking v1 add stake should route to the precompile"); + + assert!(result.is_ok()); + } + + fn add_stake_v2(caller: H160, hotkey: &AccountId, netuid: u16, amount_rao: u64) { + ensure_hotkey_exists(hotkey); + precompiles::>() + .prepare_test( + caller, + addr_from_index(StakingPrecompileV2::::INDEX), + encode_with_selector( + selector_u32("addStake(bytes32,uint256,uint256)"), + ( + H256::from_slice(hotkey.as_ref()), + U256::from(amount_rao), + U256::from(netuid), + ), + ), + ) + .execute_returns(()); + } + + fn assert_proxy_effects(caller: H160, netuid: NetUid) { + let caller_account = mapped_account(caller); + let hotkey = hotkey(); + let delegate = delegate(); + + ensure_hotkey_exists(&hotkey); + + let proxies = pallet_subtensor_proxy::Proxies::::get(&caller_account).0; + assert_eq!(proxies.len(), 1); + assert_eq!(proxies[0].delegate, delegate); + + let stake_before = stake_for(&hotkey, &caller_account, netuid); + let proxied_call = RuntimeCall::SubtensorModule(pallet_subtensor::Call::add_stake { + hotkey: hotkey.clone(), + netuid, + amount_staked: PROXY_STAKE_RAO.into(), + }); + let proxy_result = Proxy::proxy( + RuntimeOrigin::signed(delegate.clone()), + caller_account.clone().into(), + Some(ProxyType::Staking), + Box::new(proxied_call), + ); + assert!(proxy_result.is_ok()); + + let stake_after = stake_for(&hotkey, &caller_account, netuid); + assert!(stake_after > stake_before); + } + + fn setup_approval_state() -> (NetUid, H160, H160, AccountId, AccountId, AccountId) { + let netuid = setup_staking_subnet(); + let source = addr_from_index(0x2001); + let spender = addr_from_index(0x2002); + let source_account = mapped_account(source); + let spender_account = mapped_account(spender); + let hotkey = hotkey(); + + fund_account(&source_account, COLDKEY_BALANCE); + add_stake_v2(source, &hotkey, TEST_NETUID_U16, INITIAL_STAKE_RAO); + pallet_subtensor::StakingOperationRateLimiter::::remove(( + hotkey.clone(), + source_account.clone(), + netuid, + )); + + ( + netuid, + source, + spender, + source_account, + spender_account, + hotkey, + ) + } + + fn assert_allowance(source: H160, spender: H160, caller: H160, expected: U256) { + assert_static_call( + &precompiles::>(), + caller, + addr_from_index(StakingPrecompileV2::::INDEX), + encode_with_selector( + selector_u32("allowance(address,address,uint256)"), + ( + precompile_utils::solidity::codec::Address(source), + precompile_utils::solidity::codec::Address(spender), + U256::from(TEST_NETUID_U16), + ), + ), + expected, + ); + } + + #[test] + fn staking_precompile_v1_add_stake_and_reads_match_runtime_state() { + new_test_ext().execute_with(|| { + let netuid = setup_staking_subnet(); + let caller = addr_from_index(0x1001); + let caller_account = mapped_account(caller); + let hotkey = hotkey(); + + fund_account(&caller_account, COLDKEY_BALANCE); + + let stake_before = stake_for(&hotkey, &caller_account, netuid); + add_stake_v1(caller, &hotkey, TEST_NETUID_U16, INITIAL_STAKE_RAO); + + let stake_after = stake_for(&hotkey, &caller_account, netuid); + assert!(stake_after > stake_before); + + assert_static_call( + &precompiles::>(), + caller, + addr_from_index(StakingPrecompile::::INDEX), + encode_with_selector( + selector_u32("getStake(bytes32,bytes32,uint256)"), + ( + H256::from_slice(hotkey.as_ref()), + H256::from_slice(caller_account.as_ref()), + U256::from(TEST_NETUID_U16), + ), + ), + substrate_to_evm(stake_after), + ); + }); + } + + #[test] + fn staking_precompile_v2_add_stake_and_reads_match_runtime_state() { + new_test_ext().execute_with(|| { + let netuid = setup_staking_subnet(); + let caller = addr_from_index(0x1002); + let caller_account = mapped_account(caller); + let hotkey = hotkey(); + + fund_account(&caller_account, COLDKEY_BALANCE); + + let stake_before = stake_for(&hotkey, &caller_account, netuid); + add_stake_v2(caller, &hotkey, TEST_NETUID_U16, INITIAL_STAKE_RAO); + + let stake_after = stake_for(&hotkey, &caller_account, netuid); + let total_coldkey_stake = total_coldkey_stake_on_subnet(&caller_account, netuid); + + assert!(stake_after > stake_before); + assert!(total_coldkey_stake >= stake_after); + + let precompiles = precompiles::>(); + let precompile_addr = addr_from_index(StakingPrecompileV2::::INDEX); + + assert_static_call( + &precompiles, + caller, + precompile_addr, + encode_with_selector( + selector_u32("getStake(bytes32,bytes32,uint256)"), + ( + H256::from_slice(hotkey.as_ref()), + H256::from_slice(caller_account.as_ref()), + U256::from(TEST_NETUID_U16), + ), + ), + U256::from(stake_after), + ); + assert_static_call( + &precompiles, + caller, + precompile_addr, + encode_with_selector( + selector_u32("getTotalColdkeyStakeOnSubnet(bytes32,uint256)"), + ( + H256::from_slice(caller_account.as_ref()), + U256::from(TEST_NETUID_U16), + ), + ), + U256::from(total_coldkey_stake), + ); + }); + } + + #[test] + fn staking_precompile_v1_rejects_missing_subnet() { + new_test_ext().execute_with(|| { + let caller = addr_from_index(0x1003); + let caller_account = mapped_account(caller); + let hotkey = hotkey(); + + fund_account(&caller_account, COLDKEY_BALANCE); + ensure_hotkey_exists(&hotkey); + fund_account( + &StakingPrecompile::::account_id(), + INITIAL_STAKE_RAO, + ); + + let rejected = execute_precompile( + &precompiles::>(), + addr_from_index(StakingPrecompile::::INDEX), + caller, + encode_with_selector( + selector_u32("addStake(bytes32,uint256)"), + ( + H256::from_slice(hotkey.as_ref()), + U256::from(INVALID_NETUID_U16), + ), + ), + substrate_to_evm(INITIAL_STAKE_RAO), + ) + .expect("staking v1 add stake should route to the precompile"); + + assert!(rejected.is_err()); + assert_eq!( + stake_for(&hotkey, &caller_account, NetUid::from(INVALID_NETUID_U16)), + 0, + ); + }); + } + + #[test] + fn staking_precompile_v2_rejects_missing_subnet() { + new_test_ext().execute_with(|| { + let caller = addr_from_index(0x1004); + let caller_account = mapped_account(caller); + let hotkey = hotkey(); + + fund_account(&caller_account, COLDKEY_BALANCE); + ensure_hotkey_exists(&hotkey); + + let rejected = execute_precompile( + &precompiles::>(), + addr_from_index(StakingPrecompileV2::::INDEX), + caller, + encode_with_selector( + selector_u32("addStake(bytes32,uint256,uint256)"), + ( + H256::from_slice(hotkey.as_ref()), + U256::from(INITIAL_STAKE_RAO), + U256::from(INVALID_NETUID_U16), + ), + ), + U256::zero(), + ) + .expect("staking v2 add stake should route to the precompile"); + + assert!(rejected.is_err()); + assert_eq!( + stake_for(&hotkey, &caller_account, NetUid::from(INVALID_NETUID_U16)), + 0, + ); + }); + } + + #[test] + fn staking_precompile_v1_remove_stake_reduces_stake() { + new_test_ext().execute_with(|| { + let netuid = setup_staking_subnet(); + let caller = addr_from_index(0x1005); + let caller_account = mapped_account(caller); + let hotkey = hotkey(); + + fund_account(&caller_account, COLDKEY_BALANCE); + add_stake_v1(caller, &hotkey, TEST_NETUID_U16, INITIAL_STAKE_RAO); + pallet_subtensor::StakingOperationRateLimiter::::remove(( + hotkey.clone(), + caller_account.clone(), + netuid, + )); + + let precompiles = precompiles::>(); + let precompile_addr = addr_from_index(StakingPrecompile::::INDEX); + let stake_before = stake_for(&hotkey, &caller_account, netuid); + + precompiles + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("removeStake(bytes32,uint256,uint256)"), + ( + H256::from_slice(hotkey.as_ref()), + substrate_to_evm(REMOVE_STAKE_RAO), + U256::from(TEST_NETUID_U16), + ), + ), + ) + .execute_returns(()); + + let stake_after = stake_for(&hotkey, &caller_account, netuid); + assert_eq!(stake_after, stake_before - REMOVE_STAKE_RAO); + }); + } + + #[test] + fn staking_precompile_v2_remove_stake_reduces_stake() { + new_test_ext().execute_with(|| { + let netuid = setup_staking_subnet(); + let caller = addr_from_index(0x1006); + let caller_account = mapped_account(caller); + let hotkey = hotkey(); + + fund_account(&caller_account, COLDKEY_BALANCE); + add_stake_v2(caller, &hotkey, TEST_NETUID_U16, INITIAL_STAKE_RAO); + pallet_subtensor::StakingOperationRateLimiter::::remove(( + hotkey.clone(), + caller_account.clone(), + netuid, + )); + + let precompiles = precompiles::>(); + let precompile_addr = addr_from_index(StakingPrecompileV2::::INDEX); + let stake_before = stake_for(&hotkey, &caller_account, netuid); + + precompiles + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("removeStake(bytes32,uint256,uint256)"), + ( + H256::from_slice(hotkey.as_ref()), + U256::from(REMOVE_STAKE_RAO), + U256::from(TEST_NETUID_U16), + ), + ), + ) + .execute_returns(()); + + let stake_after = stake_for(&hotkey, &caller_account, netuid); + assert_eq!(stake_after, stake_before - REMOVE_STAKE_RAO); + }); + } + + #[test] + fn staking_precompile_v2_add_stake_limit_increases_stake() { + new_test_ext().execute_with(|| { + let netuid = setup_staking_subnet(); + let caller = addr_from_index(0x4001); + let caller_account = mapped_account(caller); + let hotkey = hotkey(); + let precompiles = precompiles::>(); + let precompile_addr = addr_from_index(StakingPrecompileV2::::INDEX); + + fund_account(&caller_account, COLDKEY_BALANCE); + ensure_hotkey_exists(&hotkey); + + let stake_before = stake_for(&hotkey, &caller_account, netuid); + precompiles + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("addStakeLimit(bytes32,uint256,uint256,bool,uint256)"), + ( + H256::from_slice(hotkey.as_ref()), + U256::from(INITIAL_STAKE_RAO), + U256::from(1_000_000_000_000_u64), + true, + U256::from(TEST_NETUID_U16), + ), + ), + ) + .execute_returns(()); + + assert!(stake_for(&hotkey, &caller_account, netuid) > stake_before); + }); + } + + #[test] + fn staking_precompile_v2_remove_stake_limit_decreases_stake() { + new_test_ext().execute_with(|| { + let netuid = setup_staking_subnet(); + let caller = addr_from_index(0x4002); + let caller_account = mapped_account(caller); + let hotkey = hotkey(); + let precompiles = precompiles::>(); + let precompile_addr = addr_from_index(StakingPrecompileV2::::INDEX); + + fund_account(&caller_account, COLDKEY_BALANCE); + ensure_hotkey_exists(&hotkey); + precompiles + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("addStakeLimit(bytes32,uint256,uint256,bool,uint256)"), + ( + H256::from_slice(hotkey.as_ref()), + U256::from(INITIAL_STAKE_RAO), + U256::from(1_000_000_000_000_u64), + true, + U256::from(TEST_NETUID_U16), + ), + ), + ) + .execute_returns(()); + pallet_subtensor::StakingOperationRateLimiter::::remove(( + hotkey.clone(), + caller_account.clone(), + netuid, + )); + + let stake_before = stake_for(&hotkey, &caller_account, netuid); + precompiles + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("removeStakeLimit(bytes32,uint256,uint256,bool,uint256)"), + ( + H256::from_slice(hotkey.as_ref()), + U256::from(REMOVE_STAKE_RAO), + U256::from(1_000_000_000_u64), + true, + U256::from(TEST_NETUID_U16), + ), + ), + ) + .execute_returns(()); + + assert!(stake_for(&hotkey, &caller_account, netuid) < stake_before); + }); + } + + #[test] + fn staking_precompile_v2_remove_stake_full_limit_clears_stake() { + new_test_ext().execute_with(|| { + let netuid = setup_staking_subnet(); + let caller = addr_from_index(0x4003); + let caller_account = mapped_account(caller); + let hotkey = hotkey(); + let precompiles = precompiles::>(); + let precompile_addr = addr_from_index(StakingPrecompileV2::::INDEX); + + fund_account(&caller_account, COLDKEY_BALANCE); + ensure_hotkey_exists(&hotkey); + precompiles + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("addStakeLimit(bytes32,uint256,uint256,bool,uint256)"), + ( + H256::from_slice(hotkey.as_ref()), + U256::from(INITIAL_STAKE_RAO), + U256::from(1_000_000_000_000_u64), + true, + U256::from(TEST_NETUID_U16), + ), + ), + ) + .execute_returns(()); + pallet_subtensor::StakingOperationRateLimiter::::remove(( + hotkey.clone(), + caller_account.clone(), + netuid, + )); + + assert!(stake_for(&hotkey, &caller_account, netuid) > 0); + precompiles + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("removeStakeFullLimit(bytes32,uint256,uint256)"), + ( + H256::from_slice(hotkey.as_ref()), + U256::from(TEST_NETUID_U16), + U256::from(90_000_000_u64), + ), + ), + ) + .execute_returns(()); + + assert_eq!(stake_for(&hotkey, &caller_account, netuid), 0); + }); + } + + #[test] + fn staking_precompile_v2_remove_stake_full_clears_stake() { + new_test_ext().execute_with(|| { + let netuid = setup_staking_subnet(); + let caller = addr_from_index(0x4004); + let caller_account = mapped_account(caller); + let hotkey = hotkey(); + let precompiles = precompiles::>(); + let precompile_addr = addr_from_index(StakingPrecompileV2::::INDEX); + + fund_account(&caller_account, COLDKEY_BALANCE); + ensure_hotkey_exists(&hotkey); + precompiles + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("addStakeLimit(bytes32,uint256,uint256,bool,uint256)"), + ( + H256::from_slice(hotkey.as_ref()), + U256::from(INITIAL_STAKE_RAO), + U256::from(1_000_000_000_000_u64), + true, + U256::from(TEST_NETUID_U16), + ), + ), + ) + .execute_returns(()); + pallet_subtensor::StakingOperationRateLimiter::::remove(( + hotkey.clone(), + caller_account.clone(), + netuid, + )); + + assert!(stake_for(&hotkey, &caller_account, netuid) > 0); + precompiles + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("removeStakeFull(bytes32,uint256)"), + ( + H256::from_slice(hotkey.as_ref()), + U256::from(TEST_NETUID_U16), + ), + ), + ) + .execute_returns(()); + + assert_eq!(stake_for(&hotkey, &caller_account, netuid), 0); + }); + } + + #[test] + fn staking_precompile_v2_getters_match_runtime_state() { + new_test_ext().execute_with(|| { + let netuid = setup_staking_subnet(); + let caller = addr_from_index(0x4005); + let caller_account = mapped_account(caller); + let hotkey = hotkey(); + let precompiles = precompiles::>(); + let precompile_addr = addr_from_index(StakingPrecompileV2::::INDEX); + + fund_account(&caller_account, COLDKEY_BALANCE); + add_stake_v2(caller, &hotkey, TEST_NETUID_U16, INITIAL_STAKE_RAO); + + let stake = stake_for(&hotkey, &caller_account, netuid); + assert!(stake > 0); + assert_static_call( + &precompiles, + caller, + precompile_addr, + encode_with_selector( + selector_u32("getStake(bytes32,bytes32,uint256)"), + ( + H256::from_slice(hotkey.as_ref()), + H256::from_slice(caller_account.as_ref()), + U256::from(TEST_NETUID_U16), + ), + ), + U256::from(stake), + ); + assert_static_call( + &precompiles, + caller, + precompile_addr, + encode_with_selector( + selector_u32("getTotalAlphaStaked(bytes32,uint256)"), + ( + H256::from_slice(hotkey.as_ref()), + U256::from(TEST_NETUID_U16), + ), + ), + U256::from( + pallet_subtensor::Pallet::::get_stake_for_hotkey_on_subnet( + &hotkey, netuid, + ) + .to_u64(), + ), + ); + + precompiles + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("getAlphaStakedValidators(bytes32,uint256)"), + ( + H256::from_slice(hotkey.as_ref()), + U256::from(TEST_NETUID_U16), + ), + ), + ) + .with_static_call(true) + .execute_returns_raw(encode_return_value(vec![H256::from_slice( + caller_account.as_ref(), + )])); + + assert_static_call( + &precompiles, + caller, + precompile_addr, + encode_with_selector(selector_u32("getNominatorMinRequiredStake()"), ()), + U256::from(pallet_subtensor::Pallet::::get_nominator_min_required_stake()), + ); + }); + } + + #[test] + fn staking_precompile_v1_adds_and_removes_proxy() { + new_test_ext().execute_with(|| { + let netuid = setup_staking_subnet(); + let caller = addr_from_index(0x1007); + let caller_account = mapped_account(caller); + let delegate = delegate(); + let precompiles = precompiles::>(); + let precompile_addr = addr_from_index(StakingPrecompile::::INDEX); + + fund_account(&caller_account, COLDKEY_BALANCE); + fund_account(&delegate, COLDKEY_BALANCE); + + precompiles + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("addProxy(bytes32)"), + (H256::from_slice(delegate.as_ref()),), + ), + ) + .execute_returns(()); + assert_proxy_effects(caller, netuid); + + precompiles + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("removeProxy(bytes32)"), + (H256::from_slice(delegate.as_ref()),), + ), + ) + .execute_returns(()); + + let proxies = pallet_subtensor_proxy::Proxies::::get(&caller_account).0; + assert!(proxies.is_empty()); + }); + } + + #[test] + fn staking_precompile_v2_adds_and_removes_proxy() { + new_test_ext().execute_with(|| { + let netuid = setup_staking_subnet(); + let caller = addr_from_index(0x1008); + let caller_account = mapped_account(caller); + let delegate = delegate(); + let precompiles = precompiles::>(); + let precompile_addr = addr_from_index(StakingPrecompileV2::::INDEX); + + fund_account(&caller_account, COLDKEY_BALANCE); + fund_account(&delegate, COLDKEY_BALANCE); + + precompiles + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("addProxy(bytes32)"), + (H256::from_slice(delegate.as_ref()),), + ), + ) + .execute_returns(()); + assert_proxy_effects(caller, netuid); + + precompiles + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("removeProxy(bytes32)"), + (H256::from_slice(delegate.as_ref()),), + ), + ) + .execute_returns(()); + + let proxies = pallet_subtensor_proxy::Proxies::::get(&caller_account).0; + assert!(proxies.is_empty()); + }); + } + + #[test] + fn staking_precompile_v2_transfer_stake_from_requires_allowance() { + new_test_ext().execute_with(|| { + let (_, source, spender, _, _, hotkey) = setup_approval_state(); + precompiles::>() + .prepare_test( + spender, + addr_from_index(StakingPrecompileV2::::INDEX), + encode_with_selector( + selector_u32( + "transferStakeFrom(address,address,bytes32,uint256,uint256,uint256)", + ), + ( + precompile_utils::solidity::codec::Address(source), + precompile_utils::solidity::codec::Address(spender), + H256::from_slice(hotkey.as_ref()), + U256::from(TEST_NETUID_U16), + U256::from(TEST_NETUID_U16), + U256::from(1_u64), + ), + ), + ) + .execute_reverts(|output| output == b"trying to spend more than allowed"); + }); + } + + #[test] + fn staking_precompile_v2_transfer_stake_from_consumes_allowance_and_moves_stake() { + new_test_ext().execute_with(|| { + let (netuid, source, spender, source_account, spender_account, hotkey) = + setup_approval_state(); + let precompiles = precompiles::>(); + let precompile_addr = addr_from_index(StakingPrecompileV2::::INDEX); + + precompiles + .prepare_test( + source, + precompile_addr, + encode_with_selector( + selector_u32("approve(address,uint256,uint256)"), + ( + precompile_utils::solidity::codec::Address(spender), + U256::from(TEST_NETUID_U16), + U256::from(APPROVED_ALLOWANCE_RAO), + ), + ), + ) + .execute_returns(()); + + let source_stake_before = stake_for(&hotkey, &source_account, netuid); + let spender_stake_before = stake_for(&hotkey, &spender_account, netuid); + + precompiles + .prepare_test( + spender, + precompile_addr, + encode_with_selector( + selector_u32( + "transferStakeFrom(address,address,bytes32,uint256,uint256,uint256)", + ), + ( + precompile_utils::solidity::codec::Address(source), + precompile_utils::solidity::codec::Address(spender), + H256::from_slice(hotkey.as_ref()), + U256::from(TEST_NETUID_U16), + U256::from(TEST_NETUID_U16), + U256::from(TRANSFERRED_ALLOWANCE_RAO), + ), + ), + ) + .execute_returns(()); + + assert_allowance( + source, + spender, + source, + U256::from(APPROVED_ALLOWANCE_RAO - TRANSFERRED_ALLOWANCE_RAO), + ); + assert_eq!( + stake_for(&hotkey, &source_account, netuid), + source_stake_before - TRANSFERRED_ALLOWANCE_RAO, + ); + assert_eq!( + stake_for(&hotkey, &spender_account, netuid), + spender_stake_before + TRANSFERRED_ALLOWANCE_RAO, + ); + }); + } + + #[test] + fn staking_precompile_v2_transfer_stake_from_rejects_amount_above_allowance() { + new_test_ext().execute_with(|| { + let (_, source, spender, _, _, hotkey) = setup_approval_state(); + let precompiles = precompiles::>(); + let precompile_addr = addr_from_index(StakingPrecompileV2::::INDEX); + + precompiles + .prepare_test( + source, + precompile_addr, + encode_with_selector( + selector_u32("approve(address,uint256,uint256)"), + ( + precompile_utils::solidity::codec::Address(spender), + U256::from(TEST_NETUID_U16), + U256::from(TRANSFERRED_ALLOWANCE_RAO), + ), + ), + ) + .execute_returns(()); + + precompiles + .prepare_test( + spender, + precompile_addr, + encode_with_selector( + selector_u32( + "transferStakeFrom(address,address,bytes32,uint256,uint256,uint256)", + ), + ( + precompile_utils::solidity::codec::Address(source), + precompile_utils::solidity::codec::Address(spender), + H256::from_slice(hotkey.as_ref()), + U256::from(TEST_NETUID_U16), + U256::from(TEST_NETUID_U16), + U256::from(TRANSFERRED_ALLOWANCE_RAO + 1), + ), + ), + ) + .execute_reverts(|output| output == b"trying to spend more than allowed"); + }); + } + + #[test] + fn staking_precompile_v2_approval_functions_update_allowance() { + new_test_ext().execute_with(|| { + let (_, source, spender, _, _, _) = setup_approval_state(); + let precompiles = precompiles::>(); + let precompile_addr = addr_from_index(StakingPrecompileV2::::INDEX); + + assert_allowance(source, spender, source, U256::zero()); + + precompiles + .prepare_test( + source, + precompile_addr, + encode_with_selector( + selector_u32("approve(address,uint256,uint256)"), + ( + precompile_utils::solidity::codec::Address(spender), + U256::from(TEST_NETUID_U16), + U256::from(APPROVED_ALLOWANCE_RAO), + ), + ), + ) + .execute_returns(()); + assert_allowance(source, spender, source, U256::from(APPROVED_ALLOWANCE_RAO)); + + precompiles + .prepare_test( + source, + precompile_addr, + encode_with_selector( + selector_u32("increaseAllowance(address,uint256,uint256)"), + ( + precompile_utils::solidity::codec::Address(spender), + U256::from(TEST_NETUID_U16), + U256::from(APPROVED_ALLOWANCE_RAO), + ), + ), + ) + .execute_returns(()); + assert_allowance( + source, + spender, + source, + U256::from(APPROVED_ALLOWANCE_RAO * 2), + ); + + precompiles + .prepare_test( + source, + precompile_addr, + encode_with_selector( + selector_u32("decreaseAllowance(address,uint256,uint256)"), + ( + precompile_utils::solidity::codec::Address(spender), + U256::from(TEST_NETUID_U16), + U256::from(ALLOWANCE_DECREASE_RAO), + ), + ), + ) + .execute_returns(()); + assert_allowance( + source, + spender, + source, + U256::from(APPROVED_ALLOWANCE_RAO * 2 - ALLOWANCE_DECREASE_RAO), + ); + + precompiles + .prepare_test( + source, + precompile_addr, + encode_with_selector( + selector_u32("approve(address,uint256,uint256)"), + ( + precompile_utils::solidity::codec::Address(spender), + U256::from(TEST_NETUID_U16), + U256::zero(), + ), + ), + ) + .execute_returns(()); + assert_allowance(source, spender, source, U256::zero()); + }); + } + + #[test] + fn staking_precompile_v2_burn_alpha_reduces_stake() { + new_test_ext().execute_with(|| { + let netuid = setup_staking_subnet(); + let caller = addr_from_index(0x3001); + let caller_account = mapped_account(caller); + let hotkey = hotkey(); + let burn_amount = 20_000_000_000_u64; + let precompiles = precompiles::>(); + let precompile_addr = addr_from_index(StakingPrecompileV2::::INDEX); + + fund_account(&caller_account, COLDKEY_BALANCE); + add_stake_v2(caller, &hotkey, TEST_NETUID_U16, 50_000_000_000); + + let stake_before = stake_for(&hotkey, &caller_account, netuid); + assert!(stake_before > 0); + + precompiles + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("burnAlpha(bytes32,uint256,uint256)"), + ( + H256::from_slice(hotkey.as_ref()), + U256::from(burn_amount), + U256::from(TEST_NETUID_U16), + ), + ), + ) + .execute_returns(()); + + let stake_after = stake_for(&hotkey, &caller_account, netuid); + assert_eq!(stake_after, stake_before - burn_amount); + }); + } + + // cargo test --package subtensor-precompiles --lib -- staking::tests::staking_precompile_v2_burn_alpha_caps_to_available_stake --exact --nocapture + #[test] + fn staking_precompile_v2_burn_alpha_caps_to_available_stake() { + new_test_ext().execute_with(|| { + let netuid = setup_staking_subnet(); + let caller = addr_from_index(0x3002); + let caller_account = mapped_account(caller); + let hotkey = hotkey(); + let precompiles = precompiles::>(); + let precompile_addr = addr_from_index(StakingPrecompileV2::::INDEX); + + fund_account(&caller_account, COLDKEY_BALANCE); + add_stake_v2(caller, &hotkey, TEST_NETUID_U16, INITIAL_STAKE_RAO); + + let stake_before = stake_for(&hotkey, &caller_account, netuid); + assert!(stake_before > 0); + + precompiles + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("burnAlpha(bytes32,uint256,uint256)"), + ( + H256::from_slice(hotkey.as_ref()), + U256::from(stake_before + 10_000_000_000_u64), + U256::from(TEST_NETUID_U16), + ), + ), + ) + .execute_returns(()); + + let stake_after = stake_for(&hotkey, &caller_account, netuid); + assert_eq!(stake_after, 0); + }); + } + + #[test] + fn staking_precompile_v2_burn_alpha_rejects_missing_subnet() { + new_test_ext().execute_with(|| { + let caller = addr_from_index(0x3003); + let caller_account = mapped_account(caller); + let hotkey = hotkey(); + + fund_account(&caller_account, COLDKEY_BALANCE); + ensure_hotkey_exists(&hotkey); + + let rejected = execute_precompile( + &precompiles::>(), + addr_from_index(StakingPrecompileV2::::INDEX), + caller, + encode_with_selector( + selector_u32("burnAlpha(bytes32,uint256,uint256)"), + ( + H256::from_slice(hotkey.as_ref()), + U256::from(10_000_000_000_u64), + U256::from(INVALID_NETUID_U16), + ), + ), + U256::zero(), + ) + .expect("burnAlpha should route to the staking v2 precompile"); + + assert!(rejected.is_err()); + }); + } + + #[test] + fn staking_precompile_v2_burn_zero_alpha_is_noop() { + new_test_ext().execute_with(|| { + let netuid = setup_staking_subnet(); + let caller = addr_from_index(0x3004); + let caller_account = mapped_account(caller); + let hotkey = hotkey(); + let precompiles = precompiles::>(); + let precompile_addr = addr_from_index(StakingPrecompileV2::::INDEX); + + fund_account(&caller_account, COLDKEY_BALANCE); + add_stake_v2(caller, &hotkey, TEST_NETUID_U16, 10_000_000_000); + + let stake_before = stake_for(&hotkey, &caller_account, netuid); + + precompiles + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("burnAlpha(bytes32,uint256,uint256)"), + ( + H256::from_slice(hotkey.as_ref()), + U256::zero(), + U256::from(TEST_NETUID_U16), + ), + ), + ) + .execute_returns(()); + + let stake_after = stake_for(&hotkey, &caller_account, netuid); + assert_eq!(stake_after, stake_before); + }); + } +} diff --git a/precompiles/src/subnet.rs b/precompiles/src/subnet.rs index 79c08c2625..b89d972eea 100644 --- a/precompiles/src/subnet.rs +++ b/precompiles/src/subnet.rs @@ -782,3 +782,447 @@ where ) } } + +#[cfg(test)] +mod tests { + #![allow( + clippy::arithmetic_side_effects, + clippy::expect_used, + clippy::unwrap_used + )] + + use super::*; + use crate::PrecompileExt; + use crate::mock::{ + AccountId, Runtime, addr_from_index, assert_static_call, mapped_account, new_test_ext, + precompiles, selector_u32, + }; + use precompile_utils::solidity::encode_with_selector; + use precompile_utils::testing::PrecompileTesterExt; + use sp_core::{H160, H256, U256}; + use subtensor_runtime_common::TaoBalance; + + const TEST_NETUID_U16: u16 = 1; + const TEST_TEMPO: u16 = 100; + + fn setup_owner_subnet(caller: H160) -> NetUid { + let netuid = NetUid::from(TEST_NETUID_U16); + let owner = mapped_account(caller); + let owner_hotkey = AccountId::from([0x55; 32]); + + pallet_subtensor::Pallet::::init_new_network(netuid, TEST_TEMPO); + pallet_subtensor::SubnetOwner::::insert(netuid, owner); + pallet_subtensor::SubnetOwnerHotkey::::insert(netuid, owner_hotkey); + pallet_subtensor::AdminFreezeWindow::::set(0); + pallet_subtensor::OwnerHyperparamRateLimit::::set(0); + + netuid + } + + fn add_balance_to_coldkey_account(coldkey: &sp_core::crypto::AccountId32, tao: TaoBalance) { + let credit = pallet_subtensor::Pallet::::mint_tao(tao); + let _ = pallet_subtensor::Pallet::::spend_tao(coldkey, credit, tao).unwrap(); + } + + #[test] + fn subnet_precompile_registers_network_without_identity() { + new_test_ext().execute_with(|| { + let caller = addr_from_index(0x5000); + let caller_account = mapped_account(caller); + let hotkey = AccountId::from([0x44; 32]); + let precompiles = precompiles::>(); + let precompile_addr = addr_from_index(SubnetPrecompile::::INDEX); + + add_balance_to_coldkey_account(&caller_account, 1_000_000_000_000_u64.into()); + + let total_before = pallet_subtensor::TotalNetworks::::get(); + let netuid = pallet_subtensor::Pallet::::get_next_netuid(); + precompiles + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("registerNetwork(bytes32)"), + (H256::from_slice(hotkey.as_ref()),), + ), + ) + .execute_returns(()); + + let total_after = pallet_subtensor::TotalNetworks::::get(); + assert_eq!(total_after, total_before + 1); + assert_eq!( + pallet_subtensor::SubnetOwner::::get(netuid), + caller_account + ); + assert!(!pallet_subtensor::SubnetIdentitiesV3::::contains_key(netuid)); + }); + } + + #[test] + fn subnet_precompile_registers_network_with_identity() { + new_test_ext().execute_with(|| { + let caller = addr_from_index(0x5002); + let caller_account = mapped_account(caller); + let hotkey = AccountId::from([0x45; 32]); + let precompiles = precompiles::>(); + let precompile_addr = addr_from_index(SubnetPrecompile::::INDEX); + + add_balance_to_coldkey_account( + &caller_account, + 1_000_000_000_000_u64.into(), + ); + + let total_before = pallet_subtensor::TotalNetworks::::get(); + let netuid = pallet_subtensor::Pallet::::get_next_netuid(); + precompiles + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32( + "registerNetwork(bytes32,string,string,string,string,string,string,string)", + ), + ( + H256::from_slice(hotkey.as_ref()), + precompile_utils::solidity::codec::UnboundedString::from("name"), + precompile_utils::solidity::codec::UnboundedString::from("repo"), + precompile_utils::solidity::codec::UnboundedString::from("contact"), + precompile_utils::solidity::codec::UnboundedString::from("subnetUrl"), + precompile_utils::solidity::codec::UnboundedString::from("discord"), + precompile_utils::solidity::codec::UnboundedString::from("description"), + precompile_utils::solidity::codec::UnboundedString::from("additional"), + ), + ), + ) + .execute_returns(()); + + let total_after = pallet_subtensor::TotalNetworks::::get(); + assert_eq!(total_after, total_before + 1); + assert_eq!(pallet_subtensor::SubnetOwner::::get(netuid), caller_account); + assert!(pallet_subtensor::SubnetIdentitiesV3::::contains_key(netuid)); + }); + } + + #[test] + fn subnet_precompile_sets_and_gets_owner_hyperparameters() { + new_test_ext().execute_with(|| { + let caller = addr_from_index(0x5001); + let netuid = setup_owner_subnet(caller); + let precompiles = precompiles::>(); + let precompile_addr = addr_from_index(SubnetPrecompile::::INDEX); + + precompiles + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("setServingRateLimit(uint16,uint64)"), + (TEST_NETUID_U16, 100_u64), + ), + ) + .execute_returns(()); + assert_eq!( + pallet_subtensor::ServingRateLimit::::get(netuid), + 100 + ); + assert_static_call( + &precompiles, + caller, + precompile_addr, + encode_with_selector( + selector_u32("getServingRateLimit(uint16)"), + (TEST_NETUID_U16,), + ), + U256::from(100_u64), + ); + + precompiles + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("setMaxDifficulty(uint16,uint64)"), + (TEST_NETUID_U16, 102_u64), + ), + ) + .execute_returns(()); + assert_eq!(pallet_subtensor::MaxDifficulty::::get(netuid), 102); + assert_static_call( + &precompiles, + caller, + precompile_addr, + encode_with_selector(selector_u32("getMaxDifficulty(uint16)"), (TEST_NETUID_U16,)), + U256::from(102_u64), + ); + + precompiles + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("setWeightsVersionKey(uint16,uint64)"), + (TEST_NETUID_U16, 103_u64), + ), + ) + .execute_returns(()); + assert_eq!( + pallet_subtensor::WeightsVersionKey::::get(netuid), + 103 + ); + assert_static_call( + &precompiles, + caller, + precompile_addr, + encode_with_selector( + selector_u32("getWeightsVersionKey(uint16)"), + (TEST_NETUID_U16,), + ), + U256::from(103_u64), + ); + + precompiles + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("setAdjustmentAlpha(uint16,uint64)"), + (TEST_NETUID_U16, 105_u64), + ), + ) + .execute_returns(()); + assert_eq!( + pallet_subtensor::AdjustmentAlpha::::get(netuid), + 105 + ); + assert_static_call( + &precompiles, + caller, + precompile_addr, + encode_with_selector( + selector_u32("getAdjustmentAlpha(uint16)"), + (TEST_NETUID_U16,), + ), + U256::from(105_u64), + ); + + assert_static_call( + &precompiles, + caller, + precompile_addr, + encode_with_selector( + selector_u32("getMaxWeightLimit(uint16)"), + (TEST_NETUID_U16,), + ), + U256::from(0xFFFF_u64), + ); + + precompiles + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("setImmunityPeriod(uint16,uint16)"), + (TEST_NETUID_U16, 107_u16), + ), + ) + .execute_returns(()); + assert_eq!( + pallet_subtensor::ImmunityPeriod::::get(netuid), + 107 + ); + assert_static_call( + &precompiles, + caller, + precompile_addr, + encode_with_selector( + selector_u32("getImmunityPeriod(uint16)"), + (TEST_NETUID_U16,), + ), + U256::from(107_u64), + ); + + precompiles + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("setMinAllowedWeights(uint16,uint16)"), + (TEST_NETUID_U16, 108_u16), + ), + ) + .execute_returns(()); + assert_eq!( + pallet_subtensor::MinAllowedWeights::::get(netuid), + 108 + ); + assert_static_call( + &precompiles, + caller, + precompile_addr, + encode_with_selector( + selector_u32("getMinAllowedWeights(uint16)"), + (TEST_NETUID_U16,), + ), + U256::from(108_u64), + ); + + precompiles + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("setRho(uint16,uint16)"), + (TEST_NETUID_U16, 110_u16), + ), + ) + .execute_returns(()); + assert_eq!(pallet_subtensor::Rho::::get(netuid), 110); + assert_static_call( + &precompiles, + caller, + precompile_addr, + encode_with_selector(selector_u32("getRho(uint16)"), (TEST_NETUID_U16,)), + U256::from(110_u64), + ); + + let activity_cutoff = pallet_subtensor::MinActivityCutoff::::get() + 1; + precompiles + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("setActivityCutoff(uint16,uint16)"), + (TEST_NETUID_U16, activity_cutoff), + ), + ) + .execute_returns(()); + assert_eq!( + pallet_subtensor::ActivityCutoff::::get(netuid), + activity_cutoff + ); + assert_static_call( + &precompiles, + caller, + precompile_addr, + encode_with_selector( + selector_u32("getActivityCutoff(uint16)"), + (TEST_NETUID_U16,), + ), + U256::from(activity_cutoff), + ); + + precompiles + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("setBondsMovingAverage(uint16,uint64)"), + (TEST_NETUID_U16, 115_u64), + ), + ) + .execute_returns(()); + assert_eq!( + pallet_subtensor::BondsMovingAverage::::get(netuid), + 115 + ); + assert_static_call( + &precompiles, + caller, + precompile_addr, + encode_with_selector( + selector_u32("getBondsMovingAverage(uint16)"), + (TEST_NETUID_U16,), + ), + U256::from(115_u64), + ); + + precompiles + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("setCommitRevealWeightsEnabled(uint16,bool)"), + (TEST_NETUID_U16, true), + ), + ) + .execute_returns(()); + assert!(pallet_subtensor::CommitRevealWeightsEnabled::::get(netuid)); + assert_static_call( + &precompiles, + caller, + precompile_addr, + encode_with_selector( + selector_u32("getCommitRevealWeightsEnabled(uint16)"), + (TEST_NETUID_U16,), + ), + U256::one(), + ); + + precompiles + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("setLiquidAlphaEnabled(uint16,bool)"), + (TEST_NETUID_U16, true), + ), + ) + .execute_returns(()); + assert!(pallet_subtensor::LiquidAlphaOn::::get(netuid)); + assert_static_call( + &precompiles, + caller, + precompile_addr, + encode_with_selector( + selector_u32("getLiquidAlphaEnabled(uint16)"), + (TEST_NETUID_U16,), + ), + U256::one(), + ); + + precompiles + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("setYuma3Enabled(uint16,bool)"), + (TEST_NETUID_U16, true), + ), + ) + .execute_returns(()); + assert!(pallet_subtensor::Yuma3On::::get(netuid)); + assert_static_call( + &precompiles, + caller, + precompile_addr, + encode_with_selector(selector_u32("getYuma3Enabled(uint16)"), (TEST_NETUID_U16,)), + U256::one(), + ); + + precompiles + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("setCommitRevealWeightsInterval(uint16,uint64)"), + (TEST_NETUID_U16, 99_u64), + ), + ) + .execute_returns(()); + assert_eq!( + pallet_subtensor::RevealPeriodEpochs::::get(netuid), + 99 + ); + assert_static_call( + &precompiles, + caller, + precompile_addr, + encode_with_selector( + selector_u32("getCommitRevealWeightsInterval(uint16)"), + (TEST_NETUID_U16,), + ), + U256::from(99_u64), + ); + }); + } +} diff --git a/precompiles/src/voting_power.rs b/precompiles/src/voting_power.rs index 74e1731b6e..af7896dac1 100644 --- a/precompiles/src/voting_power.rs +++ b/precompiles/src/voting_power.rs @@ -129,3 +129,136 @@ where Ok(U256::from(total)) } } + +#[cfg(test)] +mod tests { + #![allow(clippy::arithmetic_side_effects)] + + use super::*; + use crate::PrecompileExt; + use crate::mock::{ + AccountId, Runtime, addr_from_index, assert_static_call, new_test_ext, precompiles, + selector_u32, + }; + use precompile_utils::solidity::encode_with_selector; + use sp_core::{H160, H256, U256}; + + const TEST_NETUID_U16: u16 = 1; + const TEST_TEMPO: u16 = 100; + const DEFAULT_ALPHA: u64 = 3_570_000_000_000_000; + + fn setup_subnet() -> NetUid { + let netuid = NetUid::from(TEST_NETUID_U16); + pallet_subtensor::Pallet::::init_new_network(netuid, TEST_TEMPO); + netuid + } + + fn hotkey(byte: u8) -> AccountId { + AccountId::from([byte; 32]) + } + + fn assert_voting_power_call( + caller: H160, + signature: &str, + args: impl precompile_utils::solidity::Codec, + expected: U256, + ) { + assert_static_call( + &precompiles::>(), + caller, + addr_from_index(VotingPowerPrecompile::::INDEX), + encode_with_selector(selector_u32(signature), args), + expected, + ); + } + + #[test] + fn voting_power_precompile_returns_default_zero_values() { + new_test_ext().execute_with(|| { + let netuid = setup_subnet(); + let caller = addr_from_index(0x6001); + let existing_hotkey = hotkey(0x11); + let unknown_hotkey = hotkey(0x22); + + assert!(!pallet_subtensor::VotingPowerTrackingEnabled::::get(netuid)); + assert_voting_power_call( + caller, + "isVotingPowerTrackingEnabled(uint16)", + (TEST_NETUID_U16,), + U256::zero(), + ); + assert_voting_power_call( + caller, + "getVotingPowerDisableAtBlock(uint16)", + (TEST_NETUID_U16,), + U256::zero(), + ); + assert_voting_power_call( + caller, + "getVotingPowerEmaAlpha(uint16)", + (TEST_NETUID_U16,), + U256::from(DEFAULT_ALPHA), + ); + assert_voting_power_call( + caller, + "getVotingPower(uint16,bytes32)", + ( + TEST_NETUID_U16, + H256::from_slice(existing_hotkey.as_slice()), + ), + U256::zero(), + ); + assert_voting_power_call( + caller, + "getVotingPower(uint16,bytes32)", + (TEST_NETUID_U16, H256::from_slice(unknown_hotkey.as_slice())), + U256::zero(), + ); + assert_voting_power_call( + caller, + "getTotalVotingPower(uint16)", + (TEST_NETUID_U16,), + U256::zero(), + ); + }); + } + + #[test] + fn voting_power_precompile_reads_enabled_tracking_and_stored_power() { + new_test_ext().execute_with(|| { + let netuid = setup_subnet(); + let caller = addr_from_index(0x6002); + let first_hotkey = hotkey(0x33); + let second_hotkey = hotkey(0x44); + + pallet_subtensor::VotingPowerTrackingEnabled::::insert(netuid, true); + pallet_subtensor::VotingPower::::insert(netuid, &first_hotkey, 123_u64); + pallet_subtensor::VotingPower::::insert(netuid, &second_hotkey, 456_u64); + + assert_voting_power_call( + caller, + "isVotingPowerTrackingEnabled(uint16)", + (TEST_NETUID_U16,), + U256::one(), + ); + assert_voting_power_call( + caller, + "getVotingPowerDisableAtBlock(uint16)", + (TEST_NETUID_U16,), + U256::zero(), + ); + assert_voting_power_call( + caller, + "getVotingPower(uint16,bytes32)", + (TEST_NETUID_U16, H256::from_slice(first_hotkey.as_slice())), + U256::from(123_u64), + ); + assert_voting_power_call( + caller, + "getTotalVotingPower(uint16)", + (TEST_NETUID_U16,), + U256::from(579_u64), + ); + }); + } +} diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index e163661a8d..48269f5eb5 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -25,6 +25,7 @@ safe-math.workspace = true scale-info = { workspace = true, features = ["derive"] } serde_json = { workspace = true, features = ["alloc"] } pallet-aura = { workspace = true } +pallet-alpha-assets = { workspace = true } pallet-balances = { workspace = true } pallet-subtensor = { workspace = true } pallet-subtensor-swap = { workspace = true } @@ -186,6 +187,7 @@ std = [ "frame-system/std", "frame-try-runtime/std", "pallet-subtensor/std", + "pallet-alpha-assets/std", "pallet-balances/std", "pallet-grandpa/std", "pallet-insecure-randomness-collective-flip/std", diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 7c4112837d..00d3839fa7 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -73,7 +73,7 @@ use sp_std::prelude::*; use sp_version::NativeVersion; use sp_version::RuntimeVersion; use stp_shield::ShieldedTransaction; -use substrate_fixed::types::U96F32; +use substrate_fixed::types::{U64F64, U96F32}; use subtensor_precompiles::Precompiles; use subtensor_runtime_common::{AlphaBalance, AuthorshipInfo, TaoBalance, time::*, *}; use subtensor_swap_interface::{Order, SwapHandler}; @@ -272,7 +272,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: 401, + spec_version: 406, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, @@ -379,7 +379,7 @@ impl frame_system::Config for Runtime { type MaxConsumers = frame_support::traits::ConstU32<16>; type Nonce = Nonce; type Block = Block; - type SingleBlockMigrations = Migrations; + type SingleBlockMigrations = (); type MultiBlockMigrator = (); type PreInherents = (); type PostInherents = (); @@ -526,6 +526,8 @@ impl pallet_balances::Config for Runtime { type DoneSlashHandler = (); } +impl pallet_alpha_assets::Config for Runtime {} + // Implement AuthorshipInfo trait for Runtime to satisfy pallet transaction // fee OnUnbalanced trait bounds pub struct BlockAuthorFromAura(core::marker::PhantomData); @@ -1122,10 +1124,12 @@ parameter_types! { // 0 days pub const InitialStartCallDelay: u64 = 0; pub const SubtensorInitialKeySwapOnSubnetCost: TaoBalance = TaoBalance::new(1_000_000); // 0.001 TAO - pub const HotkeySwapOnSubnetInterval : BlockNumber = 24 * 60 * 60 / 12; // 1 day + pub const HotkeySwapOnSubnetInterval : BlockNumber = prod_or_fast!(24 * 60 * 60 / 12, 1); // 1 day pub const LeaseDividendsDistributionInterval: BlockNumber = 100; // 100 blocks pub const MaxImmuneUidsPercentage: Percent = Percent::from_percent(80); pub const EvmKeyAssociateRateLimit: u64 = EVM_KEY_ASSOCIATE_RATELIMIT; + pub const SubtensorPalletId: PalletId = PalletId(*b"subtensr"); + pub const BurnAccountId: PalletId = PalletId(*b"burntnsr"); } impl pallet_subtensor::Config for Runtime { @@ -1199,8 +1203,11 @@ impl pallet_subtensor::Config for Runtime { type GetCommitments = GetCommitmentsStruct; type MaxImmuneUidsPercentage = MaxImmuneUidsPercentage; type CommitmentsInterface = CommitmentsI; + type AlphaAssets = AlphaAssets; type EvmKeyAssociateRateLimit = EvmKeyAssociateRateLimit; type AuthorshipProvider = BlockAuthorFromAura; + type SubtensorPalletId = SubtensorPalletId; + type BurnAccountId = BurnAccountId; type WeightInfo = pallet_subtensor::weights::SubstrateWeight; } @@ -1679,6 +1686,7 @@ construct_runtime!( Swap: pallet_subtensor_swap = 28, Contracts: pallet_contracts = 29, MevShield: pallet_shield = 30, + AlphaAssets: pallet_alpha_assets = 31, } ); @@ -2510,6 +2518,10 @@ impl_runtime_apis! { fn get_selective_mechagraph(netuid: NetUid, mecid: MechId, metagraph_indexes: Vec) -> Option> { SubtensorModule::get_selective_mechagraph(netuid, mecid, metagraph_indexes) } + + fn get_subnet_account_id(netuid: NetUid) -> Option { + SubtensorModule::get_subnet_account_id(netuid) + } } impl subtensor_custom_rpc_runtime_api::StakeInfoRuntimeApi for Runtime { @@ -2528,6 +2540,14 @@ impl_runtime_apis! { fn get_stake_fee( origin: Option<(AccountId32, NetUid)>, origin_coldkey_account: AccountId32, destination: Option<(AccountId32, NetUid)>, destination_coldkey_account: AccountId32, amount: u64 ) -> u64 { SubtensorModule::get_stake_fee( origin, origin_coldkey_account, destination, destination_coldkey_account, amount ) } + + fn get_hotkey_conviction(hotkey: AccountId32, netuid: NetUid) -> U64F64 { + SubtensorModule::hotkey_conviction(&hotkey, netuid) + } + + fn get_most_convicted_hotkey_on_subnet(netuid: NetUid) -> Option { + SubtensorModule::subnet_king(netuid) + } } impl subtensor_custom_rpc_runtime_api::SubnetRegistrationRuntimeApi for Runtime { diff --git a/runtime/tests/account_conversion.rs b/runtime/tests/account_conversion.rs new file mode 100644 index 0000000000..11aad3da85 --- /dev/null +++ b/runtime/tests/account_conversion.rs @@ -0,0 +1,53 @@ +#![allow(clippy::unwrap_used)] + +use node_subtensor_runtime::{BuildStorage, RuntimeGenesisConfig, SubtensorModule, System}; +use subtensor_runtime_common::NetUid; + +fn new_test_ext() -> sp_io::TestExternalities { + sp_tracing::try_init_simple(); + let mut ext: sp_io::TestExternalities = RuntimeGenesisConfig { + balances: pallet_balances::GenesisConfig { + balances: vec![], + dev_accounts: None, + }, + ..Default::default() + } + .build_storage() + .unwrap() + .into(); + ext.execute_with(|| System::set_block_number(1)); + ext +} + +/// Test full-range netuids on real ss58 accounts to ensure no panics +/// cargo test --package node-subtensor-runtime --test account_conversion -- test_subnet_account_id_no_panics --exact --nocapture +#[test] +#[ignore] +fn test_subnet_account_id_no_panics() { + new_test_ext().execute_with(|| { + for raw_netuid in 0u16..=u16::MAX { + let netuid = NetUid::from(raw_netuid); + SubtensorModule::init_new_network(netuid, 10); + + let account_id = SubtensorModule::get_subnet_account_id(netuid).unwrap(); + let roudtrip_netuid = SubtensorModule::is_subnet_account_id(&account_id); + assert_eq!(netuid, roudtrip_netuid.unwrap()); + } + }); +} + +/// Quick sanity test +/// cargo test --package node-subtensor-runtime --test account_conversion -- test_subnet_account_id_no_panics_quick --exact --nocapture +#[test] +fn test_subnet_account_id_no_panics_quick() { + new_test_ext().execute_with(|| { + for raw_netuid in 0u16..=1024u16 { + let netuid = NetUid::from(raw_netuid); + SubtensorModule::init_new_network(netuid, 10); + + let account_id = SubtensorModule::get_subnet_account_id(netuid).unwrap(); + let roudtrip_netuid = SubtensorModule::is_subnet_account_id(&account_id); + assert_eq!(netuid, roudtrip_netuid.unwrap()); + } + }); +} diff --git a/runtime/tests/precompiles.rs b/runtime/tests/precompiles.rs index 519e434533..91c7e358a6 100644 --- a/runtime/tests/precompiles.rs +++ b/runtime/tests/precompiles.rs @@ -10,6 +10,7 @@ use sp_core::{H160, H256, U256}; use sp_runtime::traits::Hash; use std::collections::BTreeSet; use subtensor_precompiles::{BalanceTransferPrecompile, PrecompileExt, Precompiles}; +use subtensor_runtime_common::TaoBalance; type AccountId = ::AccountId; @@ -22,6 +23,11 @@ fn new_test_ext() -> sp_io::TestExternalities { ext } +fn add_balance_to_coldkey_account(coldkey: &sp_core::crypto::AccountId32, tao: TaoBalance) { + let credit = pallet_subtensor::Pallet::::mint_tao(tao); + let _ = pallet_subtensor::Pallet::::spend_tao(coldkey, credit, tao).unwrap(); +} + fn execute_precompile( precompiles: &Precompiles, precompile_address: H160, @@ -73,10 +79,7 @@ fn balance_transfer_precompile_transfers_balance() { let destination_account: AccountId = destination_raw.0.into(); let amount = 123_456; - pallet_subtensor::Pallet::::add_balance_to_coldkey_account( - &dispatch_account, - (amount * 2).into(), - ); + add_balance_to_coldkey_account(&dispatch_account, (amount * 2).into()); let source_balance_before = pallet_balances::Pallet::::free_balance(&dispatch_account); @@ -117,10 +120,7 @@ fn balance_transfer_precompile_respects_dispatch_guard_policy() { let destination_account: AccountId = destination_raw.0.into(); let amount = 100; - pallet_subtensor::Pallet::::add_balance_to_coldkey_account( - &dispatch_account, - 1_000_000_u64.into(), - ); + add_balance_to_coldkey_account(&dispatch_account, 1_000_000_u64.into()); let replacement_coldkey = AccountId::from([9u8; 32]); let replacement_hash = diff --git a/ts-tests/suites/zombienet_staking/02.04-claim-root-hotkey-swap.test.ts b/ts-tests/suites/zombienet_staking/02.04-claim-root-hotkey-swap.test.ts new file mode 100644 index 0000000000..0124bae671 --- /dev/null +++ b/ts-tests/suites/zombienet_staking/02.04-claim-root-hotkey-swap.test.ts @@ -0,0 +1,285 @@ +import { expect, beforeAll } from "vitest"; +import { + addNewSubnetwork, + addStake, + burnedRegister, + forceSetBalance, + generateKeyringPair, + getRootClaimable, + startCall, + sudoSetAdminFreezeWindow, + sudoSetEmaPriceHalvingPeriod, + sudoSetLockReductionInterval, + sudoSetRootClaimThreshold, + sudoSetSubnetMovingAlpha, + sudoSetSubtokenEnabled, + sudoSetTempo, + tao, + waitForBlocks, +} from "../../utils"; +import { subtensor } from "@polkadot-api/descriptors"; +import type { TypedApi } from "polkadot-api"; +import { swapHotkey } from "../../utils/swap.ts"; +import { describeSuite } from "@moonwall/cli"; +import type { KeyringPair } from "@moonwall/util"; + +// Shared setup: creates two subnets, registers oldHotkey on both, +// stakes on ROOT and both subnets, waits for RootClaimable to accumulate. +async function setupTwoSubnetsWithClaimable( + api: TypedApi, + ROOT_NETUID: number, + log: (msg: string) => void +): Promise<{ + oldHotkey: KeyringPair; + oldHotkeyColdkey: KeyringPair; + newHotkey: KeyringPair; + netuid1: number; + netuid2: number; +}> { + const oldHotkey = generateKeyringPair("sr25519"); + const oldHotkeyColdkey = generateKeyringPair("sr25519"); + const newHotkey = generateKeyringPair("sr25519"); + const owner1Hotkey = generateKeyringPair("sr25519"); + const owner1Coldkey = generateKeyringPair("sr25519"); + const owner2Hotkey = generateKeyringPair("sr25519"); + const owner2Coldkey = generateKeyringPair("sr25519"); + + for (const kp of [ + oldHotkey, + oldHotkeyColdkey, + newHotkey, + owner1Hotkey, + owner1Coldkey, + owner2Hotkey, + owner2Coldkey, + ]) { + await forceSetBalance(api, kp.address); + } + + await sudoSetAdminFreezeWindow(api, 0); + await sudoSetSubtokenEnabled(api, ROOT_NETUID, true); + + const netuid1 = await addNewSubnetwork(api, owner1Hotkey, owner1Coldkey); + await startCall(api, netuid1, owner1Coldkey); + log(`Created netuid1: ${netuid1}`); + + const netuid2 = await addNewSubnetwork(api, owner2Hotkey, owner2Coldkey); + await startCall(api, netuid2, owner2Coldkey); + log(`Created netuid2: ${netuid2}`); + + for (const netuid of [netuid1, netuid2]) { + await sudoSetTempo(api, netuid, 1); + await sudoSetEmaPriceHalvingPeriod(api, netuid, 1); + await sudoSetRootClaimThreshold(api, netuid, 0n); + } + await sudoSetSubnetMovingAlpha(api, BigInt(4294967296)); + + // Register oldHotkey on both subnets so it appears in epoch hotkey_emission + // and receives root_alpha_dividends → RootClaimable on both netuids + await burnedRegister(api, netuid1, oldHotkey.address, oldHotkeyColdkey); + log("oldHotkey registered on netuid1"); + await burnedRegister(api, netuid2, oldHotkey.address, oldHotkeyColdkey); + log("oldHotkey registered on netuid2"); + + // ROOT stake drives root_alpha_dividends for oldHotkey + await addStake(api, oldHotkeyColdkey, oldHotkey.address, ROOT_NETUID, tao(100)); + log("Added ROOT stake for oldHotkey"); + + await addStake(api, oldHotkeyColdkey, oldHotkey.address, netuid1, tao(50)); + await addStake(api, oldHotkeyColdkey, oldHotkey.address, netuid2, tao(50)); + + await addStake(api, owner1Coldkey, owner1Hotkey.address, netuid1, tao(50)); + await addStake(api, owner2Coldkey, owner2Hotkey.address, netuid2, tao(50)); + + log("Waiting 30 blocks for RootClaimable to accumulate on both subnets..."); + await waitForBlocks(api, 30); + + return { oldHotkey, oldHotkeyColdkey, newHotkey, netuid1, netuid2 }; +} + +describeSuite({ + id: "0203_swap_hotkey_root_claimable", + title: "▶ swap_hotkey RootClaimable per-subnet transfer", + foundationMethods: "zombie", + testCases: ({ it, context, log }) => { + let api: TypedApi; + const ROOT_NETUID = 0; + + beforeAll(async () => { + api = context.papi("Node").getTypedApi(subtensor); + await sudoSetLockReductionInterval(api, 1); + }); + + it({ + id: "T01", + title: "single-subnet swap doesn't move root claimable if it is not root", + test: async () => { + const { oldHotkey, oldHotkeyColdkey, newHotkey, netuid1, netuid2 } = await setupTwoSubnetsWithClaimable( + api, + ROOT_NETUID, + log + ); + + const claimableMapBefore = await getRootClaimable(api, oldHotkey.address); + log( + `RootClaimable[oldHotkey] before swap: ${ + [...claimableMapBefore.entries()].map(([k, v]) => `netuid${k}=${v}`).join(", ") || "(none)" + }` + ); + + expect( + claimableMapBefore.get(netuid1) ?? 0n, + "oldHotkey should have RootClaimable on netuid1 before swap" + ).toBeGreaterThan(0n); + expect( + claimableMapBefore.get(netuid2) ?? 0n, + "oldHotkey should have RootClaimable on netuid2 before swap" + ).toBeGreaterThan(0n); + expect( + (await getRootClaimable(api, newHotkey.address)).size, + "newHotkey should have no RootClaimable before swap" + ).toBe(0); + + // Swap oldHotkey → newHotkey on netuid1 ONLY + log(`Swapping oldHotkey → newHotkey on netuid1=${netuid1} only...`); + await swapHotkey(api, oldHotkeyColdkey, oldHotkey.address, newHotkey.address, netuid1); + log("Swap done"); + + const oldAfter = await getRootClaimable(api, oldHotkey.address); + const newAfter = await getRootClaimable(api, newHotkey.address); + + log( + `RootClaimable[oldHotkey] after swap: netuid1=${oldAfter.get(netuid1) ?? 0n}, netuid2=${oldAfter.get(netuid2) ?? 0n}` + ); + log( + `RootClaimable[newHotkey] after swap: netuid1=${newAfter.get(netuid1) ?? 0n}, netuid2=${newAfter.get(netuid2) ?? 0n}` + ); + + expect(newAfter.get(netuid1) ?? 0n, "newHotkey should not have RootClaimable for netuid1").toEqual(0n); + expect( + oldAfter.get(netuid1) ?? 0n, + "oldHotkey should retain RootClaimable for netuid1" + ).toBeGreaterThan(0n); + + expect( + oldAfter.get(netuid2) ?? 0n, + "oldHotkey should retain RootClaimable for netuid2" + ).toBeGreaterThan(0n); + expect(newAfter.get(netuid2) ?? 0n, "newHotkey should have no RootClaimable for netuid2").toBe(0n); + + log( + "✅ Single-subnet swap doesn't transfer RootClaimable for the subnet if it was done for non-root subnet" + ); + }, + }); + + it({ + id: "T02", + title: "full swap (no netuid) moves RootClaimable for all subnets to newHotkey", + test: async () => { + const { oldHotkey, oldHotkeyColdkey, newHotkey, netuid1, netuid2 } = await setupTwoSubnetsWithClaimable( + api, + ROOT_NETUID, + log + ); + + const claimableMapBefore = await getRootClaimable(api, oldHotkey.address); + log( + `RootClaimable[oldHotkey] before swap: ${ + [...claimableMapBefore.entries()].map(([k, v]) => `netuid${k}=${v}`).join(", ") || "(none)" + }` + ); + + expect( + claimableMapBefore.get(netuid1) ?? 0n, + "oldHotkey should have RootClaimable on netuid1 before swap" + ).toBeGreaterThan(0n); + expect( + claimableMapBefore.get(netuid2) ?? 0n, + "oldHotkey should have RootClaimable on netuid2 before swap" + ).toBeGreaterThan(0n); + + // Full swap — no netuid + log("Swapping oldHotkey → newHotkey on ALL subnets..."); + await swapHotkey(api, oldHotkeyColdkey, oldHotkey.address, newHotkey.address); + log("Swap done"); + + const oldAfter = await getRootClaimable(api, oldHotkey.address); + const newAfter = await getRootClaimable(api, newHotkey.address); + + log( + `RootClaimable[oldHotkey] after swap: netuid1=${oldAfter.get(netuid1) ?? 0n}, netuid2=${oldAfter.get(netuid2) ?? 0n}` + ); + log( + `RootClaimable[newHotkey] after swap: netuid1=${newAfter.get(netuid1) ?? 0n}, netuid2=${newAfter.get(netuid2) ?? 0n}` + ); + + expect(newAfter.get(netuid1) ?? 0n, "newHotkey should have RootClaimable for netuid1").toBeGreaterThan( + 0n + ); + expect(newAfter.get(netuid2) ?? 0n, "newHotkey should have RootClaimable for netuid2").toBeGreaterThan( + 0n + ); + + expect(oldAfter.get(netuid1) ?? 0n, "oldHotkey should have no RootClaimable for netuid1").toBe(0n); + expect(oldAfter.get(netuid2) ?? 0n, "oldHotkey should have no RootClaimable for netuid2").toBe(0n); + + log("✅ Full swap correctly transferred RootClaimable for both subnets to newHotkey"); + }, + }); + + it({ + id: "T03", + title: "single-subnet swap moves root claimable if it is root", + test: async () => { + const { oldHotkey, oldHotkeyColdkey, newHotkey, netuid1, netuid2 } = await setupTwoSubnetsWithClaimable( + api, + ROOT_NETUID, + log + ); + + const claimableMapBefore = await getRootClaimable(api, oldHotkey.address); + log( + `RootClaimable[oldHotkey] before swap: ${ + [...claimableMapBefore.entries()].map(([k, v]) => `netuid${k}=${v}`).join(", ") || "(none)" + }` + ); + + expect( + claimableMapBefore.get(netuid1) ?? 0n, + "oldHotkey should have RootClaimable on netuid1 before swap" + ).toBeGreaterThan(0n); + expect( + claimableMapBefore.get(netuid2) ?? 0n, + "oldHotkey should have RootClaimable on netuid2 before swap" + ).toBeGreaterThan(0n); + + log("Swapping oldHotkey → newHotkey for root subnet..."); + await swapHotkey(api, oldHotkeyColdkey, oldHotkey.address, newHotkey.address, 0); + log("Swap done"); + + const oldAfter = await getRootClaimable(api, oldHotkey.address); + const newAfter = await getRootClaimable(api, newHotkey.address); + + log( + `RootClaimable[oldHotkey] after swap: netuid1=${oldAfter.get(netuid1) ?? 0n}, netuid2=${oldAfter.get(netuid2) ?? 0n}` + ); + log( + `RootClaimable[newHotkey] after swap: netuid1=${newAfter.get(netuid1) ?? 0n}, netuid2=${newAfter.get(netuid2) ?? 0n}` + ); + + expect(newAfter.get(netuid1) ?? 0n, "newHotkey should have RootClaimable for netuid1").toBeGreaterThan( + 0n + ); + expect(newAfter.get(netuid2) ?? 0n, "newHotkey should have RootClaimable for netuid2").toBeGreaterThan( + 0n + ); + + expect(oldAfter.get(netuid1) ?? 0n, "oldHotkey should have no RootClaimable for netuid1").toBe(0n); + expect(oldAfter.get(netuid2) ?? 0n, "oldHotkey should have no RootClaimable for netuid2").toBe(0n); + + log("✅ Single swap correctly transferred RootClaimable if it is done for root subnet"); + }, + }); + }, +}); diff --git a/ts-tests/utils/swap.ts b/ts-tests/utils/swap.ts new file mode 100644 index 0000000000..78086792a5 --- /dev/null +++ b/ts-tests/utils/swap.ts @@ -0,0 +1,19 @@ +import { waitForTransactionWithRetry } from "./transactions.js"; +import type { KeyringPair } from "@moonwall/util"; +import type { subtensor } from "@polkadot-api/descriptors"; +import type { TypedApi } from "polkadot-api"; + +export async function swapHotkey( + api: TypedApi, + coldkey: KeyringPair, + oldHotkey: string, + newHotkey: string, + netuid?: number +): Promise { + const tx = api.tx.SubtensorModule.swap_hotkey({ + hotkey: oldHotkey, + new_hotkey: newHotkey, + netuid: netuid ?? undefined, + }); + await waitForTransactionWithRetry(api, tx, coldkey, "swap_hotkey"); +}