From da23e439e1a8d0d4b20c64d1c97012f068fcef4b Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Wed, 5 Nov 2025 13:06:23 -0500 Subject: [PATCH 001/240] Make max number of subnet mechanisms configurable --- pallets/admin-utils/src/lib.rs | 14 ++++++++++++++ pallets/subtensor/src/lib.rs | 6 +++++- pallets/subtensor/src/subnets/mechanism.rs | 18 +++++++++++++++++- 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 01e9e7b33e..a96824bfbf 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -2186,6 +2186,20 @@ pub mod pallet { log::debug!("set_tao_flow_smoothing_factor( {smoothing_factor:?} ) "); Ok(()) } + + /// Sets the global maximum number of mechanisms in a subnet + #[pallet::call_index(84)] + #[pallet::weight(Weight::from_parts(15_000_000, 0) + .saturating_add(::DbWeight::get().reads(1_u64)) + .saturating_add(::DbWeight::get().writes(1_u64)))] + pub fn sudo_set_max_mechanism_count( + origin: OriginFor, + max_mechanism_count: MechId, + ) -> DispatchResult { + ensure_root(origin)?; + pallet_subtensor::Pallet::::do_set_max_mechanism_count(max_mechanism_count)?; + Ok(()) + } } } diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 5df8a9c429..5fbdd1e971 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -2041,9 +2041,13 @@ pub mod pallet { } #[pallet::type_value] /// -- ITEM (Maximum number of sub-subnets) - pub fn MaxMechanismCount() -> MechId { + pub fn DefaultMaxMechanismCount() -> MechId { MechId::from(2) } + #[pallet::storage] + /// ITEM( max_mechanism_count ) + pub type MaxMechanismCount = + StorageValue<_, MechId, ValueQuery, DefaultMaxMechanismCount>; #[pallet::type_value] /// -- ITEM (Rate limit for mechanism count updates) pub fn MechanismCountSetRateLimit() -> u64 { diff --git a/pallets/subtensor/src/subnets/mechanism.rs b/pallets/subtensor/src/subnets/mechanism.rs index 6598c308f2..727c0bd3a6 100644 --- a/pallets/subtensor/src/subnets/mechanism.rs +++ b/pallets/subtensor/src/subnets/mechanism.rs @@ -95,7 +95,7 @@ impl Pallet { Ok(()) } - /// Set the desired valus of sub-subnet count for a subnet identified + /// Set the desired value of sub-subnet count for a subnet identified /// by netuid pub fn do_set_mechanism_count(netuid: NetUid, mechanism_count: MechId) -> DispatchResult { // Make sure the subnet exists @@ -124,6 +124,22 @@ impl Pallet { Ok(()) } + /// Set the global maximum number of mechanisms per subnet + pub fn do_set_max_mechanism_count(max_mechanism_count: MechId) -> DispatchResult { + // Max count cannot be zero + ensure!(max_mechanism_count > 0.into(), Error::::InvalidValue); + + // Make sure we are not allowing numbers that will break the math + ensure!( + max_mechanism_count <= MechId::from(MAX_MECHANISM_COUNT_PER_SUBNET), + Error::::InvalidValue + ); + + MaxMechanismCount::::set(max_mechanism_count); + + Ok(()) + } + /// Update current count for a subnet identified by netuid /// - Cleans up all sub-subnet maps if count is reduced /// From 7f1a809de806dce9e1d64114f88e724ec5f1db09 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Wed, 5 Nov 2025 13:08:49 -0500 Subject: [PATCH 002/240] Spec bump --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 266a755708..d95eb45ee9 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 338, + spec_version: 339, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From a6da90ebaa6cdae64878df74a95a5bad81d773a4 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Wed, 12 Nov 2025 13:06:04 -0500 Subject: [PATCH 003/240] Add enforcing the mechanism count vs. max uids limit --- pallets/admin-utils/src/lib.rs | 6 ++++++ pallets/admin-utils/src/tests/mod.rs | 23 ++++++++++++++++++++++ pallets/subtensor/src/macros/errors.rs | 2 ++ pallets/subtensor/src/subnets/mechanism.rs | 18 +++++++++++++++++ 4 files changed, 49 insertions(+) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 08e4c1e58a..7bdf12ec3f 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -536,6 +536,12 @@ pub mod pallet { max_allowed_uids <= DefaultMaxAllowedUids::::get(), Error::::MaxAllowedUidsGreaterThanDefaultMaxAllowedUids ); + // Prevent chain bloat: Require max UIDs to be limited + let mechanism_count = pallet_subtensor::MechanismCountCurrent::::get(netuid); + pallet_subtensor::Pallet::::check_max_uids_vs_mechanism_count( + max_allowed_uids, + mechanism_count.into(), + )?; pallet_subtensor::Pallet::::set_max_allowed_uids(netuid, max_allowed_uids); pallet_subtensor::Pallet::::record_owner_rl( maybe_owner, diff --git a/pallets/admin-utils/src/tests/mod.rs b/pallets/admin-utils/src/tests/mod.rs index 1aaefc8f8d..d03b184f84 100644 --- a/pallets/admin-utils/src/tests/mod.rs +++ b/pallets/admin-utils/src/tests/mod.rs @@ -540,6 +540,20 @@ fn test_sudo_set_max_allowed_uids() { Error::::MaxAllowedUidsGreaterThanDefaultMaxAllowedUids ); + // Chain bloat check against mechanism count + // Set MechanismCountCurrent to exceed 256 / DefaultMaxAllowedUids (16) + MechanismCountCurrent::::insert(netuid, MechId::from(32)); + let large_max_uids = 16_u16; + assert_noop!( + AdminUtils::sudo_set_max_allowed_uids( + <::RuntimeOrigin>::root(), + netuid, + large_max_uids + ), + SubtensorError::::TooManyUIDsPerMechanism + ); + MechanismCountCurrent::::insert(netuid, MechId::from(1)); + // Normal case assert_ok!(AdminUtils::sudo_set_max_allowed_uids( <::RuntimeOrigin>::root(), @@ -2341,6 +2355,7 @@ fn test_sudo_set_mechanism_count() { add_network(netuid, 10); // Set the Subnet Owner SubnetOwner::::insert(netuid, sn_owner); + MaxAllowedUids::::insert(netuid, 256_u16); assert_eq!( AdminUtils::sudo_set_mechanism_count( @@ -2354,7 +2369,13 @@ fn test_sudo_set_mechanism_count() { AdminUtils::sudo_set_mechanism_count(RuntimeOrigin::root(), netuid, ss_count_bad), pallet_subtensor::Error::::InvalidValue ); + assert_noop!( + AdminUtils::sudo_set_mechanism_count(RuntimeOrigin::root(), netuid, ss_count_ok), + pallet_subtensor::Error::::TooManyUIDsPerMechanism + ); + // Reduce max UIDs to 128 + MaxAllowedUids::::insert(netuid, 128_u16); assert_ok!(AdminUtils::sudo_set_mechanism_count( <::RuntimeOrigin>::root(), netuid, @@ -2380,6 +2401,8 @@ fn test_sudo_set_mechanism_count_and_emissions() { add_network(netuid, 10); // Set the Subnet Owner SubnetOwner::::insert(netuid, sn_owner); + MaxMechanismCount::::set(MechId::from(2)); + MaxAllowedUids::::set(netuid, 128_u16); assert_ok!(AdminUtils::sudo_set_mechanism_count( <::RuntimeOrigin>::signed(sn_owner), diff --git a/pallets/subtensor/src/macros/errors.rs b/pallets/subtensor/src/macros/errors.rs index 5a15330075..7470fdb3fb 100644 --- a/pallets/subtensor/src/macros/errors.rs +++ b/pallets/subtensor/src/macros/errors.rs @@ -266,5 +266,7 @@ mod errors { InvalidRootClaimThreshold, /// Exceeded subnet limit number or zero. InvalidSubnetNumber, + /// The maximum allowed UIDs times mechanism count should not exceed 256. + TooManyUIDsPerMechanism, } } diff --git a/pallets/subtensor/src/subnets/mechanism.rs b/pallets/subtensor/src/subnets/mechanism.rs index 727c0bd3a6..4b7a284870 100644 --- a/pallets/subtensor/src/subnets/mechanism.rs +++ b/pallets/subtensor/src/subnets/mechanism.rs @@ -95,6 +95,20 @@ impl Pallet { Ok(()) } + pub fn check_max_uids_vs_mechanism_count( + max_uids: u16, + mechanism_count: MechId, + ) -> DispatchResult { + let max_uids_for_one_mechanism = 256_u16; + let max_uids_for_mechanism_count = + max_uids_for_one_mechanism.safe_div(u8::from(mechanism_count) as u16); + ensure!( + max_uids_for_mechanism_count >= max_uids, + Error::::TooManyUIDsPerMechanism + ); + Ok(()) + } + /// Set the desired value of sub-subnet count for a subnet identified /// by netuid pub fn do_set_mechanism_count(netuid: NetUid, mechanism_count: MechId) -> DispatchResult { @@ -113,6 +127,10 @@ impl Pallet { Error::::InvalidValue ); + // Prevent chain bloat: Require max UIDs to be limited + let max_uids = MaxAllowedUids::::get(netuid); + Self::check_max_uids_vs_mechanism_count(max_uids, mechanism_count)?; + // Make sure we are not allowing numbers that will break the math ensure!( mechanism_count <= MechId::from(MAX_MECHANISM_COUNT_PER_SUBNET), From d17f6f6ef6635ec9ffb5c7476cd7130e0dee3417 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Wed, 12 Nov 2025 13:14:27 -0500 Subject: [PATCH 004/240] Fix merge issues --- pallets/subtensor/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 858ecd6e2a..c8155d3c16 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -2310,6 +2310,7 @@ pub mod pallet { MechId::from(1) } + #[pallet::type_value] /// -- ITEM (Maximum number of sub-subnets) pub fn DefaultMaxMechanismCount() -> MechId { MechId::from(2) @@ -2318,7 +2319,6 @@ pub mod pallet { /// ITEM( max_mechanism_count ) pub type MaxMechanismCount = StorageValue<_, MechId, ValueQuery, DefaultMaxMechanismCount>; - #[pallet::type_value] /// -- ITEM (Rate limit for mechanism count updates) #[pallet::type_value] pub fn MechanismCountSetRateLimit() -> u64 { From 33cbc8be0b00688af3048502aee66047e9770c69 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Mon, 17 Nov 2025 17:03:09 -0300 Subject: [PATCH 005/240] fix + tests --- pallets/admin-utils/src/lib.rs | 2 +- pallets/admin-utils/src/tests/mod.rs | 40 +++++++++++++++++++--- pallets/subtensor/src/lib.rs | 6 ++-- pallets/subtensor/src/subnets/mechanism.rs | 11 +++--- 4 files changed, 45 insertions(+), 14 deletions(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index e3e53d5e2a..d6f547fe11 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -538,7 +538,7 @@ pub mod pallet { ); // Prevent chain bloat: Require max UIDs to be limited let mechanism_count = pallet_subtensor::MechanismCountCurrent::::get(netuid); - pallet_subtensor::Pallet::::check_max_uids_vs_mechanism_count( + pallet_subtensor::Pallet::::ensure_max_uids_over_all_mechanisms( max_allowed_uids, mechanism_count.into(), )?; diff --git a/pallets/admin-utils/src/tests/mod.rs b/pallets/admin-utils/src/tests/mod.rs index d03b184f84..c72932ab11 100644 --- a/pallets/admin-utils/src/tests/mod.rs +++ b/pallets/admin-utils/src/tests/mod.rs @@ -10,7 +10,10 @@ use pallet_subtensor::{ TargetRegistrationsPerInterval, Tempo, WeightsVersionKeyRateLimit, *, }; // use pallet_subtensor::{migrations, Event}; -use pallet_subtensor::{Event, utils::rate_limiting::TransactionType}; +use pallet_subtensor::{ + Event, subnets::mechanism::MAX_MECHANISM_COUNT_PER_SUBNET, + utils::rate_limiting::TransactionType, +}; use sp_consensus_grandpa::AuthorityId as GrandpaId; use sp_core::{Get, Pair, U256, ed25519}; use substrate_fixed::types::I96F32; @@ -540,10 +543,10 @@ fn test_sudo_set_max_allowed_uids() { Error::::MaxAllowedUidsGreaterThanDefaultMaxAllowedUids ); - // Chain bloat check against mechanism count - // Set MechanismCountCurrent to exceed 256 / DefaultMaxAllowedUids (16) + // Trying to set max allowed uids that would cause max_allowed_uids * mechanism_count > 256 + MaxAllowedUids::::insert(netuid, 8); MechanismCountCurrent::::insert(netuid, MechId::from(32)); - let large_max_uids = 16_u16; + let large_max_uids = 16; assert_noop!( AdminUtils::sudo_set_max_allowed_uids( <::RuntimeOrigin>::root(), @@ -2888,3 +2891,32 @@ fn test_sudo_set_min_allowed_uids() { ); }); } + +#[test] +fn test_sudo_set_max_mechanism_count() { + new_test_ext().execute_with(|| { + // Normal case + assert_ok!(AdminUtils::sudo_set_max_mechanism_count( + <::RuntimeOrigin>::root(), + MechId::from(10) + )); + + // Zero fails + assert_noop!( + AdminUtils::sudo_set_max_mechanism_count( + <::RuntimeOrigin>::root(), + MechId::from(0) + ), + pallet_subtensor::Error::::InvalidValue + ); + + // Over max bound fails + assert_noop!( + AdminUtils::sudo_set_max_mechanism_count( + <::RuntimeOrigin>::root(), + MechId::from(MAX_MECHANISM_COUNT_PER_SUBNET + 1) + ), + pallet_subtensor::Error::::InvalidValue + ); + }); +} diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index c8155d3c16..5cc2cf77e3 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -2310,15 +2310,17 @@ pub mod pallet { MechId::from(1) } + /// -- ITEM (Maximum number of mechanisms) #[pallet::type_value] - /// -- ITEM (Maximum number of sub-subnets) pub fn DefaultMaxMechanismCount() -> MechId { MechId::from(2) } - #[pallet::storage] + /// ITEM( max_mechanism_count ) + #[pallet::storage] pub type MaxMechanismCount = StorageValue<_, MechId, ValueQuery, DefaultMaxMechanismCount>; + /// -- ITEM (Rate limit for mechanism count updates) #[pallet::type_value] pub fn MechanismCountSetRateLimit() -> u64 { diff --git a/pallets/subtensor/src/subnets/mechanism.rs b/pallets/subtensor/src/subnets/mechanism.rs index 4b7a284870..8551e2908f 100644 --- a/pallets/subtensor/src/subnets/mechanism.rs +++ b/pallets/subtensor/src/subnets/mechanism.rs @@ -95,21 +95,18 @@ impl Pallet { Ok(()) } - pub fn check_max_uids_vs_mechanism_count( + pub fn ensure_max_uids_over_all_mechanisms( max_uids: u16, mechanism_count: MechId, ) -> DispatchResult { - let max_uids_for_one_mechanism = 256_u16; - let max_uids_for_mechanism_count = - max_uids_for_one_mechanism.safe_div(u8::from(mechanism_count) as u16); ensure!( - max_uids_for_mechanism_count >= max_uids, + max_uids * u8::from(mechanism_count) as u16 <= 256, Error::::TooManyUIDsPerMechanism ); Ok(()) } - /// Set the desired value of sub-subnet count for a subnet identified + /// Set the desired value of mechanism count for a subnet identified /// by netuid pub fn do_set_mechanism_count(netuid: NetUid, mechanism_count: MechId) -> DispatchResult { // Make sure the subnet exists @@ -129,7 +126,7 @@ impl Pallet { // Prevent chain bloat: Require max UIDs to be limited let max_uids = MaxAllowedUids::::get(netuid); - Self::check_max_uids_vs_mechanism_count(max_uids, mechanism_count)?; + Self::ensure_max_uids_over_all_mechanisms(max_uids, mechanism_count)?; // Make sure we are not allowing numbers that will break the math ensure!( From 045d12332e9644582278b6f3314482c06346137c Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Mon, 17 Nov 2025 17:04:37 -0300 Subject: [PATCH 006/240] DefaultMaxAllowedUids from 4096 to 256 --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 9ece1dd025..407e4598cf 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -977,7 +977,7 @@ parameter_types! { pub const SubtensorInitialRho: u16 = 10; pub const SubtensorInitialAlphaSigmoidSteepness: i16 = 1000; pub const SubtensorInitialKappa: u16 = 32_767; // 0.5 = 65535/2 - pub const SubtensorInitialMaxAllowedUids: u16 = 4096; + pub const SubtensorInitialMaxAllowedUids: u16 = 256; pub const SubtensorInitialIssuance: u64 = 0; pub const SubtensorInitialMinAllowedWeights: u16 = 1024; pub const SubtensorInitialEmissionValue: u16 = 0; From 5b54733056c556a2e2312a043e555deb5c51654c Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Mon, 17 Nov 2025 17:32:26 -0300 Subject: [PATCH 007/240] fix clippy --- pallets/subtensor/src/subnets/mechanism.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/subnets/mechanism.rs b/pallets/subtensor/src/subnets/mechanism.rs index 8551e2908f..75baf34eb9 100644 --- a/pallets/subtensor/src/subnets/mechanism.rs +++ b/pallets/subtensor/src/subnets/mechanism.rs @@ -100,7 +100,7 @@ impl Pallet { mechanism_count: MechId, ) -> DispatchResult { ensure!( - max_uids * u8::from(mechanism_count) as u16 <= 256, + max_uids.saturating_mul(u8::from(mechanism_count) as u16) <= 256, Error::::TooManyUIDsPerMechanism ); Ok(()) From 3832d5916dc70cf98023f3b87d473c094f2f5075 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Mon, 17 Nov 2025 18:25:52 -0300 Subject: [PATCH 008/240] remove magic number --- pallets/subtensor/src/subnets/mechanism.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pallets/subtensor/src/subnets/mechanism.rs b/pallets/subtensor/src/subnets/mechanism.rs index 75baf34eb9..4780e8a3b7 100644 --- a/pallets/subtensor/src/subnets/mechanism.rs +++ b/pallets/subtensor/src/subnets/mechanism.rs @@ -99,8 +99,10 @@ impl Pallet { max_uids: u16, mechanism_count: MechId, ) -> DispatchResult { + let max_uids_over_all_mechanisms = + max_uids.saturating_mul(u8::from(mechanism_count) as u16); ensure!( - max_uids.saturating_mul(u8::from(mechanism_count) as u16) <= 256, + max_uids_over_all_mechanisms <= T::DefaultMaxAllowedUids::get(), Error::::TooManyUIDsPerMechanism ); Ok(()) From 993f033e23072a5a6f1aaccefdebfcb0698dc303 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Wed, 26 Nov 2025 23:10:34 -0300 Subject: [PATCH 009/240] clean up swap coldkey --- pallets/subtensor/src/macros/dispatches.rs | 2 +- pallets/subtensor/src/macros/genesis.rs | 1 - pallets/subtensor/src/swap/swap_coldkey.rs | 278 ++++++-------------- pallets/subtensor/src/tests/claim_root.rs | 2 - pallets/subtensor/src/tests/swap_coldkey.rs | 83 +----- 5 files changed, 91 insertions(+), 275 deletions(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index edf561810e..da115591cc 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -1090,7 +1090,7 @@ mod dispatches { old_coldkey: T::AccountId, new_coldkey: T::AccountId, swap_cost: TaoCurrency, - ) -> DispatchResultWithPostInfo { + ) -> DispatchResult { // Ensure it's called with root privileges (scheduler has root privileges) ensure_root(origin)?; log::debug!("swap_coldkey: {:?} -> {:?}", old_coldkey, new_coldkey); diff --git a/pallets/subtensor/src/macros/genesis.rs b/pallets/subtensor/src/macros/genesis.rs index 7bf8ba2a53..6924e04511 100644 --- a/pallets/subtensor/src/macros/genesis.rs +++ b/pallets/subtensor/src/macros/genesis.rs @@ -101,7 +101,6 @@ mod genesis { netuid, U64F64::saturating_from_num(1_000_000_000), ); - // TotalColdkeyAlpha::::insert(hotkey.clone(), netuid, 1_000_000_000); SubnetAlphaOut::::insert(netuid, AlphaCurrency::from(1_000_000_000)); let mut staking_hotkeys = StakingHotkeys::::get(hotkey.clone()); if !staking_hotkeys.contains(&hotkey) { diff --git a/pallets/subtensor/src/swap/swap_coldkey.rs b/pallets/subtensor/src/swap/swap_coldkey.rs index c81138b58c..d061c50173 100644 --- a/pallets/subtensor/src/swap/swap_coldkey.rs +++ b/pallets/subtensor/src/swap/swap_coldkey.rs @@ -1,228 +1,136 @@ use super::*; -use frame_support::weights::Weight; -use sp_core::Get; use substrate_fixed::types::U64F64; impl Pallet { - /// Swaps the coldkey associated with a set of hotkeys from an old coldkey to a new coldkey. - /// - /// # Arguments - /// - /// * `origin` - The origin of the call, which must be signed by the old coldkey. - /// * `new_coldkey` - The account ID of the new coldkey. - /// - /// # Returns - /// - /// Returns a `DispatchResultWithPostInfo` indicating success or failure, along with the weight consumed. - /// - /// # Errors - /// - /// This function will return an error if: - /// - The caller is not a valid signed origin. - /// - The old coldkey (caller) is in arbitration. - /// - The new coldkey is already associated with other hotkeys or is a hotkey itself. - /// - There's not enough balance to pay for the swap. - /// - /// # Events - /// - /// Emits a `ColdkeySwapped` event when successful. - /// - /// # Weight - /// - /// Weight is tracked and updated throughout the function execution. + /// Logic for the coldkey swap operation. Checks for collisions, balances, and cooldowns + /// before executing the swap. pub fn do_swap_coldkey( old_coldkey: &T::AccountId, new_coldkey: &T::AccountId, swap_cost: TaoCurrency, - ) -> DispatchResultWithPostInfo { - // 2. Initialize the weight for this operation - let mut weight: Weight = T::DbWeight::get().reads(2); - // 3. Ensure the new coldkey is not associated with any hotkeys + ) -> DispatchResult { ensure!( StakingHotkeys::::get(new_coldkey).is_empty(), Error::::ColdKeyAlreadyAssociated ); - weight = weight.saturating_add(T::DbWeight::get().reads(1)); - - // 4. Ensure the new coldkey is not a hotkey ensure!( !Self::hotkey_account_exists(new_coldkey), Error::::NewColdKeyIsHotkey ); - weight = weight.saturating_add(T::DbWeight::get().reads(1)); - - // 5. Swap the identity if the old coldkey has one - if let Some(identity) = IdentitiesV2::::take(old_coldkey) { - IdentitiesV2::::insert(new_coldkey, identity); - } - - // 6. Ensure sufficient balance for the swap cost ensure!( Self::can_remove_balance_from_coldkey_account(old_coldkey, swap_cost.into()), Error::::NotEnoughBalanceToPaySwapColdKey ); - // 7. Remove and recycle the swap cost from the old coldkey's account - let actual_burn_amount = - Self::remove_balance_from_coldkey_account(old_coldkey, swap_cost.into())?; - Self::recycle_tao(actual_burn_amount); + // Swap the identity if the old coldkey has one + if let Some(identity) = IdentitiesV2::::take(old_coldkey) { + IdentitiesV2::::insert(new_coldkey, identity); + } - // 8. Update the weight for the balance operations - weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); + // Remove and recycle the swap cost from the old coldkey's account + let burn_amount = Self::remove_balance_from_coldkey_account(old_coldkey, swap_cost.into())?; + Self::recycle_tao(burn_amount); - // 9. Perform the actual coldkey swap - let _ = Self::perform_swap_coldkey(old_coldkey, new_coldkey, &mut weight); + Self::perform_swap_coldkey(old_coldkey, new_coldkey)?; - // 10. Update the last transaction block for the new coldkey Self::set_last_tx_block(new_coldkey, Self::get_current_block_as_u64()); - weight.saturating_accrue(T::DbWeight::get().writes(1)); - // 11. Remove the coldkey swap scheduled record ColdkeySwapScheduled::::remove(old_coldkey); - // 12. Emit the ColdkeySwapped event Self::deposit_event(Event::ColdkeySwapped { old_coldkey: old_coldkey.clone(), new_coldkey: new_coldkey.clone(), swap_cost, }); - - // 12. Return the result with the updated weight - Ok(Some(weight).into()) + Ok(()) } - /// Performs the actual coldkey swap operation, transferring all associated data and balances from the old coldkey to the new coldkey. - /// - /// # Arguments - /// - /// * `old_coldkey` - The account ID of the old coldkey. - /// * `new_coldkey` - The account ID of the new coldkey. - /// * `weight` - A mutable reference to the current transaction weight. - /// - /// # Returns - /// - /// Returns a `DispatchResult` indicating success or failure of the operation. - /// - /// # Steps - /// - /// 1. Swap TotalHotkeyColdkeyStakesThisInterval: - /// - For each hotkey owned by the old coldkey, transfer its stake and block data to the new coldkey. - /// - /// 2. Swap subnet ownership: - /// - For each subnet, if the old coldkey is the owner, transfer ownership to the new coldkey. - /// - /// 3. Swap Stakes: - /// - For each hotkey staking for the old coldkey, transfer its stake to the new coldkey. + /// Transfer all assets, stakes, subnet ownerships, and hotkey associations from `old_coldkey` to `new_coldkey`. /// - /// 4. Swap total coldkey stake: - /// - Transfer the total stake from the old coldkey to the new coldkey. - /// - /// 5. Swap StakingHotkeys: - /// - Transfer the list of staking hotkeys from the old coldkey to the new coldkey. - /// - /// 6. Swap hotkey owners: - /// - For each hotkey owned by the old coldkey, transfer ownership to the new coldkey. - /// - Update the list of owned hotkeys for both old and new coldkeys. - /// - /// 7. Transfer remaining balance: - /// - Transfer any remaining balance from the old coldkey to the new coldkey. - /// - /// Throughout the process, the function updates the transaction weight to reflect the operations performed. - /// - /// # Notes - /// - /// This function is a critical part of the coldkey swap process and should be called only after all necessary checks and validations have been performed. + /// # Warning + /// This function performs NO validation checks. It assumes the caller has done all the checks before calling it. pub fn perform_swap_coldkey( old_coldkey: &T::AccountId, new_coldkey: &T::AccountId, - weight: &mut Weight, ) -> DispatchResult { - // 1. Swap TotalHotkeyColdkeyStakesThisInterval - // TotalHotkeyColdkeyStakesThisInterval: MAP ( hotkey, coldkey ) --> ( stake, block ) | Stake of the hotkey for the coldkey. - // for hotkey in OwnedHotkeys::::get(old_coldkey).iter() { - // let (stake, block) = - // TotalHotkeyColdkeyStakesThisInterval::::get(&hotkey, old_coldkey); - // TotalHotkeyColdkeyStakesThisInterval::::remove(&hotkey, old_coldkey); - // TotalHotkeyColdkeyStakesThisInterval::::insert(&hotkey, new_coldkey, (stake, block)); - // weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2)); - // } (DEPRECATED) - - // 2. Swap subnet owner. - // SubnetOwner: MAP ( netuid ) --> (coldkey) | Owner of the subnet. for netuid in Self::get_all_subnet_netuids() { - let subnet_owner = SubnetOwner::::get(netuid); - if subnet_owner == *old_coldkey { - SubnetOwner::::insert(netuid, new_coldkey.clone()); - } - weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); + Self::transfer_subnet_ownership(netuid, old_coldkey, new_coldkey); + Self::transfer_coldkey_stake(netuid, old_coldkey, new_coldkey); + } - if let Some(old_auto_stake_hotkey) = AutoStakeDestination::::get(old_coldkey, netuid) - { - AutoStakeDestination::::remove(old_coldkey, netuid); - AutoStakeDestination::::insert( - new_coldkey, - netuid, - old_auto_stake_hotkey.clone(), - ); - AutoStakeDestinationColdkeys::::mutate(old_auto_stake_hotkey, netuid, |v| { - // Remove old/new coldkeys (avoid duplicates), then add the new one. - v.retain(|c| *c != *old_coldkey && *c != *new_coldkey); - v.push(new_coldkey.clone()); - }); - } + Self::transfer_staking_hotkeys(old_coldkey, new_coldkey); + Self::transfer_hotkeys_ownership(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 { + Self::kill_coldkey_account(old_coldkey, remaining_balance)?; + Self::add_balance_to_coldkey_account(new_coldkey, remaining_balance); + } + + Ok(()) + } + + /// Transfer the ownership of the subnet to the new coldkey if it is owned by the old coldkey. + fn transfer_subnet_ownership( + netuid: NetUid, + old_coldkey: &T::AccountId, + new_coldkey: &T::AccountId, + ) { + let subnet_owner = SubnetOwner::::get(netuid); + if subnet_owner == *old_coldkey { + SubnetOwner::::insert(netuid, new_coldkey.clone()); + } + + if let Some(old_auto_stake_hotkey) = AutoStakeDestination::::get(old_coldkey, netuid) { + AutoStakeDestination::::remove(old_coldkey, netuid); + AutoStakeDestination::::insert(new_coldkey, netuid, old_auto_stake_hotkey.clone()); + AutoStakeDestinationColdkeys::::mutate(old_auto_stake_hotkey, netuid, |v| { + // Remove old/new coldkeys (avoid duplicates), then add the new one. + v.retain(|c| *c != *old_coldkey && *c != *new_coldkey); + v.push(new_coldkey.clone()); + }); } + } - // 3. Swap Stake. - // StakingHotkeys: MAP ( coldkey ) --> Vec( hotkey ) + /// Transfer the stake of all staking hotkeys linked to the old coldkey to the new coldkey. + fn transfer_coldkey_stake( + netuid: NetUid, + old_coldkey: &T::AccountId, + new_coldkey: &T::AccountId, + ) { for hotkey in StakingHotkeys::::get(old_coldkey) { - // 3.1 Swap Alpha - for netuid in Self::get_all_subnet_netuids() { - // Get the stake on the old (hot,coldkey) account. - let old_alpha: U64F64 = Alpha::::get((&hotkey, old_coldkey, netuid)); - // Get the stake on the new (hot,coldkey) account. - let new_alpha: U64F64 = Alpha::::get((&hotkey, new_coldkey, netuid)); - // Add the stake to new account. - Alpha::::insert( - (&hotkey, new_coldkey, netuid), - new_alpha.saturating_add(old_alpha), + // Get the stake on the old (hot,coldkey) account. + let old_alpha: U64F64 = Alpha::::get((&hotkey, old_coldkey, netuid)); + // Get the stake on the new (hot,coldkey) account. + let new_alpha: U64F64 = Alpha::::get((&hotkey, new_coldkey, netuid)); + // Add the stake to new account. + Alpha::::insert( + (&hotkey, new_coldkey, netuid), + new_alpha.saturating_add(old_alpha), + ); + // Remove the value from the old account. + Alpha::::remove((&hotkey, old_coldkey, netuid)); + + if new_alpha.saturating_add(old_alpha) > U64F64::from(0u64) { + Self::transfer_root_claimed_for_new_keys( + netuid, + &hotkey, + &hotkey, + old_coldkey, + new_coldkey, ); - // Remove the value from the old account. - Alpha::::remove((&hotkey, old_coldkey, netuid)); - if new_alpha.saturating_add(old_alpha) > U64F64::from(0u64) { - Self::transfer_root_claimed_for_new_keys( - netuid, - &hotkey, - &hotkey, - old_coldkey, - new_coldkey, - ); - - if netuid == NetUid::ROOT { - // Register new coldkey with root stake - Self::maybe_add_coldkey_index(new_coldkey); - } + if netuid == NetUid::ROOT { + // Register new coldkey with root stake + Self::maybe_add_coldkey_index(new_coldkey); } } - // Add the weight for the read and write. - weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2)); } + } - // 4. Swap TotalColdkeyAlpha (DEPRECATED) - // for netuid in Self::get_all_subnet_netuids() { - // let old_alpha_stake: u64 = TotalColdkeyAlpha::::get(old_coldkey, netuid); - // let new_alpha_stake: u64 = TotalColdkeyAlpha::::get(new_coldkey, netuid); - // TotalColdkeyAlpha::::insert( - // new_coldkey, - // netuid, - // new_alpha_stake.saturating_add(old_alpha_stake), - // ); - // TotalColdkeyAlpha::::remove(old_coldkey, netuid); - // } - // weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2)); - - // 5. Swap StakingHotkeys. - // StakingHotkeys: MAP ( coldkey ) --> Vec | Hotkeys staking for the coldkey. + /// Transfer staking hotkeys from the old coldkey to the new coldkey. + fn transfer_staking_hotkeys(old_coldkey: &T::AccountId, new_coldkey: &T::AccountId) { let old_staking_hotkeys: Vec = StakingHotkeys::::get(old_coldkey); let mut new_staking_hotkeys: Vec = StakingHotkeys::::get(new_coldkey); for hotkey in old_staking_hotkeys { @@ -231,13 +139,13 @@ impl Pallet { new_staking_hotkeys.push(hotkey); } } + StakingHotkeys::::remove(old_coldkey); StakingHotkeys::::insert(new_coldkey, new_staking_hotkeys); - weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2)); + } - // 6. Swap hotkey owners. - // Owner: MAP ( hotkey ) --> coldkey | Owner of the hotkey. - // OwnedHotkeys: MAP ( coldkey ) --> Vec | Hotkeys owned by the coldkey. + /// 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) { 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() { @@ -252,19 +160,5 @@ impl Pallet { } OwnedHotkeys::::remove(old_coldkey); OwnedHotkeys::::insert(new_coldkey, new_owned_hotkeys); - weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2)); - - // 7. Transfer remaining balance. - // Balance: MAP ( coldkey ) --> u64 | Balance of the coldkey. - // Transfer any remaining balance from old_coldkey to new_coldkey - let remaining_balance = Self::get_coldkey_balance(old_coldkey); - if remaining_balance > 0 { - Self::kill_coldkey_account(old_coldkey, remaining_balance)?; - Self::add_balance_to_coldkey_account(new_coldkey, remaining_balance); - } - weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2)); - - // Return ok. - Ok(()) } } diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index b910fa1e83..4a98d23b63 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -1184,12 +1184,10 @@ fn test_claim_root_with_swap_coldkey() { ); // Swap coldkey - let mut weight = Weight::zero(); assert_ok!(SubtensorModule::perform_swap_coldkey( &coldkey, &new_coldkey, - &mut weight )); // Check swapped keys claimed values diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index 9d3bdbfc62..090d743f3a 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -13,7 +13,6 @@ use frame_support::error::BadOrigin; use frame_support::traits::OnInitialize; use frame_support::traits::schedule::DispatchTime; use frame_support::traits::schedule::v3::Named as ScheduleNamed; -use frame_support::weights::Weight; use frame_support::{assert_err, assert_noop, assert_ok}; use frame_system::{Config, RawOrigin}; use sp_core::{Get, H256, U256}; @@ -28,36 +27,6 @@ use super::mock::*; use crate::transaction_extension::SubtensorTransactionExtension; use crate::*; use crate::{Call, ColdkeySwapScheduleDuration, Error}; -// // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_coldkey -- test_swap_total_hotkey_coldkey_stakes_this_interval --exact --nocapture -// #[test] -// fn test_swap_total_hotkey_coldkey_stakes_this_interval() { -// new_test_ext(1).execute_with(|| { -// let old_coldkey = U256::from(1); -// let new_coldkey = U256::from(2); -// let hotkey = U256::from(3); -// let stake = 100; -// let block = 42; - -// OwnedHotkeys::::insert(old_coldkey, vec![hotkey]); -// TotalHotkeyColdkeyStakesThisInterval::::insert(hotkey, old_coldkey, (stake, block)); - -// let mut weight = Weight::zero(); -// assert_ok!(SubtensorModule::perform_swap_coldkey( -// &old_coldkey, -// &new_coldkey, -// &mut weight -// )); - -// assert!(!TotalHotkeyColdkeyStakesThisInterval::::contains_key( -// hotkey, -// old_coldkey -// )); -// assert_eq!( -// TotalHotkeyColdkeyStakesThisInterval::::get(hotkey, new_coldkey), -// (stake, block) -// ); -// }); -// } // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_coldkey -- test_swap_subnet_owner --exact --nocapture #[test] @@ -70,11 +39,9 @@ fn test_swap_subnet_owner() { add_network(netuid, 1, 0); SubnetOwner::::insert(netuid, old_coldkey); - let mut weight = Weight::zero(); assert_ok!(SubtensorModule::perform_swap_coldkey( &old_coldkey, &new_coldkey, - &mut weight )); assert_eq!(SubnetOwner::::get(netuid), new_coldkey); @@ -115,11 +82,9 @@ fn test_swap_total_coldkey_stake() { )); let total_stake_before_swap = SubtensorModule::get_total_stake_for_coldkey(&old_coldkey); - let mut weight = Weight::zero(); assert_ok!(SubtensorModule::perform_swap_coldkey( &old_coldkey, &new_coldkey, - &mut weight )); assert_eq!( @@ -143,11 +108,9 @@ fn test_swap_staking_hotkeys() { StakingHotkeys::::insert(old_coldkey, vec![hotkey]); - let mut weight = Weight::zero(); assert_ok!(SubtensorModule::perform_swap_coldkey( &old_coldkey, &new_coldkey, - &mut weight )); assert!(StakingHotkeys::::get(old_coldkey).is_empty()); @@ -166,11 +129,9 @@ fn test_swap_hotkey_owners() { Owner::::insert(hotkey, old_coldkey); OwnedHotkeys::::insert(old_coldkey, vec![hotkey]); - let mut weight = Weight::zero(); assert_ok!(SubtensorModule::perform_swap_coldkey( &old_coldkey, &new_coldkey, - &mut weight )); assert_eq!(Owner::::get(hotkey), new_coldkey); @@ -188,11 +149,9 @@ fn test_transfer_remaining_balance() { SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, balance); - let mut weight = Weight::zero(); assert_ok!(SubtensorModule::perform_swap_coldkey( &old_coldkey, &new_coldkey, - &mut weight )); assert_eq!(SubtensorModule::get_coldkey_balance(&old_coldkey), 0); @@ -207,11 +166,9 @@ fn test_swap_with_no_stake() { let old_coldkey = U256::from(1); let new_coldkey = U256::from(2); - let mut weight = Weight::zero(); assert_ok!(SubtensorModule::perform_swap_coldkey( &old_coldkey, &new_coldkey, - &mut weight )); assert_eq!( @@ -236,11 +193,9 @@ fn test_swap_with_multiple_hotkeys() { OwnedHotkeys::::insert(old_coldkey, vec![hotkey1, hotkey2]); - let mut weight = Weight::zero(); assert_ok!(SubtensorModule::perform_swap_coldkey( &old_coldkey, &new_coldkey, - &mut weight )); assert!(OwnedHotkeys::::get(old_coldkey).is_empty()); @@ -265,11 +220,9 @@ fn test_swap_with_multiple_subnets() { SubnetOwner::::insert(netuid1, old_coldkey); SubnetOwner::::insert(netuid2, old_coldkey); - let mut weight = Weight::zero(); assert_ok!(SubtensorModule::perform_swap_coldkey( &old_coldkey, &new_coldkey, - &mut weight )); assert_eq!(SubnetOwner::::get(netuid1), new_coldkey); @@ -284,11 +237,9 @@ fn test_swap_with_zero_balance() { let old_coldkey = U256::from(1); let new_coldkey = U256::from(2); - let mut weight = Weight::zero(); assert_ok!(SubtensorModule::perform_swap_coldkey( &old_coldkey, &new_coldkey, - &mut weight )); assert_eq!(Balances::free_balance(old_coldkey), 0); @@ -324,16 +275,13 @@ fn test_swap_idempotency() { // Get stake before swap let stake_before_swap = SubtensorModule::get_total_stake_for_coldkey(&old_coldkey); - let mut weight = Weight::zero(); assert_ok!(SubtensorModule::perform_swap_coldkey( &old_coldkey, &new_coldkey, - &mut weight )); assert_ok!(SubtensorModule::perform_swap_coldkey( &old_coldkey, &new_coldkey, - &mut weight )); assert_eq!( @@ -405,16 +353,13 @@ fn test_swap_with_max_values() { netuid2, ); - let mut weight = Weight::zero(); assert_ok!(SubtensorModule::perform_swap_coldkey( &old_coldkey, &new_coldkey, - &mut weight )); assert_ok!(SubtensorModule::perform_swap_coldkey( &old_coldkey2, &new_coldkey2, - &mut weight )); assert_eq!( @@ -469,11 +414,9 @@ fn test_swap_with_non_existent_new_coldkey() { netuid, ); - let mut weight = Weight::zero(); assert_ok!(SubtensorModule::perform_swap_coldkey( &old_coldkey, &new_coldkey, - &mut weight )); assert_eq!( @@ -505,11 +448,9 @@ fn test_swap_with_max_hotkeys() { OwnedHotkeys::::insert(old_coldkey, hotkeys.clone()); - let mut weight = Weight::zero(); assert_ok!(SubtensorModule::perform_swap_coldkey( &old_coldkey, &new_coldkey, - &mut weight )); assert!(OwnedHotkeys::::get(old_coldkey).is_empty()); @@ -552,11 +493,9 @@ fn test_swap_effect_on_delegated_stake() { let coldkey_stake_before = SubtensorModule::get_total_stake_for_coldkey(&old_coldkey); let delegator_stake_before = SubtensorModule::get_total_stake_for_coldkey(&delegator); - let mut weight = Weight::zero(); assert_ok!(SubtensorModule::perform_swap_coldkey( &old_coldkey, &new_coldkey, - &mut weight )); assert_abs_diff_eq!( @@ -630,11 +569,9 @@ fn test_swap_concurrent_modifications() { netuid, ); - let mut weight = Weight::zero(); assert_ok!(SubtensorModule::perform_swap_coldkey( &old_coldkey, &new_coldkey, - &mut weight )); assert_eq!( @@ -663,11 +600,9 @@ fn test_swap_with_invalid_subnet_ownership() { // Simulate an invalid state where the subnet owner doesn't match the old_coldkey SubnetOwner::::insert(netuid, U256::from(3)); - let mut weight = Weight::zero(); assert_ok!(SubtensorModule::perform_swap_coldkey( &old_coldkey, &new_coldkey, - &mut weight )); // The swap should not affect the mismatched subnet ownership @@ -865,7 +800,6 @@ fn test_swap_stake_for_coldkey() { let stake_amount1 = DefaultMinStake::::get().to_u64() * 10; let stake_amount2 = DefaultMinStake::::get().to_u64() * 20; let stake_amount3 = DefaultMinStake::::get().to_u64() * 30; - let mut weight = Weight::zero(); // Setup initial state // Add a network @@ -936,7 +870,7 @@ fn test_swap_stake_for_coldkey() { let initial_total_hotkey2_stake = SubtensorModule::get_total_stake_for_hotkey(&hotkey2); // Perform the swap - SubtensorModule::perform_swap_coldkey(&old_coldkey, &new_coldkey, &mut weight); + SubtensorModule::perform_swap_coldkey(&old_coldkey, &new_coldkey); // Verify stake is additive, not replaced assert_abs_diff_eq!( @@ -1021,7 +955,6 @@ fn test_swap_staking_hotkeys_for_coldkey() { let hotkey2 = U256::from(5); let stake_amount1 = DefaultMinStake::::get().to_u64() * 10; let stake_amount2 = DefaultMinStake::::get().to_u64() * 20; - let mut weight = Weight::zero(); // Setup initial state // Add a network @@ -1064,7 +997,7 @@ fn test_swap_staking_hotkeys_for_coldkey() { ); // Perform the swap - SubtensorModule::perform_swap_coldkey(&old_coldkey, &new_coldkey, &mut weight); + SubtensorModule::perform_swap_coldkey(&old_coldkey, &new_coldkey); // Verify StakingHotkeys transfer assert_eq!( @@ -1086,7 +1019,6 @@ fn test_swap_delegated_stake_for_coldkey() { let hotkey2 = U256::from(5); let stake_amount1 = DefaultMinStake::::get().to_u64() * 10; let stake_amount2 = DefaultMinStake::::get().to_u64() * 20; - let mut weight = Weight::zero(); let netuid = NetUid::from(1); // Setup initial state @@ -1150,7 +1082,7 @@ fn test_swap_delegated_stake_for_coldkey() { let total_hotkey2_stake = SubtensorModule::get_total_stake_for_hotkey(&hotkey2); // Perform the swap - SubtensorModule::perform_swap_coldkey(&old_coldkey, &new_coldkey, &mut weight); + SubtensorModule::perform_swap_coldkey(&old_coldkey, &new_coldkey); // Verify stake transfer assert_eq!( @@ -1228,7 +1160,6 @@ fn test_swap_subnet_owner_for_coldkey() { let new_coldkey = U256::from(2); let netuid1 = NetUid::from(1); let netuid2 = NetUid::from(2); - let mut weight = Weight::zero(); // Initialize SubnetOwner for old_coldkey add_network(netuid1, 13, 0); @@ -1240,7 +1171,7 @@ fn test_swap_subnet_owner_for_coldkey() { TotalNetworks::::put(3); // Perform the swap - SubtensorModule::perform_swap_coldkey(&old_coldkey, &new_coldkey, &mut weight); + SubtensorModule::perform_swap_coldkey(&old_coldkey, &new_coldkey); // Verify the swap assert_eq!(SubnetOwner::::get(netuid1), new_coldkey); @@ -1523,11 +1454,9 @@ fn test_coldkey_swap_total() { SubtensorModule::get_total_stake_for_coldkey(&coldkey), ck_stake ); - let mut weight = Weight::zero(); assert_ok!(SubtensorModule::perform_swap_coldkey( &coldkey, &new_coldkey, - &mut weight )); assert_eq!( SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), @@ -1664,11 +1593,9 @@ fn test_coldkey_delegations() { )); // Perform the swap - let mut weight = Weight::zero(); assert_ok!(SubtensorModule::perform_swap_coldkey( &coldkey, &new_coldkey, - &mut weight )); // Verify stake was moved for the delegate @@ -2594,11 +2521,9 @@ fn test_swap_auto_stake_destination_coldkeys() { AutoStakeDestinationColdkeys::::insert(hotkey, netuid, coldkeys.clone()); AutoStakeDestination::::insert(old_coldkey, netuid, hotkey); - let mut weight = Weight::zero(); assert_ok!(SubtensorModule::perform_swap_coldkey( &old_coldkey, &new_coldkey, - &mut weight )); let new_coldkeys = AutoStakeDestinationColdkeys::::get(hotkey, netuid); From 14fe3f6caeca57244644df07c46d589d9d659a30 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Sun, 30 Nov 2025 12:45:30 +0000 Subject: [PATCH 010/240] deprecate old calls --- pallets/subtensor/src/macros/dispatches.rs | 129 +++------------------ 1 file changed, 16 insertions(+), 113 deletions(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index da115591cc..45b0de46ee 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -6,11 +6,10 @@ use frame_support::pallet_macros::pallet_section; #[pallet_section] mod dispatches { use crate::subnets::leasing::SubnetLeasingWeightInfo; - use frame_support::traits::schedule::DispatchTime; use frame_support::traits::schedule::v3::Anon as ScheduleAnon; use frame_system::pallet_prelude::BlockNumberFor; use sp_core::ecdsa::Signature; - use sp_runtime::{Percent, traits::Saturating}; + use sp_runtime::Percent; use crate::MAX_CRV3_COMMIT_SIZE_BYTES; use crate::MAX_NUM_ROOT_CLAIMS; @@ -1068,34 +1067,17 @@ mod dispatches { /// The extrinsic for user to change the coldkey associated with their account. /// - /// # Arguments - /// - /// * `origin` - The origin of the call, must be signed by the old coldkey. - /// * `old_coldkey` - The current coldkey associated with the account. - /// * `new_coldkey` - The new coldkey to be associated with the account. - /// - /// # Returns - /// - /// Returns a `DispatchResultWithPostInfo` indicating success or failure of the operation. - /// - /// # Weight - /// - /// Weight is calculated based on the number of database reads and writes. + /// WARNING: This is deprecated in favor of `announce_coldkey_swap`/`coldkey_swap` #[pallet::call_index(71)] - #[pallet::weight((Weight::from_parts(161_700_000, 0) - .saturating_add(T::DbWeight::get().reads(16_u64)) - .saturating_add(T::DbWeight::get().writes(9)), DispatchClass::Operational, Pays::Yes))] + #[pallet::weight(Weight::zero())] + #[deprecated(note = "Deprecated, please migrate to `announce_coldkey_swap`/`coldkey_swap`")] pub fn swap_coldkey( - origin: OriginFor, - old_coldkey: T::AccountId, - new_coldkey: T::AccountId, - swap_cost: TaoCurrency, + _origin: OriginFor, + _old_coldkey: T::AccountId, + _new_coldkey: T::AccountId, + _swap_cost: TaoCurrency, ) -> DispatchResult { - // Ensure it's called with root privileges (scheduler has root privileges) - ensure_root(origin)?; - log::debug!("swap_coldkey: {:?} -> {:?}", old_coldkey, new_coldkey); - - Self::do_swap_coldkey(&old_coldkey, &new_coldkey, swap_cost) + Err(Error::::Deprecated.into()) } /// Sets the childkey take for a given hotkey. @@ -1332,94 +1314,15 @@ mod dispatches { /// Schedules a coldkey swap operation to be executed at a future block. /// - /// This function allows a user to schedule the swapping of their coldkey to a new one - /// at a specified future block. The swap is not executed immediately but is scheduled - /// to occur at the specified block number. - /// - /// # Arguments - /// - /// * `origin` - The origin of the call, which should be signed by the current coldkey owner. - /// * `new_coldkey` - The account ID of the new coldkey that will replace the current one. - /// * `when` - The block number at which the coldkey swap should be executed. - /// - /// # Returns - /// - /// Returns a `DispatchResultWithPostInfo` indicating whether the scheduling was successful. - /// - /// # Errors - /// - /// This function may return an error if: - /// * The origin is not signed. - /// * The scheduling fails due to conflicts or system constraints. - /// - /// # Notes - /// - /// - The actual swap is not performed by this function. It merely schedules the swap operation. - /// - The weight of this call is set to a fixed value and may need adjustment based on benchmarking. - /// - /// # TODO - /// - /// - Implement proper weight calculation based on the complexity of the operation. - /// - Consider adding checks to prevent scheduling too far into the future. - /// TODO: Benchmark this call + /// WARNING: This function is deprecated, please migrate to `announce_coldkey_swap`/`coldkey_swap` #[pallet::call_index(73)] - #[pallet::weight((Weight::from_parts(37_830_000, 0) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(2)), DispatchClass::Normal, Pays::Yes))] + #[pallet::weight(Weight::zero())] + #[deprecated(note = "Deprecated, please migrate to `announce_coldkey_swap`/`coldkey_swap`")] pub fn schedule_swap_coldkey( - origin: OriginFor, - new_coldkey: T::AccountId, - ) -> DispatchResultWithPostInfo { - let who = ensure_signed(origin)?; - let current_block = >::block_number(); - - // If the coldkey has a scheduled swap, check if we can reschedule it - if ColdkeySwapScheduled::::contains_key(&who) { - let (scheduled_block, _scheduled_coldkey) = ColdkeySwapScheduled::::get(&who); - let reschedule_duration = ColdkeySwapRescheduleDuration::::get(); - let redo_when = scheduled_block.saturating_add(reschedule_duration); - ensure!(redo_when <= current_block, Error::::SwapAlreadyScheduled); - } - - // Calculate the swap cost and ensure sufficient balance - let swap_cost = Self::get_key_swap_cost(); - ensure!( - Self::can_remove_balance_from_coldkey_account(&who, swap_cost.into()), - Error::::NotEnoughBalanceToPaySwapColdKey - ); - - let current_block: BlockNumberFor = >::block_number(); - let duration: BlockNumberFor = ColdkeySwapScheduleDuration::::get(); - let when: BlockNumberFor = current_block.saturating_add(duration); - - let call = Call::::swap_coldkey { - old_coldkey: who.clone(), - new_coldkey: new_coldkey.clone(), - swap_cost, - }; - - let bound_call = ::Preimages::bound(LocalCallOf::::from(call.clone())) - .map_err(|_| Error::::FailedToSchedule)?; - - T::Scheduler::schedule( - DispatchTime::At(when), - None, - 63, - frame_system::RawOrigin::Root.into(), - bound_call, - ) - .map_err(|_| Error::::FailedToSchedule)?; - - ColdkeySwapScheduled::::insert(&who, (when, new_coldkey.clone())); - // Emit the SwapScheduled event - Self::deposit_event(Event::ColdkeySwapScheduled { - old_coldkey: who.clone(), - new_coldkey: new_coldkey.clone(), - execution_block: when, - swap_cost, - }); - - Ok(().into()) + _origin: OriginFor, + _new_coldkey: T::AccountId, + ) -> DispatchResult { + Err(Error::::Deprecated.into()) } /// ---- Set prometheus information for the neuron. From 7c485f0fd7748a4194555384a3e6ee58040fc3c3 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Sun, 30 Nov 2025 12:46:07 +0000 Subject: [PATCH 011/240] add new storage for announcements --- pallets/subtensor/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index dd7645e8ff..d19ff35ebe 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1369,6 +1369,12 @@ pub mod pallet { DefaultColdkeySwapScheduled, >; + /// A map of the coldkey swap announcements from a coldkey + /// to the block number the announcement was made and the new coldkey. + #[pallet::storage] + pub type ColdkeySwapAnnouncements = + StorageMap<_, Twox64Concat, T::AccountId, (BlockNumberFor, T::AccountId), OptionQuery>; + /// --- DMAP ( hot, netuid ) --> alpha | Returns the total amount of alpha a hotkey owns. #[pallet::storage] pub type TotalHotkeyAlpha = StorageDoubleMap< From 54eded034e418f51fb4eda0c0a64e72492903c32 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Sun, 30 Nov 2025 12:46:55 +0000 Subject: [PATCH 012/240] remove perform_swap_coldkey --- pallets/subtensor/src/swap/swap_coldkey.rs | 61 ++++++++-------------- 1 file changed, 23 insertions(+), 38 deletions(-) diff --git a/pallets/subtensor/src/swap/swap_coldkey.rs b/pallets/subtensor/src/swap/swap_coldkey.rs index d061c50173..e11ef2b53f 100644 --- a/pallets/subtensor/src/swap/swap_coldkey.rs +++ b/pallets/subtensor/src/swap/swap_coldkey.rs @@ -2,72 +2,57 @@ use super::*; use substrate_fixed::types::U64F64; impl Pallet { - /// Logic for the coldkey swap operation. Checks for collisions, balances, and cooldowns - /// before executing the swap. + /// Transfer all assets, stakes, subnet ownerships, and hotkey associations from `old_coldkey` to + /// to `new_coldkey`. pub fn do_swap_coldkey( old_coldkey: &T::AccountId, new_coldkey: &T::AccountId, - swap_cost: TaoCurrency, ) -> DispatchResult { ensure!( - StakingHotkeys::::get(new_coldkey).is_empty(), + StakingHotkeys::::get(&new_coldkey).is_empty(), Error::::ColdKeyAlreadyAssociated ); ensure!( - !Self::hotkey_account_exists(new_coldkey), + !Self::hotkey_account_exists(&new_coldkey), Error::::NewColdKeyIsHotkey ); + + // Remove and recycle the swap cost from the old coldkey's account + let swap_cost = Self::get_key_swap_cost(); ensure!( Self::can_remove_balance_from_coldkey_account(old_coldkey, swap_cost.into()), Error::::NotEnoughBalanceToPaySwapColdKey ); + let burn_amount = Self::remove_balance_from_coldkey_account(old_coldkey, swap_cost.into())?; + Self::recycle_tao(burn_amount); // Swap the identity if the old coldkey has one if let Some(identity) = IdentitiesV2::::take(old_coldkey) { - IdentitiesV2::::insert(new_coldkey, identity); + IdentitiesV2::::insert(new_coldkey.clone(), identity); } - // Remove and recycle the swap cost from the old coldkey's account - let burn_amount = Self::remove_balance_from_coldkey_account(old_coldkey, swap_cost.into())?; - Self::recycle_tao(burn_amount); - - Self::perform_swap_coldkey(old_coldkey, new_coldkey)?; - - Self::set_last_tx_block(new_coldkey, Self::get_current_block_as_u64()); - - ColdkeySwapScheduled::::remove(old_coldkey); - - Self::deposit_event(Event::ColdkeySwapped { - old_coldkey: old_coldkey.clone(), - new_coldkey: new_coldkey.clone(), - swap_cost, - }); - Ok(()) - } - - /// Transfer all assets, stakes, subnet ownerships, and hotkey associations from `old_coldkey` to `new_coldkey`. - /// - /// # Warning - /// This function performs NO validation checks. It assumes the caller has done all the checks before calling it. - pub fn perform_swap_coldkey( - old_coldkey: &T::AccountId, - new_coldkey: &T::AccountId, - ) -> DispatchResult { for netuid in Self::get_all_subnet_netuids() { - Self::transfer_subnet_ownership(netuid, old_coldkey, new_coldkey); - Self::transfer_coldkey_stake(netuid, old_coldkey, new_coldkey); + Self::transfer_subnet_ownership(netuid, old_coldkey, &new_coldkey); + 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_staking_hotkeys(old_coldkey, &new_coldkey); + Self::transfer_hotkeys_ownership(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 { Self::kill_coldkey_account(old_coldkey, remaining_balance)?; - Self::add_balance_to_coldkey_account(new_coldkey, remaining_balance); + Self::add_balance_to_coldkey_account(&new_coldkey, remaining_balance); } + Self::set_last_tx_block(&new_coldkey, Self::get_current_block_as_u64()); + ColdkeySwapAnnouncements::::remove(old_coldkey); + + Self::deposit_event(Event::ColdkeySwapped { + old_coldkey: old_coldkey.clone(), + new_coldkey: new_coldkey.clone(), + swap_cost, + }); Ok(()) } From d16bba55a829675c018e6b1a57f21c01e85a10b6 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Sun, 30 Nov 2025 21:30:03 +0100 Subject: [PATCH 013/240] added new extrinsics --- pallets/subtensor/src/macros/dispatches.rs | 57 ++++++++++++++++++++++ pallets/subtensor/src/swap/swap_coldkey.rs | 1 - 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 45b0de46ee..3ec118a563 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2334,5 +2334,62 @@ mod dispatches { Ok(()) } + + /// Announces a coldkey swap. This is required before the coldkey swap can be performed after the delay period. + #[pallet::call_index(125)] + #[pallet::weight(Weight::zero())] + pub fn announce_coldkey_swap( + origin: OriginFor, + new_coldkey: T::AccountId, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let now = >::block_number(); + + ColdkeySwapAnnouncements::::insert(who.clone(), (now, new_coldkey.clone())); + + Self::deposit_event(Event::ColdkeySwapAnnounced { + who: who.clone(), + new_coldkey: new_coldkey.clone(), + block_number: now, + }); + Ok(()) + } + + /// Removes a coldkey swap announcement. + #[pallet::call_index(126)] + #[pallet::weight(Weight::zero())] + pub fn remove_coldkey_swap_announcement(origin: OriginFor) -> DispatchResult { + let who = ensure_signed(origin)?; + + ensure!( + ColdkeySwapAnnouncements::::contains_key(who.clone()), + Error::::ColdkeySwapAnnouncementNotFound + ); + + ColdkeySwapAnnouncements::::remove(who.clone()); + + Self::deposit_event(Event::ColdkeySwapAnnouncementRemoved { who: who.clone() }); + Ok(()) + } + + /// Performs a coldkey swap iff an announcement has been made. + #[pallet::call_index(127)] + #[pallet::weight(Weight::zero())] + pub fn coldkey_swap_announced(origin: OriginFor) -> DispatchResult { + let who = ensure_signed(origin)?; + + let (when, new_coldkey) = ColdkeySwapAnnouncements::::take(who.clone()) + .ok_or(Error::::ColdkeySwapAnnouncementNotFound)?; + + let now = >::block_number(); + let delay = when + ColdkeySwapScheduleDuration::::get(); + ensure!(now >= delay, Error::::ColdkeySwapTooEarly); + + Self::do_swap_coldkey(&who, &new_coldkey)?; + + ColdkeySwapAnnouncements::::remove(who.clone()); + + Ok(()) + } } } diff --git a/pallets/subtensor/src/swap/swap_coldkey.rs b/pallets/subtensor/src/swap/swap_coldkey.rs index e11ef2b53f..885e211300 100644 --- a/pallets/subtensor/src/swap/swap_coldkey.rs +++ b/pallets/subtensor/src/swap/swap_coldkey.rs @@ -46,7 +46,6 @@ impl Pallet { } Self::set_last_tx_block(&new_coldkey, Self::get_current_block_as_u64()); - ColdkeySwapAnnouncements::::remove(old_coldkey); Self::deposit_event(Event::ColdkeySwapped { old_coldkey: old_coldkey.clone(), From dfdf22dd481c252cce93b6333887ca77bb6c4b15 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Sun, 30 Nov 2025 21:30:13 +0100 Subject: [PATCH 014/240] update errors --- pallets/subtensor/src/macros/errors.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pallets/subtensor/src/macros/errors.rs b/pallets/subtensor/src/macros/errors.rs index 5a15330075..e3221b573a 100644 --- a/pallets/subtensor/src/macros/errors.rs +++ b/pallets/subtensor/src/macros/errors.rs @@ -150,10 +150,10 @@ mod errors { TooManyChildren, /// Default transaction rate limit exceeded. TxRateLimitExceeded, - /// Swap already scheduled. - SwapAlreadyScheduled, - /// failed to swap coldkey - FailedToSchedule, + /// Coldkey swap announcement not found + ColdkeySwapAnnouncementNotFound, + /// Coldkey swap too early. + ColdkeySwapTooEarly, /// New coldkey is hotkey NewColdKeyIsHotkey, /// Childkey take is invalid. @@ -266,5 +266,7 @@ mod errors { InvalidRootClaimThreshold, /// Exceeded subnet limit number or zero. InvalidSubnetNumber, + /// Deprecated call. + Deprecated, } } From ed2bab6fb37f17b0402d934f443971258b2cb93d Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Sun, 30 Nov 2025 21:32:32 +0100 Subject: [PATCH 015/240] updated events --- pallets/subtensor/src/macros/events.rs | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index d015205d4d..3efc74e413 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -170,6 +170,20 @@ mod events { MaxDelegateTakeSet(u16), /// minimum delegate take is set by sudo/admin transaction MinDelegateTakeSet(u16), + /// A coldkey swap announcement has been made. + ColdkeySwapAnnounced { + /// The account ID of the coldkey that made the announcement. + who: T::AccountId, + /// The account ID of the new coldkey. + new_coldkey: T::AccountId, + /// The block number the announcement was made. + block_number: BlockNumberFor, + }, + /// A coldkey swap announcement has been removed. + ColdkeySwapAnnouncementRemoved { + /// The account ID of the coldkey that made the announcement. + who: T::AccountId, + }, /// A coldkey has been swapped ColdkeySwapped { /// the account ID of old coldkey @@ -190,17 +204,6 @@ mod events { ::AccountId, >>::Balance, }, - /// A coldkey swap has been scheduled - ColdkeySwapScheduled { - /// The account ID of the old coldkey - old_coldkey: T::AccountId, - /// The account ID of the new coldkey - new_coldkey: T::AccountId, - /// The arbitration block for the coldkey swap - execution_block: BlockNumberFor, - /// The swap cost - swap_cost: TaoCurrency, - }, /// The arbitration period has been extended ArbitrationPeriodExtended { /// The account ID of the coldkey From 7b4bdab75e5371df6ca86e8e0b4b49d80d7ed569 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Sun, 30 Nov 2025 21:33:00 +0100 Subject: [PATCH 016/240] fix claim root test --- pallets/subtensor/src/tests/claim_root.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index 4a98d23b63..0158579553 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -1184,11 +1184,9 @@ fn test_claim_root_with_swap_coldkey() { ); // Swap coldkey - - assert_ok!(SubtensorModule::perform_swap_coldkey( - &coldkey, - &new_coldkey, - )); + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, swap_cost.to_u64()); + assert_ok!(SubtensorModule::do_swap_coldkey(&coldkey, &new_coldkey,)); // Check swapped keys claimed values From d92a19b3f1046bb343b3dad134e8f6b745b88c71 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Sun, 30 Nov 2025 21:47:45 +0100 Subject: [PATCH 017/240] added new tests for swap coldkey announce/remove --- pallets/subtensor/src/tests/swap_coldkey.rs | 131 +++++++++++++++++++- 1 file changed, 130 insertions(+), 1 deletion(-) diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index 090d743f3a..9f0787ec2f 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -28,7 +28,100 @@ use crate::transaction_extension::SubtensorTransactionExtension; use crate::*; use crate::{Call, ColdkeySwapScheduleDuration, Error}; -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_coldkey -- test_swap_subnet_owner --exact --nocapture +#[test] +fn test_announce_coldkey_swap_works() { + new_test_ext(1).execute_with(|| { + let who = U256::from(1); + let new_coldkey = U256::from(1); + + assert_eq!(ColdkeySwapAnnouncements::::iter().count(), 0); + + assert_ok!(SubtensorModule::announce_coldkey_swap( + RuntimeOrigin::signed(who.clone()), + new_coldkey, + )); + + let now = System::block_number(); + assert_eq!( + ColdkeySwapAnnouncements::::iter().collect::>(), + vec![(who.clone(), (now, new_coldkey))] + ); + assert_eq!( + last_event(), + RuntimeEvent::SubtensorModule(Event::ColdkeySwapAnnounced { + who, + new_coldkey, + block_number: now, + }) + ); + }); +} + +#[test] +fn test_announce_coldkey_swap_bad_origin_fails() { + new_test_ext(1).execute_with(|| { + let new_coldkey = U256::from(1); + + assert_noop!( + SubtensorModule::announce_coldkey_swap(RuntimeOrigin::none(), new_coldkey), + BadOrigin + ); + + assert_noop!( + SubtensorModule::announce_coldkey_swap(RuntimeOrigin::root(), new_coldkey), + BadOrigin + ); + }); +} + +#[test] +fn test_remove_coldkey_swap_announcement_works() { + new_test_ext(1).execute_with(|| { + let who = U256::from(1); + let new_coldkey = U256::from(2); + let now = System::block_number(); + ColdkeySwapAnnouncements::::insert(who.clone(), (now, new_coldkey)); + + assert_ok!(SubtensorModule::remove_coldkey_swap_announcement( + RuntimeOrigin::signed(who.clone()), + )); + + assert_eq!(ColdkeySwapAnnouncements::::iter().count(), 0); + assert_eq!( + last_event(), + RuntimeEvent::SubtensorModule(Event::ColdkeySwapAnnouncementRemoved { who }) + ); + }); +} + +#[test] +fn test_remove_coldkey_swap_announcement_fails_if_no_announcement_exists() { + new_test_ext(1).execute_with(|| { + let who = U256::from(1); + let new_coldkey = U256::from(2); + + assert_noop!( + SubtensorModule::remove_coldkey_swap_announcement(RuntimeOrigin::signed(who.clone())), + Error::::ColdkeySwapAnnouncementNotFound + ); + }); +} + +#[test] +fn test_remove_coldkey_swap_announcement_bad_origin_fails() { + new_test_ext(1).execute_with(|| { + assert_noop!( + SubtensorModule::remove_coldkey_swap_announcement(RuntimeOrigin::none()), + BadOrigin + ); + + assert_noop!( + SubtensorModule::remove_coldkey_swap_announcement(RuntimeOrigin::root()), + BadOrigin + ); + }); +} + #[test] fn test_swap_subnet_owner() { new_test_ext(1).execute_with(|| { @@ -2539,3 +2632,39 @@ fn test_swap_auto_stake_destination_coldkeys() { ); }); } + +#[test] +#[allow(deprecated)] +fn test_swap_coldkey_deprecated() { + new_test_ext(1).execute_with(|| { + let old_coldkey = U256::from(1); + let new_coldkey = U256::from(2); + + assert_noop!( + SubtensorModule::swap_coldkey( + <::RuntimeOrigin>::root(), + old_coldkey, + new_coldkey, + TaoCurrency::MAX + ), + Error::::Deprecated + ); + }); +} + +#[test] +#[allow(deprecated)] +fn test_schedule_swap_coldkey_deprecated() { + new_test_ext(1).execute_with(|| { + let old_coldkey = U256::from(1); + let new_coldkey = U256::from(2); + + assert_noop!( + SubtensorModule::schedule_swap_coldkey( + <::RuntimeOrigin>::root(), + new_coldkey, + ), + Error::::Deprecated + ); + }); +} From a6f7fc6b44883eb7d53abaa760ac27a611080a74 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Sun, 30 Nov 2025 21:52:41 +0100 Subject: [PATCH 018/240] No need to remove because we take --- pallets/subtensor/src/macros/dispatches.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 3ec118a563..8a6d8fd610 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2387,8 +2387,6 @@ mod dispatches { Self::do_swap_coldkey(&who, &new_coldkey)?; - ColdkeySwapAnnouncements::::remove(who.clone()); - Ok(()) } } From 40f4d9ad246417148e5baa6bda6cf8bd42e2285c Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Sun, 30 Nov 2025 22:33:02 +0100 Subject: [PATCH 019/240] renamed to swap_coldkey_announced --- pallets/subtensor/src/macros/dispatches.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 8a6d8fd610..f9da6ef853 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2375,7 +2375,7 @@ mod dispatches { /// Performs a coldkey swap iff an announcement has been made. #[pallet::call_index(127)] #[pallet::weight(Weight::zero())] - pub fn coldkey_swap_announced(origin: OriginFor) -> DispatchResult { + pub fn swap_coldkey_announced(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; let (when, new_coldkey) = ColdkeySwapAnnouncements::::take(who.clone()) From c5b033043047400ee85ff0aeed193decfff2c8ab Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Tue, 2 Dec 2025 17:07:02 +0100 Subject: [PATCH 020/240] update SubtensorTransactionExtension --- .../subtensor/src/transaction_extension.rs | 134 +++++++----------- 1 file changed, 52 insertions(+), 82 deletions(-) diff --git a/pallets/subtensor/src/transaction_extension.rs b/pallets/subtensor/src/transaction_extension.rs index cf1d410ea9..7a9e226eb7 100644 --- a/pallets/subtensor/src/transaction_extension.rs +++ b/pallets/subtensor/src/transaction_extension.rs @@ -1,24 +1,24 @@ -use crate::{ - BalancesCall, Call, ColdkeySwapScheduled, Config, CustomTransactionError, Error, Pallet, - TransactionType, -}; +use crate::{BalancesCall, Call, Config, CustomTransactionError, Error, Pallet, TransactionType}; use codec::{Decode, DecodeWithMemTracking, Encode}; use frame_support::dispatch::{DispatchInfo, PostDispatchInfo}; -use frame_support::pallet_prelude::Weight; use frame_support::traits::IsSubType; use scale_info::TypeInfo; use sp_runtime::traits::{ AsSystemOriginSigner, DispatchInfoOf, Dispatchable, Implication, TransactionExtension, ValidateResult, }; -use sp_runtime::transaction_validity::{ - TransactionSource, TransactionValidity, TransactionValidityError, ValidTransaction, +use sp_runtime::{ + impl_tx_ext_default, + transaction_validity::{TransactionSource, TransactionValidity, ValidTransaction}, }; use sp_std::marker::PhantomData; use sp_std::vec::Vec; use subtensor_macros::freeze_struct; use subtensor_runtime_common::{NetUid, NetUidStorageIndex}; +type CallOf = ::RuntimeCall; +type OriginOf = ::RuntimeOrigin; + #[freeze_struct("2e02eb32e5cb25d3")] #[derive(Default, Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, TypeInfo)] pub struct SubtensorTransactionExtension(pub PhantomData); @@ -31,9 +31,7 @@ impl sp_std::fmt::Debug for SubtensorTransac impl SubtensorTransactionExtension where - ::RuntimeCall: - Dispatchable, - ::RuntimeCall: IsSubType>, + CallOf: Dispatchable + IsSubType>, { pub fn new() -> Self { Self(Default::default()) @@ -52,30 +50,29 @@ where pub fn result_to_validity(result: Result<(), Error>, priority: u64) -> TransactionValidity { if let Err(err) = result { Err(match err { - Error::::AmountTooLow => CustomTransactionError::StakeAmountTooLow.into(), - Error::::SubnetNotExists => CustomTransactionError::SubnetNotExists.into(), - Error::::NotEnoughBalanceToStake => CustomTransactionError::BalanceTooLow.into(), + Error::::AmountTooLow => CustomTransactionError::StakeAmountTooLow, + Error::::SubnetNotExists => CustomTransactionError::SubnetNotExists, + Error::::NotEnoughBalanceToStake => CustomTransactionError::BalanceTooLow, Error::::HotKeyAccountNotExists => { - CustomTransactionError::HotkeyAccountDoesntExist.into() + CustomTransactionError::HotkeyAccountDoesntExist } Error::::NotEnoughStakeToWithdraw => { - CustomTransactionError::NotEnoughStakeToWithdraw.into() - } - Error::::InsufficientLiquidity => { - CustomTransactionError::InsufficientLiquidity.into() + CustomTransactionError::NotEnoughStakeToWithdraw } - Error::::SlippageTooHigh => CustomTransactionError::SlippageTooHigh.into(), - Error::::TransferDisallowed => CustomTransactionError::TransferDisallowed.into(), + Error::::InsufficientLiquidity => CustomTransactionError::InsufficientLiquidity, + Error::::SlippageTooHigh => CustomTransactionError::SlippageTooHigh, + Error::::TransferDisallowed => CustomTransactionError::TransferDisallowed, Error::::HotKeyNotRegisteredInNetwork => { - CustomTransactionError::HotKeyNotRegisteredInNetwork.into() + CustomTransactionError::HotKeyNotRegisteredInNetwork } - Error::::InvalidIpAddress => CustomTransactionError::InvalidIpAddress.into(), + Error::::InvalidIpAddress => CustomTransactionError::InvalidIpAddress, Error::::ServingRateLimitExceeded => { - CustomTransactionError::ServingRateLimitExceeded.into() + CustomTransactionError::ServingRateLimitExceeded } - Error::::InvalidPort => CustomTransactionError::InvalidPort.into(), - _ => CustomTransactionError::BadRequest.into(), - }) + Error::::InvalidPort => CustomTransactionError::InvalidPort, + _ => CustomTransactionError::BadRequest, + } + .into()) } else { Ok(ValidTransaction { priority, @@ -89,52 +86,41 @@ impl TransactionExtension<::RuntimeCall> for SubtensorTransactionExtension where - ::RuntimeCall: - Dispatchable, - ::RuntimeOrigin: AsSystemOriginSigner + Clone, - ::RuntimeCall: IsSubType>, - ::RuntimeCall: IsSubType>, + CallOf: Dispatchable + + IsSubType> + + IsSubType>, + OriginOf: AsSystemOriginSigner + Clone, { const IDENTIFIER: &'static str = "SubtensorTransactionExtension"; type Implicit = (); - type Val = Option; + type Val = (); type Pre = (); - fn weight(&self, _call: &::RuntimeCall) -> Weight { - // TODO: benchmark transaction extension - Weight::zero() - } - fn validate( &self, - origin: ::RuntimeOrigin, - call: &::RuntimeCall, - _info: &DispatchInfoOf<::RuntimeCall>, + origin: OriginOf, + call: &CallOf, + _info: &DispatchInfoOf>, _len: usize, _self_implicit: Self::Implicit, _inherited_implication: &impl Implication, _source: TransactionSource, - ) -> ValidateResult::RuntimeCall> { + ) -> ValidateResult> { // Ensure the transaction is signed, else we just skip the extension. let Some(who) = origin.as_system_origin_signer() else { - return Ok((Default::default(), None, origin)); + return Ok((Default::default(), (), origin)); }; - // Verify ColdkeySwapScheduled map for coldkey - match call.is_sub_type() { - // Whitelist - Some(Call::schedule_swap_coldkey { .. }) => {} - _ => { - if ColdkeySwapScheduled::::contains_key(who) { - return Err(CustomTransactionError::ColdkeyInSwapSchedule.into()); - } - } + // Ensure the origin coldkey is not announced for a swap. + if !matches!(call.is_sub_type(), Some(Call::announce_coldkey_swap { .. })) { + return Err(CustomTransactionError::ColdkeySwapAnnounced.into()); } + match call.is_sub_type() { Some(Call::commit_weights { netuid, .. }) => { if Self::check_weights_min_stake(who, *netuid) { - Ok((Default::default(), Some(who.clone()), origin)) + Ok((Default::default(), (), origin)) } else { Err(CustomTransactionError::StakeAmountTooLow.into()) } @@ -158,7 +144,7 @@ where match Pallet::::find_commit_block_via_hash(provided_hash) { Some(commit_block) => { if Pallet::::is_reveal_block_range(*netuid, commit_block) { - Ok((Default::default(), Some(who.clone()), origin)) + Ok((Default::default(), (), origin)) } else { Err(CustomTransactionError::CommitBlockNotInRevealRange.into()) } @@ -203,7 +189,7 @@ where if provided_hashes.len() == batch_reveal_block.len() { if Pallet::::is_batch_reveal_block_range(*netuid, batch_reveal_block) { - Ok((Default::default(), Some(who.clone()), origin)) + Ok((Default::default(), (), origin)) } else { Err(CustomTransactionError::CommitBlockNotInRevealRange.into()) } @@ -219,7 +205,7 @@ where } Some(Call::set_weights { netuid, .. }) => { if Self::check_weights_min_stake(who, *netuid) { - Ok((Default::default(), Some(who.clone()), origin)) + Ok((Default::default(), (), origin)) } else { Err(CustomTransactionError::StakeAmountTooLow.into()) } @@ -233,7 +219,7 @@ where if *reveal_round < pallet_drand::LastStoredRound::::get() { return Err(CustomTransactionError::InvalidRevealRound.into()); } - Ok((Default::default(), Some(who.clone()), origin)) + Ok((Default::default(), (), origin)) } else { Err(CustomTransactionError::StakeAmountTooLow.into()) } @@ -249,7 +235,7 @@ where return Err(CustomTransactionError::RateLimitExceeded.into()); } - Ok((Default::default(), Some(who.clone()), origin)) + Ok((Default::default(), (), origin)) } Some(Call::serve_axon { netuid, @@ -276,41 +262,25 @@ where ), 0u64, ) - .map(|validity| (validity, Some(who.clone()), origin.clone())) + .map(|validity| (validity, (), origin.clone())) } Some(Call::register_network { .. }) => { if !TransactionType::RegisterNetwork.passes_rate_limit::(who) { return Err(CustomTransactionError::RateLimitExceeded.into()); } - Ok((Default::default(), Some(who.clone()), origin)) + Ok((Default::default(), (), origin)) } Some(Call::associate_evm_key { netuid, .. }) => { - match Pallet::::get_uid_for_net_and_hotkey(*netuid, who) { - Ok(uid) => { - match Pallet::::ensure_evm_key_associate_rate_limit(*netuid, uid) { - Ok(_) => Ok((Default::default(), Some(who.clone()), origin)), - Err(_) => { - Err(CustomTransactionError::EvmKeyAssociateRateLimitExceeded.into()) - } - } - } - Err(_) => Err(CustomTransactionError::UidNotFound.into()), - } + let uid = Pallet::::get_uid_for_net_and_hotkey(*netuid, who) + .map_err(|_| CustomTransactionError::UidNotFound)?; + Pallet::::ensure_evm_key_associate_rate_limit(*netuid, uid) + .map_err(|_| CustomTransactionError::EvmKeyAssociateRateLimitExceeded)?; + Ok((Default::default(), (), origin)) } - _ => Ok((Default::default(), Some(who.clone()), origin)), + _ => Ok((Default::default(), (), origin)), } } - // NOTE: Add later when we put in a pre and post dispatch step. - fn prepare( - self, - _val: Self::Val, - _origin: &::RuntimeOrigin, - _call: &::RuntimeCall, - _info: &DispatchInfoOf<::RuntimeCall>, - _len: usize, - ) -> Result { - Ok(()) - } + impl_tx_ext_default!(::RuntimeCall; weight prepare); } From e96625a06b9c321ccc167806de4da47883cb64cf Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Tue, 2 Dec 2025 20:05:08 +0100 Subject: [PATCH 021/240] fix announcement --- pallets/subtensor/src/macros/dispatches.rs | 32 ++++----- pallets/subtensor/src/macros/errors.rs | 2 + pallets/subtensor/src/tests/swap_coldkey.rs | 79 +++++++++++++-------- 3 files changed, 64 insertions(+), 49 deletions(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index f9da6ef853..e64f7eae53 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2345,6 +2345,15 @@ mod dispatches { let who = ensure_signed(origin)?; let now = >::block_number(); + if let Some(existing) = ColdkeySwapAnnouncements::::get(who.clone()) { + let delay = ColdkeySwapScheduleDuration::::get(); + let when = existing.0; + ensure!( + now > when + delay, + Error::::ColdkeySwapReannouncedTooEarly + ); + } + ColdkeySwapAnnouncements::::insert(who.clone(), (now, new_coldkey.clone())); Self::deposit_event(Event::ColdkeySwapAnnounced { @@ -2355,25 +2364,8 @@ mod dispatches { Ok(()) } - /// Removes a coldkey swap announcement. - #[pallet::call_index(126)] - #[pallet::weight(Weight::zero())] - pub fn remove_coldkey_swap_announcement(origin: OriginFor) -> DispatchResult { - let who = ensure_signed(origin)?; - - ensure!( - ColdkeySwapAnnouncements::::contains_key(who.clone()), - Error::::ColdkeySwapAnnouncementNotFound - ); - - ColdkeySwapAnnouncements::::remove(who.clone()); - - Self::deposit_event(Event::ColdkeySwapAnnouncementRemoved { who: who.clone() }); - Ok(()) - } - /// Performs a coldkey swap iff an announcement has been made. - #[pallet::call_index(127)] + #[pallet::call_index(126)] #[pallet::weight(Weight::zero())] pub fn swap_coldkey_announced(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; @@ -2382,8 +2374,8 @@ mod dispatches { .ok_or(Error::::ColdkeySwapAnnouncementNotFound)?; let now = >::block_number(); - let delay = when + ColdkeySwapScheduleDuration::::get(); - ensure!(now >= delay, Error::::ColdkeySwapTooEarly); + let delay = ColdkeySwapScheduleDuration::::get(); + ensure!(now > when + delay, Error::::ColdkeySwapTooEarly); Self::do_swap_coldkey(&who, &new_coldkey)?; diff --git a/pallets/subtensor/src/macros/errors.rs b/pallets/subtensor/src/macros/errors.rs index e3221b573a..e83a7fc747 100644 --- a/pallets/subtensor/src/macros/errors.rs +++ b/pallets/subtensor/src/macros/errors.rs @@ -154,6 +154,8 @@ mod errors { ColdkeySwapAnnouncementNotFound, /// Coldkey swap too early. ColdkeySwapTooEarly, + /// Coldkey swap reannounced too early. + ColdkeySwapReannouncedTooEarly, /// New coldkey is hotkey NewColdKeyIsHotkey, /// Childkey take is invalid. diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index 9f0787ec2f..989317c3bd 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -29,10 +29,10 @@ use crate::*; use crate::{Call, ColdkeySwapScheduleDuration, Error}; #[test] -fn test_announce_coldkey_swap_works() { +fn test_announce_coldkey_swap_with_no_announcement_works() { new_test_ext(1).execute_with(|| { let who = U256::from(1); - let new_coldkey = U256::from(1); + let new_coldkey = U256::from(2); assert_eq!(ColdkeySwapAnnouncements::::iter().count(), 0); @@ -58,65 +58,86 @@ fn test_announce_coldkey_swap_works() { } #[test] -fn test_announce_coldkey_swap_bad_origin_fails() { +fn test_announce_coldkey_swap_with_existing_announcement_past_delay_works() { new_test_ext(1).execute_with(|| { - let new_coldkey = U256::from(1); + let who = U256::from(1); + let new_coldkey = U256::from(2); + let new_coldkey_2 = U256::from(3); - assert_noop!( - SubtensorModule::announce_coldkey_swap(RuntimeOrigin::none(), new_coldkey), - BadOrigin + assert_eq!(ColdkeySwapAnnouncements::::iter().count(), 0); + + assert_ok!(SubtensorModule::announce_coldkey_swap( + RuntimeOrigin::signed(who.clone()), + new_coldkey, + )); + + let now = System::block_number(); + assert_eq!( + ColdkeySwapAnnouncements::::iter().collect::>(), + vec![(who.clone(), (now, new_coldkey))] ); - assert_noop!( - SubtensorModule::announce_coldkey_swap(RuntimeOrigin::root(), new_coldkey), - BadOrigin + let delay = ColdkeySwapScheduleDuration::::get() + 1; + System::run_to_block::(now + delay); + + assert_ok!(SubtensorModule::announce_coldkey_swap( + RuntimeOrigin::signed(who.clone()), + new_coldkey_2, + )); + + let now = System::block_number(); + assert_eq!( + ColdkeySwapAnnouncements::::iter().collect::>(), + vec![(who.clone(), (now, new_coldkey_2))] ); }); } #[test] -fn test_remove_coldkey_swap_announcement_works() { +fn test_announce_coldkey_swap_with_existing_announcement_not_past_delay_fails() { new_test_ext(1).execute_with(|| { let who = U256::from(1); let new_coldkey = U256::from(2); - let now = System::block_number(); - ColdkeySwapAnnouncements::::insert(who.clone(), (now, new_coldkey)); + let new_coldkey_2 = U256::from(3); + + assert_eq!(ColdkeySwapAnnouncements::::iter().count(), 0); - assert_ok!(SubtensorModule::remove_coldkey_swap_announcement( + assert_ok!(SubtensorModule::announce_coldkey_swap( RuntimeOrigin::signed(who.clone()), + new_coldkey, )); - assert_eq!(ColdkeySwapAnnouncements::::iter().count(), 0); + let now = System::block_number(); assert_eq!( - last_event(), - RuntimeEvent::SubtensorModule(Event::ColdkeySwapAnnouncementRemoved { who }) + ColdkeySwapAnnouncements::::iter().collect::>(), + vec![(who.clone(), (now, new_coldkey))] ); - }); -} -#[test] -fn test_remove_coldkey_swap_announcement_fails_if_no_announcement_exists() { - new_test_ext(1).execute_with(|| { - let who = U256::from(1); - let new_coldkey = U256::from(2); + let unmet_delay = ColdkeySwapScheduleDuration::::get(); + System::run_to_block::(now + unmet_delay); assert_noop!( - SubtensorModule::remove_coldkey_swap_announcement(RuntimeOrigin::signed(who.clone())), - Error::::ColdkeySwapAnnouncementNotFound + SubtensorModule::announce_coldkey_swap( + RuntimeOrigin::signed(who.clone()), + new_coldkey_2, + ), + Error::::ColdkeySwapReannouncedTooEarly ); }); } #[test] -fn test_remove_coldkey_swap_announcement_bad_origin_fails() { +fn test_announce_coldkey_swap_with_bad_origin_fails() { new_test_ext(1).execute_with(|| { + let new_coldkey = U256::from(1); + assert_noop!( - SubtensorModule::remove_coldkey_swap_announcement(RuntimeOrigin::none()), + SubtensorModule::announce_coldkey_swap(RuntimeOrigin::none(), new_coldkey), BadOrigin ); assert_noop!( - SubtensorModule::remove_coldkey_swap_announcement(RuntimeOrigin::root()), + SubtensorModule::announce_coldkey_swap(RuntimeOrigin::root(), new_coldkey), BadOrigin ); }); From 949e93643757e1276b4efd84b9edd96f5a67afea Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Tue, 2 Dec 2025 20:07:25 +0100 Subject: [PATCH 022/240] fix most tests --- pallets/subtensor/src/tests/swap_coldkey.rs | 804 +++++--------------- 1 file changed, 207 insertions(+), 597 deletions(-) diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index 989317c3bd..060d87c36d 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -8,7 +8,7 @@ use approx::assert_abs_diff_eq; use codec::Encode; -use frame_support::dispatch::DispatchInfo; +use frame_support::dispatch::{DispatchInfo, GetDispatchInfo}; use frame_support::error::BadOrigin; use frame_support::traits::OnInitialize; use frame_support::traits::schedule::DispatchTime; @@ -16,7 +16,7 @@ use frame_support::traits::schedule::v3::Named as ScheduleNamed; use frame_support::{assert_err, assert_noop, assert_ok}; use frame_system::{Config, RawOrigin}; use sp_core::{Get, H256, U256}; -use sp_runtime::traits::{DispatchInfoOf, TransactionExtension}; +use sp_runtime::traits::{DispatchInfoOf, DispatchTransaction, TransactionExtension}; use sp_runtime::{DispatchError, traits::TxBaseImplication}; use substrate_fixed::types::U96F32; use subtensor_runtime_common::{AlphaCurrency, Currency, SubnetInfo, TaoCurrency}; @@ -153,16 +153,15 @@ fn test_swap_subnet_owner() { add_network(netuid, 1, 0); SubnetOwner::::insert(netuid, old_coldkey); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &old_coldkey, - &new_coldkey, - )); + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); + + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); assert_eq!(SubnetOwner::::get(netuid), new_coldkey); }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::swap_coldkey::test_swap_total_coldkey_stake --exact --show-output #[test] fn test_swap_total_coldkey_stake() { new_test_ext(1).execute_with(|| { @@ -196,10 +195,10 @@ fn test_swap_total_coldkey_stake() { )); let total_stake_before_swap = SubtensorModule::get_total_stake_for_coldkey(&old_coldkey); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &old_coldkey, - &new_coldkey, - )); + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); + + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); assert_eq!( SubtensorModule::get_total_stake_for_coldkey(&old_coldkey), @@ -212,7 +211,6 @@ fn test_swap_total_coldkey_stake() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_coldkey -- test_swap_staking_hotkeys --exact --nocapture #[test] fn test_swap_staking_hotkeys() { new_test_ext(1).execute_with(|| { @@ -222,17 +220,16 @@ fn test_swap_staking_hotkeys() { StakingHotkeys::::insert(old_coldkey, vec![hotkey]); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &old_coldkey, - &new_coldkey, - )); + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); + + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); assert!(StakingHotkeys::::get(old_coldkey).is_empty()); assert_eq!(StakingHotkeys::::get(new_coldkey), vec![hotkey]); }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_coldkey -- test_swap_hotkey_owners --exact --nocapture #[test] fn test_swap_hotkey_owners() { new_test_ext(1).execute_with(|| { @@ -243,17 +240,17 @@ fn test_swap_hotkey_owners() { Owner::::insert(hotkey, old_coldkey); OwnedHotkeys::::insert(old_coldkey, vec![hotkey]); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &old_coldkey, - &new_coldkey, - )); + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); + + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); assert_eq!(Owner::::get(hotkey), new_coldkey); assert!(OwnedHotkeys::::get(old_coldkey).is_empty()); assert_eq!(OwnedHotkeys::::get(new_coldkey), vec![hotkey]); }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_coldkey -- test_transfer_remaining_balance --exact --nocapture + #[test] fn test_transfer_remaining_balance() { new_test_ext(1).execute_with(|| { @@ -263,27 +260,26 @@ fn test_transfer_remaining_balance() { SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, balance); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &old_coldkey, - &new_coldkey, - )); + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); + + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); assert_eq!(SubtensorModule::get_coldkey_balance(&old_coldkey), 0); assert_eq!(SubtensorModule::get_coldkey_balance(&new_coldkey), balance); }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::swap_coldkey::test_swap_with_no_stake --exact --show-output #[test] fn test_swap_with_no_stake() { new_test_ext(1).execute_with(|| { let old_coldkey = U256::from(1); let new_coldkey = U256::from(2); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &old_coldkey, - &new_coldkey, - )); + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); + + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); assert_eq!( SubtensorModule::get_total_stake_for_coldkey(&old_coldkey), @@ -296,7 +292,6 @@ fn test_swap_with_no_stake() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_coldkey -- test_swap_with_multiple_hotkeys --exact --nocapture #[test] fn test_swap_with_multiple_hotkeys() { new_test_ext(1).execute_with(|| { @@ -307,10 +302,10 @@ fn test_swap_with_multiple_hotkeys() { OwnedHotkeys::::insert(old_coldkey, vec![hotkey1, hotkey2]); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &old_coldkey, - &new_coldkey, - )); + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); + + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); assert!(OwnedHotkeys::::get(old_coldkey).is_empty()); assert_eq!( @@ -320,7 +315,6 @@ fn test_swap_with_multiple_hotkeys() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_coldkey -- test_swap_with_multiple_subnets --exact --nocapture #[test] fn test_swap_with_multiple_subnets() { new_test_ext(1).execute_with(|| { @@ -334,82 +328,51 @@ fn test_swap_with_multiple_subnets() { SubnetOwner::::insert(netuid1, old_coldkey); SubnetOwner::::insert(netuid2, old_coldkey); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &old_coldkey, - &new_coldkey, - )); + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); + + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); assert_eq!(SubnetOwner::::get(netuid1), new_coldkey); assert_eq!(SubnetOwner::::get(netuid2), new_coldkey); }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_coldkey -- test_swap_with_zero_balance --exact --nocapture +// TODO #[test] fn test_swap_with_zero_balance() { new_test_ext(1).execute_with(|| { let old_coldkey = U256::from(1); let new_coldkey = U256::from(2); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &old_coldkey, - &new_coldkey, - )); - - assert_eq!(Balances::free_balance(old_coldkey), 0); - assert_eq!(Balances::free_balance(new_coldkey), 0); - }); -} - -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::swap_coldkey::test_swap_idempotency --exact --show-output -#[test] -fn test_swap_idempotency() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - let hotkey = U256::from(3); - let netuid = NetUid::from(1u16); - let stake = DefaultMinStake::::get().to_u64() * 10; - let reserve = stake * 10; - - mock::setup_reserves(netuid, reserve.into(), reserve.into()); - - // Add a network - add_network(netuid, 1, 0); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, stake); // Give old coldkey some balance - // Stake to a hotkey - register_ok_neuron(netuid, hotkey, old_coldkey, 1001000); - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(old_coldkey), - hotkey, - netuid, - stake.into() - )); + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); - // Get stake before swap - let stake_before_swap = SubtensorModule::get_total_stake_for_coldkey(&old_coldkey); + println!( + "old_coldkey balance: {}", + Balances::free_balance(old_coldkey) + ); + println!( + "new_coldkey balance: {}", + Balances::free_balance(new_coldkey) + ); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &old_coldkey, - &new_coldkey, - )); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &old_coldkey, - &new_coldkey, - )); + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); - assert_eq!( - SubtensorModule::get_total_stake_for_coldkey(&old_coldkey), - TaoCurrency::ZERO + println!( + "old_coldkey balance: {}", + Balances::free_balance(old_coldkey) ); - assert_eq!( - SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), - stake_before_swap + println!( + "new_coldkey balance: {}", + Balances::free_balance(new_coldkey) ); + + assert_eq!(Balances::free_balance(old_coldkey), 0); + assert_eq!(Balances::free_balance(new_coldkey), 0); }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::swap_coldkey::test_swap_with_max_values --exact --show-output #[test] fn test_swap_with_max_values() { new_test_ext(1).execute_with(|| { @@ -467,13 +430,14 @@ fn test_swap_with_max_values() { netuid2, ); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &old_coldkey, - &new_coldkey, - )); - assert_ok!(SubtensorModule::perform_swap_coldkey( + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey2, swap_cost.to_u64()); + + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); + assert_ok!(SubtensorModule::do_swap_coldkey( &old_coldkey2, - &new_coldkey2, + &new_coldkey2 )); assert_eq!( @@ -497,7 +461,6 @@ fn test_swap_with_max_values() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::swap_coldkey::test_swap_with_non_existent_new_coldkey --exact --show-output #[test] fn test_swap_with_non_existent_new_coldkey() { new_test_ext(1).execute_with(|| { @@ -528,10 +491,10 @@ fn test_swap_with_non_existent_new_coldkey() { netuid, ); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &old_coldkey, - &new_coldkey, - )); + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); + + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey)); assert_eq!( SubtensorModule::get_total_stake_for_coldkey(&old_coldkey), @@ -551,7 +514,6 @@ fn test_swap_with_non_existent_new_coldkey() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_coldkey -- test_swap_with_max_hotkeys --exact --nocapture #[test] fn test_swap_with_max_hotkeys() { new_test_ext(1).execute_with(|| { @@ -562,17 +524,16 @@ fn test_swap_with_max_hotkeys() { OwnedHotkeys::::insert(old_coldkey, hotkeys.clone()); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &old_coldkey, - &new_coldkey, - )); + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); + + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey)); assert!(OwnedHotkeys::::get(old_coldkey).is_empty()); assert_eq!(OwnedHotkeys::::get(new_coldkey), hotkeys); }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_coldkey -- test_swap_effect_on_delegated_stake --exact --nocapture #[test] fn test_swap_effect_on_delegated_stake() { new_test_ext(1).execute_with(|| { @@ -607,10 +568,10 @@ fn test_swap_effect_on_delegated_stake() { let coldkey_stake_before = SubtensorModule::get_total_stake_for_coldkey(&old_coldkey); let delegator_stake_before = SubtensorModule::get_total_stake_for_coldkey(&delegator); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &old_coldkey, - &new_coldkey, - )); + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); + + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); assert_abs_diff_eq!( SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), @@ -630,7 +591,7 @@ fn test_swap_effect_on_delegated_stake() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::swap_coldkey::test_swap_concurrent_modifications --exact --show-output +// TODO #[test] fn test_swap_concurrent_modifications() { new_test_ext(1).execute_with(|| { @@ -683,10 +644,10 @@ fn test_swap_concurrent_modifications() { netuid, ); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &old_coldkey, - &new_coldkey, - )); + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); + + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); assert_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -701,7 +662,6 @@ fn test_swap_concurrent_modifications() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_coldkey -- test_swap_with_invalid_subnet_ownership --exact --nocapture #[test] fn test_swap_with_invalid_subnet_ownership() { new_test_ext(1).execute_with(|| { @@ -714,17 +674,16 @@ fn test_swap_with_invalid_subnet_ownership() { // Simulate an invalid state where the subnet owner doesn't match the old_coldkey SubnetOwner::::insert(netuid, U256::from(3)); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &old_coldkey, - &new_coldkey, - )); + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); + + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); // The swap should not affect the mismatched subnet ownership assert_eq!(SubnetOwner::::get(netuid), U256::from(3)); }); } -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --package pallet-subtensor --lib -- tests::swap_coldkey::test_do_swap_coldkey_success --exact --show-output #[test] fn test_do_swap_coldkey_success() { new_test_ext(1).execute_with(|| { @@ -819,12 +778,7 @@ fn test_do_swap_coldkey_success() { let total_ck_stake = SubtensorModule::get_total_stake_for_coldkey(&old_coldkey); // Perform the swap - assert_ok!(SubtensorModule::do_swap_coldkey( - // <::RuntimeOrigin>::signed(old_coldkey), - &old_coldkey, - &new_coldkey, - swap_cost - )); + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); // Log state after swap log::info!( @@ -903,7 +857,6 @@ fn test_do_swap_coldkey_success() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --package pallet-subtensor --lib -- tests::swap_coldkey::test_swap_stake_for_coldkey --exact --show-output #[test] fn test_swap_stake_for_coldkey() { new_test_ext(1).execute_with(|| { @@ -983,8 +936,11 @@ fn test_swap_stake_for_coldkey() { let initial_total_hotkey1_stake = SubtensorModule::get_total_stake_for_hotkey(&hotkey1); let initial_total_hotkey2_stake = SubtensorModule::get_total_stake_for_hotkey(&hotkey2); + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); + // Perform the swap - SubtensorModule::perform_swap_coldkey(&old_coldkey, &new_coldkey); + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); // Verify stake is additive, not replaced assert_abs_diff_eq!( @@ -1058,7 +1014,6 @@ fn test_swap_stake_for_coldkey() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --package pallet-subtensor --lib -- tests::swap_coldkey::test_swap_staking_hotkeys_for_coldkey --exact --show-output #[test] fn test_swap_staking_hotkeys_for_coldkey() { new_test_ext(1).execute_with(|| { @@ -1110,8 +1065,11 @@ fn test_swap_staking_hotkeys_for_coldkey() { netuid, ); + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); + // Perform the swap - SubtensorModule::perform_swap_coldkey(&old_coldkey, &new_coldkey); + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); // Verify StakingHotkeys transfer assert_eq!( @@ -1122,7 +1080,6 @@ fn test_swap_staking_hotkeys_for_coldkey() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --package pallet-subtensor --lib -- tests::swap_coldkey::test_swap_delegated_stake_for_coldkey --exact --show-output #[test] fn test_swap_delegated_stake_for_coldkey() { new_test_ext(1).execute_with(|| { @@ -1195,8 +1152,11 @@ fn test_swap_delegated_stake_for_coldkey() { let total_hotkey1_stake = SubtensorModule::get_total_stake_for_hotkey(&hotkey1); let total_hotkey2_stake = SubtensorModule::get_total_stake_for_hotkey(&hotkey2); + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); + // Perform the swap - SubtensorModule::perform_swap_coldkey(&old_coldkey, &new_coldkey); + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); // Verify stake transfer assert_eq!( @@ -1266,7 +1226,6 @@ fn test_swap_delegated_stake_for_coldkey() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test swap_coldkey -- test_swap_subnet_owner_for_coldkey --exact --nocapture #[test] fn test_swap_subnet_owner_for_coldkey() { new_test_ext(1).execute_with(|| { @@ -1284,8 +1243,11 @@ fn test_swap_subnet_owner_for_coldkey() { // Set up TotalNetworks TotalNetworks::::put(3); + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); + // Perform the swap - SubtensorModule::perform_swap_coldkey(&old_coldkey, &new_coldkey); + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); // Verify the swap assert_eq!(SubnetOwner::::get(netuid1), new_coldkey); @@ -1293,7 +1255,6 @@ fn test_swap_subnet_owner_for_coldkey() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test swap_coldkey -- test_do_swap_coldkey_with_subnet_ownership --exact --nocapture #[test] fn test_do_swap_coldkey_with_subnet_ownership() { new_test_ext(1).execute_with(|| { @@ -1317,18 +1278,17 @@ fn test_do_swap_coldkey_with_subnet_ownership() { // Populate OwnedHotkeys map OwnedHotkeys::::insert(old_coldkey, vec![hotkey]); + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); + // Perform the swap - assert_ok!(SubtensorModule::do_swap_coldkey( - &old_coldkey, - &new_coldkey, - swap_cost.into() - )); + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); // Verify subnet ownership transfer assert_eq!(SubnetOwner::::get(netuid), new_coldkey); }); } -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test swap_coldkey -- test_coldkey_has_associated_hotkeys --exact --nocapture + #[test] fn test_coldkey_has_associated_hotkeys() { new_test_ext(1).execute_with(|| { @@ -1343,7 +1303,6 @@ fn test_coldkey_has_associated_hotkeys() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --package pallet-subtensor --lib -- tests::swap_coldkey::test_coldkey_swap_total --exact --show-output #[test] fn test_coldkey_swap_total() { new_test_ext(1).execute_with(|| { @@ -1562,16 +1521,16 @@ fn test_coldkey_swap_total() { vec![hotkey3, delegate3] ); + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, swap_cost.to_u64()); + // Perform the swap let new_coldkey = U256::from(1100); assert_eq!( SubtensorModule::get_total_stake_for_coldkey(&coldkey), ck_stake ); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &coldkey, - &new_coldkey, - )); + assert_ok!(SubtensorModule::do_swap_coldkey(&coldkey, &new_coldkey,)); assert_eq!( SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), ck_stake @@ -1660,7 +1619,6 @@ fn test_coldkey_swap_total() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --package pallet-subtensor --lib -- tests::swap_coldkey::test_coldkey_delegations --exact --show-output #[test] fn test_coldkey_delegations() { new_test_ext(1).execute_with(|| { @@ -1706,11 +1664,11 @@ fn test_coldkey_delegations() { stake.into() )); + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, swap_cost.to_u64()); + // Perform the swap - assert_ok!(SubtensorModule::perform_swap_coldkey( - &coldkey, - &new_coldkey, - )); + assert_ok!(SubtensorModule::do_swap_coldkey(&coldkey, &new_coldkey,)); // Verify stake was moved for the delegate let approx_total_stake = TaoCurrency::from(stake * 2 - fee * 2); @@ -1746,74 +1704,6 @@ fn test_coldkey_delegations() { }); } -#[test] -fn test_schedule_swap_coldkey_success() { - new_test_ext(1).execute_with(|| { - // Initialize test accounts - let old_coldkey: U256 = U256::from(1); - let new_coldkey: U256 = U256::from(2); - - let swap_cost = SubtensorModule::get_key_swap_cost(); - - // Add balance to the old coldkey account - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64() + 1_000); - - // Schedule the coldkey swap - assert_ok!(SubtensorModule::schedule_swap_coldkey( - <::RuntimeOrigin>::signed(old_coldkey), - new_coldkey - )); - - // Get the current block number - let current_block: u64 = System::block_number(); - - // Calculate the expected execution block (5 days from now) - let expected_execution_block: u64 = current_block + 5 * 24 * 60 * 60 / 12; - - // Check for the SwapScheduled event - System::assert_last_event( - Event::ColdkeySwapScheduled { - old_coldkey, - new_coldkey, - execution_block: expected_execution_block, - swap_cost, - } - .into(), - ); - - // TODO: Add additional checks to ensure the swap is correctly scheduled in the system - // For example, verify that the swap is present in the appropriate storage or scheduler - }); -} - -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test swap_coldkey -- test_schedule_swap_coldkey_duplicate --exact --nocapture -#[test] -fn test_schedule_swap_coldkey_duplicate() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - - let swap_cost = SubtensorModule::get_key_swap_cost(); - - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64() + 2_000); - - assert_ok!(SubtensorModule::schedule_swap_coldkey( - <::RuntimeOrigin>::signed(old_coldkey), - new_coldkey - )); - - // Attempt to schedule again - assert_noop!( - SubtensorModule::schedule_swap_coldkey( - <::RuntimeOrigin>::signed(old_coldkey), - new_coldkey - ), - Error::::SwapAlreadyScheduled - ); - }); -} - -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --package pallet-subtensor --lib -- tests::swap_coldkey::test_schedule_swap_coldkey_execution --exact --show-output --nocapture #[test] fn test_schedule_swap_coldkey_execution() { new_test_ext(1).execute_with(|| { @@ -1844,28 +1734,16 @@ fn test_schedule_swap_coldkey_execution() { ); let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); // Schedule the swap - assert_ok!(SubtensorModule::schedule_swap_coldkey( - <::RuntimeOrigin>::signed(old_coldkey), - new_coldkey - )); + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); // Get the scheduled execution block let current_block = System::block_number(); let execution_block = current_block + ColdkeySwapScheduleDuration::::get(); - System::assert_last_event( - Event::ColdkeySwapScheduled { - old_coldkey, - new_coldkey, - execution_block, - swap_cost, - } - .into(), - ); - - run_to_block(execution_block - 1); + System::run_to_block::(execution_block - 1); let stake_before_swap = SubtensorModule::get_total_stake_for_coldkey(&old_coldkey); @@ -1909,121 +1787,6 @@ fn test_schedule_swap_coldkey_execution() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test swap_coldkey -- test_direct_swap_coldkey_call_fails --exact --nocapture -#[test] -fn test_direct_swap_coldkey_call_fails() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - - assert_noop!( - SubtensorModule::swap_coldkey( - <::RuntimeOrigin>::signed(old_coldkey), - old_coldkey, - new_coldkey, - TaoCurrency::ZERO - ), - BadOrigin - ); - }); -} - -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test swap_coldkey -- test_schedule_swap_coldkey_with_pending_swap --exact --nocapture -#[test] -fn test_schedule_swap_coldkey_with_pending_swap() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey1 = U256::from(2); - let new_coldkey2 = U256::from(3); - - let swap_cost = SubtensorModule::get_key_swap_cost(); - - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64() + 1_000); - - assert_ok!(SubtensorModule::schedule_swap_coldkey( - <::RuntimeOrigin>::signed(old_coldkey), - new_coldkey1 - )); - - // Attempt to schedule another swap before the first one executes - assert_noop!( - SubtensorModule::schedule_swap_coldkey( - <::RuntimeOrigin>::signed(old_coldkey), - new_coldkey2 - ), - Error::::SwapAlreadyScheduled - ); - }); -} - -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test swap_coldkey -- test_schedule_swap_coldkey_failure_and_reschedule --exact --nocapture -#[test] -fn test_schedule_swap_coldkey_failure_and_reschedule() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey1 = U256::from(2); - let new_coldkey2 = U256::from(3); - - let swap_cost = SubtensorModule::get_key_swap_cost(); - - // Two swaps - SubtensorModule::add_balance_to_coldkey_account( - &old_coldkey, - swap_cost.to_u64() + 1_000 * 2, - ); - - assert_ok!(SubtensorModule::schedule_swap_coldkey( - <::RuntimeOrigin>::signed(old_coldkey), - new_coldkey1 - )); - - let current_block = >::block_number(); - let duration = ColdkeySwapScheduleDuration::::get(); - let when = current_block.saturating_add(duration); - - // Setup first key to fail - // -- will fail if the new coldkey is already a hotkey (has an Owner) - Owner::::insert(new_coldkey1, U256::from(4)); - - // First swap fails - run_to_block(when - 1); - next_block(); - - // Check the failure - next_block(); // Still in the scheduled-swap map - assert!(ColdkeySwapScheduled::::contains_key(old_coldkey)); - - // Try to schedule the second swap - assert_noop!( - SubtensorModule::schedule_swap_coldkey( - <::RuntimeOrigin>::signed(old_coldkey), - new_coldkey2 - ), - Error::::SwapAlreadyScheduled - ); - - // Wait for correct duration after first swap fails - let fail_duration = ColdkeySwapRescheduleDuration::::get(); - run_to_block(when + fail_duration); - - // Schedule the second swap - assert_ok!(SubtensorModule::schedule_swap_coldkey( - <::RuntimeOrigin>::signed(old_coldkey), - new_coldkey2 - )); - - let current_block = >::block_number(); - let duration = ColdkeySwapScheduleDuration::::get(); - let when = current_block.saturating_add(duration); - run_to_block(when - 1); - next_block(); - - // Check the success - next_block(); // Now in the scheduled-swap map - assert!(!ColdkeySwapScheduled::::contains_key(old_coldkey)); - }); -} - #[test] fn test_coldkey_swap_delegate_identity_updated() { new_test_ext(1).execute_with(|| { @@ -2062,11 +1825,8 @@ fn test_coldkey_swap_delegate_identity_updated() { assert!(IdentitiesV2::::get(old_coldkey).is_some()); assert!(IdentitiesV2::::get(new_coldkey).is_none()); - assert_ok!(SubtensorModule::do_swap_coldkey( - &old_coldkey, - &new_coldkey, - burn_cost - )); + // Perform the swap + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); assert!(IdentitiesV2::::get(old_coldkey).is_none()); assert!(IdentitiesV2::::get(new_coldkey).is_some()); @@ -2103,11 +1863,7 @@ fn test_coldkey_swap_no_identity_no_changes() { assert!(IdentitiesV2::::get(old_coldkey).is_none()); // Perform the coldkey swap - assert_ok!(SubtensorModule::do_swap_coldkey( - &old_coldkey, - &new_coldkey, - burn_cost - )); + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); // Ensure no identities have been changed assert!(IdentitiesV2::::get(old_coldkey).is_none()); @@ -2153,11 +1909,7 @@ fn test_coldkey_swap_no_identity_no_changes_newcoldkey_exists() { assert!(IdentitiesV2::::get(old_coldkey).is_none()); // Perform the coldkey swap - assert_ok!(SubtensorModule::do_swap_coldkey( - &old_coldkey, - &new_coldkey, - burn_cost - )); + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); // Ensure no identities have been changed assert!(IdentitiesV2::::get(old_coldkey).is_none()); @@ -2165,7 +1917,6 @@ fn test_coldkey_swap_no_identity_no_changes_newcoldkey_exists() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test swap_coldkey -- test_cant_schedule_swap_without_enough_to_burn --exact --nocapture #[test] fn test_cant_schedule_swap_without_enough_to_burn() { new_test_ext(1).execute_with(|| { @@ -2173,18 +1924,13 @@ fn test_cant_schedule_swap_without_enough_to_burn() { let new_coldkey = U256::from(4); let hotkey = U256::from(5); - let burn_cost = SubtensorModule::get_key_swap_cost(); assert_noop!( - SubtensorModule::schedule_swap_coldkey( - <::RuntimeOrigin>::signed(old_coldkey), - new_coldkey - ), + SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey), Error::::NotEnoughBalanceToPaySwapColdKey ); }); } -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --package pallet-subtensor --lib -- tests::swap_coldkey::test_coldkey_in_swap_schedule_prevents_funds_usage --exact --show-output --nocapture #[test] fn test_coldkey_in_swap_schedule_prevents_funds_usage() { // Testing the signed extension validate function @@ -2241,17 +1987,12 @@ fn test_coldkey_in_swap_schedule_prevents_funds_usage() { )); // Schedule the coldkey for a swap - assert_ok!(SubtensorModule::schedule_swap_coldkey( - <::RuntimeOrigin>::signed(who), - new_coldkey + assert_ok!(SubtensorModule::announce_coldkey_swap( + ::RuntimeOrigin::signed(who), + new_coldkey, )); - assert!(ColdkeySwapScheduled::::contains_key(who)); - - // Setup the extension - let info: DispatchInfo = - DispatchInfoOf::<::RuntimeCall>::default(); - let extension = SubtensorTransactionExtension::::new(); + assert!(ColdkeySwapAnnouncements::::contains_key(who)); // Try each call @@ -2261,20 +2002,12 @@ fn test_coldkey_in_swap_schedule_prevents_funds_usage() { netuid, amount_staked: stake.into(), }); - let result = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); + let info = call.get_dispatch_info(); + let ext = SubtensorTransactionExtension::::new(); // Should fail - assert_eq!( - // Should get an invalid transaction error - result.unwrap_err(), - CustomTransactionError::ColdkeyInSwapSchedule.into() + assert_noop!( + ext.dispatch_transaction(RuntimeOrigin::signed(who).into(), call, &info, 0, 0), + CustomTransactionError::ColdkeySwapAnnounced ); // Add stake limit @@ -2285,20 +2018,11 @@ fn test_coldkey_in_swap_schedule_prevents_funds_usage() { limit_price: stake.into(), allow_partial: false, }); - let result = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - // Should fail - assert_eq!( - // Should get an invalid transaction error - result.unwrap_err(), - CustomTransactionError::ColdkeyInSwapSchedule.into() + let info = call.get_dispatch_info(); + let ext = SubtensorTransactionExtension::::new(); + assert_noop!( + ext.dispatch_transaction(RuntimeOrigin::signed(who).into(), call, &info, 0, 0), + CustomTransactionError::ColdkeySwapAnnounced ); // Swap stake @@ -2308,20 +2032,11 @@ fn test_coldkey_in_swap_schedule_prevents_funds_usage() { destination_netuid: netuid, alpha_amount: stake.into(), }); - let result = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - // Should fail - assert_eq!( - // Should get an invalid transaction error - result.unwrap_err(), - CustomTransactionError::ColdkeyInSwapSchedule.into() + let info = call.get_dispatch_info(); + let ext = SubtensorTransactionExtension::::new(); + assert_noop!( + ext.dispatch_transaction(RuntimeOrigin::signed(who).into(), call, &info, 0, 0), + CustomTransactionError::ColdkeySwapAnnounced ); // Swap stake limit @@ -2333,20 +2048,11 @@ fn test_coldkey_in_swap_schedule_prevents_funds_usage() { limit_price: stake.into(), allow_partial: false, }); - let result = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - // Should fail - assert_eq!( - // Should get an invalid transaction error - result.unwrap_err(), - CustomTransactionError::ColdkeyInSwapSchedule.into() + let info = call.get_dispatch_info(); + let ext = SubtensorTransactionExtension::::new(); + assert_noop!( + ext.dispatch_transaction(RuntimeOrigin::signed(who).into(), call, &info, 0, 0), + CustomTransactionError::ColdkeySwapAnnounced ); // Move stake @@ -2357,20 +2063,11 @@ fn test_coldkey_in_swap_schedule_prevents_funds_usage() { destination_netuid: netuid, alpha_amount: stake.into(), }); - let result = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - // Should fail - assert_eq!( - // Should get an invalid transaction error - result.unwrap_err(), - CustomTransactionError::ColdkeyInSwapSchedule.into() + let info = call.get_dispatch_info(); + let ext = SubtensorTransactionExtension::::new(); + assert_noop!( + ext.dispatch_transaction(RuntimeOrigin::signed(who).into(), call, &info, 0, 0), + CustomTransactionError::ColdkeySwapAnnounced ); // Transfer stake @@ -2381,20 +2078,11 @@ fn test_coldkey_in_swap_schedule_prevents_funds_usage() { destination_netuid: netuid, alpha_amount: stake.into(), }); - let result = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - // Should fail - assert_eq!( - // Should get an invalid transaction error - result.unwrap_err(), - CustomTransactionError::ColdkeyInSwapSchedule.into() + let info = call.get_dispatch_info(); + let ext = SubtensorTransactionExtension::::new(); + assert_noop!( + ext.dispatch_transaction(RuntimeOrigin::signed(who).into(), call, &info, 0, 0), + CustomTransactionError::ColdkeySwapAnnounced ); // Transfer all @@ -2402,20 +2090,11 @@ fn test_coldkey_in_swap_schedule_prevents_funds_usage() { dest: new_coldkey, keep_alive: false, }); - let result = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - // Should fail - assert_eq!( - // Should get an invalid transaction error - result.unwrap_err(), - CustomTransactionError::ColdkeyInSwapSchedule.into() + let info = call.get_dispatch_info(); + let ext = SubtensorTransactionExtension::::new(); + assert_noop!( + ext.dispatch_transaction(RuntimeOrigin::signed(who).into(), call, &info, 0, 0), + CustomTransactionError::ColdkeySwapAnnounced ); // Transfer keep alive @@ -2423,20 +2102,11 @@ fn test_coldkey_in_swap_schedule_prevents_funds_usage() { dest: new_coldkey, value: 100_000_000_000, }); - let result = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - // Should fail - assert_eq!( - // Should get an invalid transaction error - result.unwrap_err(), - CustomTransactionError::ColdkeyInSwapSchedule.into() + let info = call.get_dispatch_info(); + let ext = SubtensorTransactionExtension::::new(); + assert_noop!( + ext.dispatch_transaction(RuntimeOrigin::signed(who).into(), call, &info, 0, 0), + CustomTransactionError::ColdkeySwapAnnounced ); // Transfer allow death @@ -2444,38 +2114,20 @@ fn test_coldkey_in_swap_schedule_prevents_funds_usage() { dest: new_coldkey, value: 100_000_000_000, }); - let result = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - // Should fail - assert_eq!( - // Should get an invalid transaction error - result.unwrap_err(), - CustomTransactionError::ColdkeyInSwapSchedule.into() + let info = call.get_dispatch_info(); + let ext = SubtensorTransactionExtension::::new(); + assert_noop!( + ext.dispatch_transaction(RuntimeOrigin::signed(who).into(), call, &info, 0, 0), + CustomTransactionError::ColdkeySwapAnnounced ); // Burned register let call = RuntimeCall::SubtensorModule(SubtensorCall::burned_register { netuid, hotkey }); - let result = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - // Should fail - assert_eq!( - // Should get an invalid transaction error - result.unwrap_err(), - CustomTransactionError::ColdkeyInSwapSchedule.into() + let info = call.get_dispatch_info(); + let ext = SubtensorTransactionExtension::::new(); + assert_noop!( + ext.dispatch_transaction(RuntimeOrigin::signed(who).into(), call, &info, 0, 0), + CustomTransactionError::ColdkeySwapAnnounced ); remove_stake_rate_limit_for_tests(&hotkey, &coldkey, netuid); @@ -2486,20 +2138,11 @@ fn test_coldkey_in_swap_schedule_prevents_funds_usage() { netuid, amount_unstaked: (DefaultMinStake::::get().to_u64() * 2).into(), }); - let result = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - // Should fail - assert_eq!( - // Should get an invalid transaction error - result.unwrap_err(), - CustomTransactionError::ColdkeyInSwapSchedule.into() + let info = call.get_dispatch_info(); + let ext = SubtensorTransactionExtension::::new(); + assert_noop!( + ext.dispatch_transaction(RuntimeOrigin::signed(who).into(), call, &info, 0, 0), + CustomTransactionError::ColdkeySwapAnnounced ); // Remove stake limit @@ -2510,46 +2153,27 @@ fn test_coldkey_in_swap_schedule_prevents_funds_usage() { limit_price: 123456789.into(), // should be low enough allow_partial: true, }); - let result = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - // Should fail - assert_eq!( - // Should get an invalid transaction error - result.unwrap_err(), - CustomTransactionError::ColdkeyInSwapSchedule.into() + let info = call.get_dispatch_info(); + let ext = SubtensorTransactionExtension::::new(); + assert_noop!( + ext.dispatch_transaction(RuntimeOrigin::signed(who).into(), call, &info, 0, 0), + CustomTransactionError::ColdkeySwapAnnounced ); - // Schedule swap should succeed - let call = RuntimeCall::SubtensorModule(SubtensorCall::schedule_swap_coldkey { - new_coldkey: hotkey, - }); - let result = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - // Should be ok - assert_ok!(result); + // Announce coldkey swap should succeed + let call = + RuntimeCall::SubtensorModule(SubtensorCall::announce_coldkey_swap { new_coldkey }); + let info = call.get_dispatch_info(); + let ext = SubtensorTransactionExtension::::new(); + assert_ok!(ext.dispatch_transaction(RuntimeOrigin::signed(who).into(), call, &info, 0, 0)); }); } -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --package pallet-subtensor --lib -- tests::swap_coldkey::test_coldkey_in_swap_schedule_prevents_critical_calls --exact --show-output --nocapture #[test] -fn test_coldkey_in_swap_schedule_prevents_critical_calls() { +fn test_announced_coldkey_swap_prevents_critical_calls() { // Testing the signed extension validate function // correctly filters transactions that are critical - // while a coldkey swap is scheduled. + // while a coldkey swap is announced. new_test_ext(0).execute_with(|| { let netuid = NetUid::from(1); @@ -2587,37 +2211,24 @@ fn test_coldkey_in_swap_schedule_prevents_critical_calls() { )); // Schedule the coldkey for a swap - assert_ok!(SubtensorModule::schedule_swap_coldkey( - <::RuntimeOrigin>::signed(who), - new_coldkey + assert_ok!(SubtensorModule::announce_coldkey_swap( + ::RuntimeOrigin::signed(who), + new_coldkey, )); - assert!(ColdkeySwapScheduled::::contains_key(who)); - - // Setup the extension - let info: DispatchInfo = - DispatchInfoOf::<::RuntimeCall>::default(); - let extension = SubtensorTransactionExtension::::new(); + assert!(ColdkeySwapAnnouncements::::contains_key(who)); // Try each call // Dissolve network + let ext = SubtensorTransactionExtension::::new(); let call = RuntimeCall::SubtensorModule(SubtensorCall::dissolve_network { netuid, coldkey }); - let result = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - // Should fail - assert_eq!( - // Should get an invalid transaction error - result.unwrap_err(), - CustomTransactionError::ColdkeyInSwapSchedule.into() + let info = call.get_dispatch_info(); + + assert_noop!( + ext.dispatch_transaction(RuntimeOrigin::signed(who).into(), call, &info, 0, 0,), + CustomTransactionError::ColdkeySwapAnnounced ); }); } @@ -2635,10 +2246,9 @@ fn test_swap_auto_stake_destination_coldkeys() { AutoStakeDestinationColdkeys::::insert(hotkey, netuid, coldkeys.clone()); AutoStakeDestination::::insert(old_coldkey, netuid, hotkey); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &old_coldkey, - &new_coldkey, - )); + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); let new_coldkeys = AutoStakeDestinationColdkeys::::get(hotkey, netuid); assert!(new_coldkeys.contains(&new_coldkey)); From e8cee056110cee7447b10335c8e0f58d0ff39f5d Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Wed, 3 Dec 2025 16:58:49 +0100 Subject: [PATCH 023/240] error cases tests + clean up --- pallets/subtensor/src/macros/dispatches.rs | 6 +- pallets/subtensor/src/macros/errors.rs | 8 +- pallets/subtensor/src/tests/swap_coldkey.rs | 137 ++++++++++++++------ 3 files changed, 105 insertions(+), 46 deletions(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index e64f7eae53..be92fec5a0 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2350,7 +2350,7 @@ mod dispatches { let when = existing.0; ensure!( now > when + delay, - Error::::ColdkeySwapReannouncedTooEarly + Error::::ColdKeySwapReannouncedTooEarly ); } @@ -2371,11 +2371,11 @@ mod dispatches { let who = ensure_signed(origin)?; let (when, new_coldkey) = ColdkeySwapAnnouncements::::take(who.clone()) - .ok_or(Error::::ColdkeySwapAnnouncementNotFound)?; + .ok_or(Error::::ColdKeySwapAnnouncementNotFound)?; let now = >::block_number(); let delay = ColdkeySwapScheduleDuration::::get(); - ensure!(now > when + delay, Error::::ColdkeySwapTooEarly); + ensure!(now > when + delay, Error::::ColdKeySwapTooEarly); Self::do_swap_coldkey(&who, &new_coldkey)?; diff --git a/pallets/subtensor/src/macros/errors.rs b/pallets/subtensor/src/macros/errors.rs index e83a7fc747..49bbdfd2f8 100644 --- a/pallets/subtensor/src/macros/errors.rs +++ b/pallets/subtensor/src/macros/errors.rs @@ -138,8 +138,6 @@ mod errors { ColdKeyAlreadyAssociated, /// The coldkey balance is not enough to pay for the swap NotEnoughBalanceToPaySwapColdKey, - /// The coldkey is in arbitration - ColdkeyIsInArbitration, /// Attempting to set an invalid child for a hotkey on a network. InvalidChild, /// Duplicate child when setting children. @@ -151,11 +149,11 @@ mod errors { /// Default transaction rate limit exceeded. TxRateLimitExceeded, /// Coldkey swap announcement not found - ColdkeySwapAnnouncementNotFound, + ColdKeySwapAnnouncementNotFound, /// Coldkey swap too early. - ColdkeySwapTooEarly, + ColdKeySwapTooEarly, /// Coldkey swap reannounced too early. - ColdkeySwapReannouncedTooEarly, + ColdKeySwapReannouncedTooEarly, /// New coldkey is hotkey NewColdKeyIsHotkey, /// Childkey take is invalid. diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index 060d87c36d..0d4456b86a 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -29,7 +29,7 @@ use crate::*; use crate::{Call, ColdkeySwapScheduleDuration, Error}; #[test] -fn test_announce_coldkey_swap_with_no_announcement_works() { +fn test_announce_coldkey_swap_works() { new_test_ext(1).execute_with(|| { let who = U256::from(1); let new_coldkey = U256::from(2); @@ -121,7 +121,7 @@ fn test_announce_coldkey_swap_with_existing_announcement_not_past_delay_fails() RuntimeOrigin::signed(who.clone()), new_coldkey_2, ), - Error::::ColdkeySwapReannouncedTooEarly + Error::::ColdKeySwapReannouncedTooEarly ); }); } @@ -143,6 +143,103 @@ fn test_announce_coldkey_swap_with_bad_origin_fails() { }); } +#[test] +fn test_swap_coldkey_announced_too_early_fails() { + new_test_ext(1).execute_with(|| { + let who = U256::from(1); + let new_coldkey = U256::from(2); + + assert_ok!(SubtensorModule::announce_coldkey_swap( + RuntimeOrigin::signed(who.clone()), + new_coldkey, + )); + + assert_noop!( + SubtensorModule::swap_coldkey_announced( + ::RuntimeOrigin::signed(who) + ), + Error::::ColdKeySwapTooEarly + ); + }) +} + +#[test] +fn test_swap_coldkey_announced_with_already_associated_coldkey_fails() { + new_test_ext(1).execute_with(|| { + let who = U256::from(1); + let new_coldkey = U256::from(2); + let hotkey = U256::from(3); + + assert_ok!(SubtensorModule::announce_coldkey_swap( + RuntimeOrigin::signed(who.clone()), + new_coldkey, + )); + + let now = System::block_number(); + let delay = ColdkeySwapScheduleDuration::::get() + 1; + System::run_to_block::(now + delay); + + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&who, swap_cost.to_u64()); + + SubtensorModule::create_account_if_non_existent(&new_coldkey, &hotkey); + + assert_noop!( + SubtensorModule::swap_coldkey_announced( + ::RuntimeOrigin::signed(who) + ), + Error::::ColdKeyAlreadyAssociated + ); + }) +} + +#[test] +fn test_swap_coldkey_announced_using_registered_hotkey_fails() { + new_test_ext(1).execute_with(|| { + let who = U256::from(1); + let new_coldkey = U256::from(2); + let hotkey = U256::from(3); + + assert_ok!(SubtensorModule::announce_coldkey_swap( + RuntimeOrigin::signed(who.clone()), + hotkey.clone(), + )); + + let now = System::block_number(); + let delay = ColdkeySwapScheduleDuration::::get() + 1; + System::run_to_block::(now + delay); + + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&who, swap_cost.to_u64()); + + SubtensorModule::create_account_if_non_existent(&new_coldkey, &hotkey); + + assert_noop!( + SubtensorModule::swap_coldkey_announced( + ::RuntimeOrigin::signed(who) + ), + Error::::NewColdKeyIsHotkey + ); + }) +} + +#[test] +fn test_swap_coldkey_with_not_enough_balance_to_pay_swap_cost_fails() { + new_test_ext(1).execute_with(|| { + let who = U256::from(1); + let new_coldkey = U256::from(2); + + let now = System::block_number(); + let delay = ColdkeySwapScheduleDuration::::get() + 1; + System::run_to_block::(now + delay); + + assert_noop!( + SubtensorModule::do_swap_coldkey(&who, &new_coldkey), + Error::::NotEnoughBalanceToPaySwapColdKey + ); + }); +} + #[test] fn test_swap_subnet_owner() { new_test_ext(1).execute_with(|| { @@ -662,28 +759,6 @@ fn test_swap_concurrent_modifications() { }); } -#[test] -fn test_swap_with_invalid_subnet_ownership() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - let netuid = NetUid::from(1u16); - - SubnetOwner::::insert(netuid, old_coldkey); - - // Simulate an invalid state where the subnet owner doesn't match the old_coldkey - SubnetOwner::::insert(netuid, U256::from(3)); - - let swap_cost = SubtensorModule::get_key_swap_cost(); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); - - assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); - - // The swap should not affect the mismatched subnet ownership - assert_eq!(SubnetOwner::::get(netuid), U256::from(3)); - }); -} - #[test] fn test_do_swap_coldkey_success() { new_test_ext(1).execute_with(|| { @@ -1917,20 +1992,6 @@ fn test_coldkey_swap_no_identity_no_changes_newcoldkey_exists() { }); } -#[test] -fn test_cant_schedule_swap_without_enough_to_burn() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(3); - let new_coldkey = U256::from(4); - let hotkey = U256::from(5); - - assert_noop!( - SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey), - Error::::NotEnoughBalanceToPaySwapColdKey - ); - }); -} - #[test] fn test_coldkey_in_swap_schedule_prevents_funds_usage() { // Testing the signed extension validate function From dcc85bc35c48b149fba426cbc18ef728b0248a18 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Thu, 4 Dec 2025 15:16:05 +0100 Subject: [PATCH 024/240] preserve new identity + tests --- pallets/subtensor/src/swap/swap_coldkey.rs | 14 +- pallets/subtensor/src/tests/swap_coldkey.rs | 516 +++++++------------- 2 files changed, 193 insertions(+), 337 deletions(-) diff --git a/pallets/subtensor/src/swap/swap_coldkey.rs b/pallets/subtensor/src/swap/swap_coldkey.rs index 885e211300..491daf9c98 100644 --- a/pallets/subtensor/src/swap/swap_coldkey.rs +++ b/pallets/subtensor/src/swap/swap_coldkey.rs @@ -26,13 +26,16 @@ impl Pallet { let burn_amount = Self::remove_balance_from_coldkey_account(old_coldkey, swap_cost.into())?; Self::recycle_tao(burn_amount); - // Swap the identity if the old coldkey has one - if let Some(identity) = IdentitiesV2::::take(old_coldkey) { + // Swap the identity if the old coldkey has one and the new coldkey doesn't + if IdentitiesV2::::get(new_coldkey).is_none() + && let Some(identity) = IdentitiesV2::::take(old_coldkey) + { IdentitiesV2::::insert(new_coldkey.clone(), identity); } for netuid in Self::get_all_subnet_netuids() { Self::transfer_subnet_ownership(netuid, old_coldkey, &new_coldkey); + Self::transfer_auto_stake_destination(netuid, old_coldkey, &new_coldkey); Self::transfer_coldkey_stake(netuid, old_coldkey, &new_coldkey); } Self::transfer_staking_hotkeys(old_coldkey, &new_coldkey); @@ -65,7 +68,14 @@ impl Pallet { if subnet_owner == *old_coldkey { SubnetOwner::::insert(netuid, new_coldkey.clone()); } + } + /// Transfer the auto stake destination from the old coldkey to the new coldkey if it is set. + fn transfer_auto_stake_destination( + netuid: NetUid, + old_coldkey: &T::AccountId, + new_coldkey: &T::AccountId, + ) { if let Some(old_auto_stake_hotkey) = AutoStakeDestination::::get(old_coldkey, netuid) { AutoStakeDestination::::remove(old_coldkey, netuid); AutoStakeDestination::::insert(new_coldkey, netuid, old_auto_stake_hotkey.clone()); diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index 0d4456b86a..df6480495d 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -93,6 +93,23 @@ fn test_announce_coldkey_swap_with_existing_announcement_past_delay_works() { }); } +#[test] +fn test_announce_coldkey_swap_with_bad_origin_fails() { + new_test_ext(1).execute_with(|| { + let new_coldkey = U256::from(1); + + assert_noop!( + SubtensorModule::announce_coldkey_swap(RuntimeOrigin::none(), new_coldkey), + BadOrigin + ); + + assert_noop!( + SubtensorModule::announce_coldkey_swap(RuntimeOrigin::root(), new_coldkey), + BadOrigin + ); + }); +} + #[test] fn test_announce_coldkey_swap_with_existing_announcement_not_past_delay_fails() { new_test_ext(1).execute_with(|| { @@ -127,22 +144,180 @@ fn test_announce_coldkey_swap_with_existing_announcement_not_past_delay_fails() } #[test] -fn test_announce_coldkey_swap_with_bad_origin_fails() { +fn test_swap_coldkey_announced_works() { new_test_ext(1).execute_with(|| { - let new_coldkey = U256::from(1); + let who = U256::from(1); + let new_coldkey = U256::from(2); + let hotkey1 = U256::from(1001); + let hotkey2 = U256::from(1002); + let now = System::block_number(); + + // Setup networks and subnet ownerships + let netuid1 = NetUid::from(1); + let netuid2 = NetUid::from(2); + add_network(netuid1, 1, 0); + add_network(netuid2, 1, 0); + SubnetOwner::::insert(netuid1, who); + SubnetOwner::::insert(netuid2, who); + + // Setup auto stake destinations + AutoStakeDestination::::insert(who, netuid1, hotkey1); + AutoStakeDestination::::insert(who, netuid2, hotkey2); + AutoStakeDestinationColdkeys::::insert( + hotkey1, + netuid1, + vec![who, U256::from(3), U256::from(4)], + ); + AutoStakeDestinationColdkeys::::insert( + hotkey2, + netuid2, + vec![U256::from(7), U256::from(8), who], + ); + + // Setup identity + let identity = ChainIdentityV2::default(); + IdentitiesV2::::insert(who, identity.clone()); + assert_eq!(IdentitiesV2::::get(who), Some(identity.clone())); + assert!(IdentitiesV2::::get(new_coldkey).is_none()); + + // Announce the coldkey swap + assert_ok!(SubtensorModule::announce_coldkey_swap( + RuntimeOrigin::signed(who.clone()), + new_coldkey, + )); + assert_eq!( + ColdkeySwapAnnouncements::::get(who), + Some((now, new_coldkey)) + ); + + // Run some blocks for the announcement to be past the delay + let delay = ColdkeySwapScheduleDuration::::get() + 1; + System::run_to_block::(now + delay); + + // Add balance to the old coldkey to pay for the swap cost + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&who, swap_cost.to_u64()); + + let balance_before = SubtensorModule::get_coldkey_balance(&who); + // let total_issuance_before = SubtensorModule::get_total_issuance(); + + assert_ok!(SubtensorModule::swap_coldkey_announced( + RuntimeOrigin::signed(who), + )); + + // Ensure the announcement has been consumed + assert_eq!(ColdkeySwapAnnouncements::::get(who), None); + + // Ensure the cost has been withdrawn from the old coldkey and recycled + let balance_after = SubtensorModule::get_coldkey_balance(&who); + assert_eq!(balance_after, balance_before - swap_cost.to_u64()); + // let total_issuance_after = TotalIssuance::::get(); + // TODO: handle panics + // assert_eq!(total_issuance_after, total_issuance_before - swap_cost); + + // Ensure the identity is correctly swapped + assert!(IdentitiesV2::::get(&who).is_none()); + assert_eq!(IdentitiesV2::::get(&new_coldkey), Some(identity)); + + // Ensure the subnet ownerships are correctly swapped + assert_eq!(SubnetOwner::::get(netuid1), new_coldkey); + assert_eq!(SubnetOwner::::get(netuid2), new_coldkey); + + // Ensure the auto stake destinations are correctly swapped + assert!(AutoStakeDestination::::get(who, netuid1).is_none()); + assert!(AutoStakeDestination::::get(who, netuid2).is_none()); + assert_eq!( + AutoStakeDestination::::get(new_coldkey, netuid1), + Some(hotkey1) + ); + assert_eq!( + AutoStakeDestination::::get(new_coldkey, netuid2), + Some(hotkey2) + ); + assert_eq!( + AutoStakeDestinationColdkeys::::get(hotkey1, netuid1), + vec![U256::from(3), U256::from(4), new_coldkey] + ); + assert_eq!( + AutoStakeDestinationColdkeys::::get(hotkey2, netuid2), + vec![U256::from(7), U256::from(8), new_coldkey] + ); + + // Ensure the stake is correctly swapped + }); +} + +#[test] +fn test_swap_coldkey_announced_preserves_new_coldkey_identity() { + new_test_ext(1).execute_with(|| { + let who = U256::from(1); + let new_coldkey = U256::from(2); + + let old_identity = ChainIdentityV2 { + name: b"Old identity".to_vec(), + ..Default::default() + }; + IdentitiesV2::::insert(who, old_identity.clone()); + + let new_identity = ChainIdentityV2 { + name: b"New identity".to_vec(), + ..Default::default() + }; + IdentitiesV2::::insert(new_coldkey, new_identity.clone()); + + assert_ok!(SubtensorModule::announce_coldkey_swap( + RuntimeOrigin::signed(who.clone()), + new_coldkey + )); + + let now = System::block_number(); + let delay = ColdkeySwapScheduleDuration::::get() + 1; + System::run_to_block::(now + delay); + + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&who, swap_cost.to_u64()); + + assert_ok!(SubtensorModule::swap_coldkey_announced( + RuntimeOrigin::signed(who) + )); + + // Identity is preserved + assert_eq!(IdentitiesV2::::get(who), Some(old_identity)); + assert_eq!(IdentitiesV2::::get(new_coldkey), Some(new_identity)); + }); +} + +#[test] +fn test_swap_coldkey_announced_with_bad_origin_fails() { + new_test_ext(1).execute_with(|| { + let who = U256::from(1); + let new_coldkey = U256::from(2); assert_noop!( - SubtensorModule::announce_coldkey_swap(RuntimeOrigin::none(), new_coldkey), + SubtensorModule::swap_coldkey_announced(RuntimeOrigin::none()), BadOrigin ); assert_noop!( - SubtensorModule::announce_coldkey_swap(RuntimeOrigin::root(), new_coldkey), + SubtensorModule::swap_coldkey_announced(RuntimeOrigin::root()), BadOrigin ); }); } +#[test] +fn test_swap_coldkey_announced_without_announcement_fails() { + new_test_ext(1).execute_with(|| { + let who = U256::from(1); + let new_coldkey = U256::from(2); + + assert_noop!( + SubtensorModule::swap_coldkey_announced(RuntimeOrigin::signed(who)), + Error::::ColdKeySwapAnnouncementNotFound + ); + }) +} + #[test] fn test_swap_coldkey_announced_too_early_fails() { new_test_ext(1).execute_with(|| { @@ -194,7 +369,7 @@ fn test_swap_coldkey_announced_with_already_associated_coldkey_fails() { } #[test] -fn test_swap_coldkey_announced_using_registered_hotkey_fails() { +fn test_swap_coldkey_announced_with_hotkey_fails() { new_test_ext(1).execute_with(|| { let who = U256::from(1); let new_coldkey = U256::from(2); @@ -228,7 +403,7 @@ fn test_swap_coldkey_with_not_enough_balance_to_pay_swap_cost_fails() { new_test_ext(1).execute_with(|| { let who = U256::from(1); let new_coldkey = U256::from(2); - + let now = System::block_number(); let delay = ColdkeySwapScheduleDuration::::get() + 1; System::run_to_block::(now + delay); @@ -240,25 +415,6 @@ fn test_swap_coldkey_with_not_enough_balance_to_pay_swap_cost_fails() { }); } -#[test] -fn test_swap_subnet_owner() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - let netuid = NetUid::from(1u16); - - add_network(netuid, 1, 0); - SubnetOwner::::insert(netuid, old_coldkey); - - let swap_cost = SubtensorModule::get_key_swap_cost(); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); - - assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); - - assert_eq!(SubnetOwner::::get(netuid), new_coldkey); - }); -} - #[test] fn test_swap_total_coldkey_stake() { new_test_ext(1).execute_with(|| { @@ -412,29 +568,6 @@ fn test_swap_with_multiple_hotkeys() { }); } -#[test] -fn test_swap_with_multiple_subnets() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - let netuid1 = NetUid::from(1); - let netuid2 = NetUid::from(2); - - add_network(netuid1, 1, 0); - add_network(netuid2, 1, 0); - SubnetOwner::::insert(netuid1, old_coldkey); - SubnetOwner::::insert(netuid2, old_coldkey); - - let swap_cost = SubtensorModule::get_key_swap_cost(); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); - - assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); - - assert_eq!(SubnetOwner::::get(netuid1), new_coldkey); - assert_eq!(SubnetOwner::::get(netuid2), new_coldkey); - }); -} - // TODO #[test] fn test_swap_with_zero_balance() { @@ -1301,83 +1434,6 @@ fn test_swap_delegated_stake_for_coldkey() { }); } -#[test] -fn test_swap_subnet_owner_for_coldkey() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - let netuid1 = NetUid::from(1); - let netuid2 = NetUid::from(2); - - // Initialize SubnetOwner for old_coldkey - add_network(netuid1, 13, 0); - add_network(netuid2, 14, 0); - SubnetOwner::::insert(netuid1, old_coldkey); - SubnetOwner::::insert(netuid2, old_coldkey); - - // Set up TotalNetworks - TotalNetworks::::put(3); - - let swap_cost = SubtensorModule::get_key_swap_cost(); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); - - // Perform the swap - assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); - - // Verify the swap - assert_eq!(SubnetOwner::::get(netuid1), new_coldkey); - assert_eq!(SubnetOwner::::get(netuid2), new_coldkey); - }); -} - -#[test] -fn test_do_swap_coldkey_with_subnet_ownership() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - let hotkey = U256::from(3); - let netuid = NetUid::from(1u16); - let stake_amount = 1000; - let swap_cost = SubtensorModule::get_key_swap_cost().to_u64(); - - // Setup initial state - add_network(netuid, 13, 0); - register_ok_neuron(netuid, hotkey, old_coldkey, 0); - - // Set TotalNetworks because swap relies on it - crate::TotalNetworks::::set(1); - - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, stake_amount + swap_cost); - SubnetOwner::::insert(netuid, old_coldkey); - - // Populate OwnedHotkeys map - OwnedHotkeys::::insert(old_coldkey, vec![hotkey]); - - let swap_cost = SubtensorModule::get_key_swap_cost(); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); - - // Perform the swap - assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); - - // Verify subnet ownership transfer - assert_eq!(SubnetOwner::::get(netuid), new_coldkey); - }); -} - -#[test] -fn test_coldkey_has_associated_hotkeys() { - new_test_ext(1).execute_with(|| { - let coldkey = U256::from(1); - let hotkey = U256::from(2); - let netuid = NetUid::from(1u16); - - // Setup initial state - add_network(netuid, 13, 0); - register_ok_neuron(netuid, hotkey, coldkey, 0); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1000); - }); -} - #[test] fn test_coldkey_swap_total() { new_test_ext(1).execute_with(|| { @@ -1779,139 +1835,6 @@ fn test_coldkey_delegations() { }); } -#[test] -fn test_schedule_swap_coldkey_execution() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - let hotkey = U256::from(3); - let netuid = NetUid::from(1u16); - let stake_amount = DefaultMinStake::::get().to_u64() * 10; - let reserve = stake_amount * 10; - - mock::setup_reserves(netuid, reserve.into(), reserve.into()); - - add_network(netuid, 13, 0); - register_ok_neuron(netuid, hotkey, old_coldkey, 0); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, 1000000000000000); - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(old_coldkey), - hotkey, - netuid, - stake_amount.into() - )); - - // Check initial ownership - assert_eq!( - Owner::::get(hotkey), - old_coldkey, - "Initial ownership check failed" - ); - - let swap_cost = SubtensorModule::get_key_swap_cost(); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); - - // Schedule the swap - assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); - - // Get the scheduled execution block - let current_block = System::block_number(); - let execution_block = current_block + ColdkeySwapScheduleDuration::::get(); - - System::run_to_block::(execution_block - 1); - - let stake_before_swap = SubtensorModule::get_total_stake_for_coldkey(&old_coldkey); - - run_to_block(execution_block); - - // Run on_initialize for the execution block - >::on_initialize(execution_block); - - // Also run Scheduler's on_initialize - as OnInitialize>::on_initialize( - execution_block, - ); - - // Check if the swap has occurred - let new_owner = Owner::::get(hotkey); - assert_eq!( - new_owner, new_coldkey, - "Ownership was not updated as expected" - ); - - assert_eq!( - SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), - stake_before_swap, - "Stake was not transferred to new coldkey" - ); - assert_eq!( - SubtensorModule::get_total_stake_for_coldkey(&old_coldkey), - TaoCurrency::ZERO, - "Old coldkey still has stake" - ); - - // Check for the SwapExecuted event - System::assert_has_event( - Event::ColdkeySwapped { - old_coldkey, - new_coldkey, - swap_cost, - } - .into(), - ); - }); -} - -#[test] -fn test_coldkey_swap_delegate_identity_updated() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - - let netuid = NetUid::from(1); - let burn_cost = TaoCurrency::from(10); - let tempo = 1; - - SubtensorModule::set_burn(netuid, burn_cost); - add_network(netuid, tempo, 0); - - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, 100_000_000_000); - mock::setup_reserves(netuid, 1_000_000_000_000.into(), 1_000_000_000_000.into()); - - assert_ok!(SubtensorModule::burned_register( - <::RuntimeOrigin>::signed(old_coldkey), - netuid, - old_coldkey - )); - - let name: Vec = b"The Third Coolest Identity".to_vec(); - let identity: ChainIdentityV2 = ChainIdentityV2 { - name: name.clone(), - url: vec![], - image: vec![], - github_repo: vec![], - discord: vec![], - description: vec![], - additional: vec![], - }; - - IdentitiesV2::::insert(old_coldkey, identity.clone()); - - assert!(IdentitiesV2::::get(old_coldkey).is_some()); - assert!(IdentitiesV2::::get(new_coldkey).is_none()); - - // Perform the swap - assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); - - assert!(IdentitiesV2::::get(old_coldkey).is_none()); - assert!(IdentitiesV2::::get(new_coldkey).is_some()); - assert_eq!( - IdentitiesV2::::get(new_coldkey).expect("Expected an Identity"), - identity - ); - }); -} - #[test] fn test_coldkey_swap_no_identity_no_changes() { new_test_ext(1).execute_with(|| { @@ -1946,52 +1869,6 @@ fn test_coldkey_swap_no_identity_no_changes() { }); } -#[test] -fn test_coldkey_swap_no_identity_no_changes_newcoldkey_exists() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(3); - let new_coldkey = U256::from(4); - - let netuid = NetUid::from(1); - let burn_cost = TaoCurrency::from(10); - let tempo = 1; - - SubtensorModule::set_burn(netuid, burn_cost); - add_network(netuid, tempo, 0); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, 100_000_000_000); - mock::setup_reserves(netuid, 1_000_000_000_000.into(), 1_000_000_000_000.into()); - - assert_ok!(SubtensorModule::burned_register( - <::RuntimeOrigin>::signed(old_coldkey), - netuid, - old_coldkey - )); - - let name: Vec = b"The Coolest Identity".to_vec(); - let identity: ChainIdentityV2 = ChainIdentityV2 { - name: name.clone(), - url: vec![], - github_repo: vec![], - image: vec![], - discord: vec![], - description: vec![], - additional: vec![], - }; - - IdentitiesV2::::insert(new_coldkey, identity.clone()); - // Ensure the new coldkey does have an identity before the swap - assert!(IdentitiesV2::::get(new_coldkey).is_some()); - assert!(IdentitiesV2::::get(old_coldkey).is_none()); - - // Perform the coldkey swap - assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); - - // Ensure no identities have been changed - assert!(IdentitiesV2::::get(old_coldkey).is_none()); - assert!(IdentitiesV2::::get(new_coldkey).is_some()); - }); -} - #[test] fn test_coldkey_in_swap_schedule_prevents_funds_usage() { // Testing the signed extension validate function @@ -2294,37 +2171,6 @@ fn test_announced_coldkey_swap_prevents_critical_calls() { }); } -#[test] -fn test_swap_auto_stake_destination_coldkeys() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - let hotkey = U256::from(3); - let netuid = NetUid::from(1u16); - let coldkeys = vec![U256::from(4), U256::from(5), old_coldkey]; - - add_network(netuid, 1, 0); - AutoStakeDestinationColdkeys::::insert(hotkey, netuid, coldkeys.clone()); - AutoStakeDestination::::insert(old_coldkey, netuid, hotkey); - - let swap_cost = SubtensorModule::get_key_swap_cost(); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); - assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); - - let new_coldkeys = AutoStakeDestinationColdkeys::::get(hotkey, netuid); - assert!(new_coldkeys.contains(&new_coldkey)); - assert!(!new_coldkeys.contains(&old_coldkey)); - assert_eq!( - AutoStakeDestination::::try_get(old_coldkey, netuid), - Err(()) - ); - assert_eq!( - AutoStakeDestination::::try_get(new_coldkey, netuid), - Ok(hotkey) - ); - }); -} - #[test] #[allow(deprecated)] fn test_swap_coldkey_deprecated() { From 2f1ad59e2f2e7a4c6c7b246bbbb9282ee086242d Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Thu, 4 Dec 2025 23:21:56 +0100 Subject: [PATCH 025/240] comprehensive test --- pallets/subtensor/src/tests/swap_coldkey.rs | 429 ++++++++------------ 1 file changed, 167 insertions(+), 262 deletions(-) diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index df6480495d..cba3df5486 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -150,8 +150,36 @@ fn test_swap_coldkey_announced_works() { let new_coldkey = U256::from(2); let hotkey1 = U256::from(1001); let hotkey2 = U256::from(1002); + let hotkey3 = U256::from(1003); + let swap_cost = SubtensorModule::get_key_swap_cost(); + let left_over = 12345; + let min_stake = DefaultMinStake::::get().to_u64(); + let stake1 = min_stake * 10; + let stake2 = min_stake * 20; + let stake3 = min_stake * 30; let now = System::block_number(); + SubtensorModule::add_balance_to_coldkey_account( + &who, + stake1 + stake2 + stake3 + swap_cost.to_u64() + left_over, + ); + + // Announce the coldkey swap + assert_ok!(SubtensorModule::announce_coldkey_swap( + RuntimeOrigin::signed(who.clone()), + new_coldkey, + )); + assert_eq!( + ColdkeySwapAnnouncements::::get(who), + Some((now, new_coldkey)) + ); + + // Run some blocks for the announcement to be past the delay + // WARN: this requires to be done before staking to neurons to avoid + // value mismatch due to coinbase run + let delay = ColdkeySwapScheduleDuration::::get() + 1; + System::run_to_block::(now + delay); + // Setup networks and subnet ownerships let netuid1 = NetUid::from(1); let netuid2 = NetUid::from(2); @@ -160,6 +188,12 @@ fn test_swap_coldkey_announced_works() { SubnetOwner::::insert(netuid1, who); SubnetOwner::::insert(netuid2, who); + // Setup reserves + let reserve1 = (stake1 + stake3) * 10; + let reserve2 = stake2 * 10; + mock::setup_reserves(netuid1, reserve1.into(), reserve1.into()); + mock::setup_reserves(netuid2, reserve2.into(), reserve2.into()); + // Setup auto stake destinations AutoStakeDestination::::insert(who, netuid1, hotkey1); AutoStakeDestination::::insert(who, netuid2, hotkey2); @@ -174,35 +208,55 @@ fn test_swap_coldkey_announced_works() { vec![U256::from(7), U256::from(8), who], ); + // Setup neurons with stake + register_ok_neuron(netuid1, hotkey1, who, 0); + register_ok_neuron(netuid2, hotkey2, who, 0); + register_ok_neuron(netuid1, hotkey3, who, 0); + + let hotkeys = vec![hotkey1, hotkey2, hotkey3]; + assert_eq!(StakingHotkeys::::get(&who), hotkeys); + assert_eq!(OwnedHotkeys::::get(&who), hotkeys); + assert_eq!(Owner::::get(hotkey1), who); + assert_eq!(Owner::::get(hotkey2), who); + assert_eq!(Owner::::get(hotkey3), who); + + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(who), + hotkey1, + netuid1, + stake1.into() + )); + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(who), + hotkey2, + netuid2, + stake2.into() + )); + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(who), + hotkey3, + netuid1, + stake3.into() + )); + let hk1_alpha = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey1, &who, netuid1); + let hk2_alpha = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey2, &who, netuid2); + let hk3_alpha = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey3, &who, netuid1); + let total_ck_stake = SubtensorModule::get_total_stake_for_coldkey(&who); + // Setup identity let identity = ChainIdentityV2::default(); IdentitiesV2::::insert(who, identity.clone()); assert_eq!(IdentitiesV2::::get(who), Some(identity.clone())); assert!(IdentitiesV2::::get(new_coldkey).is_none()); - // Announce the coldkey swap - assert_ok!(SubtensorModule::announce_coldkey_swap( - RuntimeOrigin::signed(who.clone()), - new_coldkey, - )); - assert_eq!( - ColdkeySwapAnnouncements::::get(who), - Some((now, new_coldkey)) - ); - - // Run some blocks for the announcement to be past the delay - let delay = ColdkeySwapScheduleDuration::::get() + 1; - System::run_to_block::(now + delay); - - // Add balance to the old coldkey to pay for the swap cost - let swap_cost = SubtensorModule::get_key_swap_cost(); - SubtensorModule::add_balance_to_coldkey_account(&who, swap_cost.to_u64()); - let balance_before = SubtensorModule::get_coldkey_balance(&who); - // let total_issuance_before = SubtensorModule::get_total_issuance(); + let total_stake_before = SubtensorModule::get_total_stake(); assert_ok!(SubtensorModule::swap_coldkey_announced( - RuntimeOrigin::signed(who), + ::RuntimeOrigin::signed(who) )); // Ensure the announcement has been consumed @@ -210,9 +264,12 @@ fn test_swap_coldkey_announced_works() { // Ensure the cost has been withdrawn from the old coldkey and recycled let balance_after = SubtensorModule::get_coldkey_balance(&who); - assert_eq!(balance_after, balance_before - swap_cost.to_u64()); - // let total_issuance_after = TotalIssuance::::get(); + assert_eq!( + balance_before - swap_cost.to_u64(), + balance_after + left_over + ); // TODO: handle panics + // let total_issuance_after = TotalIssuance::::get(); // assert_eq!(total_issuance_after, total_issuance_before - swap_cost); // Ensure the identity is correctly swapped @@ -243,12 +300,91 @@ fn test_swap_coldkey_announced_works() { vec![U256::from(7), U256::from(8), new_coldkey] ); - // Ensure the stake is correctly swapped + // Ensure the coldkey stake is correctly swapped + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey1, &who, netuid1), + AlphaCurrency::ZERO + ); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey2, &who, netuid2), + AlphaCurrency::ZERO + ); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey3, &who, netuid1), + AlphaCurrency::ZERO + ); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey1, + &new_coldkey, + netuid1 + ), + hk1_alpha + ); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey2, + &new_coldkey, + netuid2 + ), + hk2_alpha + ); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey3, + &new_coldkey, + netuid1 + ), + hk3_alpha + ); + assert_eq!( + SubtensorModule::get_total_stake_for_coldkey(&who), + TaoCurrency::ZERO + ); + assert_eq!( + SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), + total_ck_stake, + ); + + // Ensure the staking hotkeys are correctly swapped + assert!(StakingHotkeys::::get(&who).is_empty()); + assert_eq!(StakingHotkeys::::get(&new_coldkey), hotkeys); + + // Ensure the hotkey ownership is correctly swapped + assert!(OwnedHotkeys::::get(&who).is_empty()); + assert_eq!(OwnedHotkeys::::get(&new_coldkey), hotkeys); + assert_eq!(Owner::::get(hotkey1), new_coldkey); + assert_eq!(Owner::::get(hotkey2), new_coldkey); + assert_eq!(Owner::::get(hotkey3), new_coldkey); + + // Ensure the remaining balance is transferred to the new coldkey + assert_eq!(SubtensorModule::get_coldkey_balance(&who), 0); + assert_eq!( + SubtensorModule::get_coldkey_balance(&new_coldkey), + left_over + ); + + // Ensure total stake is unchanged + assert_eq!( + SubtensorModule::get_total_stake(), + total_stake_before, + "Total stake changed unexpectedly" + ); + + // Verify event emission + System::assert_last_event( + Event::ColdkeySwapped { + old_coldkey: who, + new_coldkey, + swap_cost, + } + .into(), + ); }); } #[test] -fn test_swap_coldkey_announced_preserves_new_coldkey_identity() { +fn test_do_swap_coldkey_preserves_new_coldkey_identity() { new_test_ext(1).execute_with(|| { let who = U256::from(1); let new_coldkey = U256::from(2); @@ -265,21 +401,10 @@ fn test_swap_coldkey_announced_preserves_new_coldkey_identity() { }; IdentitiesV2::::insert(new_coldkey, new_identity.clone()); - assert_ok!(SubtensorModule::announce_coldkey_swap( - RuntimeOrigin::signed(who.clone()), - new_coldkey - )); - - let now = System::block_number(); - let delay = ColdkeySwapScheduleDuration::::get() + 1; - System::run_to_block::(now + delay); - let swap_cost = SubtensorModule::get_key_swap_cost(); SubtensorModule::add_balance_to_coldkey_account(&who, swap_cost.to_u64()); - assert_ok!(SubtensorModule::swap_coldkey_announced( - RuntimeOrigin::signed(who) - )); + assert_ok!(SubtensorModule::do_swap_coldkey(&who, &new_coldkey)); // Identity is preserved assert_eq!(IdentitiesV2::::get(who), Some(old_identity)); @@ -464,25 +589,6 @@ fn test_swap_total_coldkey_stake() { }); } -#[test] -fn test_swap_staking_hotkeys() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - let hotkey = U256::from(3); - - StakingHotkeys::::insert(old_coldkey, vec![hotkey]); - - let swap_cost = SubtensorModule::get_key_swap_cost(); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); - - assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); - - assert!(StakingHotkeys::::get(old_coldkey).is_empty()); - assert_eq!(StakingHotkeys::::get(new_coldkey), vec![hotkey]); - }); -} - #[test] fn test_swap_hotkey_owners() { new_test_ext(1).execute_with(|| { @@ -892,179 +998,6 @@ fn test_swap_concurrent_modifications() { }); } -#[test] -fn test_do_swap_coldkey_success() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - let hotkey1 = U256::from(3); - let hotkey2 = U256::from(4); - let netuid = NetUid::from(1u16); - let stake_amount1 = DefaultMinStake::::get().to_u64() * 10; - let stake_amount2 = DefaultMinStake::::get().to_u64() * 20; - let swap_cost = SubtensorModule::get_key_swap_cost(); - let free_balance_old = 12345 + swap_cost.to_u64(); - - let reserve = (stake_amount1 + stake_amount2) * 10; - mock::setup_reserves(netuid, reserve.into(), reserve.into()); - - // Setup initial state - add_network(netuid, 13, 0); - register_ok_neuron(netuid, hotkey1, old_coldkey, 0); - register_ok_neuron(netuid, hotkey2, old_coldkey, 0); - - // Add balance to old coldkey - SubtensorModule::add_balance_to_coldkey_account( - &old_coldkey, - stake_amount1 + stake_amount2 + free_balance_old, - ); - - // Log initial state - log::info!( - "Initial total stake: {}", - SubtensorModule::get_total_stake() - ); - log::info!( - "Initial old coldkey stake: {}", - SubtensorModule::get_total_stake_for_coldkey(&old_coldkey) - ); - log::info!( - "Initial new coldkey stake: {}", - SubtensorModule::get_total_stake_for_coldkey(&new_coldkey) - ); - - // Add stake to the neurons - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(old_coldkey), - hotkey1, - netuid, - stake_amount1.into() - )); - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(old_coldkey), - hotkey2, - netuid, - stake_amount2.into() - )); - - // Insert an Identity - let name: Vec = b"The fourth Coolest Identity".to_vec(); - let identity: ChainIdentityV2 = ChainIdentityV2 { - name: name.clone(), - url: vec![], - github_repo: vec![], - image: vec![], - discord: vec![], - description: vec![], - additional: vec![], - }; - - IdentitiesV2::::insert(old_coldkey, identity.clone()); - - assert!(IdentitiesV2::::get(old_coldkey).is_some()); - assert!(IdentitiesV2::::get(new_coldkey).is_none()); - - // Log state after adding stake - log::info!( - "Total stake after adding: {}", - SubtensorModule::get_total_stake() - ); - log::info!( - "Old coldkey stake after adding: {}", - SubtensorModule::get_total_stake_for_coldkey(&old_coldkey) - ); - log::info!( - "New coldkey stake after adding: {}", - SubtensorModule::get_total_stake_for_coldkey(&new_coldkey) - ); - - // Record total stake before swap - let total_stake_before_swap = SubtensorModule::get_total_stake(); - - let hk1_alpha = Alpha::::get((hotkey1, old_coldkey, netuid)); - let hk2_alpha = Alpha::::get((hotkey2, old_coldkey, netuid)); - let total_ck_stake = SubtensorModule::get_total_stake_for_coldkey(&old_coldkey); - - // Perform the swap - assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); - - // Log state after swap - log::info!( - "Total stake after swap: {}", - SubtensorModule::get_total_stake() - ); - log::info!( - "Old coldkey stake after swap: {}", - SubtensorModule::get_total_stake_for_coldkey(&old_coldkey) - ); - log::info!( - "New coldkey stake after swap: {}", - SubtensorModule::get_total_stake_for_coldkey(&new_coldkey) - ); - - // Verify the swap - assert_eq!(Owner::::get(hotkey1), new_coldkey); - assert_eq!(Owner::::get(hotkey2), new_coldkey); - assert_eq!( - SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), - total_ck_stake - ); - assert_eq!( - SubtensorModule::get_total_stake_for_coldkey(&old_coldkey), - TaoCurrency::ZERO - ); - assert_eq!( - Alpha::::get((hotkey1, new_coldkey, netuid)), - hk1_alpha - ); - assert_eq!( - Alpha::::get((hotkey2, new_coldkey, netuid)), - hk2_alpha - ); - assert!(!Alpha::::contains_key((hotkey1, old_coldkey, netuid))); - assert!(!Alpha::::contains_key((hotkey2, old_coldkey, netuid))); - - // Verify OwnedHotkeys - let new_owned_hotkeys = OwnedHotkeys::::get(new_coldkey); - assert!(new_owned_hotkeys.contains(&hotkey1)); - assert!(new_owned_hotkeys.contains(&hotkey2)); - assert_eq!(new_owned_hotkeys.len(), 2); - assert!(!OwnedHotkeys::::contains_key(old_coldkey)); - - // Verify balance transfer - assert_eq!( - SubtensorModule::get_coldkey_balance(&new_coldkey), - free_balance_old - swap_cost.to_u64() - ); - assert_eq!(SubtensorModule::get_coldkey_balance(&old_coldkey), 0); - - // Verify total stake remains unchanged - assert_eq!( - SubtensorModule::get_total_stake(), - total_stake_before_swap, - "Total stake changed unexpectedly" - ); - - // Verify identities were swapped - assert!(IdentitiesV2::::get(old_coldkey).is_none()); - assert!(IdentitiesV2::::get(new_coldkey).is_some()); - assert_eq!( - IdentitiesV2::::get(new_coldkey).expect("Expected an Identity"), - identity - ); - - // Verify event emission - System::assert_last_event( - Event::ColdkeySwapped { - old_coldkey, - new_coldkey, - swap_cost, - } - .into(), - ); - }); -} - #[test] fn test_swap_stake_for_coldkey() { new_test_ext(1).execute_with(|| { @@ -1835,40 +1768,6 @@ fn test_coldkey_delegations() { }); } -#[test] -fn test_coldkey_swap_no_identity_no_changes() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - - let netuid = NetUid::from(1); - let burn_cost = TaoCurrency::from(10); - let tempo = 1; - - SubtensorModule::set_burn(netuid, burn_cost); - add_network(netuid, tempo, 0); - - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, 100_000_000_000); - mock::setup_reserves(netuid, 1_000_000_000_000.into(), 1_000_000_000_000.into()); - - assert_ok!(SubtensorModule::burned_register( - <::RuntimeOrigin>::signed(old_coldkey), - netuid, - old_coldkey - )); - - // Ensure the old coldkey does not have an identity before the swap - assert!(IdentitiesV2::::get(old_coldkey).is_none()); - - // Perform the coldkey swap - assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); - - // Ensure no identities have been changed - assert!(IdentitiesV2::::get(old_coldkey).is_none()); - assert!(IdentitiesV2::::get(new_coldkey).is_none()); - }); -} - #[test] fn test_coldkey_in_swap_schedule_prevents_funds_usage() { // Testing the signed extension validate function @@ -2206,3 +2105,9 @@ fn test_schedule_swap_coldkey_deprecated() { ); }); } + +// TEST STAKING HOTKEY ARE ADDITIVE TO THE EXISTING ONES + +// TEST HOTKEYS OWNERSHIP IS ADDITIVE TO THE EXISTING ONES + +// TEST TRANSFER ROOT CLAIM WITH NEW KEYS + ROOT CASE From 74a0dad6365bd7a3fd6c0c564c98d564d1a49a5a Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Fri, 5 Dec 2025 14:37:18 +0100 Subject: [PATCH 026/240] fix extension + tests --- pallets/subtensor/src/tests/swap_coldkey.rs | 889 ++---------------- .../subtensor/src/transaction_extension.rs | 12 +- 2 files changed, 103 insertions(+), 798 deletions(-) diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index cba3df5486..5bb1c600cb 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -175,7 +175,7 @@ fn test_swap_coldkey_announced_works() { ); // Run some blocks for the announcement to be past the delay - // WARN: this requires to be done before staking to neurons to avoid + // WARN: this is required before staking to neurons to avoid // value mismatch due to coinbase run let delay = ColdkeySwapScheduleDuration::::get() + 1; System::run_to_block::(now + delay); @@ -268,9 +268,6 @@ fn test_swap_coldkey_announced_works() { balance_before - swap_cost.to_u64(), balance_after + left_over ); - // TODO: handle panics - // let total_issuance_after = TotalIssuance::::get(); - // assert_eq!(total_issuance_after, total_issuance_before - swap_cost); // Ensure the identity is correctly swapped assert!(IdentitiesV2::::get(&who).is_none()); @@ -540,95 +537,6 @@ fn test_swap_coldkey_with_not_enough_balance_to_pay_swap_cost_fails() { }); } -#[test] -fn test_swap_total_coldkey_stake() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - let other_coldkey = U256::from(3); - let hotkey = U256::from(4); - let other_hotkey = U256::from(5); - let stake = DefaultMinStake::::get().to_u64() * 10; - - let netuid = NetUid::from(1u16); - add_network(netuid, 1, 0); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, stake * 2 + 1_000); - register_ok_neuron(netuid, hotkey, old_coldkey, 1001000); - register_ok_neuron(netuid, other_hotkey, other_coldkey, 1001000); - - let reserve = stake * 10; - mock::setup_reserves(netuid, reserve.into(), reserve.into()); - - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(old_coldkey), - hotkey, - netuid, - stake.into() - )); - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(old_coldkey), - other_hotkey, - netuid, - stake.into() - )); - let total_stake_before_swap = SubtensorModule::get_total_stake_for_coldkey(&old_coldkey); - - let swap_cost = SubtensorModule::get_key_swap_cost(); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); - - assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); - - assert_eq!( - SubtensorModule::get_total_stake_for_coldkey(&old_coldkey), - TaoCurrency::ZERO - ); - assert_eq!( - SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), - total_stake_before_swap - ); - }); -} - -#[test] -fn test_swap_hotkey_owners() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - let hotkey = U256::from(3); - - Owner::::insert(hotkey, old_coldkey); - OwnedHotkeys::::insert(old_coldkey, vec![hotkey]); - - let swap_cost = SubtensorModule::get_key_swap_cost(); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); - - assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); - - assert_eq!(Owner::::get(hotkey), new_coldkey); - assert!(OwnedHotkeys::::get(old_coldkey).is_empty()); - assert_eq!(OwnedHotkeys::::get(new_coldkey), vec![hotkey]); - }); -} - -#[test] -fn test_transfer_remaining_balance() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - let balance = 100; - - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, balance); - - let swap_cost = SubtensorModule::get_key_swap_cost(); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); - - assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); - - assert_eq!(SubtensorModule::get_coldkey_balance(&old_coldkey), 0); - assert_eq!(SubtensorModule::get_coldkey_balance(&new_coldkey), balance); - }); -} - #[test] fn test_swap_with_no_stake() { new_test_ext(1).execute_with(|| { @@ -651,64 +559,6 @@ fn test_swap_with_no_stake() { }); } -#[test] -fn test_swap_with_multiple_hotkeys() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - let hotkey1 = U256::from(3); - let hotkey2 = U256::from(4); - - OwnedHotkeys::::insert(old_coldkey, vec![hotkey1, hotkey2]); - - let swap_cost = SubtensorModule::get_key_swap_cost(); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); - - assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); - - assert!(OwnedHotkeys::::get(old_coldkey).is_empty()); - assert_eq!( - OwnedHotkeys::::get(new_coldkey), - vec![hotkey1, hotkey2] - ); - }); -} - -// TODO -#[test] -fn test_swap_with_zero_balance() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - - let swap_cost = SubtensorModule::get_key_swap_cost(); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); - - println!( - "old_coldkey balance: {}", - Balances::free_balance(old_coldkey) - ); - println!( - "new_coldkey balance: {}", - Balances::free_balance(new_coldkey) - ); - - assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); - - println!( - "old_coldkey balance: {}", - Balances::free_balance(old_coldkey) - ); - println!( - "new_coldkey balance: {}", - Balances::free_balance(new_coldkey) - ); - - assert_eq!(Balances::free_balance(old_coldkey), 0); - assert_eq!(Balances::free_balance(new_coldkey), 0); - }); -} - #[test] fn test_swap_with_max_values() { new_test_ext(1).execute_with(|| { @@ -797,79 +647,6 @@ fn test_swap_with_max_values() { }); } -#[test] -fn test_swap_with_non_existent_new_coldkey() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - let hotkey = U256::from(3); - let stake = DefaultMinStake::::get().to_u64() * 10; - let netuid = NetUid::from(1); - - add_network(netuid, 1, 0); - register_ok_neuron(netuid, hotkey, old_coldkey, 1001000); - // Give old coldkey some balance. - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, stake + 1_000); - - let reserve = stake * 10; - mock::setup_reserves(netuid, reserve.into(), reserve.into()); - - // Stake to hotkey. - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(old_coldkey), - hotkey, - netuid, - stake.into() - )); - let expected_stake = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey, - &old_coldkey, - netuid, - ); - - let swap_cost = SubtensorModule::get_key_swap_cost(); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); - - assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey)); - - assert_eq!( - SubtensorModule::get_total_stake_for_coldkey(&old_coldkey), - TaoCurrency::ZERO - ); - - let actual_stake = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey, - &new_coldkey, - netuid, - ); - assert_abs_diff_eq!( - actual_stake, - expected_stake, - epsilon = expected_stake / 1000.into() - ); - }); -} - -#[test] -fn test_swap_with_max_hotkeys() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - let max_hotkeys = 1000; - let hotkeys: Vec = (0..max_hotkeys).map(U256::from).collect(); - - OwnedHotkeys::::insert(old_coldkey, hotkeys.clone()); - - let swap_cost = SubtensorModule::get_key_swap_cost(); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); - - assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey)); - - assert!(OwnedHotkeys::::get(old_coldkey).is_empty()); - assert_eq!(OwnedHotkeys::::get(new_coldkey), hotkeys); - }); -} - #[test] fn test_swap_effect_on_delegated_stake() { new_test_ext(1).execute_with(|| { @@ -927,300 +704,6 @@ fn test_swap_effect_on_delegated_stake() { }); } -// TODO -#[test] -fn test_swap_concurrent_modifications() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - let hotkey = U256::from(3); - let netuid = NetUid::from(1); - let initial_stake = 1_000_000_000_000; - let additional_stake = 500_000_000_000; - - let reserve = (initial_stake + additional_stake) * 1000; - mock::setup_reserves(netuid, reserve.into(), reserve.into()); - - // Setup initial state - add_network(netuid, 1, 1); - SubtensorModule::add_balance_to_coldkey_account( - &new_coldkey, - initial_stake + additional_stake + 1_000_000, - ); - register_ok_neuron(netuid, hotkey, new_coldkey, 1001000); - - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(new_coldkey), - hotkey, - netuid, - initial_stake.into() - )); - - // Verify initial stake - let stake_before_swap = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey, - &new_coldkey, - netuid, - ); - - // Wait some blocks - step_block(10); - - // Simulate concurrent stake addition - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(new_coldkey), - hotkey, - netuid, - additional_stake.into() - )); - - let stake_with_additional = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey, - &new_coldkey, - netuid, - ); - - let swap_cost = SubtensorModule::get_key_swap_cost(); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); - - assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); - - assert_eq!( - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey, - &new_coldkey, - netuid - ), - stake_with_additional - ); - assert!(stake_with_additional > stake_before_swap); - assert!(!Alpha::::contains_key((hotkey, old_coldkey, netuid))); - }); -} - -#[test] -fn test_swap_stake_for_coldkey() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - let hotkey1 = U256::from(3); - let hotkey2 = U256::from(4); - let stake_amount1 = DefaultMinStake::::get().to_u64() * 10; - let stake_amount2 = DefaultMinStake::::get().to_u64() * 20; - let stake_amount3 = DefaultMinStake::::get().to_u64() * 30; - - // Setup initial state - // Add a network - let netuid = NetUid::from(1u16); - add_network(netuid, 1, 0); - - // Register hotkeys - register_ok_neuron(netuid, hotkey1, old_coldkey, 0); - register_ok_neuron(netuid, hotkey2, old_coldkey, 0); - // Give some balance to old coldkey - SubtensorModule::add_balance_to_coldkey_account( - &old_coldkey, - stake_amount1 + stake_amount2 + 1_000_000, - ); - - let reserve = (stake_amount1 + stake_amount2 + stake_amount3) * 10; - mock::setup_reserves(netuid, reserve.into(), reserve.into()); - - // Stake to hotkeys - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(old_coldkey), - hotkey1, - netuid, - stake_amount1.into() - )); - let expected_stake_alpha1 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey1, - &old_coldkey, - netuid, - ); - - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(old_coldkey), - hotkey2, - netuid, - stake_amount2.into() - )); - let expected_stake_alpha2 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey2, - &old_coldkey, - netuid, - ); - - // Insert existing for same hotkey1 - // give new coldkey some balance - SubtensorModule::add_balance_to_coldkey_account(&new_coldkey, stake_amount3 + 1_000_000); - // Stake to hotkey1 - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(new_coldkey), - hotkey1, - netuid, - stake_amount3.into() - )); - let expected_stake_alpha3 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey1, - &new_coldkey, - netuid, - ); - - // Record initial values - let initial_total_issuance = SubtensorModule::get_total_issuance(); - let initial_total_stake = SubtensorModule::get_total_stake(); - let initial_total_stake_for_old_coldkey = - SubtensorModule::get_total_stake_for_coldkey(&old_coldkey); - let initial_total_stake_for_new_coldkey = - SubtensorModule::get_total_stake_for_coldkey(&new_coldkey); - let initial_total_hotkey1_stake = SubtensorModule::get_total_stake_for_hotkey(&hotkey1); - let initial_total_hotkey2_stake = SubtensorModule::get_total_stake_for_hotkey(&hotkey2); - - let swap_cost = SubtensorModule::get_key_swap_cost(); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); - - // Perform the swap - assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); - - // Verify stake is additive, not replaced - assert_abs_diff_eq!( - SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), - initial_total_stake_for_old_coldkey + initial_total_stake_for_new_coldkey, - epsilon = 2.into() - ); - - // Verify ownership transfer - assert_eq!( - SubtensorModule::get_owned_hotkeys(&new_coldkey), - vec![hotkey1, hotkey2] - ); - assert_eq!(SubtensorModule::get_owned_hotkeys(&old_coldkey), vec![]); - - // Verify stake transfer - assert_eq!( - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey1, - &new_coldkey, - netuid - ), - expected_stake_alpha1 + expected_stake_alpha3 - ); - assert_eq!( - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey2, - &new_coldkey, - netuid - ), - expected_stake_alpha2 - ); - assert_eq!( - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey1, - &old_coldkey, - netuid - ), - AlphaCurrency::ZERO - ); - assert_eq!( - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey2, - &old_coldkey, - netuid - ), - AlphaCurrency::ZERO - ); - - // Verify TotalHotkeyStake remains unchanged - assert_eq!( - SubtensorModule::get_total_stake_for_hotkey(&hotkey1), - initial_total_hotkey1_stake - ); - assert_eq!( - SubtensorModule::get_total_stake_for_hotkey(&hotkey2), - initial_total_hotkey2_stake - ); - - // Verify total stake and issuance remain unchanged - assert_eq!( - SubtensorModule::get_total_stake(), - initial_total_stake, - "Total stake changed unexpectedly" - ); - assert_eq!( - SubtensorModule::get_total_issuance(), - initial_total_issuance, - "Total issuance changed unexpectedly" - ); - }); -} - -#[test] -fn test_swap_staking_hotkeys_for_coldkey() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - let other_coldkey = U256::from(3); - let hotkey1 = U256::from(4); - let hotkey2 = U256::from(5); - let stake_amount1 = DefaultMinStake::::get().to_u64() * 10; - let stake_amount2 = DefaultMinStake::::get().to_u64() * 20; - - // Setup initial state - // Add a network - let netuid = NetUid::from(1u16); - add_network(netuid, 1, 0); - // Give some balance to old coldkey - SubtensorModule::add_balance_to_coldkey_account( - &old_coldkey, - stake_amount1 + stake_amount2 + 1_000_000, - ); // Register hotkeys - register_ok_neuron(netuid, hotkey1, old_coldkey, 0); - register_ok_neuron(netuid, hotkey2, other_coldkey, 0); - - let reserve = (stake_amount1 + stake_amount2) * 10; - mock::setup_reserves(netuid, reserve.into(), reserve.into()); - - // Stake to hotkeys - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(old_coldkey), - hotkey1, - netuid, - stake_amount1.into() - )); - let expected_stake_alpha1 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey1, - &old_coldkey, - netuid, - ); - - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(old_coldkey), - hotkey2, - netuid, - stake_amount2.into() - )); - let expected_stake_alpha2 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey2, - &old_coldkey, - netuid, - ); - - let swap_cost = SubtensorModule::get_key_swap_cost(); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); - - // Perform the swap - assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); - - // Verify StakingHotkeys transfer - assert_eq!( - StakingHotkeys::::get(new_coldkey), - vec![hotkey1, hotkey2] - ); - assert_eq!(StakingHotkeys::::get(old_coldkey), vec![]); - }); -} - #[test] fn test_swap_delegated_stake_for_coldkey() { new_test_ext(1).execute_with(|| { @@ -1769,307 +1252,121 @@ fn test_coldkey_delegations() { } #[test] -fn test_coldkey_in_swap_schedule_prevents_funds_usage() { - // Testing the signed extension validate function - // correctly filters transactions that attempt to use funds - // while a coldkey swap is scheduled. - +fn test_subtensor_extension_rejects_any_call_that_is_not_swap_coldkey_announced() { new_test_ext(0).execute_with(|| { let netuid = NetUid::from(1); - let version_key: u64 = 0; - let coldkey = U256::from(0); + let who = U256::from(0); let new_coldkey = U256::from(1); - let hotkey: U256 = U256::from(2); // Add the hotkey field - assert_ne!(hotkey, coldkey); // Ensure hotkey is NOT the same as coldkey !!! - - let stake = 100_000_000_000; - let reserve = stake * 100; + let hotkey = U256::from(2); + let stake = DefaultMinStake::::get().to_u64(); + assert_ne!(hotkey, who); + // Setup reserves + let reserve = stake * 10; mock::setup_reserves(netuid, reserve.into(), reserve.into()); - let who = coldkey; // The coldkey signs this transaction - - // Disallowed transactions are - // - add_stake - // - add_stake_limit - // - swap_stake - // - swap_stake_limit - // - move_stake - // - transfer_stake - // - balances.transfer_all - // - balances.transfer_allow_death - // - balances.transfer_keep_alive - - // Allowed transactions are: - // - remove_stake - // - remove_stake_limit - // others... - - // Create netuid + // Setup network and neuron add_network(netuid, 1, 0); - // Register the hotkey - SubtensorModule::append_neuron(netuid, &hotkey, 0); - crate::Owner::::insert(hotkey, coldkey); + register_ok_neuron(netuid, hotkey, who, 0); SubtensorModule::add_balance_to_coldkey_account(&who, u64::MAX); - // Set the minimum stake to 0. - SubtensorModule::set_stake_threshold(0); - // Add stake to the hotkey - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(who), - hotkey, - netuid, - stake.into() - )); - // Schedule the coldkey for a swap assert_ok!(SubtensorModule::announce_coldkey_swap( ::RuntimeOrigin::signed(who), new_coldkey, )); - assert!(ColdkeySwapAnnouncements::::contains_key(who)); - // Try each call - - // Add stake - let call = RuntimeCall::SubtensorModule(SubtensorCall::add_stake { - hotkey, - netuid, - amount_staked: stake.into(), - }); - let info = call.get_dispatch_info(); - let ext = SubtensorTransactionExtension::::new(); - // Should fail - assert_noop!( - ext.dispatch_transaction(RuntimeOrigin::signed(who).into(), call, &info, 0, 0), - CustomTransactionError::ColdkeySwapAnnounced - ); - - // Add stake limit - let call = RuntimeCall::SubtensorModule(SubtensorCall::add_stake_limit { - hotkey, - netuid, - amount_staked: stake.into(), - limit_price: stake.into(), - allow_partial: false, - }); - let info = call.get_dispatch_info(); - let ext = SubtensorTransactionExtension::::new(); - assert_noop!( - ext.dispatch_transaction(RuntimeOrigin::signed(who).into(), call, &info, 0, 0), - CustomTransactionError::ColdkeySwapAnnounced - ); - - // Swap stake - let call = RuntimeCall::SubtensorModule(SubtensorCall::swap_stake { - hotkey, - origin_netuid: netuid, - destination_netuid: netuid, - alpha_amount: stake.into(), - }); - let info = call.get_dispatch_info(); - let ext = SubtensorTransactionExtension::::new(); - assert_noop!( - ext.dispatch_transaction(RuntimeOrigin::signed(who).into(), call, &info, 0, 0), - CustomTransactionError::ColdkeySwapAnnounced - ); - - // Swap stake limit - let call = RuntimeCall::SubtensorModule(SubtensorCall::swap_stake_limit { - hotkey, - origin_netuid: netuid, - destination_netuid: netuid, - alpha_amount: stake.into(), - limit_price: stake.into(), - allow_partial: false, - }); - let info = call.get_dispatch_info(); - let ext = SubtensorTransactionExtension::::new(); - assert_noop!( - ext.dispatch_transaction(RuntimeOrigin::signed(who).into(), call, &info, 0, 0), - CustomTransactionError::ColdkeySwapAnnounced - ); - - // Move stake - let call = RuntimeCall::SubtensorModule(SubtensorCall::move_stake { - origin_hotkey: hotkey, - destination_hotkey: hotkey, - origin_netuid: netuid, - destination_netuid: netuid, - alpha_amount: stake.into(), - }); - let info = call.get_dispatch_info(); - let ext = SubtensorTransactionExtension::::new(); - assert_noop!( - ext.dispatch_transaction(RuntimeOrigin::signed(who).into(), call, &info, 0, 0), - CustomTransactionError::ColdkeySwapAnnounced - ); - - // Transfer stake - let call = RuntimeCall::SubtensorModule(SubtensorCall::transfer_stake { - destination_coldkey: new_coldkey, - hotkey, - origin_netuid: netuid, - destination_netuid: netuid, - alpha_amount: stake.into(), - }); - let info = call.get_dispatch_info(); - let ext = SubtensorTransactionExtension::::new(); - assert_noop!( - ext.dispatch_transaction(RuntimeOrigin::signed(who).into(), call, &info, 0, 0), - CustomTransactionError::ColdkeySwapAnnounced - ); - - // Transfer all - let call = RuntimeCall::Balances(BalancesCall::transfer_all { - dest: new_coldkey, - keep_alive: false, - }); - let info = call.get_dispatch_info(); - let ext = SubtensorTransactionExtension::::new(); - assert_noop!( - ext.dispatch_transaction(RuntimeOrigin::signed(who).into(), call, &info, 0, 0), - CustomTransactionError::ColdkeySwapAnnounced - ); - - // Transfer keep alive - let call = RuntimeCall::Balances(BalancesCall::transfer_keep_alive { - dest: new_coldkey, - value: 100_000_000_000, - }); - let info = call.get_dispatch_info(); - let ext = SubtensorTransactionExtension::::new(); - assert_noop!( - ext.dispatch_transaction(RuntimeOrigin::signed(who).into(), call, &info, 0, 0), - CustomTransactionError::ColdkeySwapAnnounced - ); - - // Transfer allow death - let call = RuntimeCall::Balances(BalancesCall::transfer_allow_death { - dest: new_coldkey, - value: 100_000_000_000, - }); - let info = call.get_dispatch_info(); - let ext = SubtensorTransactionExtension::::new(); - assert_noop!( - ext.dispatch_transaction(RuntimeOrigin::signed(who).into(), call, &info, 0, 0), - CustomTransactionError::ColdkeySwapAnnounced - ); - - // Burned register - let call = RuntimeCall::SubtensorModule(SubtensorCall::burned_register { netuid, hotkey }); - let info = call.get_dispatch_info(); - let ext = SubtensorTransactionExtension::::new(); - assert_noop!( - ext.dispatch_transaction(RuntimeOrigin::signed(who).into(), call, &info, 0, 0), - CustomTransactionError::ColdkeySwapAnnounced - ); - - remove_stake_rate_limit_for_tests(&hotkey, &coldkey, netuid); - - // Remove stake - let call = RuntimeCall::SubtensorModule(SubtensorCall::remove_stake { - hotkey, - netuid, - amount_unstaked: (DefaultMinStake::::get().to_u64() * 2).into(), - }); - let info = call.get_dispatch_info(); - let ext = SubtensorTransactionExtension::::new(); - assert_noop!( - ext.dispatch_transaction(RuntimeOrigin::signed(who).into(), call, &info, 0, 0), - CustomTransactionError::ColdkeySwapAnnounced - ); - - // Remove stake limit - let call = RuntimeCall::SubtensorModule(SubtensorCall::remove_stake_limit { - hotkey, - netuid, - amount_unstaked: (DefaultMinStake::::get().to_u64() * 2).into(), - limit_price: 123456789.into(), // should be low enough - allow_partial: true, - }); - let info = call.get_dispatch_info(); - let ext = SubtensorTransactionExtension::::new(); - assert_noop!( - ext.dispatch_transaction(RuntimeOrigin::signed(who).into(), call, &info, 0, 0), - CustomTransactionError::ColdkeySwapAnnounced - ); - - // Announce coldkey swap should succeed - let call = - RuntimeCall::SubtensorModule(SubtensorCall::announce_coldkey_swap { new_coldkey }); + let forbidden_calls: Vec = vec![ + RuntimeCall::SubtensorModule(SubtensorCall::dissolve_network { + netuid, + coldkey: who, + }), + RuntimeCall::SubtensorModule(SubtensorCall::add_stake { + hotkey, + netuid, + amount_staked: stake.into(), + }), + RuntimeCall::SubtensorModule(SubtensorCall::add_stake_limit { + hotkey, + netuid, + amount_staked: stake.into(), + limit_price: stake.into(), + allow_partial: false, + }), + RuntimeCall::SubtensorModule(SubtensorCall::swap_stake { + hotkey, + origin_netuid: netuid, + destination_netuid: netuid, + alpha_amount: stake.into(), + }), + RuntimeCall::SubtensorModule(SubtensorCall::swap_stake_limit { + hotkey, + origin_netuid: netuid, + destination_netuid: netuid, + alpha_amount: stake.into(), + limit_price: stake.into(), + allow_partial: false, + }), + RuntimeCall::SubtensorModule(SubtensorCall::move_stake { + origin_hotkey: hotkey, + destination_hotkey: hotkey, + origin_netuid: netuid, + destination_netuid: netuid, + alpha_amount: stake.into(), + }), + RuntimeCall::SubtensorModule(SubtensorCall::transfer_stake { + destination_coldkey: new_coldkey, + hotkey, + origin_netuid: netuid, + destination_netuid: netuid, + alpha_amount: stake.into(), + }), + RuntimeCall::SubtensorModule(SubtensorCall::remove_stake { + hotkey, + netuid, + amount_unstaked: (DefaultMinStake::::get().to_u64() * 2).into(), + }), + RuntimeCall::SubtensorModule(SubtensorCall::remove_stake_limit { + hotkey, + netuid, + amount_unstaked: (stake * 2).into(), + limit_price: 123456789.into(), + allow_partial: true, + }), + RuntimeCall::SubtensorModule(SubtensorCall::burned_register { netuid, hotkey }), + RuntimeCall::Balances(BalancesCall::transfer_all { + dest: new_coldkey, + keep_alive: false, + }), + RuntimeCall::Balances(BalancesCall::transfer_keep_alive { + dest: new_coldkey, + value: 100_000_000_000, + }), + RuntimeCall::Balances(BalancesCall::transfer_allow_death { + dest: new_coldkey, + value: 100_000_000_000, + }), + ]; + + for call in forbidden_calls { + let info = call.get_dispatch_info(); + let ext = SubtensorTransactionExtension::::new(); + assert_noop!( + ext.dispatch_transaction(RuntimeOrigin::signed(who).into(), call, &info, 0, 0), + CustomTransactionError::ColdkeySwapAnnounced + ); + } + + // Swap coldkey announced should succeed + let call = RuntimeCall::SubtensorModule(SubtensorCall::swap_coldkey_announced {}); let info = call.get_dispatch_info(); let ext = SubtensorTransactionExtension::::new(); assert_ok!(ext.dispatch_transaction(RuntimeOrigin::signed(who).into(), call, &info, 0, 0)); }); } -#[test] -fn test_announced_coldkey_swap_prevents_critical_calls() { - // Testing the signed extension validate function - // correctly filters transactions that are critical - // while a coldkey swap is announced. - - new_test_ext(0).execute_with(|| { - let netuid = NetUid::from(1); - let version_key: u64 = 0; - let coldkey = U256::from(0); - let new_coldkey = U256::from(1); - let hotkey: U256 = U256::from(2); // Add the hotkey field - assert_ne!(hotkey, coldkey); // Ensure hotkey is NOT the same as coldkey !!! - let stake = 100_000_000_000; - let reserve = stake * 10; - - mock::setup_reserves(netuid, reserve.into(), reserve.into()); - - let who = coldkey; // The coldkey signs this transaction - - // Disallowed transactions are - // - dissolve_network - - // Create netuid - add_network(netuid, 1, 0); - // Register the hotkey - SubtensorModule::append_neuron(netuid, &hotkey, 0); - crate::Owner::::insert(hotkey, coldkey); - - SubtensorModule::add_balance_to_coldkey_account(&who, u64::MAX); - - // Set the minimum stake to 0. - SubtensorModule::set_stake_threshold(0); - // Add stake to the hotkey - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(who), - hotkey, - netuid, - stake.into() - )); - - // Schedule the coldkey for a swap - assert_ok!(SubtensorModule::announce_coldkey_swap( - ::RuntimeOrigin::signed(who), - new_coldkey, - )); - - assert!(ColdkeySwapAnnouncements::::contains_key(who)); - - // Try each call - - // Dissolve network - let ext = SubtensorTransactionExtension::::new(); - let call = - RuntimeCall::SubtensorModule(SubtensorCall::dissolve_network { netuid, coldkey }); - let info = call.get_dispatch_info(); - - assert_noop!( - ext.dispatch_transaction(RuntimeOrigin::signed(who).into(), call, &info, 0, 0,), - CustomTransactionError::ColdkeySwapAnnounced - ); - }); -} - #[test] #[allow(deprecated)] fn test_swap_coldkey_deprecated() { diff --git a/pallets/subtensor/src/transaction_extension.rs b/pallets/subtensor/src/transaction_extension.rs index 7a9e226eb7..32eb057f98 100644 --- a/pallets/subtensor/src/transaction_extension.rs +++ b/pallets/subtensor/src/transaction_extension.rs @@ -1,4 +1,7 @@ -use crate::{BalancesCall, Call, Config, CustomTransactionError, Error, Pallet, TransactionType}; +use crate::{ + BalancesCall, Call, ColdkeySwapAnnouncements, Config, CustomTransactionError, Error, Pallet, + TransactionType, +}; use codec::{Decode, DecodeWithMemTracking, Encode}; use frame_support::dispatch::{DispatchInfo, PostDispatchInfo}; use frame_support::traits::IsSubType; @@ -113,7 +116,12 @@ where }; // Ensure the origin coldkey is not announced for a swap. - if !matches!(call.is_sub_type(), Some(Call::announce_coldkey_swap { .. })) { + if ColdkeySwapAnnouncements::::contains_key(who) + && !matches!( + call.is_sub_type(), + Some(Call::swap_coldkey_announced { .. }) + ) + { return Err(CustomTransactionError::ColdkeySwapAnnounced.into()); } From e6d6925ca8a4554be27e10c412f612ab04a8f252 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Fri, 5 Dec 2025 14:42:28 +0100 Subject: [PATCH 027/240] rename tests --- pallets/subtensor/src/tests/swap_coldkey.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index 5bb1c600cb..c13002a9ac 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -521,7 +521,7 @@ fn test_swap_coldkey_announced_with_hotkey_fails() { } #[test] -fn test_swap_coldkey_with_not_enough_balance_to_pay_swap_cost_fails() { +fn test_do_swap_coldkey_with_not_enough_balance_to_pay_swap_cost_fails() { new_test_ext(1).execute_with(|| { let who = U256::from(1); let new_coldkey = U256::from(2); @@ -538,7 +538,7 @@ fn test_swap_coldkey_with_not_enough_balance_to_pay_swap_cost_fails() { } #[test] -fn test_swap_with_no_stake() { +fn test_do_swap_coldkey_with_no_stake() { new_test_ext(1).execute_with(|| { let old_coldkey = U256::from(1); let new_coldkey = U256::from(2); @@ -560,7 +560,7 @@ fn test_swap_with_no_stake() { } #[test] -fn test_swap_with_max_values() { +fn test_do_swap_coldkey_with_max_values() { new_test_ext(1).execute_with(|| { let old_coldkey = U256::from(1); let new_coldkey = U256::from(2); @@ -648,7 +648,7 @@ fn test_swap_with_max_values() { } #[test] -fn test_swap_effect_on_delegated_stake() { +fn test_do_swap_coldkey_effect_on_delegated_stake() { new_test_ext(1).execute_with(|| { let subnet_owner_coldkey = U256::from(1001); let subnet_owner_hotkey = U256::from(1002); @@ -1167,7 +1167,7 @@ fn test_coldkey_swap_total() { } #[test] -fn test_coldkey_delegations() { +fn test_do_swap_coldkey_effect_on_delegations() { new_test_ext(1).execute_with(|| { let new_coldkey = U256::from(0); let owner = U256::from(1); From 760f4d37bd31c059535dfe44ad45545c9d445a7a Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Fri, 5 Dec 2025 15:39:56 +0100 Subject: [PATCH 028/240] use hash instead of coldkey during announcement --- pallets/subtensor/src/macros/dispatches.rs | 24 ++++-- pallets/subtensor/src/macros/errors.rs | 2 + pallets/subtensor/src/macros/events.rs | 4 +- pallets/subtensor/src/tests/swap_coldkey.rs | 95 ++++++++++++++------- 4 files changed, 85 insertions(+), 40 deletions(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index be92fec5a0..05ef213056 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -9,7 +9,7 @@ mod dispatches { use frame_support::traits::schedule::v3::Anon as ScheduleAnon; use frame_system::pallet_prelude::BlockNumberFor; use sp_core::ecdsa::Signature; - use sp_runtime::Percent; + use sp_runtime::{Percent, traits::Hash}; use crate::MAX_CRV3_COMMIT_SIZE_BYTES; use crate::MAX_NUM_ROOT_CLAIMS; @@ -2335,12 +2335,13 @@ mod dispatches { Ok(()) } - /// Announces a coldkey swap. This is required before the coldkey swap can be performed after the delay period. + /// Announces a coldkey swap using coldkey hash. + /// This is required before the coldkey swap can be performed after the delay period. #[pallet::call_index(125)] #[pallet::weight(Weight::zero())] pub fn announce_coldkey_swap( origin: OriginFor, - new_coldkey: T::AccountId, + new_coldkey_hash: T::Hash, ) -> DispatchResult { let who = ensure_signed(origin)?; let now = >::block_number(); @@ -2354,25 +2355,34 @@ mod dispatches { ); } - ColdkeySwapAnnouncements::::insert(who.clone(), (now, new_coldkey.clone())); + ColdkeySwapAnnouncements::::insert(who.clone(), (now, new_coldkey_hash.clone())); Self::deposit_event(Event::ColdkeySwapAnnounced { who: who.clone(), - new_coldkey: new_coldkey.clone(), + new_coldkey_hash: new_coldkey_hash.clone(), block_number: now, }); Ok(()) } /// Performs a coldkey swap iff an announcement has been made. + /// The provided new coldkey must match the announced coldkey hash. #[pallet::call_index(126)] #[pallet::weight(Weight::zero())] - pub fn swap_coldkey_announced(origin: OriginFor) -> DispatchResult { + pub fn swap_coldkey_announced( + origin: OriginFor, + new_coldkey: T::AccountId, + ) -> DispatchResult { let who = ensure_signed(origin)?; - let (when, new_coldkey) = ColdkeySwapAnnouncements::::take(who.clone()) + let (when, new_coldkey_hash) = ColdkeySwapAnnouncements::::take(who.clone()) .ok_or(Error::::ColdKeySwapAnnouncementNotFound)?; + ensure!( + new_coldkey_hash == T::Hashing::hash_of(&new_coldkey), + Error::::AnnouncedColdkeyHashDoesNotMatch + ); + let now = >::block_number(); let delay = ColdkeySwapScheduleDuration::::get(); ensure!(now > when + delay, Error::::ColdKeySwapTooEarly); diff --git a/pallets/subtensor/src/macros/errors.rs b/pallets/subtensor/src/macros/errors.rs index 49bbdfd2f8..710d9c4487 100644 --- a/pallets/subtensor/src/macros/errors.rs +++ b/pallets/subtensor/src/macros/errors.rs @@ -154,6 +154,8 @@ mod errors { ColdKeySwapTooEarly, /// Coldkey swap reannounced too early. ColdKeySwapReannouncedTooEarly, + /// The announced coldkey hash does not match the new coldkey hash. + AnnouncedColdkeyHashDoesNotMatch, /// New coldkey is hotkey NewColdKeyIsHotkey, /// Childkey take is invalid. diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index 3efc74e413..c2f34daba9 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -174,8 +174,8 @@ mod events { ColdkeySwapAnnounced { /// The account ID of the coldkey that made the announcement. who: T::AccountId, - /// The account ID of the new coldkey. - new_coldkey: T::AccountId, + /// The hash of the new coldkey. + new_coldkey_hash: T::Hash, /// The block number the announcement was made. block_number: BlockNumberFor, }, diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index c13002a9ac..e529c644ea 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -16,6 +16,7 @@ use frame_support::traits::schedule::v3::Named as ScheduleNamed; use frame_support::{assert_err, assert_noop, assert_ok}; use frame_system::{Config, RawOrigin}; use sp_core::{Get, H256, U256}; +use sp_runtime::traits::Hash; use sp_runtime::traits::{DispatchInfoOf, DispatchTransaction, TransactionExtension}; use sp_runtime::{DispatchError, traits::TxBaseImplication}; use substrate_fixed::types::U96F32; @@ -33,24 +34,25 @@ fn test_announce_coldkey_swap_works() { new_test_ext(1).execute_with(|| { let who = U256::from(1); let new_coldkey = U256::from(2); + let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); assert_eq!(ColdkeySwapAnnouncements::::iter().count(), 0); assert_ok!(SubtensorModule::announce_coldkey_swap( RuntimeOrigin::signed(who.clone()), - new_coldkey, + new_coldkey_hash, )); let now = System::block_number(); assert_eq!( ColdkeySwapAnnouncements::::iter().collect::>(), - vec![(who.clone(), (now, new_coldkey))] + vec![(who.clone(), (now, new_coldkey_hash))] ); assert_eq!( last_event(), RuntimeEvent::SubtensorModule(Event::ColdkeySwapAnnounced { who, - new_coldkey, + new_coldkey_hash, block_number: now, }) ); @@ -62,19 +64,21 @@ fn test_announce_coldkey_swap_with_existing_announcement_past_delay_works() { new_test_ext(1).execute_with(|| { let who = U256::from(1); let new_coldkey = U256::from(2); + let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); let new_coldkey_2 = U256::from(3); + let new_coldkey_2_hash = ::Hashing::hash_of(&new_coldkey_2); assert_eq!(ColdkeySwapAnnouncements::::iter().count(), 0); assert_ok!(SubtensorModule::announce_coldkey_swap( RuntimeOrigin::signed(who.clone()), - new_coldkey, + new_coldkey_hash, )); let now = System::block_number(); assert_eq!( ColdkeySwapAnnouncements::::iter().collect::>(), - vec![(who.clone(), (now, new_coldkey))] + vec![(who.clone(), (now, new_coldkey_hash))] ); let delay = ColdkeySwapScheduleDuration::::get() + 1; @@ -82,13 +86,13 @@ fn test_announce_coldkey_swap_with_existing_announcement_past_delay_works() { assert_ok!(SubtensorModule::announce_coldkey_swap( RuntimeOrigin::signed(who.clone()), - new_coldkey_2, + new_coldkey_2_hash, )); let now = System::block_number(); assert_eq!( ColdkeySwapAnnouncements::::iter().collect::>(), - vec![(who.clone(), (now, new_coldkey_2))] + vec![(who.clone(), (now, new_coldkey_2_hash))] ); }); } @@ -97,14 +101,15 @@ fn test_announce_coldkey_swap_with_existing_announcement_past_delay_works() { fn test_announce_coldkey_swap_with_bad_origin_fails() { new_test_ext(1).execute_with(|| { let new_coldkey = U256::from(1); + let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); assert_noop!( - SubtensorModule::announce_coldkey_swap(RuntimeOrigin::none(), new_coldkey), + SubtensorModule::announce_coldkey_swap(RuntimeOrigin::none(), new_coldkey_hash), BadOrigin ); assert_noop!( - SubtensorModule::announce_coldkey_swap(RuntimeOrigin::root(), new_coldkey), + SubtensorModule::announce_coldkey_swap(RuntimeOrigin::root(), new_coldkey_hash), BadOrigin ); }); @@ -115,19 +120,21 @@ fn test_announce_coldkey_swap_with_existing_announcement_not_past_delay_fails() new_test_ext(1).execute_with(|| { let who = U256::from(1); let new_coldkey = U256::from(2); + let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); let new_coldkey_2 = U256::from(3); + let new_coldkey_2_hash = ::Hashing::hash_of(&new_coldkey_2); assert_eq!(ColdkeySwapAnnouncements::::iter().count(), 0); assert_ok!(SubtensorModule::announce_coldkey_swap( RuntimeOrigin::signed(who.clone()), - new_coldkey, + new_coldkey_hash, )); let now = System::block_number(); assert_eq!( ColdkeySwapAnnouncements::::iter().collect::>(), - vec![(who.clone(), (now, new_coldkey))] + vec![(who.clone(), (now, new_coldkey_hash))] ); let unmet_delay = ColdkeySwapScheduleDuration::::get(); @@ -136,7 +143,7 @@ fn test_announce_coldkey_swap_with_existing_announcement_not_past_delay_fails() assert_noop!( SubtensorModule::announce_coldkey_swap( RuntimeOrigin::signed(who.clone()), - new_coldkey_2, + new_coldkey_2_hash, ), Error::::ColdKeySwapReannouncedTooEarly ); @@ -148,6 +155,7 @@ fn test_swap_coldkey_announced_works() { new_test_ext(1).execute_with(|| { let who = U256::from(1); let new_coldkey = U256::from(2); + let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); let hotkey1 = U256::from(1001); let hotkey2 = U256::from(1002); let hotkey3 = U256::from(1003); @@ -167,11 +175,11 @@ fn test_swap_coldkey_announced_works() { // Announce the coldkey swap assert_ok!(SubtensorModule::announce_coldkey_swap( RuntimeOrigin::signed(who.clone()), - new_coldkey, + new_coldkey_hash, )); assert_eq!( - ColdkeySwapAnnouncements::::get(who), - Some((now, new_coldkey)) + ColdkeySwapAnnouncements::::iter().collect::>(), + vec![(who.clone(), (now, new_coldkey_hash))] ); // Run some blocks for the announcement to be past the delay @@ -256,7 +264,8 @@ fn test_swap_coldkey_announced_works() { let total_stake_before = SubtensorModule::get_total_stake(); assert_ok!(SubtensorModule::swap_coldkey_announced( - ::RuntimeOrigin::signed(who) + ::RuntimeOrigin::signed(who), + new_coldkey )); // Ensure the announcement has been consumed @@ -416,12 +425,12 @@ fn test_swap_coldkey_announced_with_bad_origin_fails() { let new_coldkey = U256::from(2); assert_noop!( - SubtensorModule::swap_coldkey_announced(RuntimeOrigin::none()), + SubtensorModule::swap_coldkey_announced(RuntimeOrigin::none(), new_coldkey), BadOrigin ); assert_noop!( - SubtensorModule::swap_coldkey_announced(RuntimeOrigin::root()), + SubtensorModule::swap_coldkey_announced(RuntimeOrigin::root(), new_coldkey), BadOrigin ); }); @@ -434,26 +443,48 @@ fn test_swap_coldkey_announced_without_announcement_fails() { let new_coldkey = U256::from(2); assert_noop!( - SubtensorModule::swap_coldkey_announced(RuntimeOrigin::signed(who)), + SubtensorModule::swap_coldkey_announced(RuntimeOrigin::signed(who), new_coldkey), Error::::ColdKeySwapAnnouncementNotFound ); }) } +#[test] +fn test_swap_coldkey_announced_with_mismatched_coldkey_hash_fails() { + new_test_ext(1).execute_with(|| { + let who = U256::from(1); + let new_coldkey = U256::from(2); + let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); + let other_coldkey = U256::from(3); + + assert_ok!(SubtensorModule::announce_coldkey_swap( + RuntimeOrigin::signed(who.clone()), + new_coldkey_hash, + )); + + assert_noop!( + SubtensorModule::swap_coldkey_announced(RuntimeOrigin::signed(who), other_coldkey), + Error::::AnnouncedColdkeyHashDoesNotMatch + ); + }) +} + #[test] fn test_swap_coldkey_announced_too_early_fails() { new_test_ext(1).execute_with(|| { let who = U256::from(1); let new_coldkey = U256::from(2); + let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); assert_ok!(SubtensorModule::announce_coldkey_swap( RuntimeOrigin::signed(who.clone()), - new_coldkey, + new_coldkey_hash, )); assert_noop!( SubtensorModule::swap_coldkey_announced( - ::RuntimeOrigin::signed(who) + ::RuntimeOrigin::signed(who), + new_coldkey ), Error::::ColdKeySwapTooEarly ); @@ -465,11 +496,12 @@ fn test_swap_coldkey_announced_with_already_associated_coldkey_fails() { new_test_ext(1).execute_with(|| { let who = U256::from(1); let new_coldkey = U256::from(2); + let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); let hotkey = U256::from(3); assert_ok!(SubtensorModule::announce_coldkey_swap( RuntimeOrigin::signed(who.clone()), - new_coldkey, + new_coldkey_hash, )); let now = System::block_number(); @@ -483,7 +515,8 @@ fn test_swap_coldkey_announced_with_already_associated_coldkey_fails() { assert_noop!( SubtensorModule::swap_coldkey_announced( - ::RuntimeOrigin::signed(who) + ::RuntimeOrigin::signed(who), + new_coldkey ), Error::::ColdKeyAlreadyAssociated ); @@ -496,10 +529,11 @@ fn test_swap_coldkey_announced_with_hotkey_fails() { let who = U256::from(1); let new_coldkey = U256::from(2); let hotkey = U256::from(3); + let hotkey_hash = ::Hashing::hash_of(&hotkey); assert_ok!(SubtensorModule::announce_coldkey_swap( RuntimeOrigin::signed(who.clone()), - hotkey.clone(), + hotkey_hash, )); let now = System::block_number(); @@ -513,7 +547,8 @@ fn test_swap_coldkey_announced_with_hotkey_fails() { assert_noop!( SubtensorModule::swap_coldkey_announced( - ::RuntimeOrigin::signed(who) + ::RuntimeOrigin::signed(who), + hotkey ), Error::::NewColdKeyIsHotkey ); @@ -1257,6 +1292,7 @@ fn test_subtensor_extension_rejects_any_call_that_is_not_swap_coldkey_announced( let netuid = NetUid::from(1); let who = U256::from(0); let new_coldkey = U256::from(1); + let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); let hotkey = U256::from(2); let stake = DefaultMinStake::::get().to_u64(); assert_ne!(hotkey, who); @@ -1274,7 +1310,7 @@ fn test_subtensor_extension_rejects_any_call_that_is_not_swap_coldkey_announced( // Schedule the coldkey for a swap assert_ok!(SubtensorModule::announce_coldkey_swap( ::RuntimeOrigin::signed(who), - new_coldkey, + new_coldkey_hash, )); assert!(ColdkeySwapAnnouncements::::contains_key(who)); @@ -1360,7 +1396,8 @@ fn test_subtensor_extension_rejects_any_call_that_is_not_swap_coldkey_announced( } // Swap coldkey announced should succeed - let call = RuntimeCall::SubtensorModule(SubtensorCall::swap_coldkey_announced {}); + let call = + RuntimeCall::SubtensorModule(SubtensorCall::swap_coldkey_announced { new_coldkey }); let info = call.get_dispatch_info(); let ext = SubtensorTransactionExtension::::new(); assert_ok!(ext.dispatch_transaction(RuntimeOrigin::signed(who).into(), call, &info, 0, 0)); @@ -1403,8 +1440,4 @@ fn test_schedule_swap_coldkey_deprecated() { }); } -// TEST STAKING HOTKEY ARE ADDITIVE TO THE EXISTING ONES - -// TEST HOTKEYS OWNERSHIP IS ADDITIVE TO THE EXISTING ONES - // TEST TRANSFER ROOT CLAIM WITH NEW KEYS + ROOT CASE From 07f5e7258bd1da099f2c45d144e6a4a6a33ce3d4 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Fri, 5 Dec 2025 19:09:15 +0100 Subject: [PATCH 029/240] remove unused RescheduleDuration --- chain-extensions/src/mock.rs | 3 --- pallets/admin-utils/src/tests/mock.rs | 5 ----- pallets/subtensor/src/macros/config.rs | 3 --- pallets/subtensor/src/tests/mock.rs | 3 --- pallets/transaction-fee/src/tests/mock.rs | 5 ----- runtime/src/lib.rs | 7 +------ 6 files changed, 1 insertion(+), 25 deletions(-) diff --git a/chain-extensions/src/mock.rs b/chain-extensions/src/mock.rs index 98ea096199..edb9245fa7 100644 --- a/chain-extensions/src/mock.rs +++ b/chain-extensions/src/mock.rs @@ -325,9 +325,7 @@ parameter_types! { 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 InitialNetworkMaxStake: u64 = u64::MAX; // (DEPRECATED) pub const InitialColdkeySwapScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days - pub const InitialColdkeySwapRescheduleDuration: u64 = 24 * 60 * 60 / 12; // Default as 1 day pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days pub const InitialTaoWeight: u64 = 0; // 100% global weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks @@ -398,7 +396,6 @@ impl pallet_subtensor::Config for Test { type Yuma3On = InitialYuma3On; type Preimages = Preimage; type InitialColdkeySwapScheduleDuration = InitialColdkeySwapScheduleDuration; - type InitialColdkeySwapRescheduleDuration = InitialColdkeySwapRescheduleDuration; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialTaoWeight = InitialTaoWeight; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; diff --git a/pallets/admin-utils/src/tests/mock.rs b/pallets/admin-utils/src/tests/mock.rs index 0140808baa..6e587f0683 100644 --- a/pallets/admin-utils/src/tests/mock.rs +++ b/pallets/admin-utils/src/tests/mock.rs @@ -131,17 +131,13 @@ parameter_types! { 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 InitialSubnetLimit: u16 = 10; // (DEPRECATED) 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 InitialHotkeyEmissionTempo: u64 = 1; // (DEPRECATED) - // pub const InitialNetworkMaxStake: u64 = u64::MAX; // (DEPRECATED) pub const InitialColdkeySwapScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // 5 days - pub const InitialColdkeySwapRescheduleDuration: u64 = 24 * 60 * 60 / 12; // 1 day pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // 5 days pub const InitialTaoWeight: u64 = u64::MAX/10; // 10% global weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks @@ -211,7 +207,6 @@ impl pallet_subtensor::Config for Test { type Yuma3On = InitialYuma3On; type Preimages = (); type InitialColdkeySwapScheduleDuration = InitialColdkeySwapScheduleDuration; - type InitialColdkeySwapRescheduleDuration = InitialColdkeySwapRescheduleDuration; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialTaoWeight = InitialTaoWeight; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; diff --git a/pallets/subtensor/src/macros/config.rs b/pallets/subtensor/src/macros/config.rs index a735bde1e1..e08dffa073 100644 --- a/pallets/subtensor/src/macros/config.rs +++ b/pallets/subtensor/src/macros/config.rs @@ -221,9 +221,6 @@ mod config { /// Coldkey swap schedule duartion. #[pallet::constant] type InitialColdkeySwapScheduleDuration: Get>; - /// Coldkey swap reschedule duration. - #[pallet::constant] - type InitialColdkeySwapRescheduleDuration: Get>; /// Dissolve network schedule duration #[pallet::constant] type InitialDissolveNetworkScheduleDuration: Get>; diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index 090fcf8f75..2a064fbbdc 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -212,9 +212,7 @@ parameter_types! { 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 InitialNetworkMaxStake: u64 = u64::MAX; // (DEPRECATED) pub const InitialColdkeySwapScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days - pub const InitialColdkeySwapRescheduleDuration: u64 = 24 * 60 * 60 / 12; // Default as 1 day pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days pub const InitialTaoWeight: u64 = 0; // 100% global weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks @@ -285,7 +283,6 @@ impl crate::Config for Test { type Yuma3On = InitialYuma3On; type Preimages = Preimage; type InitialColdkeySwapScheduleDuration = InitialColdkeySwapScheduleDuration; - type InitialColdkeySwapRescheduleDuration = InitialColdkeySwapRescheduleDuration; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialTaoWeight = InitialTaoWeight; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; diff --git a/pallets/transaction-fee/src/tests/mock.rs b/pallets/transaction-fee/src/tests/mock.rs index ee5b1693ba..57f0b4cbda 100644 --- a/pallets/transaction-fee/src/tests/mock.rs +++ b/pallets/transaction-fee/src/tests/mock.rs @@ -196,17 +196,13 @@ parameter_types! { 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 InitialSubnetLimit: u16 = 10; // (DEPRECATED) 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 InitialHotkeyEmissionTempo: u64 = 1; // (DEPRECATED) - // pub const InitialNetworkMaxStake: u64 = u64::MAX; // (DEPRECATED) pub const InitialColdkeySwapScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // 5 days - pub const InitialColdkeySwapRescheduleDuration: u64 = 24 * 60 * 60 / 12; // 1 day pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // 5 days pub const InitialTaoWeight: u64 = u64::MAX/10; // 10% global weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks @@ -276,7 +272,6 @@ impl pallet_subtensor::Config for Test { type Yuma3On = InitialYuma3On; type Preimages = (); type InitialColdkeySwapScheduleDuration = InitialColdkeySwapScheduleDuration; - type InitialColdkeySwapRescheduleDuration = InitialColdkeySwapRescheduleDuration; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialTaoWeight = InitialTaoWeight; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 391036e380..5855c1063b 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -1039,7 +1039,6 @@ parameter_types! { pub const SubtensorInitialMinAllowedUids: u16 = 64; pub const SubtensorInitialMinLockCost: u64 = 1_000_000_000_000; // 1000 TAO pub const SubtensorInitialSubnetOwnerCut: u16 = 11_796; // 18 percent - // pub const SubtensorInitialSubnetLimit: u16 = 12; // (DEPRECATED) pub const SubtensorInitialNetworkLockReductionInterval: u64 = 14 * 7200; pub const SubtensorInitialNetworkRateLimit: u64 = 7200; pub const SubtensorInitialKeySwapCost: u64 = 100_000_000; // 0.1 TAO @@ -1047,14 +1046,11 @@ parameter_types! { 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 SubtensorInitialNetworkMaxStake: u64 = u64::MAX; // (DEPRECATED) pub const InitialColdkeySwapScheduleDuration: BlockNumber = 5 * 24 * 60 * 60 / 12; // 5 days - pub const InitialColdkeySwapRescheduleDuration: BlockNumber = 24 * 60 * 60 / 12; // 1 day pub const InitialDissolveNetworkScheduleDuration: BlockNumber = 5 * 24 * 60 * 60 / 12; // 5 days pub const SubtensorInitialTaoWeight: u64 = 971_718_665_099_567_868; // 0.05267697438728329% tao weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks - // 7 * 24 * 60 * 60 / 12 = 7 days - pub const DurationOfStartCall: u64 = prod_or_fast!(7 * 24 * 60 * 60 / 12, 10); + pub const DurationOfStartCall: u64 = prod_or_fast!(7 * 24 * 60 * 60 / 12, 10); // 7 days pub const SubtensorInitialKeySwapOnSubnetCost: u64 = 1_000_000; // 0.001 TAO pub const HotkeySwapOnSubnetInterval : BlockNumber = 5 * 24 * 60 * 60 / 12; // 5 days pub const LeaseDividendsDistributionInterval: BlockNumber = 100; // 100 blocks @@ -1121,7 +1117,6 @@ impl pallet_subtensor::Config for Runtime { type InitialTaoWeight = SubtensorInitialTaoWeight; type Preimages = Preimage; type InitialColdkeySwapScheduleDuration = InitialColdkeySwapScheduleDuration; - type InitialColdkeySwapRescheduleDuration = InitialColdkeySwapRescheduleDuration; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; type DurationOfStartCall = DurationOfStartCall; From a962bcdbb854880f674e36008dc90cb9d17e18bc Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Fri, 5 Dec 2025 19:37:15 +0100 Subject: [PATCH 030/240] fix TransactionError naming --- pallets/subtensor/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index d19ff35ebe..b05621bcef 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -2433,7 +2433,7 @@ pub mod pallet { #[derive(Debug, PartialEq)] pub enum CustomTransactionError { - ColdkeyInSwapSchedule, + ColdkeySwapAnnounced, StakeAmountTooLow, BalanceTooLow, SubnetNotExists, @@ -2460,7 +2460,7 @@ pub enum CustomTransactionError { impl From for u8 { fn from(variant: CustomTransactionError) -> u8 { match variant { - CustomTransactionError::ColdkeyInSwapSchedule => 0, + CustomTransactionError::ColdkeySwapAnnounced => 0, CustomTransactionError::StakeAmountTooLow => 1, CustomTransactionError::BalanceTooLow => 2, CustomTransactionError::SubnetNotExists => 3, From 688ed6890e7e811b1477ce5d17ca70c4e24fee7e Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Fri, 5 Dec 2025 19:37:45 +0100 Subject: [PATCH 031/240] announcements are hash of coldkey instead of raw coldkey --- pallets/subtensor/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index b05621bcef..ee13699223 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1373,7 +1373,7 @@ pub mod pallet { /// to the block number the announcement was made and the new coldkey. #[pallet::storage] pub type ColdkeySwapAnnouncements = - StorageMap<_, Twox64Concat, T::AccountId, (BlockNumberFor, T::AccountId), OptionQuery>; + StorageMap<_, Twox64Concat, T::AccountId, (BlockNumberFor, T::Hash), OptionQuery>; /// --- DMAP ( hot, netuid ) --> alpha | Returns the total amount of alpha a hotkey owns. #[pallet::storage] From dfd326d340c02132fc8f0f00235be4826738c6cd Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Fri, 5 Dec 2025 23:24:08 +0100 Subject: [PATCH 032/240] rename config parameters/admin dispatches --- chain-extensions/src/mock.rs | 4 +- pallets/admin-utils/src/benchmarking.rs | 2 +- pallets/admin-utils/src/lib.rs | 57 +++++++-------------- pallets/admin-utils/src/tests/mock.rs | 4 +- pallets/admin-utils/src/tests/mod.rs | 8 +-- pallets/subtensor/src/lib.rs | 45 +++------------- pallets/subtensor/src/macros/config.rs | 2 +- pallets/subtensor/src/macros/dispatches.rs | 4 +- pallets/subtensor/src/macros/events.rs | 6 +-- pallets/subtensor/src/tests/mock.rs | 4 +- pallets/subtensor/src/tests/swap_coldkey.rs | 14 ++--- pallets/subtensor/src/utils/misc.rs | 16 ++---- pallets/transaction-fee/src/tests/mock.rs | 4 +- runtime/src/lib.rs | 4 +- 14 files changed, 57 insertions(+), 117 deletions(-) diff --git a/chain-extensions/src/mock.rs b/chain-extensions/src/mock.rs index edb9245fa7..ee1b3d0099 100644 --- a/chain-extensions/src/mock.rs +++ b/chain-extensions/src/mock.rs @@ -325,7 +325,7 @@ parameter_types! { 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 InitialColdkeySwapScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days + pub const InitialColdKeySwapAnnouncementDelay: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days 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 @@ -395,7 +395,7 @@ impl pallet_subtensor::Config for Test { type LiquidAlphaOn = InitialLiquidAlphaOn; type Yuma3On = InitialYuma3On; type Preimages = Preimage; - type InitialColdkeySwapScheduleDuration = InitialColdkeySwapScheduleDuration; + type InitialColdkeySwapAnnouncementDelay = InitialColdkeySwapAnnouncementDelay; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialTaoWeight = InitialTaoWeight; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; diff --git a/pallets/admin-utils/src/benchmarking.rs b/pallets/admin-utils/src/benchmarking.rs index 08589e530b..28f2b1d75d 100644 --- a/pallets/admin-utils/src/benchmarking.rs +++ b/pallets/admin-utils/src/benchmarking.rs @@ -480,7 +480,7 @@ mod benchmarks { } #[benchmark] - fn sudo_set_coldkey_swap_schedule_duration() { + fn sudo_set_coldkey_swap_announcement_delay() { #[extrinsic_call] _(RawOrigin::Root, 100u32.into()); } diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index de6ac5825b..a2967ac499 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -1348,44 +1348,6 @@ pub mod pallet { res } - /// Sets the duration of the coldkey swap schedule. - /// - /// This extrinsic allows the root account to set the duration for the coldkey swap schedule. - /// The coldkey swap schedule determines how long it takes for a coldkey swap operation to complete. - /// - /// # Arguments - /// * `origin` - The origin of the call, which must be the root account. - /// * `duration` - The new duration for the coldkey swap schedule, in number of blocks. - /// - /// # Errors - /// * `BadOrigin` - If the caller is not the root account. - /// - /// # Weight - /// Weight is handled by the `#[pallet::weight]` attribute. - #[pallet::call_index(54)] - #[pallet::weight(( - Weight::from_parts(5_000_000, 0) - .saturating_add(T::DbWeight::get().reads(0_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)), - DispatchClass::Operational, - Pays::Yes - ))] - pub fn sudo_set_coldkey_swap_schedule_duration( - origin: OriginFor, - duration: BlockNumberFor, - ) -> DispatchResult { - // Ensure the call is made by the root account - ensure_root(origin)?; - - // Set the new duration of schedule coldkey swap - pallet_subtensor::Pallet::::set_coldkey_swap_schedule_duration(duration); - - // Log the change - log::trace!("ColdkeySwapScheduleDurationSet( duration: {duration:?} )"); - - Ok(()) - } - /// Sets the duration of the dissolve network schedule. /// /// This extrinsic allows the root account to set the duration for the dissolve network schedule. @@ -2213,6 +2175,25 @@ pub mod pallet { log::debug!("set_tao_flow_smoothing_factor( {smoothing_factor:?} ) "); Ok(()) } + + /// Sets the announcement delay for coldkey swap. + #[pallet::call_index(84)] + #[pallet::weight(( + Weight::from_parts(5_000_000, 0) + .saturating_add(T::DbWeight::get().reads(0_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)), + DispatchClass::Operational, + Pays::Yes + ))] + pub fn sudo_set_coldkey_swap_announcement_delay( + origin: OriginFor, + duration: BlockNumberFor, + ) -> DispatchResult { + ensure_root(origin)?; + pallet_subtensor::Pallet::::set_coldkey_swap_announcement_delay(duration); + log::trace!("ColdkeySwapScheduleDurationSet( duration: {duration:?} )"); + Ok(()) + } } } diff --git a/pallets/admin-utils/src/tests/mock.rs b/pallets/admin-utils/src/tests/mock.rs index 6e587f0683..e49afe124f 100644 --- a/pallets/admin-utils/src/tests/mock.rs +++ b/pallets/admin-utils/src/tests/mock.rs @@ -137,7 +137,7 @@ parameter_types! { 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 InitialColdkeySwapScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // 5 days + pub const InitialColdkeySwapAnnouncementDelay: u64 = 5 * 24 * 60 * 60 / 12; // 5 days pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // 5 days pub const InitialTaoWeight: u64 = u64::MAX/10; // 10% global weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks @@ -206,7 +206,7 @@ impl pallet_subtensor::Config for Test { type LiquidAlphaOn = InitialLiquidAlphaOn; type Yuma3On = InitialYuma3On; type Preimages = (); - type InitialColdkeySwapScheduleDuration = InitialColdkeySwapScheduleDuration; + type InitialColdkeySwapAnnouncementDelay = InitialColdkeySwapAnnouncementDelay; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialTaoWeight = InitialTaoWeight; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; diff --git a/pallets/admin-utils/src/tests/mod.rs b/pallets/admin-utils/src/tests/mod.rs index 1aaefc8f8d..7ca9372385 100644 --- a/pallets/admin-utils/src/tests/mod.rs +++ b/pallets/admin-utils/src/tests/mod.rs @@ -1382,7 +1382,7 @@ fn test_sudo_get_set_alpha() { } #[test] -fn test_sudo_set_coldkey_swap_schedule_duration() { +fn test_sudo_set_coldkey_swap_announcement_delay() { new_test_ext().execute_with(|| { // Arrange let root = RuntimeOrigin::root(); @@ -1391,12 +1391,12 @@ fn test_sudo_set_coldkey_swap_schedule_duration() { // Act & Assert: Non-root account should fail assert_noop!( - AdminUtils::sudo_set_coldkey_swap_schedule_duration(non_root, new_duration), + AdminUtils::sudo_set_coldkey_swap_announcement_delay(non_root, new_duration), DispatchError::BadOrigin ); // Act: Root account should succeed - assert_ok!(AdminUtils::sudo_set_coldkey_swap_schedule_duration( + assert_ok!(AdminUtils::sudo_set_coldkey_swap_announcement_delay( root.clone(), new_duration )); @@ -1408,7 +1408,7 @@ fn test_sudo_set_coldkey_swap_schedule_duration() { ); // Act & Assert: Setting the same value again should succeed (idempotent operation) - assert_ok!(AdminUtils::sudo_set_coldkey_swap_schedule_duration( + assert_ok!(AdminUtils::sudo_set_coldkey_swap_announcement_delay( root, new_duration )); diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index ee13699223..258f97915e 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -943,17 +943,11 @@ pub mod pallet { pub fn DefaultAlphaValues() -> (u16, u16) { (45875, 58982) } - - /// Default value for coldkey swap schedule duration + + /// Default value for coldkey swap announcement delay. #[pallet::type_value] - pub fn DefaultColdkeySwapScheduleDuration() -> BlockNumberFor { - T::InitialColdkeySwapScheduleDuration::get() - } - - /// Default value for coldkey swap reschedule duration - #[pallet::type_value] - pub fn DefaultColdkeySwapRescheduleDuration() -> BlockNumberFor { - T::InitialColdkeySwapRescheduleDuration::get() + pub fn DefaultColdkeySwapAnnouncementDelay() -> BlockNumberFor { + T::InitialColdkeySwapAnnouncementDelay::get() } /// Default value for applying pending items (e.g. childkeys). @@ -1012,15 +1006,6 @@ pub mod pallet { 360 } - /// Default value for coldkey swap scheduled - #[pallet::type_value] - pub fn DefaultColdkeySwapScheduled() -> (BlockNumberFor, T::AccountId) { - #[allow(clippy::expect_used)] - let default_account = T::AccountId::decode(&mut TrailingZeroInput::zeroes()) - .expect("trailing zeroes always produce a valid account ID; qed"); - (BlockNumberFor::::from(0_u32), default_account) - } - /// Default value for setting subnet owner hotkey rate limit #[pallet::type_value] pub fn DefaultSetSNOwnerHotkeyRateLimit() -> u64 { @@ -1072,16 +1057,6 @@ pub mod pallet { pub type OwnerHyperparamRateLimit = StorageValue<_, u16, ValueQuery, DefaultOwnerHyperparamRateLimit>; - /// Duration of coldkey swap schedule before execution - #[pallet::storage] - pub type ColdkeySwapScheduleDuration = - StorageValue<_, BlockNumberFor, ValueQuery, DefaultColdkeySwapScheduleDuration>; - - /// Duration of coldkey swap reschedule before execution - #[pallet::storage] - pub type ColdkeySwapRescheduleDuration = - StorageValue<_, BlockNumberFor, ValueQuery, DefaultColdkeySwapRescheduleDuration>; - /// Duration of dissolve network schedule before execution #[pallet::storage] pub type DissolveNetworkScheduleDuration = @@ -1358,16 +1333,10 @@ pub mod pallet { ValueQuery, >; - /// --- DMAP ( cold ) --> (block_expected, new_coldkey), Maps coldkey to the block to swap at and new coldkey. + /// The delay after an announcement before a coldkey swap can be performed. #[pallet::storage] - pub type ColdkeySwapScheduled = StorageMap< - _, - Blake2_128Concat, - T::AccountId, - (BlockNumberFor, T::AccountId), - ValueQuery, - DefaultColdkeySwapScheduled, - >; + pub type ColdkeySwapAnnouncementDelay = + StorageValue<_, BlockNumberFor, ValueQuery, DefaultColdkeySwapAnnouncementDelay>; /// A map of the coldkey swap announcements from a coldkey /// to the block number the announcement was made and the new coldkey. diff --git a/pallets/subtensor/src/macros/config.rs b/pallets/subtensor/src/macros/config.rs index e08dffa073..6f8b5466ee 100644 --- a/pallets/subtensor/src/macros/config.rs +++ b/pallets/subtensor/src/macros/config.rs @@ -220,7 +220,7 @@ mod config { // type InitialHotkeyEmissionTempo: Get; /// Coldkey swap schedule duartion. #[pallet::constant] - type InitialColdkeySwapScheduleDuration: Get>; + type InitialColdkeySwapAnnouncementDelay: Get>; /// Dissolve network schedule duration #[pallet::constant] type InitialDissolveNetworkScheduleDuration: Get>; diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 05ef213056..178565a8fe 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2347,7 +2347,7 @@ mod dispatches { let now = >::block_number(); if let Some(existing) = ColdkeySwapAnnouncements::::get(who.clone()) { - let delay = ColdkeySwapScheduleDuration::::get(); + let delay = ColdkeySwapAnnouncementDelay::::get(); let when = existing.0; ensure!( now > when + delay, @@ -2384,7 +2384,7 @@ mod dispatches { ); let now = >::block_number(); - let delay = ColdkeySwapScheduleDuration::::get(); + let delay = ColdkeySwapAnnouncementDelay::::get(); ensure!(now > when + delay, Error::::ColdKeySwapTooEarly); Self::do_swap_coldkey(&who, &new_coldkey)?; diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index c2f34daba9..4babd7f0a8 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -225,15 +225,15 @@ mod events { SubnetIdentityRemoved(NetUid), /// A dissolve network extrinsic scheduled. DissolveNetworkScheduled { - /// The account ID schedule the dissolve network extrisnic + /// The account ID schedule the dissolve network extrinsic account: T::AccountId, /// network ID will be dissolved netuid: NetUid, /// extrinsic execution block number execution_block: BlockNumberFor, }, - /// The duration of schedule coldkey swap has been set - ColdkeySwapScheduleDurationSet(BlockNumberFor), + /// The coldkey swap announcement delay has been set. + ColdkeySwapAnnouncementDelaySet(BlockNumberFor), /// The duration of dissolve network has been set DissolveNetworkScheduleDurationSet(BlockNumberFor), /// Commit-reveal v3 weights have been successfully committed. diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index 2a064fbbdc..1d17b38cb9 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -212,7 +212,7 @@ parameter_types! { 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 InitialColdkeySwapScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days + pub const InitialColdkeySwapAnnouncementDelay: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days 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 @@ -282,7 +282,7 @@ impl crate::Config for Test { type LiquidAlphaOn = InitialLiquidAlphaOn; type Yuma3On = InitialYuma3On; type Preimages = Preimage; - type InitialColdkeySwapScheduleDuration = InitialColdkeySwapScheduleDuration; + type InitialColdkeySwapAnnouncementDelay = InitialColdkeySwapAnnouncementDelay; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialTaoWeight = InitialTaoWeight; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index e529c644ea..367dda4927 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -27,7 +27,7 @@ use super::mock; use super::mock::*; use crate::transaction_extension::SubtensorTransactionExtension; use crate::*; -use crate::{Call, ColdkeySwapScheduleDuration, Error}; +use crate::{Call, Error}; #[test] fn test_announce_coldkey_swap_works() { @@ -81,7 +81,7 @@ fn test_announce_coldkey_swap_with_existing_announcement_past_delay_works() { vec![(who.clone(), (now, new_coldkey_hash))] ); - let delay = ColdkeySwapScheduleDuration::::get() + 1; + let delay = ColdkeySwapAnnouncementDelay::::get() + 1; System::run_to_block::(now + delay); assert_ok!(SubtensorModule::announce_coldkey_swap( @@ -137,7 +137,7 @@ fn test_announce_coldkey_swap_with_existing_announcement_not_past_delay_fails() vec![(who.clone(), (now, new_coldkey_hash))] ); - let unmet_delay = ColdkeySwapScheduleDuration::::get(); + let unmet_delay = ColdkeySwapAnnouncementDelay::::get(); System::run_to_block::(now + unmet_delay); assert_noop!( @@ -185,7 +185,7 @@ fn test_swap_coldkey_announced_works() { // Run some blocks for the announcement to be past the delay // WARN: this is required before staking to neurons to avoid // value mismatch due to coinbase run - let delay = ColdkeySwapScheduleDuration::::get() + 1; + let delay = ColdkeySwapAnnouncementDelay::::get() + 1; System::run_to_block::(now + delay); // Setup networks and subnet ownerships @@ -505,7 +505,7 @@ fn test_swap_coldkey_announced_with_already_associated_coldkey_fails() { )); let now = System::block_number(); - let delay = ColdkeySwapScheduleDuration::::get() + 1; + let delay = ColdkeySwapAnnouncementDelay::::get() + 1; System::run_to_block::(now + delay); let swap_cost = SubtensorModule::get_key_swap_cost(); @@ -537,7 +537,7 @@ fn test_swap_coldkey_announced_with_hotkey_fails() { )); let now = System::block_number(); - let delay = ColdkeySwapScheduleDuration::::get() + 1; + let delay = ColdkeySwapAnnouncementDelay::::get() + 1; System::run_to_block::(now + delay); let swap_cost = SubtensorModule::get_key_swap_cost(); @@ -562,7 +562,7 @@ fn test_do_swap_coldkey_with_not_enough_balance_to_pay_swap_cost_fails() { let new_coldkey = U256::from(2); let now = System::block_number(); - let delay = ColdkeySwapScheduleDuration::::get() + 1; + let delay = ColdkeySwapAnnouncementDelay::::get() + 1; System::run_to_block::(now + delay); assert_noop!( diff --git a/pallets/subtensor/src/utils/misc.rs b/pallets/subtensor/src/utils/misc.rs index cf2d55d168..cbf1e3fa8d 100644 --- a/pallets/subtensor/src/utils/misc.rs +++ b/pallets/subtensor/src/utils/misc.rs @@ -830,19 +830,9 @@ impl Pallet { TransferToggle::::get(netuid) } - /// Set the duration for coldkey swap - /// - /// # Arguments - /// - /// * `duration` - The blocks for coldkey swap execution. - /// - /// # Effects - /// - /// * Update the ColdkeySwapScheduleDuration storage. - /// * Emits a ColdkeySwapScheduleDurationSet evnet. - pub fn set_coldkey_swap_schedule_duration(duration: BlockNumberFor) { - ColdkeySwapScheduleDuration::::set(duration); - Self::deposit_event(Event::ColdkeySwapScheduleDurationSet(duration)); + pub fn set_coldkey_swap_announcement_delay(duration: BlockNumberFor) { + ColdkeySwapAnnouncementDelay::::set(duration); + Self::deposit_event(Event::ColdkeySwapAnnouncementDelaySet(duration)); } /// Set the duration for dissolve network diff --git a/pallets/transaction-fee/src/tests/mock.rs b/pallets/transaction-fee/src/tests/mock.rs index 57f0b4cbda..4ebef59c38 100644 --- a/pallets/transaction-fee/src/tests/mock.rs +++ b/pallets/transaction-fee/src/tests/mock.rs @@ -202,7 +202,7 @@ parameter_types! { 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 InitialColdkeySwapScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // 5 days + pub const InitialColdkeySwapAnnouncementDelay: u64 = 5 * 24 * 60 * 60 / 12; // 5 days pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // 5 days pub const InitialTaoWeight: u64 = u64::MAX/10; // 10% global weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks @@ -271,7 +271,7 @@ impl pallet_subtensor::Config for Test { type LiquidAlphaOn = InitialLiquidAlphaOn; type Yuma3On = InitialYuma3On; type Preimages = (); - type InitialColdkeySwapScheduleDuration = InitialColdkeySwapScheduleDuration; + type InitialColdkeySwapAnnouncementDelay = InitialColdkeySwapAnnouncementDelay; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialTaoWeight = InitialTaoWeight; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 5855c1063b..637112de29 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -1046,7 +1046,7 @@ parameter_types! { 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 InitialColdkeySwapScheduleDuration: BlockNumber = 5 * 24 * 60 * 60 / 12; // 5 days + pub const InitialColdkeySwapAnnouncementDelay: BlockNumber = 5 * 24 * 60 * 60 / 12; // 5 days pub const InitialDissolveNetworkScheduleDuration: BlockNumber = 5 * 24 * 60 * 60 / 12; // 5 days pub const SubtensorInitialTaoWeight: u64 = 971_718_665_099_567_868; // 0.05267697438728329% tao weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks @@ -1116,7 +1116,7 @@ impl pallet_subtensor::Config for Runtime { type Yuma3On = InitialYuma3On; type InitialTaoWeight = SubtensorInitialTaoWeight; type Preimages = Preimage; - type InitialColdkeySwapScheduleDuration = InitialColdkeySwapScheduleDuration; + type InitialColdkeySwapAnnouncementDelay = InitialColdkeySwapAnnouncementDelay; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; type DurationOfStartCall = DurationOfStartCall; From 370db1a6acb74e9ca3b5e57f081ffbbd3a241423 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Mon, 8 Dec 2025 11:55:47 +0100 Subject: [PATCH 033/240] added migration + fix old migration --- .../migrate_coldkey_swap_scheduled.rs | 7 -- ...coldkey_swap_scheduled_to_announcements.rs | 91 +++++++++++++++++++ pallets/subtensor/src/migrations/mod.rs | 1 + pallets/subtensor/src/tests/migration.rs | 72 ++++++++++++++- pallets/subtensor/src/tests/mock.rs | 2 +- 5 files changed, 164 insertions(+), 9 deletions(-) create mode 100644 pallets/subtensor/src/migrations/migrate_coldkey_swap_scheduled_to_announcements.rs diff --git a/pallets/subtensor/src/migrations/migrate_coldkey_swap_scheduled.rs b/pallets/subtensor/src/migrations/migrate_coldkey_swap_scheduled.rs index 8854f76387..243d953ac1 100644 --- a/pallets/subtensor/src/migrations/migrate_coldkey_swap_scheduled.rs +++ b/pallets/subtensor/src/migrations/migrate_coldkey_swap_scheduled.rs @@ -55,13 +55,6 @@ pub fn migrate_coldkey_swap_scheduled() -> Weight { } } - let default_value = DefaultColdkeySwapScheduled::::get(); - ColdkeySwapScheduled::::translate::<(), _>(|_coldkey: AccountIdOf, _: ()| { - Some((default_value.0, default_value.1.clone())) - }); - // write once for each item in the map, no matter remove or translate - weight.saturating_accrue(T::DbWeight::get().writes(curr_keys.len() as u64)); - // ------------------------------ // Step 2: Mark Migration as Completed // ------------------------------ diff --git a/pallets/subtensor/src/migrations/migrate_coldkey_swap_scheduled_to_announcements.rs b/pallets/subtensor/src/migrations/migrate_coldkey_swap_scheduled_to_announcements.rs new file mode 100644 index 0000000000..adaebdc6b9 --- /dev/null +++ b/pallets/subtensor/src/migrations/migrate_coldkey_swap_scheduled_to_announcements.rs @@ -0,0 +1,91 @@ +use super::*; +use crate::AccountIdOf; +use frame_support::{pallet_prelude::Blake2_128Concat, traits::Get, weights::Weight}; +use frame_system::pallet_prelude::BlockNumberFor; +use scale_info::prelude::string::String; +use sp_io::storage::clear; +use sp_runtime::traits::Hash; + +pub mod deprecated { + use super::*; + use frame_support::storage_alias; + + #[storage_alias] + pub type ColdkeySwapScheduleDuration = + StorageValue, BlockNumberFor, OptionQuery>; + + #[storage_alias] + pub type ColdkeySwapRescheduleDuration = + StorageValue, BlockNumberFor, OptionQuery>; + + #[storage_alias] + pub type ColdkeySwapScheduled = StorageMap< + Pallet, + Blake2_128Concat, + AccountIdOf, + (BlockNumberFor, AccountIdOf), + OptionQuery, + >; +} + +pub fn migrate_coldkey_swap_scheduled_to_announcements() -> Weight { + let migration_name = b"migrate_coldkey_swap_scheduled_to_announcements".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) + ); + + // Remove ColdkeySwapScheduleDuration and ColdkeySwapRescheduleDuration + let pallet_name = twox_128(b"SubtensorModule"); + let storage_name1 = twox_128(b"ColdkeySwapScheduleDuration"); + let storage_name2 = twox_128(b"ColdkeySwapRescheduleDuration"); + clear(&[pallet_name, storage_name1].concat()); + clear(&[pallet_name, storage_name2].concat()); + weight.saturating_accrue(T::DbWeight::get().writes(2)); + + // Migrate the ColdkeySwapScheduled entries to ColdkeySwapAnnouncements entries + let now = >::block_number(); + let scheduled = deprecated::ColdkeySwapScheduled::::iter(); + let delay = ColdkeySwapAnnouncementDelay::::get(); + + for (who, (when, new_coldkey)) in scheduled { + // Only migrate the scheduled coldkey swaps that are in the future + if when > now { + let coldkey_hash = ::Hashing::hash_of(&new_coldkey); + // The announcement should be at the scheduled time - delay to be able to call + // the swap_coldkey_announced call at the old scheduled time + ColdkeySwapAnnouncements::::insert(who, (when - delay, coldkey_hash)); + weight.saturating_accrue(T::DbWeight::get().writes(1)); + } + weight.saturating_accrue(T::DbWeight::get().reads(1)); + } + + let results = deprecated::ColdkeySwapScheduled::::clear(u32::MAX, None); + weight.saturating_accrue( + T::DbWeight::get().reads_writes(results.loops as u64, results.backend as u64), + ); + + // ------------------------------ + // Step 2: Mark Migration as Completed + // ------------------------------ + + HasMigrationRun::::insert(&migration_name, true); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + + log::info!( + "Migration '{:?}' completed successfully.", + String::from_utf8_lossy(&migration_name) + ); + + weight +} diff --git a/pallets/subtensor/src/migrations/mod.rs b/pallets/subtensor/src/migrations/mod.rs index 41c1333a89..6aec87cd4d 100644 --- a/pallets/subtensor/src/migrations/mod.rs +++ b/pallets/subtensor/src/migrations/mod.rs @@ -7,6 +7,7 @@ use sp_io::storage::clear_prefix; pub mod migrate_auto_stake_destination; pub mod migrate_chain_identity; pub mod migrate_coldkey_swap_scheduled; +pub mod migrate_coldkey_swap_scheduled_to_announcements; pub mod migrate_commit_reveal_settings; pub mod migrate_commit_reveal_v2; pub mod migrate_create_root_network; diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index b694459eaa..776f8609e3 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -25,7 +25,7 @@ use pallet_drand::types::RoundNumber; use scale_info::prelude::collections::VecDeque; use sp_core::{H256, U256, crypto::Ss58Codec}; use sp_io::hashing::twox_128; -use sp_runtime::traits::Zero; +use sp_runtime::{traits::Hash, traits::Zero}; use substrate_fixed::types::extra::U2; use substrate_fixed::types::{I96F32, U64F64}; use subtensor_runtime_common::{NetUidStorageIndex, TaoCurrency}; @@ -2724,3 +2724,73 @@ fn test_migrate_reset_unactive_sn_idempotence() { assert_eq!(TotalIssuance::::get(), total_issuance_before); }); } + +#[test] +fn test_migrate_coldkey_swap_scheduled_to_announcements() { + new_test_ext(1000).execute_with(|| { + use crate::migrations::migrate_coldkey_swap_scheduled_to_announcements::*; + let now = frame_system::Pallet::::block_number(); + + // Set the schedule duration and reschedule duration + deprecated::ColdkeySwapScheduleDuration::::set(Some(now + 100)); + deprecated::ColdkeySwapRescheduleDuration::::set(Some(now + 200)); + + // Set some scheduled coldkey swaps + deprecated::ColdkeySwapScheduled::::insert( + U256::from(1), + (now + 100, U256::from(10)), + ); + deprecated::ColdkeySwapScheduled::::insert( + U256::from(2), + (now - 200, U256::from(20)), + ); + deprecated::ColdkeySwapScheduled::::insert( + U256::from(3), + (now + 200, U256::from(30)), + ); + deprecated::ColdkeySwapScheduled::::insert( + U256::from(4), + (now - 400, U256::from(40)), + ); + deprecated::ColdkeySwapScheduled::::insert( + U256::from(5), + (now + 300, U256::from(50)), + ); + + let w = migrate_coldkey_swap_scheduled_to_announcements::(); + + assert!(!w.is_zero(), "weight must be non-zero"); + + // Ensure the deprecated storage is cleared + assert!(!deprecated::ColdkeySwapScheduleDuration::::exists()); + assert!(!deprecated::ColdkeySwapRescheduleDuration::::exists()); + assert_eq!(deprecated::ColdkeySwapScheduled::::iter().count(), 0); + + // Ensure scheduled have been migrated to announcements if not executed yet + // The announcement should be at the scheduled time - delay to be able to call + // the swap_coldkey_announced call at the old scheduled time + let delay = ColdkeySwapAnnouncementDelay::::get(); + assert_eq!(ColdkeySwapAnnouncements::::iter().count(), 3); + assert_eq!( + ColdkeySwapAnnouncements::::get(U256::from(1)), + Some(( + now + 100 - delay, + ::Hashing::hash_of(&U256::from(10)) + )) + ); + assert_eq!( + ColdkeySwapAnnouncements::::get(U256::from(3)), + Some(( + now + 200 - delay, + ::Hashing::hash_of(&U256::from(30)) + )) + ); + assert_eq!( + ColdkeySwapAnnouncements::::get(U256::from(5)), + Some(( + now + 300 - delay, + ::Hashing::hash_of(&U256::from(50)) + )) + ); + }); +} diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index 1d17b38cb9..d0830198b9 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -212,7 +212,7 @@ parameter_types! { 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 = 5 * 24 * 60 * 60 / 12; // Default as 5 days + pub const InitialColdkeySwapAnnouncementDelay: u64 = 50; 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 From 492b4179dfa14f26a95faecbf216fd906d470d2a Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Mon, 8 Dec 2025 11:55:59 +0100 Subject: [PATCH 034/240] cargo fmt --- pallets/subtensor/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 258f97915e..2801aa9f16 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -943,7 +943,7 @@ pub mod pallet { pub fn DefaultAlphaValues() -> (u16, u16) { (45875, 58982) } - + /// Default value for coldkey swap announcement delay. #[pallet::type_value] pub fn DefaultColdkeySwapAnnouncementDelay() -> BlockNumberFor { From 1c6fb23611ea19b0c4196885656f7a4aa8301321 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Mon, 8 Dec 2025 12:12:21 +0100 Subject: [PATCH 035/240] clippy fix --- chain-extensions/src/mock.rs | 2 +- pallets/admin-utils/src/tests/mod.rs | 17 +++---- pallets/subtensor/src/macros/dispatches.rs | 9 ++-- ...coldkey_swap_scheduled_to_announcements.rs | 4 +- pallets/subtensor/src/swap/swap_coldkey.rs | 18 +++---- pallets/subtensor/src/tests/swap_coldkey.rs | 49 +++++++++---------- 6 files changed, 49 insertions(+), 50 deletions(-) diff --git a/chain-extensions/src/mock.rs b/chain-extensions/src/mock.rs index ee1b3d0099..8a38fea2a7 100644 --- a/chain-extensions/src/mock.rs +++ b/chain-extensions/src/mock.rs @@ -325,7 +325,7 @@ parameter_types! { 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 = 5 * 24 * 60 * 60 / 12; // Default as 5 days + pub const InitialColdkeySwapAnnouncementDelay: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days 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 diff --git a/pallets/admin-utils/src/tests/mod.rs b/pallets/admin-utils/src/tests/mod.rs index 7ca9372385..5fcfeed083 100644 --- a/pallets/admin-utils/src/tests/mod.rs +++ b/pallets/admin-utils/src/tests/mod.rs @@ -1387,34 +1387,33 @@ fn test_sudo_set_coldkey_swap_announcement_delay() { // Arrange let root = RuntimeOrigin::root(); let non_root = RuntimeOrigin::signed(U256::from(1)); - let new_duration = 100u32.into(); + let new_delay = 100u32.into(); // Act & Assert: Non-root account should fail assert_noop!( - AdminUtils::sudo_set_coldkey_swap_announcement_delay(non_root, new_duration), + AdminUtils::sudo_set_coldkey_swap_announcement_delay(non_root, new_delay), DispatchError::BadOrigin ); // Act: Root account should succeed assert_ok!(AdminUtils::sudo_set_coldkey_swap_announcement_delay( root.clone(), - new_duration + new_delay )); - // Assert: Check if the duration was actually set + // Assert: Check if the delay was actually set assert_eq!( - pallet_subtensor::ColdkeySwapScheduleDuration::::get(), - new_duration + pallet_subtensor::ColdkeySwapAnnouncementDelay::::get(), + new_delay ); // Act & Assert: Setting the same value again should succeed (idempotent operation) assert_ok!(AdminUtils::sudo_set_coldkey_swap_announcement_delay( - root, - new_duration + root, new_delay )); // You might want to check for events here if your pallet emits them - System::assert_last_event(Event::ColdkeySwapScheduleDurationSet(new_duration).into()); + System::assert_last_event(Event::ColdkeySwapAnnouncementDelaySet(new_delay).into()); }); } diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 178565a8fe..e4a4b6f106 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -9,7 +9,7 @@ mod dispatches { use frame_support::traits::schedule::v3::Anon as ScheduleAnon; use frame_system::pallet_prelude::BlockNumberFor; use sp_core::ecdsa::Signature; - use sp_runtime::{Percent, traits::Hash}; + use sp_runtime::{Percent, Saturating, traits::Hash}; use crate::MAX_CRV3_COMMIT_SIZE_BYTES; use crate::MAX_NUM_ROOT_CLAIMS; @@ -2350,7 +2350,7 @@ mod dispatches { let delay = ColdkeySwapAnnouncementDelay::::get(); let when = existing.0; ensure!( - now > when + delay, + now > when.saturating_add(delay), Error::::ColdKeySwapReannouncedTooEarly ); } @@ -2385,7 +2385,10 @@ mod dispatches { let now = >::block_number(); let delay = ColdkeySwapAnnouncementDelay::::get(); - ensure!(now > when + delay, Error::::ColdKeySwapTooEarly); + ensure!( + now > when.saturating_add(delay), + Error::::ColdKeySwapTooEarly + ); Self::do_swap_coldkey(&who, &new_coldkey)?; diff --git a/pallets/subtensor/src/migrations/migrate_coldkey_swap_scheduled_to_announcements.rs b/pallets/subtensor/src/migrations/migrate_coldkey_swap_scheduled_to_announcements.rs index adaebdc6b9..12fa3f5768 100644 --- a/pallets/subtensor/src/migrations/migrate_coldkey_swap_scheduled_to_announcements.rs +++ b/pallets/subtensor/src/migrations/migrate_coldkey_swap_scheduled_to_announcements.rs @@ -4,7 +4,7 @@ use frame_support::{pallet_prelude::Blake2_128Concat, traits::Get, weights::Weig use frame_system::pallet_prelude::BlockNumberFor; use scale_info::prelude::string::String; use sp_io::storage::clear; -use sp_runtime::traits::Hash; +use sp_runtime::{Saturating, traits::Hash}; pub mod deprecated { use super::*; @@ -64,7 +64,7 @@ pub fn migrate_coldkey_swap_scheduled_to_announcements() -> Weight { let coldkey_hash = ::Hashing::hash_of(&new_coldkey); // The announcement should be at the scheduled time - delay to be able to call // the swap_coldkey_announced call at the old scheduled time - ColdkeySwapAnnouncements::::insert(who, (when - delay, coldkey_hash)); + ColdkeySwapAnnouncements::::insert(who, (when.saturating_sub(delay), coldkey_hash)); weight.saturating_accrue(T::DbWeight::get().writes(1)); } weight.saturating_accrue(T::DbWeight::get().reads(1)); diff --git a/pallets/subtensor/src/swap/swap_coldkey.rs b/pallets/subtensor/src/swap/swap_coldkey.rs index 491daf9c98..277f912192 100644 --- a/pallets/subtensor/src/swap/swap_coldkey.rs +++ b/pallets/subtensor/src/swap/swap_coldkey.rs @@ -9,11 +9,11 @@ impl Pallet { new_coldkey: &T::AccountId, ) -> DispatchResult { ensure!( - StakingHotkeys::::get(&new_coldkey).is_empty(), + StakingHotkeys::::get(new_coldkey).is_empty(), Error::::ColdKeyAlreadyAssociated ); ensure!( - !Self::hotkey_account_exists(&new_coldkey), + !Self::hotkey_account_exists(new_coldkey), Error::::NewColdKeyIsHotkey ); @@ -34,21 +34,21 @@ impl Pallet { } for netuid in Self::get_all_subnet_netuids() { - Self::transfer_subnet_ownership(netuid, old_coldkey, &new_coldkey); - Self::transfer_auto_stake_destination(netuid, old_coldkey, &new_coldkey); - Self::transfer_coldkey_stake(netuid, old_coldkey, &new_coldkey); + Self::transfer_subnet_ownership(netuid, old_coldkey, new_coldkey); + Self::transfer_auto_stake_destination(netuid, old_coldkey, new_coldkey); + 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_staking_hotkeys(old_coldkey, new_coldkey); + Self::transfer_hotkeys_ownership(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 { Self::kill_coldkey_account(old_coldkey, remaining_balance)?; - Self::add_balance_to_coldkey_account(&new_coldkey, remaining_balance); + Self::add_balance_to_coldkey_account(new_coldkey, remaining_balance); } - Self::set_last_tx_block(&new_coldkey, Self::get_current_block_as_u64()); + Self::set_last_tx_block(new_coldkey, Self::get_current_block_as_u64()); Self::deposit_event(Event::ColdkeySwapped { old_coldkey: old_coldkey.clone(), diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index 367dda4927..7f12665c59 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -39,14 +39,14 @@ fn test_announce_coldkey_swap_works() { assert_eq!(ColdkeySwapAnnouncements::::iter().count(), 0); assert_ok!(SubtensorModule::announce_coldkey_swap( - RuntimeOrigin::signed(who.clone()), + RuntimeOrigin::signed(who), new_coldkey_hash, )); let now = System::block_number(); assert_eq!( ColdkeySwapAnnouncements::::iter().collect::>(), - vec![(who.clone(), (now, new_coldkey_hash))] + vec![(who, (now, new_coldkey_hash))] ); assert_eq!( last_event(), @@ -71,28 +71,28 @@ fn test_announce_coldkey_swap_with_existing_announcement_past_delay_works() { assert_eq!(ColdkeySwapAnnouncements::::iter().count(), 0); assert_ok!(SubtensorModule::announce_coldkey_swap( - RuntimeOrigin::signed(who.clone()), + RuntimeOrigin::signed(who), new_coldkey_hash, )); let now = System::block_number(); assert_eq!( ColdkeySwapAnnouncements::::iter().collect::>(), - vec![(who.clone(), (now, new_coldkey_hash))] + vec![(who, (now, new_coldkey_hash))] ); let delay = ColdkeySwapAnnouncementDelay::::get() + 1; System::run_to_block::(now + delay); assert_ok!(SubtensorModule::announce_coldkey_swap( - RuntimeOrigin::signed(who.clone()), + RuntimeOrigin::signed(who), new_coldkey_2_hash, )); let now = System::block_number(); assert_eq!( ColdkeySwapAnnouncements::::iter().collect::>(), - vec![(who.clone(), (now, new_coldkey_2_hash))] + vec![(who, (now, new_coldkey_2_hash))] ); }); } @@ -127,24 +127,21 @@ fn test_announce_coldkey_swap_with_existing_announcement_not_past_delay_fails() assert_eq!(ColdkeySwapAnnouncements::::iter().count(), 0); assert_ok!(SubtensorModule::announce_coldkey_swap( - RuntimeOrigin::signed(who.clone()), + RuntimeOrigin::signed(who), new_coldkey_hash, )); let now = System::block_number(); assert_eq!( ColdkeySwapAnnouncements::::iter().collect::>(), - vec![(who.clone(), (now, new_coldkey_hash))] + vec![(who, (now, new_coldkey_hash))] ); let unmet_delay = ColdkeySwapAnnouncementDelay::::get(); System::run_to_block::(now + unmet_delay); assert_noop!( - SubtensorModule::announce_coldkey_swap( - RuntimeOrigin::signed(who.clone()), - new_coldkey_2_hash, - ), + SubtensorModule::announce_coldkey_swap(RuntimeOrigin::signed(who), new_coldkey_2_hash,), Error::::ColdKeySwapReannouncedTooEarly ); }); @@ -174,12 +171,12 @@ fn test_swap_coldkey_announced_works() { // Announce the coldkey swap assert_ok!(SubtensorModule::announce_coldkey_swap( - RuntimeOrigin::signed(who.clone()), + RuntimeOrigin::signed(who), new_coldkey_hash, )); assert_eq!( ColdkeySwapAnnouncements::::iter().collect::>(), - vec![(who.clone(), (now, new_coldkey_hash))] + vec![(who, (now, new_coldkey_hash))] ); // Run some blocks for the announcement to be past the delay @@ -222,8 +219,8 @@ fn test_swap_coldkey_announced_works() { register_ok_neuron(netuid1, hotkey3, who, 0); let hotkeys = vec![hotkey1, hotkey2, hotkey3]; - assert_eq!(StakingHotkeys::::get(&who), hotkeys); - assert_eq!(OwnedHotkeys::::get(&who), hotkeys); + assert_eq!(StakingHotkeys::::get(who), hotkeys); + assert_eq!(OwnedHotkeys::::get(who), hotkeys); assert_eq!(Owner::::get(hotkey1), who); assert_eq!(Owner::::get(hotkey2), who); assert_eq!(Owner::::get(hotkey3), who); @@ -279,8 +276,8 @@ fn test_swap_coldkey_announced_works() { ); // Ensure the identity is correctly swapped - assert!(IdentitiesV2::::get(&who).is_none()); - assert_eq!(IdentitiesV2::::get(&new_coldkey), Some(identity)); + assert!(IdentitiesV2::::get(who).is_none()); + assert_eq!(IdentitiesV2::::get(new_coldkey), Some(identity)); // Ensure the subnet ownerships are correctly swapped assert_eq!(SubnetOwner::::get(netuid1), new_coldkey); @@ -353,12 +350,12 @@ fn test_swap_coldkey_announced_works() { ); // Ensure the staking hotkeys are correctly swapped - assert!(StakingHotkeys::::get(&who).is_empty()); - assert_eq!(StakingHotkeys::::get(&new_coldkey), hotkeys); + assert!(StakingHotkeys::::get(who).is_empty()); + assert_eq!(StakingHotkeys::::get(new_coldkey), hotkeys); // Ensure the hotkey ownership is correctly swapped - assert!(OwnedHotkeys::::get(&who).is_empty()); - assert_eq!(OwnedHotkeys::::get(&new_coldkey), hotkeys); + assert!(OwnedHotkeys::::get(who).is_empty()); + assert_eq!(OwnedHotkeys::::get(new_coldkey), hotkeys); assert_eq!(Owner::::get(hotkey1), new_coldkey); assert_eq!(Owner::::get(hotkey2), new_coldkey); assert_eq!(Owner::::get(hotkey3), new_coldkey); @@ -458,7 +455,7 @@ fn test_swap_coldkey_announced_with_mismatched_coldkey_hash_fails() { let other_coldkey = U256::from(3); assert_ok!(SubtensorModule::announce_coldkey_swap( - RuntimeOrigin::signed(who.clone()), + RuntimeOrigin::signed(who), new_coldkey_hash, )); @@ -477,7 +474,7 @@ fn test_swap_coldkey_announced_too_early_fails() { let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); assert_ok!(SubtensorModule::announce_coldkey_swap( - RuntimeOrigin::signed(who.clone()), + RuntimeOrigin::signed(who), new_coldkey_hash, )); @@ -500,7 +497,7 @@ fn test_swap_coldkey_announced_with_already_associated_coldkey_fails() { let hotkey = U256::from(3); assert_ok!(SubtensorModule::announce_coldkey_swap( - RuntimeOrigin::signed(who.clone()), + RuntimeOrigin::signed(who), new_coldkey_hash, )); @@ -532,7 +529,7 @@ fn test_swap_coldkey_announced_with_hotkey_fails() { let hotkey_hash = ::Hashing::hash_of(&hotkey); assert_ok!(SubtensorModule::announce_coldkey_swap( - RuntimeOrigin::signed(who.clone()), + RuntimeOrigin::signed(who), hotkey_hash, )); From 839194540a8b1b5d708d240aefb4253514b49141 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Mon, 8 Dec 2025 14:16:23 +0100 Subject: [PATCH 036/240] reinstate swap_coldkey call + add call to remove announcement as root --- pallets/admin-utils/src/lib.rs | 2 +- pallets/subtensor/src/macros/dispatches.rs | 44 +++++-- pallets/subtensor/src/macros/errors.rs | 6 +- pallets/subtensor/src/swap/swap_coldkey.rs | 2 +- pallets/subtensor/src/tests/claim_root.rs | 8 +- pallets/subtensor/src/tests/swap_coldkey.rs | 135 ++++++++++++-------- 6 files changed, 123 insertions(+), 74 deletions(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index de1938f7ee..2b4fc35960 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -2199,7 +2199,7 @@ pub mod pallet { ) -> DispatchResult { ensure_root(origin)?; pallet_subtensor::Pallet::::set_coldkey_swap_announcement_delay(duration); - log::trace!("ColdkeySwapScheduleDurationSet( duration: {duration:?} )"); + log::trace!("ColdkeySwapAnnouncementDelaySet( duration: {duration:?} )"); Ok(()) } } diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index e4a4b6f106..6d1382596a 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -1065,19 +1065,24 @@ mod dispatches { Self::do_swap_hotkey(origin, &hotkey, &new_hotkey, netuid) } - /// The extrinsic for user to change the coldkey associated with their account. + /// Performs an arbitrary coldkey swap for any coldkey. /// - /// WARNING: This is deprecated in favor of `announce_coldkey_swap`/`coldkey_swap` + /// Only callable by root as it doesn't require an announcement and can be used to swap any coldkey. #[pallet::call_index(71)] #[pallet::weight(Weight::zero())] - #[deprecated(note = "Deprecated, please migrate to `announce_coldkey_swap`/`coldkey_swap`")] pub fn swap_coldkey( - _origin: OriginFor, - _old_coldkey: T::AccountId, - _new_coldkey: T::AccountId, - _swap_cost: TaoCurrency, + origin: OriginFor, + old_coldkey: T::AccountId, + new_coldkey: T::AccountId, + swap_cost: TaoCurrency, ) -> DispatchResult { - Err(Error::::Deprecated.into()) + ensure_root(origin)?; + + Self::do_swap_coldkey(&old_coldkey, &new_coldkey, swap_cost)?; + // We also remove any announcement for security reasons + ColdkeySwapAnnouncements::::remove(old_coldkey); + + Ok(()) } /// Sets the childkey take for a given hotkey. @@ -2351,7 +2356,7 @@ mod dispatches { let when = existing.0; ensure!( now > when.saturating_add(delay), - Error::::ColdKeySwapReannouncedTooEarly + Error::::ColdkeySwapReannouncedTooEarly ); } @@ -2376,7 +2381,7 @@ mod dispatches { let who = ensure_signed(origin)?; let (when, new_coldkey_hash) = ColdkeySwapAnnouncements::::take(who.clone()) - .ok_or(Error::::ColdKeySwapAnnouncementNotFound)?; + .ok_or(Error::::ColdkeySwapAnnouncementNotFound)?; ensure!( new_coldkey_hash == T::Hashing::hash_of(&new_coldkey), @@ -2387,12 +2392,27 @@ mod dispatches { let delay = ColdkeySwapAnnouncementDelay::::get(); ensure!( now > when.saturating_add(delay), - Error::::ColdKeySwapTooEarly + Error::::ColdkeySwapTooEarly ); - Self::do_swap_coldkey(&who, &new_coldkey)?; + let swap_cost = Self::get_key_swap_cost(); + Self::do_swap_coldkey(&who, &new_coldkey, swap_cost)?; Ok(()) } + + /// Removes a coldkey swap announcement for a coldkey. + /// + /// Only callable by root. + #[pallet::call_index(127)] + #[pallet::weight(Weight::zero())] + pub fn remove_coldkey_swap_announcement( + origin: OriginFor, + coldkey: T::AccountId, + ) -> DispatchResult { + ensure_root(origin)?; + ColdkeySwapAnnouncements::::remove(coldkey); + Ok(()) + } } } diff --git a/pallets/subtensor/src/macros/errors.rs b/pallets/subtensor/src/macros/errors.rs index 710d9c4487..174d9c547f 100644 --- a/pallets/subtensor/src/macros/errors.rs +++ b/pallets/subtensor/src/macros/errors.rs @@ -149,11 +149,11 @@ mod errors { /// Default transaction rate limit exceeded. TxRateLimitExceeded, /// Coldkey swap announcement not found - ColdKeySwapAnnouncementNotFound, + ColdkeySwapAnnouncementNotFound, /// Coldkey swap too early. - ColdKeySwapTooEarly, + ColdkeySwapTooEarly, /// Coldkey swap reannounced too early. - ColdKeySwapReannouncedTooEarly, + ColdkeySwapReannouncedTooEarly, /// The announced coldkey hash does not match the new coldkey hash. AnnouncedColdkeyHashDoesNotMatch, /// New coldkey is hotkey diff --git a/pallets/subtensor/src/swap/swap_coldkey.rs b/pallets/subtensor/src/swap/swap_coldkey.rs index 277f912192..51aec6798e 100644 --- a/pallets/subtensor/src/swap/swap_coldkey.rs +++ b/pallets/subtensor/src/swap/swap_coldkey.rs @@ -7,6 +7,7 @@ impl Pallet { pub fn do_swap_coldkey( old_coldkey: &T::AccountId, new_coldkey: &T::AccountId, + swap_cost: TaoCurrency, ) -> DispatchResult { ensure!( StakingHotkeys::::get(new_coldkey).is_empty(), @@ -18,7 +19,6 @@ impl Pallet { ); // Remove and recycle the swap cost from the old coldkey's account - let swap_cost = Self::get_key_swap_cost(); ensure!( Self::can_remove_balance_from_coldkey_account(old_coldkey, swap_cost.into()), Error::::NotEnoughBalanceToPaySwapColdKey diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index 0158579553..2fbfa309e7 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -1184,9 +1184,11 @@ fn test_claim_root_with_swap_coldkey() { ); // Swap coldkey - let swap_cost = SubtensorModule::get_key_swap_cost(); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, swap_cost.to_u64()); - assert_ok!(SubtensorModule::do_swap_coldkey(&coldkey, &new_coldkey,)); + assert_ok!(SubtensorModule::do_swap_coldkey( + &coldkey, + &new_coldkey, + TaoCurrency::ZERO + )); // Check swapped keys claimed values diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index 7f12665c59..9c48466ff9 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -142,7 +142,7 @@ fn test_announce_coldkey_swap_with_existing_announcement_not_past_delay_fails() assert_noop!( SubtensorModule::announce_coldkey_swap(RuntimeOrigin::signed(who), new_coldkey_2_hash,), - Error::::ColdKeySwapReannouncedTooEarly + Error::::ColdkeySwapReannouncedTooEarly ); }); } @@ -266,7 +266,7 @@ fn test_swap_coldkey_announced_works() { )); // Ensure the announcement has been consumed - assert_eq!(ColdkeySwapAnnouncements::::get(who), None); + assert_eq!(!ColdkeySwapAnnouncements::::contains_key(who)); // Ensure the cost has been withdrawn from the old coldkey and recycled let balance_after = SubtensorModule::get_coldkey_balance(&who); @@ -404,10 +404,11 @@ fn test_do_swap_coldkey_preserves_new_coldkey_identity() { }; IdentitiesV2::::insert(new_coldkey, new_identity.clone()); - let swap_cost = SubtensorModule::get_key_swap_cost(); - SubtensorModule::add_balance_to_coldkey_account(&who, swap_cost.to_u64()); - - assert_ok!(SubtensorModule::do_swap_coldkey(&who, &new_coldkey)); + assert_ok!(SubtensorModule::do_swap_coldkey( + &who, + &new_coldkey, + TaoCurrency::ZERO + )); // Identity is preserved assert_eq!(IdentitiesV2::::get(who), Some(old_identity)); @@ -441,7 +442,7 @@ fn test_swap_coldkey_announced_without_announcement_fails() { assert_noop!( SubtensorModule::swap_coldkey_announced(RuntimeOrigin::signed(who), new_coldkey), - Error::::ColdKeySwapAnnouncementNotFound + Error::::ColdkeySwapAnnouncementNotFound ); }) } @@ -483,7 +484,7 @@ fn test_swap_coldkey_announced_too_early_fails() { ::RuntimeOrigin::signed(who), new_coldkey ), - Error::::ColdKeySwapTooEarly + Error::::ColdkeySwapTooEarly ); }) } @@ -562,8 +563,9 @@ fn test_do_swap_coldkey_with_not_enough_balance_to_pay_swap_cost_fails() { let delay = ColdkeySwapAnnouncementDelay::::get() + 1; System::run_to_block::(now + delay); + let swap_cost = SubtensorModule::get_key_swap_cost(); assert_noop!( - SubtensorModule::do_swap_coldkey(&who, &new_coldkey), + SubtensorModule::do_swap_coldkey(&who, &new_coldkey, swap_cost), Error::::NotEnoughBalanceToPaySwapColdKey ); }); @@ -575,10 +577,11 @@ fn test_do_swap_coldkey_with_no_stake() { let old_coldkey = U256::from(1); let new_coldkey = U256::from(2); - let swap_cost = SubtensorModule::get_key_swap_cost(); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); - - assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); + assert_ok!(SubtensorModule::do_swap_coldkey( + &old_coldkey, + &new_coldkey, + TaoCurrency::ZERO + )); assert_eq!( SubtensorModule::get_total_stake_for_coldkey(&old_coldkey), @@ -648,14 +651,15 @@ fn test_do_swap_coldkey_with_max_values() { netuid2, ); - let swap_cost = SubtensorModule::get_key_swap_cost(); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey2, swap_cost.to_u64()); - - assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); + assert_ok!(SubtensorModule::do_swap_coldkey( + &old_coldkey, + &new_coldkey, + TaoCurrency::ZERO + )); assert_ok!(SubtensorModule::do_swap_coldkey( &old_coldkey2, - &new_coldkey2 + &new_coldkey2, + TaoCurrency::ZERO )); assert_eq!( @@ -713,10 +717,11 @@ fn test_do_swap_coldkey_effect_on_delegated_stake() { let coldkey_stake_before = SubtensorModule::get_total_stake_for_coldkey(&old_coldkey); let delegator_stake_before = SubtensorModule::get_total_stake_for_coldkey(&delegator); - let swap_cost = SubtensorModule::get_key_swap_cost(); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); - - assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); + assert_ok!(SubtensorModule::do_swap_coldkey( + &old_coldkey, + &new_coldkey, + TaoCurrency::ZERO + )); assert_abs_diff_eq!( SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), @@ -808,11 +813,12 @@ fn test_swap_delegated_stake_for_coldkey() { let total_hotkey1_stake = SubtensorModule::get_total_stake_for_hotkey(&hotkey1); let total_hotkey2_stake = SubtensorModule::get_total_stake_for_hotkey(&hotkey2); - let swap_cost = SubtensorModule::get_key_swap_cost(); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); - // Perform the swap - assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); + assert_ok!(SubtensorModule::do_swap_coldkey( + &old_coldkey, + &new_coldkey, + TaoCurrency::ZERO + )); // Verify stake transfer assert_eq!( @@ -1100,16 +1106,17 @@ fn test_coldkey_swap_total() { vec![hotkey3, delegate3] ); - let swap_cost = SubtensorModule::get_key_swap_cost(); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, swap_cost.to_u64()); - // Perform the swap let new_coldkey = U256::from(1100); assert_eq!( SubtensorModule::get_total_stake_for_coldkey(&coldkey), ck_stake ); - assert_ok!(SubtensorModule::do_swap_coldkey(&coldkey, &new_coldkey,)); + assert_ok!(SubtensorModule::do_swap_coldkey( + &coldkey, + &new_coldkey, + TaoCurrency::ZERO + )); assert_eq!( SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), ck_stake @@ -1243,11 +1250,12 @@ fn test_do_swap_coldkey_effect_on_delegations() { stake.into() )); - let swap_cost = SubtensorModule::get_key_swap_cost(); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, swap_cost.to_u64()); - // Perform the swap - assert_ok!(SubtensorModule::do_swap_coldkey(&coldkey, &new_coldkey,)); + assert_ok!(SubtensorModule::do_swap_coldkey( + &coldkey, + &new_coldkey, + TaoCurrency::ZERO + )); // Verify stake was moved for the delegate let approx_total_stake = TaoCurrency::from(stake * 2 - fee * 2); @@ -1283,6 +1291,44 @@ fn test_do_swap_coldkey_effect_on_delegations() { }); } +#[test] +fn test_remove_coldkey_swap_announcement_works() { + new_test_ext(1).execute_with(|| { + let who = U256::from(1); + let new_coldkey = U256::from(2); + let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); + + assert_ok!(SubtensorModule::announce_coldkey_swap( + RuntimeOrigin::signed(who), + new_coldkey_hash, + )); + + assert_ok!(SubtensorModule::remove_coldkey_swap_announcement( + RuntimeOrigin::root(), + who, + )); + + assert!(!ColdkeySwapAnnouncements::::contains_key(who)); + }); +} + +#[test] +fn test_remove_coldkey_swap_announcement_with_bad_origin_fails() { + new_test_ext(1).execute_with(|| { + let who = U256::from(1); + + assert_noop!( + SubtensorModule::remove_coldkey_swap_announcement(RuntimeOrigin::none(), who), + BadOrigin + ); + + assert_noop!( + SubtensorModule::remove_coldkey_swap_announcement(RuntimeOrigin::root(), who), + BadOrigin + ); + }); +} + #[test] fn test_subtensor_extension_rejects_any_call_that_is_not_swap_coldkey_announced() { new_test_ext(0).execute_with(|| { @@ -1401,25 +1447,6 @@ fn test_subtensor_extension_rejects_any_call_that_is_not_swap_coldkey_announced( }); } -#[test] -#[allow(deprecated)] -fn test_swap_coldkey_deprecated() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - - assert_noop!( - SubtensorModule::swap_coldkey( - <::RuntimeOrigin>::root(), - old_coldkey, - new_coldkey, - TaoCurrency::MAX - ), - Error::::Deprecated - ); - }); -} - #[test] #[allow(deprecated)] fn test_schedule_swap_coldkey_deprecated() { From 2e6e1cb81d178e66f3855dc8083c8c5bbc40acb3 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Mon, 8 Dec 2025 15:24:17 +0100 Subject: [PATCH 037/240] rework test + add tests for swap_coldkey as root --- pallets/subtensor/src/tests/swap_coldkey.rs | 652 +++++++++++++------- 1 file changed, 421 insertions(+), 231 deletions(-) diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index 9c48466ff9..666e4a69fe 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -164,258 +164,67 @@ fn test_swap_coldkey_announced_works() { let stake3 = min_stake * 30; let now = System::block_number(); - SubtensorModule::add_balance_to_coldkey_account( - &who, - stake1 + stake2 + stake3 + swap_cost.to_u64() + left_over, - ); - - // Announce the coldkey swap assert_ok!(SubtensorModule::announce_coldkey_swap( RuntimeOrigin::signed(who), new_coldkey_hash, )); - assert_eq!( - ColdkeySwapAnnouncements::::iter().collect::>(), - vec![(who, (now, new_coldkey_hash))] - ); // Run some blocks for the announcement to be past the delay - // WARN: this is required before staking to neurons to avoid - // value mismatch due to coinbase run let delay = ColdkeySwapAnnouncementDelay::::get() + 1; System::run_to_block::(now + delay); - // Setup networks and subnet ownerships - let netuid1 = NetUid::from(1); - let netuid2 = NetUid::from(2); - add_network(netuid1, 1, 0); - add_network(netuid2, 1, 0); - SubnetOwner::::insert(netuid1, who); - SubnetOwner::::insert(netuid2, who); - - // Setup reserves - let reserve1 = (stake1 + stake3) * 10; - let reserve2 = stake2 * 10; - mock::setup_reserves(netuid1, reserve1.into(), reserve1.into()); - mock::setup_reserves(netuid2, reserve2.into(), reserve2.into()); - - // Setup auto stake destinations - AutoStakeDestination::::insert(who, netuid1, hotkey1); - AutoStakeDestination::::insert(who, netuid2, hotkey2); - AutoStakeDestinationColdkeys::::insert( - hotkey1, + let ( netuid1, - vec![who, U256::from(3), U256::from(4)], - ); - AutoStakeDestinationColdkeys::::insert( - hotkey2, netuid2, - vec![U256::from(7), U256::from(8), who], - ); - - // Setup neurons with stake - register_ok_neuron(netuid1, hotkey1, who, 0); - register_ok_neuron(netuid2, hotkey2, who, 0); - register_ok_neuron(netuid1, hotkey3, who, 0); - - let hotkeys = vec![hotkey1, hotkey2, hotkey3]; - assert_eq!(StakingHotkeys::::get(who), hotkeys); - assert_eq!(OwnedHotkeys::::get(who), hotkeys); - assert_eq!(Owner::::get(hotkey1), who); - assert_eq!(Owner::::get(hotkey2), who); - assert_eq!(Owner::::get(hotkey3), who); - - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(who), + hotkeys, + hk1_alpha, + hk2_alpha, + hk3_alpha, + total_ck_stake, + identity, + balance_before, + total_stake_before, + ) = comprehensive_setup!( + who, + new_coldkey, + new_coldkey_hash, + swap_cost, + left_over, + stake1, + stake2, + stake3, hotkey1, - netuid1, - stake1.into() - )); - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(who), hotkey2, - netuid2, - stake2.into() - )); - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(who), - hotkey3, - netuid1, - stake3.into() - )); - let hk1_alpha = - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey1, &who, netuid1); - let hk2_alpha = - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey2, &who, netuid2); - let hk3_alpha = - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey3, &who, netuid1); - let total_ck_stake = SubtensorModule::get_total_stake_for_coldkey(&who); - - // Setup identity - let identity = ChainIdentityV2::default(); - IdentitiesV2::::insert(who, identity.clone()); - assert_eq!(IdentitiesV2::::get(who), Some(identity.clone())); - assert!(IdentitiesV2::::get(new_coldkey).is_none()); - - let balance_before = SubtensorModule::get_coldkey_balance(&who); - let total_stake_before = SubtensorModule::get_total_stake(); + hotkey3 + ); assert_ok!(SubtensorModule::swap_coldkey_announced( ::RuntimeOrigin::signed(who), new_coldkey )); - // Ensure the announcement has been consumed - assert_eq!(!ColdkeySwapAnnouncements::::contains_key(who)); - - // Ensure the cost has been withdrawn from the old coldkey and recycled - let balance_after = SubtensorModule::get_coldkey_balance(&who); - assert_eq!( - balance_before - swap_cost.to_u64(), - balance_after + left_over - ); - - // Ensure the identity is correctly swapped - assert!(IdentitiesV2::::get(who).is_none()); - assert_eq!(IdentitiesV2::::get(new_coldkey), Some(identity)); - - // Ensure the subnet ownerships are correctly swapped - assert_eq!(SubnetOwner::::get(netuid1), new_coldkey); - assert_eq!(SubnetOwner::::get(netuid2), new_coldkey); - - // Ensure the auto stake destinations are correctly swapped - assert!(AutoStakeDestination::::get(who, netuid1).is_none()); - assert!(AutoStakeDestination::::get(who, netuid2).is_none()); - assert_eq!( - AutoStakeDestination::::get(new_coldkey, netuid1), - Some(hotkey1) - ); - assert_eq!( - AutoStakeDestination::::get(new_coldkey, netuid2), - Some(hotkey2) - ); - assert_eq!( - AutoStakeDestinationColdkeys::::get(hotkey1, netuid1), - vec![U256::from(3), U256::from(4), new_coldkey] - ); - assert_eq!( - AutoStakeDestinationColdkeys::::get(hotkey2, netuid2), - vec![U256::from(7), U256::from(8), new_coldkey] - ); - - // Ensure the coldkey stake is correctly swapped - assert_eq!( - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey1, &who, netuid1), - AlphaCurrency::ZERO - ); - assert_eq!( - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey2, &who, netuid2), - AlphaCurrency::ZERO - ); - assert_eq!( - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey3, &who, netuid1), - AlphaCurrency::ZERO - ); - assert_eq!( - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey1, - &new_coldkey, - netuid1 - ), - hk1_alpha - ); - assert_eq!( - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey2, - &new_coldkey, - netuid2 - ), - hk2_alpha - ); - assert_eq!( - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey3, - &new_coldkey, - netuid1 - ), - hk3_alpha - ); - assert_eq!( - SubtensorModule::get_total_stake_for_coldkey(&who), - TaoCurrency::ZERO - ); - assert_eq!( - SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), + comprehensive_checks!( + who, + hotkey1, + hotkey2, + hotkey3, + hotkeys, + new_coldkey, + balance_before, + left_over, + swap_cost, + identity, + netuid1, + netuid2, + hk1_alpha, + hk2_alpha, + hk3_alpha, total_ck_stake, - ); - - // Ensure the staking hotkeys are correctly swapped - assert!(StakingHotkeys::::get(who).is_empty()); - assert_eq!(StakingHotkeys::::get(new_coldkey), hotkeys); - - // Ensure the hotkey ownership is correctly swapped - assert!(OwnedHotkeys::::get(who).is_empty()); - assert_eq!(OwnedHotkeys::::get(new_coldkey), hotkeys); - assert_eq!(Owner::::get(hotkey1), new_coldkey); - assert_eq!(Owner::::get(hotkey2), new_coldkey); - assert_eq!(Owner::::get(hotkey3), new_coldkey); - - // Ensure the remaining balance is transferred to the new coldkey - assert_eq!(SubtensorModule::get_coldkey_balance(&who), 0); - assert_eq!( - SubtensorModule::get_coldkey_balance(&new_coldkey), - left_over - ); - - // Ensure total stake is unchanged - assert_eq!( - SubtensorModule::get_total_stake(), - total_stake_before, - "Total stake changed unexpectedly" - ); - - // Verify event emission - System::assert_last_event( - Event::ColdkeySwapped { - old_coldkey: who, - new_coldkey, - swap_cost, - } - .into(), + total_stake_before ); }); } -#[test] -fn test_do_swap_coldkey_preserves_new_coldkey_identity() { - new_test_ext(1).execute_with(|| { - let who = U256::from(1); - let new_coldkey = U256::from(2); - - let old_identity = ChainIdentityV2 { - name: b"Old identity".to_vec(), - ..Default::default() - }; - IdentitiesV2::::insert(who, old_identity.clone()); - - let new_identity = ChainIdentityV2 { - name: b"New identity".to_vec(), - ..Default::default() - }; - IdentitiesV2::::insert(new_coldkey, new_identity.clone()); - - assert_ok!(SubtensorModule::do_swap_coldkey( - &who, - &new_coldkey, - TaoCurrency::ZERO - )); - - // Identity is preserved - assert_eq!(IdentitiesV2::::get(who), Some(old_identity)); - assert_eq!(IdentitiesV2::::get(new_coldkey), Some(new_identity)); - }); -} - #[test] fn test_swap_coldkey_announced_with_bad_origin_fails() { new_test_ext(1).execute_with(|| { @@ -553,6 +362,136 @@ fn test_swap_coldkey_announced_with_hotkey_fails() { }) } +#[test] +fn test_swap_coldkey_works() { + new_test_ext(1).execute_with(|| { + let old_coldkey = U256::from(1); + let new_coldkey = U256::from(2); + let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); + let hotkey1 = U256::from(1001); + let hotkey2 = U256::from(1002); + let hotkey3 = U256::from(1003); + let swap_cost = SubtensorModule::get_key_swap_cost(); + let left_over = 12345; + let min_stake = DefaultMinStake::::get().to_u64(); + let stake1 = min_stake * 10; + let stake2 = min_stake * 20; + let stake3 = min_stake * 30; + + let ( + netuid1, + netuid2, + hotkeys, + hk1_alpha, + hk2_alpha, + hk3_alpha, + total_ck_stake, + identity, + balance_before, + total_stake_before, + ) = comprehensive_setup!( + old_coldkey, + new_coldkey, + new_coldkey_hash, + swap_cost, + left_over, + stake1, + stake2, + stake3, + hotkey1, + hotkey2, + hotkey3 + ); + + assert_ok!(SubtensorModule::swap_coldkey( + ::RuntimeOrigin::root(), + old_coldkey, + new_coldkey, + swap_cost, + )); + + comprehensive_checks!( + old_coldkey, + hotkey1, + hotkey2, + hotkey3, + hotkeys, + new_coldkey, + balance_before, + left_over, + swap_cost, + identity, + netuid1, + netuid2, + hk1_alpha, + hk2_alpha, + hk3_alpha, + total_ck_stake, + total_stake_before + ); + }); +} + +#[test] +fn test_swap_coldkey_with_bad_origin_fails() { + new_test_ext(1).execute_with(|| { + let who = U256::from(1); + let old_coldkey = U256::from(2); + let new_coldkey = U256::from(3); + let swap_cost = SubtensorModule::get_key_swap_cost(); + + assert_noop!( + SubtensorModule::swap_coldkey( + ::RuntimeOrigin::signed(who), + old_coldkey, + new_coldkey, + swap_cost, + ), + BadOrigin + ); + + assert_noop!( + SubtensorModule::swap_coldkey( + ::RuntimeOrigin::none(), + old_coldkey, + new_coldkey, + swap_cost + ), + BadOrigin + ); + }); +} + +#[test] +fn test_do_swap_coldkey_preserves_new_coldkey_identity() { + new_test_ext(1).execute_with(|| { + let who = U256::from(1); + let new_coldkey = U256::from(2); + + let old_identity = ChainIdentityV2 { + name: b"Old identity".to_vec(), + ..Default::default() + }; + IdentitiesV2::::insert(who, old_identity.clone()); + + let new_identity = ChainIdentityV2 { + name: b"New identity".to_vec(), + ..Default::default() + }; + IdentitiesV2::::insert(new_coldkey, new_identity.clone()); + + assert_ok!(SubtensorModule::do_swap_coldkey( + &who, + &new_coldkey, + TaoCurrency::ZERO + )); + + // Identity is preserved + assert_eq!(IdentitiesV2::::get(who), Some(old_identity)); + assert_eq!(IdentitiesV2::::get(new_coldkey), Some(new_identity)); + }); +} + #[test] fn test_do_swap_coldkey_with_not_enough_balance_to_pay_swap_cost_fails() { new_test_ext(1).execute_with(|| { @@ -1316,14 +1255,15 @@ fn test_remove_coldkey_swap_announcement_works() { fn test_remove_coldkey_swap_announcement_with_bad_origin_fails() { new_test_ext(1).execute_with(|| { let who = U256::from(1); + let coldkey = U256::from(2); assert_noop!( - SubtensorModule::remove_coldkey_swap_announcement(RuntimeOrigin::none(), who), + SubtensorModule::remove_coldkey_swap_announcement(RuntimeOrigin::signed(who), coldkey), BadOrigin ); assert_noop!( - SubtensorModule::remove_coldkey_swap_announcement(RuntimeOrigin::root(), who), + SubtensorModule::remove_coldkey_swap_announcement(RuntimeOrigin::none(), coldkey), BadOrigin ); }); @@ -1464,4 +1404,254 @@ fn test_schedule_swap_coldkey_deprecated() { }); } -// TEST TRANSFER ROOT CLAIM WITH NEW KEYS + ROOT CASE +#[macro_export] +macro_rules! comprehensive_setup { + ( + $who:expr, + $new_coldkey:expr, + $new_coldkey_hash:expr, + $swap_cost:expr, + $left_over:expr, + $stake1:expr, + $stake2:expr, + $stake3:expr, + $hotkey1:expr, + $hotkey2:expr, + $hotkey3:expr + ) => {{ + SubtensorModule::add_balance_to_coldkey_account( + &$who, + $stake1 + $stake2 + $stake3 + $swap_cost.to_u64() + $left_over, + ); + + // Setup networks and subnet ownerships + let netuid1 = NetUid::from(1); + let netuid2 = NetUid::from(2); + add_network(netuid1, 1, 0); + add_network(netuid2, 1, 0); + SubnetOwner::::insert(netuid1, $who); + SubnetOwner::::insert(netuid2, $who); + + // Setup reserves + let reserve1 = ($stake1 + $stake3) * 10; + let reserve2 = $stake2 * 10; + mock::setup_reserves(netuid1, reserve1.into(), reserve1.into()); + mock::setup_reserves(netuid2, reserve2.into(), reserve2.into()); + + // Setup auto stake destinations + AutoStakeDestination::::insert($who, netuid1, $hotkey1); + AutoStakeDestination::::insert($who, netuid2, $hotkey2); + AutoStakeDestinationColdkeys::::insert( + $hotkey1, + netuid1, + vec![$who, U256::from(3), U256::from(4)], + ); + AutoStakeDestinationColdkeys::::insert( + $hotkey2, + netuid2, + vec![U256::from(7), U256::from(8), $who], + ); + + // Setup neurons with stake + register_ok_neuron(netuid1, $hotkey1, $who, 0); + register_ok_neuron(netuid2, $hotkey2, $who, 0); + register_ok_neuron(netuid1, $hotkey3, $who, 0); + + let hotkeys = vec![$hotkey1, $hotkey2, $hotkey3]; + assert_eq!(StakingHotkeys::::get($who), hotkeys); + assert_eq!(OwnedHotkeys::::get($who), hotkeys); + assert_eq!(Owner::::get($hotkey1), $who); + assert_eq!(Owner::::get($hotkey2), $who); + assert_eq!(Owner::::get($hotkey3), $who); + + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed($who), + $hotkey1, + netuid1, + $stake1.into() + )); + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed($who), + $hotkey2, + netuid2, + $stake2.into() + )); + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed($who), + $hotkey3, + netuid1, + $stake3.into() + )); + let hk1_alpha = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&$hotkey1, &$who, netuid1); + let hk2_alpha = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&$hotkey2, &$who, netuid2); + let hk3_alpha = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&$hotkey3, &$who, netuid1); + let total_ck_stake = SubtensorModule::get_total_stake_for_coldkey(&$who); + + // Setup identity + let identity = ChainIdentityV2::default(); + IdentitiesV2::::insert($who, identity.clone()); + assert_eq!(IdentitiesV2::::get($who), Some(identity.clone())); + assert!(IdentitiesV2::::get($new_coldkey).is_none()); + + let balance_before = SubtensorModule::get_coldkey_balance(&$who); + let total_stake_before = SubtensorModule::get_total_stake(); + + ( + netuid1, + netuid2, + hotkeys, + hk1_alpha, + hk2_alpha, + hk3_alpha, + total_ck_stake, + identity, + balance_before, + total_stake_before, + ) + }}; +} + +#[macro_export] +macro_rules! comprehensive_checks { + ( + $who:expr, + $hotkey1:expr, + $hotkey2:expr, + $hotkey3:expr, + $hotkeys:expr, + $new_coldkey:expr, + $balance_before:expr, + $left_over:expr, + $swap_cost:expr, + $identity:expr, + $netuid1:expr, + $netuid2:expr, + $hk1_alpha:expr, + $hk2_alpha:expr, + $hk3_alpha:expr, + $total_ck_stake:expr, + $total_stake_before:expr + ) => { + // Ensure the announcement has been consumed + assert!(!ColdkeySwapAnnouncements::::contains_key($who)); + + // Ensure the cost has been withdrawn from the old coldkey and recycled + let balance_after = SubtensorModule::get_coldkey_balance(&$who); + assert_eq!( + $balance_before - $swap_cost.to_u64(), + balance_after + $left_over + ); + + // Ensure the identity is correctly swapped + assert!(IdentitiesV2::::get($who).is_none()); + assert_eq!(IdentitiesV2::::get($new_coldkey), Some($identity)); + + // Ensure the subnet ownerships are correctly swapped + assert_eq!(SubnetOwner::::get($netuid1), $new_coldkey); + assert_eq!(SubnetOwner::::get($netuid2), $new_coldkey); + + // Ensure the auto stake destinations are correctly swapped + assert!(AutoStakeDestination::::get($who, $netuid1).is_none()); + assert!(AutoStakeDestination::::get($who, $netuid2).is_none()); + assert_eq!( + AutoStakeDestination::::get($new_coldkey, $netuid1), + Some($hotkey1) + ); + assert_eq!( + AutoStakeDestination::::get($new_coldkey, $netuid2), + Some($hotkey2) + ); + assert_eq!( + AutoStakeDestinationColdkeys::::get($hotkey1, $netuid1), + vec![U256::from(3), U256::from(4), $new_coldkey] + ); + assert_eq!( + AutoStakeDestinationColdkeys::::get($hotkey2, $netuid2), + vec![U256::from(7), U256::from(8), $new_coldkey] + ); + + // Ensure the coldkey stake is correctly swapped + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&$hotkey1, &$who, $netuid1), + 0.into(), + ); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&$hotkey2, &$who, $netuid2), + 0.into(), + ); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&$hotkey3, &$who, $netuid1), + 0.into(), + ); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &$hotkey1, + &$new_coldkey, + $netuid1 + ), + $hk1_alpha + ); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &$hotkey2, + &$new_coldkey, + $netuid2 + ), + $hk2_alpha + ); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &$hotkey3, + &$new_coldkey, + $netuid1 + ), + $hk3_alpha + ); + assert_eq!( + SubtensorModule::get_total_stake_for_coldkey(&$who), + TaoCurrency::ZERO + ); + assert_eq!( + SubtensorModule::get_total_stake_for_coldkey(&$new_coldkey), + $total_ck_stake, + ); + + // Ensure the staking hotkeys are correctly swapped + assert!(StakingHotkeys::::get($who).is_empty()); + assert_eq!(StakingHotkeys::::get($new_coldkey), $hotkeys); + + // Ensure the hotkey ownership is correctly swapped + assert!(OwnedHotkeys::::get($who).is_empty()); + assert_eq!(OwnedHotkeys::::get($new_coldkey), $hotkeys); + assert_eq!(Owner::::get($hotkey1), $new_coldkey); + assert_eq!(Owner::::get($hotkey2), $new_coldkey); + assert_eq!(Owner::::get($hotkey3), $new_coldkey); + + // Ensure the remaining balance is transferred to the new coldkey + assert_eq!(SubtensorModule::get_coldkey_balance(&$who), 0); + assert_eq!( + SubtensorModule::get_coldkey_balance(&$new_coldkey), + $left_over + ); + + // Ensure total stake is unchanged + assert_eq!( + SubtensorModule::get_total_stake(), + $total_stake_before, + "Total stake changed unexpectedly" + ); + + // Verify event emission + System::assert_last_event( + Event::ColdkeySwapped { + old_coldkey: $who, + new_coldkey: $new_coldkey, + swap_cost: $swap_cost, + } + .into(), + ); + }; +} From 44837ec389e1b6066cfb37d9a2c530e21581cb19 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Mon, 8 Dec 2025 16:39:30 +0100 Subject: [PATCH 038/240] fix clippy --- pallets/subtensor/src/tests/swap_coldkey.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index 666e4a69fe..8a4bf2b3bf 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -3,7 +3,8 @@ clippy::expect_used, clippy::indexing_slicing, clippy::panic, - clippy::unwrap_used + clippy::unwrap_used, + clippy::arithmetic_side_effects )] use approx::assert_abs_diff_eq; From 39ef15eba4429fae445236c617a12504375b7544 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Tue, 9 Dec 2025 15:09:55 +0100 Subject: [PATCH 039/240] remove useless block number from event --- pallets/subtensor/src/macros/dispatches.rs | 5 ++--- pallets/subtensor/src/macros/events.rs | 2 -- pallets/subtensor/src/tests/swap_coldkey.rs | 1 - 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 6d1382596a..87acf41289 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2363,9 +2363,8 @@ mod dispatches { ColdkeySwapAnnouncements::::insert(who.clone(), (now, new_coldkey_hash.clone())); Self::deposit_event(Event::ColdkeySwapAnnounced { - who: who.clone(), - new_coldkey_hash: new_coldkey_hash.clone(), - block_number: now, + who, + new_coldkey_hash, }); Ok(()) } diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index 4babd7f0a8..0c2fc49a69 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -176,8 +176,6 @@ mod events { who: T::AccountId, /// The hash of the new coldkey. new_coldkey_hash: T::Hash, - /// The block number the announcement was made. - block_number: BlockNumberFor, }, /// A coldkey swap announcement has been removed. ColdkeySwapAnnouncementRemoved { diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index 8a4bf2b3bf..9dc35358c2 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -54,7 +54,6 @@ fn test_announce_coldkey_swap_works() { RuntimeEvent::SubtensorModule(Event::ColdkeySwapAnnounced { who, new_coldkey_hash, - block_number: now, }) ); }); From 4b64484d6ce457a2c86e0c8018b174e4bcacdabe Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Tue, 9 Dec 2025 16:04:02 +0100 Subject: [PATCH 040/240] fix benchmarks --- pallets/subtensor/src/benchmarks.rs | 94 +++++++++++++++++++++-------- 1 file changed, 68 insertions(+), 26 deletions(-) diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index 476be905e9..f6fd0f67ba 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -369,17 +369,6 @@ mod pallet_benchmarks { ); } - #[benchmark] - fn schedule_swap_coldkey() { - let old_coldkey: T::AccountId = account("old_cold", 0, 1); - let new_coldkey: T::AccountId = account("new_cold", 1, 2); - let amount: u64 = 100_000_000_000_000; - Subtensor::::add_balance_to_coldkey_account(&old_coldkey, amount); - - #[extrinsic_call] - _(RawOrigin::Signed(old_coldkey.clone()), new_coldkey.clone()); - } - #[benchmark] fn sudo_set_tx_childkey_take_rate_limit() { let new_rate_limit: u64 = 100; @@ -419,14 +408,31 @@ mod pallet_benchmarks { } #[benchmark] - fn swap_coldkey() { + fn announce_coldkey_swap() { + let coldkey: T::AccountId = account("old_coldkey", 0, 0); + let new_coldkey: T::AccountId = account("new_coldkey", 0, 0); + let new_coldkey_hash: T::Hash = ::Hashing::hash_of(&new_coldkey); + + #[extrinsic_call] + _(RawOrigin::Signed(coldkey), new_coldkey_hash); + } + + #[benchmark] + fn swap_coldkey_announced() { let old_coldkey: T::AccountId = account("old_coldkey", 0, 0); let new_coldkey: T::AccountId = account("new_coldkey", 0, 0); + let new_coldkey_hash: T::Hash = ::Hashing::hash_of(&new_coldkey); let hotkey1: T::AccountId = account("hotkey1", 0, 0); - let netuid = NetUid::from(1); + + let now = frame_system::Pallet::::block_number(); + let delay = ColdkeySwapAnnouncementDelay::::get(); + ColdkeySwapAnnouncements::::insert(&old_coldkey, (now, new_coldkey_hash.clone())); + frame_system::Pallet::::set_block_number(now + delay); + let swap_cost = Subtensor::::get_key_swap_cost(); - let free_balance_old = swap_cost + 12345.into(); + Subtensor::::add_balance_to_coldkey_account(&old_coldkey, swap_cost.into()); + let netuid = NetUid::from(1); Subtensor::::init_new_network(netuid, 1); Subtensor::::set_network_registration_allowed(netuid, true); Subtensor::::set_network_pow_registration_allowed(netuid, true); @@ -444,18 +450,54 @@ mod pallet_benchmarks { old_coldkey.clone(), ); - Subtensor::::add_balance_to_coldkey_account(&old_coldkey, free_balance_old.into()); - let name: Vec = b"The fourth Coolest Identity".to_vec(); - let identity = ChainIdentityV2 { - name, - url: vec![], - github_repo: vec![], - image: vec![], - discord: vec![], - description: vec![], - additional: vec![], - }; - IdentitiesV2::::insert(&old_coldkey, identity); + #[extrinsic_call] + _(RawOrigin::Signed(old_coldkey), new_coldkey); + } + + #[benchmark] + fn remove_coldkey_swap_announcement() { + let coldkey: T::AccountId = account("old_coldkey", 0, 0); + let coldkey_hash: T::Hash = ::Hashing::hash_of(&coldkey); + let now = frame_system::Pallet::::block_number(); + + ColdkeySwapAnnouncements::::insert(&coldkey, (now, coldkey_hash)); + + #[extrinsic_call] + _(RawOrigin::Root, coldkey); + } + + #[benchmark] + fn swap_coldkey() { + let old_coldkey: T::AccountId = account("old_coldkey", 0, 0); + let new_coldkey: T::AccountId = account("new_coldkey", 0, 0); + let new_coldkey_hash: T::Hash = ::Hashing::hash_of(&new_coldkey); + let hotkey1: T::AccountId = account("hotkey1", 0, 0); + + let now = frame_system::Pallet::::block_number(); + let delay = ColdkeySwapAnnouncementDelay::::get(); + ColdkeySwapAnnouncements::::insert(&old_coldkey, (now, new_coldkey_hash.clone())); + frame_system::Pallet::::set_block_number(now + delay); + + let swap_cost = Subtensor::::get_key_swap_cost(); + Subtensor::::add_balance_to_coldkey_account(&old_coldkey, swap_cost.into()); + + let netuid = NetUid::from(1); + Subtensor::::init_new_network(netuid, 1); + Subtensor::::set_network_registration_allowed(netuid, true); + Subtensor::::set_network_pow_registration_allowed(netuid, true); + + let block_number = Subtensor::::get_current_block_as_u64(); + let (nonce, work) = + Subtensor::::create_work_for_block_number(netuid, block_number, 3, &hotkey1); + let _ = Subtensor::::register( + RawOrigin::Signed(old_coldkey.clone()).into(), + netuid, + block_number, + nonce, + work.clone(), + hotkey1.clone(), + old_coldkey.clone(), + ); #[extrinsic_call] _( From e3545208885b78dbb02aa91870df50d10b5a0a63 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Tue, 9 Dec 2025 17:17:51 +0100 Subject: [PATCH 041/240] fix benchmarks --- pallets/subtensor/src/benchmarks.rs | 34 +++++++++++++---------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index f6fd0f67ba..865ba8392c 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -426,7 +426,7 @@ mod pallet_benchmarks { let now = frame_system::Pallet::::block_number(); let delay = ColdkeySwapAnnouncementDelay::::get(); - ColdkeySwapAnnouncements::::insert(&old_coldkey, (now, new_coldkey_hash.clone())); + ColdkeySwapAnnouncements::::insert(&old_coldkey, (now, new_coldkey_hash)); frame_system::Pallet::::set_block_number(now + delay); let swap_cost = Subtensor::::get_key_swap_cost(); @@ -454,30 +454,12 @@ mod pallet_benchmarks { _(RawOrigin::Signed(old_coldkey), new_coldkey); } - #[benchmark] - fn remove_coldkey_swap_announcement() { - let coldkey: T::AccountId = account("old_coldkey", 0, 0); - let coldkey_hash: T::Hash = ::Hashing::hash_of(&coldkey); - let now = frame_system::Pallet::::block_number(); - - ColdkeySwapAnnouncements::::insert(&coldkey, (now, coldkey_hash)); - - #[extrinsic_call] - _(RawOrigin::Root, coldkey); - } - #[benchmark] fn swap_coldkey() { let old_coldkey: T::AccountId = account("old_coldkey", 0, 0); let new_coldkey: T::AccountId = account("new_coldkey", 0, 0); - let new_coldkey_hash: T::Hash = ::Hashing::hash_of(&new_coldkey); let hotkey1: T::AccountId = account("hotkey1", 0, 0); - let now = frame_system::Pallet::::block_number(); - let delay = ColdkeySwapAnnouncementDelay::::get(); - ColdkeySwapAnnouncements::::insert(&old_coldkey, (now, new_coldkey_hash.clone())); - frame_system::Pallet::::set_block_number(now + delay); - let swap_cost = Subtensor::::get_key_swap_cost(); Subtensor::::add_balance_to_coldkey_account(&old_coldkey, swap_cost.into()); @@ -507,6 +489,20 @@ mod pallet_benchmarks { swap_cost, ); } + + #[benchmark] + fn remove_coldkey_swap_announcement() { + let coldkey: T::AccountId = account("old_coldkey", 0, 0); + let coldkey_hash: T::Hash = ::Hashing::hash_of(&coldkey); + let now = frame_system::Pallet::::block_number(); + + ColdkeySwapAnnouncements::::insert(&coldkey, (now, coldkey_hash)); + + #[extrinsic_call] + _(RawOrigin::Root, coldkey); + } + + #[benchmark] fn batch_reveal_weights() { From bee751d814ae2f7e5c2b71349619b47e90c1e922 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Tue, 9 Dec 2025 17:21:28 +0100 Subject: [PATCH 042/240] cargo fmt --- pallets/subtensor/src/benchmarks.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index 865ba8392c..40c68ffe28 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -489,7 +489,7 @@ mod pallet_benchmarks { swap_cost, ); } - + #[benchmark] fn remove_coldkey_swap_announcement() { let coldkey: T::AccountId = account("old_coldkey", 0, 0); @@ -502,8 +502,6 @@ mod pallet_benchmarks { _(RawOrigin::Root, coldkey); } - - #[benchmark] fn batch_reveal_weights() { let tempo: u16 = 0; From 7136bfd52fe2a8b9a73121203fc6e2b8843c61d8 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Tue, 9 Dec 2025 19:01:03 +0100 Subject: [PATCH 043/240] fix benchmark --- pallets/subtensor/src/benchmarks.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index 40c68ffe28..e06eeec435 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -427,7 +427,7 @@ mod pallet_benchmarks { let now = frame_system::Pallet::::block_number(); let delay = ColdkeySwapAnnouncementDelay::::get(); ColdkeySwapAnnouncements::::insert(&old_coldkey, (now, new_coldkey_hash)); - frame_system::Pallet::::set_block_number(now + delay); + frame_system::Pallet::::set_block_number(now + delay + 1); let swap_cost = Subtensor::::get_key_swap_cost(); Subtensor::::add_balance_to_coldkey_account(&old_coldkey, swap_cost.into()); From f920c778cc66c93d4479551106ff87de84ffdc63 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Wed, 10 Dec 2025 15:28:09 +0100 Subject: [PATCH 044/240] fix clippy --- pallets/subtensor/src/benchmarks.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index e06eeec435..a616211407 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -427,7 +427,7 @@ mod pallet_benchmarks { let now = frame_system::Pallet::::block_number(); let delay = ColdkeySwapAnnouncementDelay::::get(); ColdkeySwapAnnouncements::::insert(&old_coldkey, (now, new_coldkey_hash)); - frame_system::Pallet::::set_block_number(now + delay + 1); + frame_system::Pallet::::set_block_number(now + delay + 1u32.into()); let swap_cost = Subtensor::::get_key_swap_cost(); Subtensor::::add_balance_to_coldkey_account(&old_coldkey, swap_cost.into()); From 4e7449591da8c7b6e5f65d1297ea7b7175fd2628 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Wed, 10 Dec 2025 19:03:04 +0100 Subject: [PATCH 045/240] fix benchmarks --- pallets/subtensor/src/macros/dispatches.rs | 38 +++++++++++++++------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 87acf41289..408b30bdfe 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -711,7 +711,7 @@ mod dispatches { /// #[pallet::call_index(2)] #[pallet::weight((Weight::from_parts(340_800_000, 0) - .saturating_add(T::DbWeight::get().reads(27_u64)) + .saturating_add(T::DbWeight::get().reads(25_u64)) .saturating_add(T::DbWeight::get().writes(16_u64)), DispatchClass::Normal, Pays::Yes))] pub fn add_stake( origin: OriginFor, @@ -1069,7 +1069,11 @@ mod dispatches { /// /// Only callable by root as it doesn't require an announcement and can be used to swap any coldkey. #[pallet::call_index(71)] - #[pallet::weight(Weight::zero())] + #[pallet::weight( + Weight::from_parts(183_600_000, 0) + .saturating_add(T::DbWeight::get().reads(17_u64)) + .saturating_add(T::DbWeight::get().writes(9_u64)) + )] pub fn swap_coldkey( origin: OriginFor, old_coldkey: T::AccountId, @@ -1495,7 +1499,7 @@ mod dispatches { /// - Thrown if key has hit transaction rate limit #[pallet::call_index(84)] #[pallet::weight((Weight::from_parts(358_500_000, 0) - .saturating_add(T::DbWeight::get().reads(44_u64)) + .saturating_add(T::DbWeight::get().reads(41_u64)) .saturating_add(T::DbWeight::get().writes(26_u64)), DispatchClass::Normal, Pays::Yes))] pub fn unstake_all_alpha(origin: OriginFor, hotkey: T::AccountId) -> DispatchResult { Self::do_unstake_all_alpha(origin, hotkey) @@ -1609,7 +1613,7 @@ mod dispatches { #[pallet::call_index(87)] #[pallet::weight(( Weight::from_parts(351_300_000, 0) - .saturating_add(T::DbWeight::get().reads(40_u64)) + .saturating_add(T::DbWeight::get().reads(37_u64)) .saturating_add(T::DbWeight::get().writes(24_u64)), DispatchClass::Normal, Pays::Yes @@ -1674,7 +1678,7 @@ mod dispatches { /// #[pallet::call_index(88)] #[pallet::weight((Weight::from_parts(402_900_000, 0) - .saturating_add(T::DbWeight::get().reads(27_u64)) + .saturating_add(T::DbWeight::get().reads(25_u64)) .saturating_add(T::DbWeight::get().writes(16_u64)), DispatchClass::Normal, Pays::Yes))] pub fn add_stake_limit( origin: OriginFor, @@ -1738,7 +1742,7 @@ mod dispatches { /// #[pallet::call_index(89)] #[pallet::weight((Weight::from_parts(377_400_000, 0) - .saturating_add(T::DbWeight::get().reads(31_u64)) + .saturating_add(T::DbWeight::get().reads(29_u64)) .saturating_add(T::DbWeight::get().writes(15_u64)), DispatchClass::Normal, Pays::Yes))] pub fn remove_stake_limit( origin: OriginFor, @@ -1782,7 +1786,7 @@ mod dispatches { #[pallet::call_index(90)] #[pallet::weight(( Weight::from_parts(411_500_000, 0) - .saturating_add(T::DbWeight::get().reads(40_u64)) + .saturating_add(T::DbWeight::get().reads(37_u64)) .saturating_add(T::DbWeight::get().writes(24_u64)), DispatchClass::Normal, Pays::Yes @@ -1960,7 +1964,7 @@ mod dispatches { /// Without limit_price it remove all the stake similar to `remove_stake` extrinsic #[pallet::call_index(103)] #[pallet::weight((Weight::from_parts(395_300_000, 10142) - .saturating_add(T::DbWeight::get().reads(31_u64)) + .saturating_add(T::DbWeight::get().reads(29_u64)) .saturating_add(T::DbWeight::get().writes(15_u64)), DispatchClass::Normal, Pays::Yes))] pub fn remove_stake_full_limit( origin: T::RuntimeOrigin, @@ -2343,7 +2347,11 @@ mod dispatches { /// Announces a coldkey swap using coldkey hash. /// This is required before the coldkey swap can be performed after the delay period. #[pallet::call_index(125)] - #[pallet::weight(Weight::zero())] + #[pallet::weight( + Weight::from_parts(16_150_000, 0) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + )] pub fn announce_coldkey_swap( origin: OriginFor, new_coldkey_hash: T::Hash, @@ -2372,7 +2380,11 @@ mod dispatches { /// Performs a coldkey swap iff an announcement has been made. /// The provided new coldkey must match the announced coldkey hash. #[pallet::call_index(126)] - #[pallet::weight(Weight::zero())] + #[pallet::weight( + Weight::from_parts(207_300_000, 0) + .saturating_add(T::DbWeight::get().reads(19_u64)) + .saturating_add(T::DbWeight::get().writes(9_u64)) + )] pub fn swap_coldkey_announced( origin: OriginFor, new_coldkey: T::AccountId, @@ -2404,7 +2416,11 @@ mod dispatches { /// /// Only callable by root. #[pallet::call_index(127)] - #[pallet::weight(Weight::zero())] + #[pallet::weight( + Weight::from_parts(4_609_000, 0) + .saturating_add(T::DbWeight::get().reads(0_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + )] pub fn remove_coldkey_swap_announcement( origin: OriginFor, coldkey: T::AccountId, From f0b851ae364448dc09f469ffb398d4c3e279158e Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Wed, 10 Dec 2025 19:13:48 +0100 Subject: [PATCH 046/240] bump spec version --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 17b626263c..0ac0fb0787 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -237,7 +237,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: 361, + spec_version: 362, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From baae06b4c1ec92f9ccceb0012d5c997f8677bffc Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Thu, 11 Dec 2025 16:24:57 +0100 Subject: [PATCH 047/240] fix weights --- pallets/admin-utils/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 2b4fc35960..6321f2363f 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -1580,7 +1580,7 @@ pub mod pallet { /// Weight is handled by the `#[pallet::weight]` attribute. #[pallet::call_index(62)] #[pallet::weight(( - Weight::from_parts(10_020_000, 3507) + Weight::from_parts(5_698_000, 0) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(0_u64)), DispatchClass::Operational, From 5a6c0b938ea74fcc0987d7c90fb7a8396a8e9f5a Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Thu, 11 Dec 2025 20:21:36 +0100 Subject: [PATCH 048/240] pays swap cost on first announcement --- pallets/subtensor/src/macros/dispatches.rs | 43 ++++- pallets/subtensor/src/macros/events.rs | 8 +- pallets/subtensor/src/swap/swap_coldkey.rs | 23 +-- pallets/subtensor/src/tests/claim_root.rs | 6 +- pallets/subtensor/src/tests/swap_coldkey.rs | 164 ++++++++++---------- 5 files changed, 131 insertions(+), 113 deletions(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 408b30bdfe..7368092e2a 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -1082,7 +1082,11 @@ mod dispatches { ) -> DispatchResult { ensure_root(origin)?; - Self::do_swap_coldkey(&old_coldkey, &new_coldkey, swap_cost)?; + if swap_cost.to_u64() > 0 { + Self::charge_swap_cost(&old_coldkey, swap_cost)?; + } + Self::do_swap_coldkey(&old_coldkey, &new_coldkey)?; + // We also remove any announcement for security reasons ColdkeySwapAnnouncements::::remove(old_coldkey); @@ -2344,8 +2348,20 @@ mod dispatches { Ok(()) } - /// Announces a coldkey swap using coldkey hash. - /// This is required before the coldkey swap can be performed after the delay period. + /// Announces a coldkey swap using BlakeTwo256 hash of the new coldkey. + /// + /// This is required before the coldkey swap can be performed + /// after the delay period. + /// + /// It can be reannounced after a delay of `ColdkeySwapReannouncementDelay` between the + /// original announcement and the reannouncement. + /// + /// The dispatch origin of this call must be the original coldkey that made the announcement. + /// + /// - `new_coldkey_hash`: The hash of the new coldkey using BlakeTwo256. + /// + /// The `ColdkeySwapAnnounced` event is emitted on successful announcement. + /// #[pallet::call_index(125)] #[pallet::weight( Weight::from_parts(16_150_000, 0) @@ -2366,6 +2382,9 @@ mod dispatches { now > when.saturating_add(delay), Error::::ColdkeySwapReannouncedTooEarly ); + } else { + let swap_cost = Self::get_key_swap_cost(); + Self::charge_swap_cost(&who, swap_cost)?; } ColdkeySwapAnnouncements::::insert(who.clone(), (now, new_coldkey_hash.clone())); @@ -2377,8 +2396,14 @@ mod dispatches { Ok(()) } - /// Performs a coldkey swap iff an announcement has been made. - /// The provided new coldkey must match the announced coldkey hash. + /// Performs a coldkey swap if an announcement has been made. + /// + /// The dispatch origin of this call must be the original coldkey that made the announcement. + /// + /// - `new_coldkey`: The new coldkey to swap to. The BlakeTwo256 hash of the new coldkey must be + /// the same as the announced coldkey hash. + /// + /// The `ColdkeySwapped` event is emitted on successful swap. #[pallet::call_index(126)] #[pallet::weight( Weight::from_parts(207_300_000, 0) @@ -2406,15 +2431,17 @@ mod dispatches { Error::::ColdkeySwapTooEarly ); - let swap_cost = Self::get_key_swap_cost(); - Self::do_swap_coldkey(&who, &new_coldkey, swap_cost)?; + Self::do_swap_coldkey(&who, &new_coldkey)?; Ok(()) } /// Removes a coldkey swap announcement for a coldkey. /// - /// Only callable by root. + /// The dispatch origin of this call must be root. + /// + /// - `coldkey`: The coldkey to remove the swap announcement for. + /// #[pallet::call_index(127)] #[pallet::weight( Weight::from_parts(4_609_000, 0) diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index 0c2fc49a69..793b30b217 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -182,14 +182,12 @@ mod events { /// The account ID of the coldkey that made the announcement. who: T::AccountId, }, - /// A coldkey has been swapped + /// A coldkey has been swapped. ColdkeySwapped { - /// the account ID of old coldkey + /// The account ID of old coldkey. old_coldkey: T::AccountId, - /// the account ID of new coldkey + /// The account ID of new coldkey. new_coldkey: T::AccountId, - /// the swap cost - swap_cost: TaoCurrency, }, /// All balance of a hotkey has been unstaked and transferred to a new coldkey AllBalanceUnstakedAndTransferredToNewColdkey { diff --git a/pallets/subtensor/src/swap/swap_coldkey.rs b/pallets/subtensor/src/swap/swap_coldkey.rs index 51aec6798e..c7119fc066 100644 --- a/pallets/subtensor/src/swap/swap_coldkey.rs +++ b/pallets/subtensor/src/swap/swap_coldkey.rs @@ -7,7 +7,6 @@ impl Pallet { pub fn do_swap_coldkey( old_coldkey: &T::AccountId, new_coldkey: &T::AccountId, - swap_cost: TaoCurrency, ) -> DispatchResult { ensure!( StakingHotkeys::::get(new_coldkey).is_empty(), @@ -18,14 +17,6 @@ impl Pallet { Error::::NewColdKeyIsHotkey ); - // Remove and recycle the swap cost from the old coldkey's account - ensure!( - Self::can_remove_balance_from_coldkey_account(old_coldkey, swap_cost.into()), - Error::::NotEnoughBalanceToPaySwapColdKey - ); - let burn_amount = Self::remove_balance_from_coldkey_account(old_coldkey, swap_cost.into())?; - Self::recycle_tao(burn_amount); - // Swap the identity if the old coldkey has one and the new coldkey doesn't if IdentitiesV2::::get(new_coldkey).is_none() && let Some(identity) = IdentitiesV2::::take(old_coldkey) @@ -53,11 +44,23 @@ impl Pallet { Self::deposit_event(Event::ColdkeySwapped { old_coldkey: old_coldkey.clone(), new_coldkey: new_coldkey.clone(), - swap_cost, }); Ok(()) } + /// Charges the swap cost from the coldkey's account and recycles the tokens. + pub fn charge_swap_cost(coldkey: &T::AccountId, swap_cost: TaoCurrency) -> DispatchResult { + ensure!( + Self::can_remove_balance_from_coldkey_account(coldkey, swap_cost.into()), + Error::::NotEnoughBalanceToPaySwapColdKey + ); + + let burn_amount = Self::remove_balance_from_coldkey_account(coldkey, swap_cost.into())?; + Self::recycle_tao(burn_amount); + + Ok(()) + } + /// Transfer the ownership of the subnet to the new coldkey if it is owned by the old coldkey. fn transfer_subnet_ownership( netuid: NetUid, diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index 2fbfa309e7..a9577157e4 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -1184,11 +1184,7 @@ fn test_claim_root_with_swap_coldkey() { ); // Swap coldkey - assert_ok!(SubtensorModule::do_swap_coldkey( - &coldkey, - &new_coldkey, - TaoCurrency::ZERO - )); + assert_ok!(SubtensorModule::do_swap_coldkey(&coldkey, &new_coldkey,)); // Check swapped keys claimed values diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index 9dc35358c2..6f003a8d0e 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -36,9 +36,17 @@ fn test_announce_coldkey_swap_works() { let who = U256::from(1); let new_coldkey = U256::from(2); let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); + let left_over = 12345; assert_eq!(ColdkeySwapAnnouncements::::iter().count(), 0); + let swap_cost = SubtensorModule::get_key_swap_cost().to_u64(); + SubtensorModule::add_balance_to_coldkey_account(&who, swap_cost + left_over); + assert_eq!( + SubtensorModule::get_coldkey_balance(&who), + swap_cost + left_over + ); + assert_ok!(SubtensorModule::announce_coldkey_swap( RuntimeOrigin::signed(who), new_coldkey_hash, @@ -49,6 +57,7 @@ fn test_announce_coldkey_swap_works() { ColdkeySwapAnnouncements::::iter().collect::>(), vec![(who, (now, new_coldkey_hash))] ); + assert_eq!(SubtensorModule::get_coldkey_balance(&who), left_over); assert_eq!( last_event(), RuntimeEvent::SubtensorModule(Event::ColdkeySwapAnnounced { @@ -70,6 +79,9 @@ 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().to_u64(); + SubtensorModule::add_balance_to_coldkey_account(&who, 2 * swap_cost); + assert_ok!(SubtensorModule::announce_coldkey_swap( RuntimeOrigin::signed(who), new_coldkey_hash, @@ -97,6 +109,38 @@ fn test_announce_coldkey_swap_with_existing_announcement_past_delay_works() { }); } +#[test] +fn test_announce_coldkey_swap_only_pays_swap_cost_if_no_announcement_exists() { + new_test_ext(1).execute_with(|| { + let who = U256::from(1); + let new_coldkey = U256::from(2); + let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); + let new_coldkey_2 = U256::from(3); + let new_coldkey_2_hash = ::Hashing::hash_of(&new_coldkey_2); + let left_over = 12345; + + let swap_cost = SubtensorModule::get_key_swap_cost().to_u64(); + SubtensorModule::add_balance_to_coldkey_account(&who, swap_cost + left_over); + assert_eq!(SubtensorModule::get_coldkey_balance(&who), swap_cost + left_over); + + assert_ok!(SubtensorModule::announce_coldkey_swap( + RuntimeOrigin::signed(who), + new_coldkey_hash, + )); + assert_eq!(SubtensorModule::get_coldkey_balance(&who), left_over); + + let now = System::block_number(); + let delay = ColdkeySwapAnnouncementDelay::::get() + 1; + System::run_to_block::(now + delay); + + assert_ok!(SubtensorModule::announce_coldkey_swap( + RuntimeOrigin::signed(who), + new_coldkey_2_hash, + )); + assert_eq!(SubtensorModule::get_coldkey_balance(&who), left_over); + }); +} + #[test] fn test_announce_coldkey_swap_with_bad_origin_fails() { new_test_ext(1).execute_with(|| { @@ -126,6 +170,9 @@ fn test_announce_coldkey_swap_with_existing_announcement_not_past_delay_fails() assert_eq!(ColdkeySwapAnnouncements::::iter().count(), 0); + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&who, swap_cost.into()); + assert_ok!(SubtensorModule::announce_coldkey_swap( RuntimeOrigin::signed(who), new_coldkey_hash, @@ -156,7 +203,6 @@ fn test_swap_coldkey_announced_works() { let hotkey1 = U256::from(1001); let hotkey2 = U256::from(1002); let hotkey3 = U256::from(1003); - let swap_cost = SubtensorModule::get_key_swap_cost(); let left_over = 12345; let min_stake = DefaultMinStake::::get().to_u64(); let stake1 = min_stake * 10; @@ -164,10 +210,7 @@ fn test_swap_coldkey_announced_works() { let stake3 = min_stake * 30; let now = System::block_number(); - assert_ok!(SubtensorModule::announce_coldkey_swap( - RuntimeOrigin::signed(who), - new_coldkey_hash, - )); + ColdkeySwapAnnouncements::::insert(who.clone(), (now, new_coldkey_hash)); // Run some blocks for the announcement to be past the delay let delay = ColdkeySwapAnnouncementDelay::::get() + 1; @@ -188,7 +231,6 @@ fn test_swap_coldkey_announced_works() { who, new_coldkey, new_coldkey_hash, - swap_cost, left_over, stake1, stake2, @@ -212,7 +254,6 @@ fn test_swap_coldkey_announced_works() { new_coldkey, balance_before, left_over, - swap_cost, identity, netuid1, netuid2, @@ -263,11 +304,9 @@ fn test_swap_coldkey_announced_with_mismatched_coldkey_hash_fails() { let new_coldkey = U256::from(2); let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); let other_coldkey = U256::from(3); + let now = System::block_number(); - assert_ok!(SubtensorModule::announce_coldkey_swap( - RuntimeOrigin::signed(who), - new_coldkey_hash, - )); + ColdkeySwapAnnouncements::::insert(who.clone(), (now, new_coldkey_hash)); assert_noop!( SubtensorModule::swap_coldkey_announced(RuntimeOrigin::signed(who), other_coldkey), @@ -282,11 +321,9 @@ fn test_swap_coldkey_announced_too_early_fails() { let who = U256::from(1); let new_coldkey = U256::from(2); let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); + let now = System::block_number(); - assert_ok!(SubtensorModule::announce_coldkey_swap( - RuntimeOrigin::signed(who), - new_coldkey_hash, - )); + ColdkeySwapAnnouncements::::insert(who.clone(), (now, new_coldkey_hash)); assert_noop!( SubtensorModule::swap_coldkey_announced( @@ -306,6 +343,9 @@ fn test_swap_coldkey_announced_with_already_associated_coldkey_fails() { let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); let hotkey = U256::from(3); + let swap_cost = SubtensorModule::get_key_swap_cost().to_u64(); + SubtensorModule::add_balance_to_coldkey_account(&who, swap_cost); + assert_ok!(SubtensorModule::announce_coldkey_swap( RuntimeOrigin::signed(who), new_coldkey_hash, @@ -315,9 +355,6 @@ fn test_swap_coldkey_announced_with_already_associated_coldkey_fails() { let delay = ColdkeySwapAnnouncementDelay::::get() + 1; System::run_to_block::(now + delay); - let swap_cost = SubtensorModule::get_key_swap_cost(); - SubtensorModule::add_balance_to_coldkey_account(&who, swap_cost.to_u64()); - SubtensorModule::create_account_if_non_existent(&new_coldkey, &hotkey); assert_noop!( @@ -337,19 +374,14 @@ fn test_swap_coldkey_announced_with_hotkey_fails() { let new_coldkey = U256::from(2); let hotkey = U256::from(3); let hotkey_hash = ::Hashing::hash_of(&hotkey); + let now = System::block_number(); - assert_ok!(SubtensorModule::announce_coldkey_swap( - RuntimeOrigin::signed(who), - hotkey_hash, - )); + ColdkeySwapAnnouncements::::insert(who.clone(), (now, hotkey_hash.clone())); let now = System::block_number(); let delay = ColdkeySwapAnnouncementDelay::::get() + 1; System::run_to_block::(now + delay); - let swap_cost = SubtensorModule::get_key_swap_cost(); - SubtensorModule::add_balance_to_coldkey_account(&who, swap_cost.to_u64()); - SubtensorModule::create_account_if_non_existent(&new_coldkey, &hotkey); assert_noop!( @@ -371,13 +403,15 @@ fn test_swap_coldkey_works() { let hotkey1 = U256::from(1001); let hotkey2 = U256::from(1002); let hotkey3 = U256::from(1003); - let swap_cost = SubtensorModule::get_key_swap_cost(); let left_over = 12345; let min_stake = DefaultMinStake::::get().to_u64(); let stake1 = min_stake * 10; let stake2 = min_stake * 20; let stake3 = min_stake * 30; + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); + let ( netuid1, netuid2, @@ -393,7 +427,6 @@ fn test_swap_coldkey_works() { old_coldkey, new_coldkey, new_coldkey_hash, - swap_cost, left_over, stake1, stake2, @@ -419,7 +452,6 @@ fn test_swap_coldkey_works() { new_coldkey, balance_before, left_over, - swap_cost, identity, netuid1, netuid2, @@ -480,11 +512,7 @@ fn test_do_swap_coldkey_preserves_new_coldkey_identity() { }; IdentitiesV2::::insert(new_coldkey, new_identity.clone()); - assert_ok!(SubtensorModule::do_swap_coldkey( - &who, - &new_coldkey, - TaoCurrency::ZERO - )); + assert_ok!(SubtensorModule::do_swap_coldkey(&who, &new_coldkey,)); // Identity is preserved assert_eq!(IdentitiesV2::::get(who), Some(old_identity)); @@ -493,18 +521,14 @@ fn test_do_swap_coldkey_preserves_new_coldkey_identity() { } #[test] -fn test_do_swap_coldkey_with_not_enough_balance_to_pay_swap_cost_fails() { +fn test_announce_coldkey_swap_with_not_enough_balance_to_pay_swap_cost_fails() { new_test_ext(1).execute_with(|| { let who = U256::from(1); let new_coldkey = U256::from(2); + let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); - let now = System::block_number(); - let delay = ColdkeySwapAnnouncementDelay::::get() + 1; - System::run_to_block::(now + delay); - - let swap_cost = SubtensorModule::get_key_swap_cost(); assert_noop!( - SubtensorModule::do_swap_coldkey(&who, &new_coldkey, swap_cost), + SubtensorModule::announce_coldkey_swap(RuntimeOrigin::signed(who), new_coldkey_hash), Error::::NotEnoughBalanceToPaySwapColdKey ); }); @@ -516,11 +540,7 @@ fn test_do_swap_coldkey_with_no_stake() { let old_coldkey = U256::from(1); let new_coldkey = U256::from(2); - assert_ok!(SubtensorModule::do_swap_coldkey( - &old_coldkey, - &new_coldkey, - TaoCurrency::ZERO - )); + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey)); assert_eq!( SubtensorModule::get_total_stake_for_coldkey(&old_coldkey), @@ -590,15 +610,10 @@ fn test_do_swap_coldkey_with_max_values() { netuid2, ); - assert_ok!(SubtensorModule::do_swap_coldkey( - &old_coldkey, - &new_coldkey, - TaoCurrency::ZERO - )); + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); assert_ok!(SubtensorModule::do_swap_coldkey( &old_coldkey2, &new_coldkey2, - TaoCurrency::ZERO )); assert_eq!( @@ -656,11 +671,7 @@ fn test_do_swap_coldkey_effect_on_delegated_stake() { let coldkey_stake_before = SubtensorModule::get_total_stake_for_coldkey(&old_coldkey); let delegator_stake_before = SubtensorModule::get_total_stake_for_coldkey(&delegator); - assert_ok!(SubtensorModule::do_swap_coldkey( - &old_coldkey, - &new_coldkey, - TaoCurrency::ZERO - )); + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); assert_abs_diff_eq!( SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), @@ -753,11 +764,7 @@ fn test_swap_delegated_stake_for_coldkey() { let total_hotkey2_stake = SubtensorModule::get_total_stake_for_hotkey(&hotkey2); // Perform the swap - assert_ok!(SubtensorModule::do_swap_coldkey( - &old_coldkey, - &new_coldkey, - TaoCurrency::ZERO - )); + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); // Verify stake transfer assert_eq!( @@ -1051,11 +1058,7 @@ fn test_coldkey_swap_total() { SubtensorModule::get_total_stake_for_coldkey(&coldkey), ck_stake ); - assert_ok!(SubtensorModule::do_swap_coldkey( - &coldkey, - &new_coldkey, - TaoCurrency::ZERO - )); + assert_ok!(SubtensorModule::do_swap_coldkey(&coldkey, &new_coldkey,)); assert_eq!( SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), ck_stake @@ -1190,11 +1193,7 @@ fn test_do_swap_coldkey_effect_on_delegations() { )); // Perform the swap - assert_ok!(SubtensorModule::do_swap_coldkey( - &coldkey, - &new_coldkey, - TaoCurrency::ZERO - )); + assert_ok!(SubtensorModule::do_swap_coldkey(&coldkey, &new_coldkey,)); // Verify stake was moved for the delegate let approx_total_stake = TaoCurrency::from(stake * 2 - fee * 2); @@ -1236,11 +1235,9 @@ fn test_remove_coldkey_swap_announcement_works() { let who = U256::from(1); let new_coldkey = U256::from(2); let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); + let now = System::block_number(); - assert_ok!(SubtensorModule::announce_coldkey_swap( - RuntimeOrigin::signed(who), - new_coldkey_hash, - )); + ColdkeySwapAnnouncements::::insert(who.clone(), (now, new_coldkey_hash.clone())); assert_ok!(SubtensorModule::remove_coldkey_swap_announcement( RuntimeOrigin::root(), @@ -1410,7 +1407,6 @@ macro_rules! comprehensive_setup { $who:expr, $new_coldkey:expr, $new_coldkey_hash:expr, - $swap_cost:expr, $left_over:expr, $stake1:expr, $stake2:expr, @@ -1421,7 +1417,7 @@ macro_rules! comprehensive_setup { ) => {{ SubtensorModule::add_balance_to_coldkey_account( &$who, - $stake1 + $stake2 + $stake3 + $swap_cost.to_u64() + $left_over, + $stake1 + $stake2 + $stake3 + $left_over, ); // Setup networks and subnet ownerships @@ -1525,7 +1521,6 @@ macro_rules! comprehensive_checks { $new_coldkey:expr, $balance_before:expr, $left_over:expr, - $swap_cost:expr, $identity:expr, $netuid1:expr, $netuid2:expr, @@ -1538,12 +1533,12 @@ macro_rules! comprehensive_checks { // Ensure the announcement has been consumed assert!(!ColdkeySwapAnnouncements::::contains_key($who)); - // Ensure the cost has been withdrawn from the old coldkey and recycled - let balance_after = SubtensorModule::get_coldkey_balance(&$who); - assert_eq!( - $balance_before - $swap_cost.to_u64(), - balance_after + $left_over - ); + // // Ensure the cost has been withdrawn from the old coldkey and recycled + // let balance_after = SubtensorModule::get_coldkey_balance(&$who); + // assert_eq!( + // $balance_before - $swap_cost.to_u64(), + // balance_after + $left_over + // ); // Ensure the identity is correctly swapped assert!(IdentitiesV2::::get($who).is_none()); @@ -1649,7 +1644,6 @@ macro_rules! comprehensive_checks { Event::ColdkeySwapped { old_coldkey: $who, new_coldkey: $new_coldkey, - swap_cost: $swap_cost, } .into(), ); From 05951304f952d915ef00c79ebbb851594e260c36 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Thu, 11 Dec 2025 20:36:08 +0100 Subject: [PATCH 049/240] store when can be performed instead of when was announced --- pallets/subtensor/src/lib.rs | 2 +- pallets/subtensor/src/macros/dispatches.rs | 19 ++++--------- pallets/subtensor/src/tests/swap_coldkey.rs | 31 +++++++++++---------- 3 files changed, 24 insertions(+), 28 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 9f0a28fed4..25f2f599a4 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1339,7 +1339,7 @@ pub mod pallet { StorageValue<_, BlockNumberFor, ValueQuery, DefaultColdkeySwapAnnouncementDelay>; /// A map of the coldkey swap announcements from a coldkey - /// to the block number the announcement was made and the new coldkey. + /// to the block number the coldkey swap can be performed. #[pallet::storage] pub type ColdkeySwapAnnouncements = StorageMap<_, Twox64Concat, T::AccountId, (BlockNumberFor, T::Hash), OptionQuery>; diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 7368092e2a..620cb05715 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2375,19 +2375,16 @@ mod dispatches { let who = ensure_signed(origin)?; let now = >::block_number(); - if let Some(existing) = ColdkeySwapAnnouncements::::get(who.clone()) { - let delay = ColdkeySwapAnnouncementDelay::::get(); - let when = existing.0; - ensure!( - now > when.saturating_add(delay), - Error::::ColdkeySwapReannouncedTooEarly - ); + if let Some((when, _)) = ColdkeySwapAnnouncements::::get(who.clone()) { + ensure!(now >= when, Error::::ColdkeySwapReannouncedTooEarly); } else { let swap_cost = Self::get_key_swap_cost(); Self::charge_swap_cost(&who, swap_cost)?; } - ColdkeySwapAnnouncements::::insert(who.clone(), (now, new_coldkey_hash.clone())); + let delay = ColdkeySwapAnnouncementDelay::::get(); + let when = now.saturating_add(delay); + ColdkeySwapAnnouncements::::insert(who.clone(), (when, new_coldkey_hash.clone())); Self::deposit_event(Event::ColdkeySwapAnnounced { who, @@ -2425,11 +2422,7 @@ mod dispatches { ); let now = >::block_number(); - let delay = ColdkeySwapAnnouncementDelay::::get(); - ensure!( - now > when.saturating_add(delay), - Error::::ColdkeySwapTooEarly - ); + ensure!(now >= when, Error::::ColdkeySwapTooEarly); Self::do_swap_coldkey(&who, &new_coldkey)?; diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index 6f003a8d0e..68b985b67f 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -53,9 +53,10 @@ fn test_announce_coldkey_swap_works() { )); let now = System::block_number(); + let delay = ColdkeySwapAnnouncementDelay::::get(); assert_eq!( ColdkeySwapAnnouncements::::iter().collect::>(), - vec![(who, (now, new_coldkey_hash))] + vec![(who, (now + delay, new_coldkey_hash))] ); assert_eq!(SubtensorModule::get_coldkey_balance(&who), left_over); assert_eq!( @@ -88,12 +89,12 @@ fn test_announce_coldkey_swap_with_existing_announcement_past_delay_works() { )); let now = System::block_number(); + let delay = ColdkeySwapAnnouncementDelay::::get(); assert_eq!( ColdkeySwapAnnouncements::::iter().collect::>(), - vec![(who, (now, new_coldkey_hash))] + vec![(who, (now + delay, new_coldkey_hash))] ); - let delay = ColdkeySwapAnnouncementDelay::::get() + 1; System::run_to_block::(now + delay); assert_ok!(SubtensorModule::announce_coldkey_swap( @@ -104,7 +105,7 @@ fn test_announce_coldkey_swap_with_existing_announcement_past_delay_works() { let now = System::block_number(); assert_eq!( ColdkeySwapAnnouncements::::iter().collect::>(), - vec![(who, (now, new_coldkey_2_hash))] + vec![(who, (now + delay, new_coldkey_2_hash))] ); }); } @@ -118,19 +119,22 @@ fn test_announce_coldkey_swap_only_pays_swap_cost_if_no_announcement_exists() { let new_coldkey_2 = U256::from(3); let new_coldkey_2_hash = ::Hashing::hash_of(&new_coldkey_2); let left_over = 12345; - + let swap_cost = SubtensorModule::get_key_swap_cost().to_u64(); SubtensorModule::add_balance_to_coldkey_account(&who, swap_cost + left_over); - assert_eq!(SubtensorModule::get_coldkey_balance(&who), swap_cost + left_over); + assert_eq!( + SubtensorModule::get_coldkey_balance(&who), + swap_cost + left_over + ); assert_ok!(SubtensorModule::announce_coldkey_swap( RuntimeOrigin::signed(who), new_coldkey_hash, )); assert_eq!(SubtensorModule::get_coldkey_balance(&who), left_over); - + let now = System::block_number(); - let delay = ColdkeySwapAnnouncementDelay::::get() + 1; + let delay = ColdkeySwapAnnouncementDelay::::get(); System::run_to_block::(now + delay); assert_ok!(SubtensorModule::announce_coldkey_swap( @@ -179,14 +183,12 @@ fn test_announce_coldkey_swap_with_existing_announcement_not_past_delay_fails() )); let now = System::block_number(); + let delay = ColdkeySwapAnnouncementDelay::::get(); assert_eq!( ColdkeySwapAnnouncements::::iter().collect::>(), - vec![(who, (now, new_coldkey_hash))] + vec![(who, (now + delay, new_coldkey_hash))] ); - let unmet_delay = ColdkeySwapAnnouncementDelay::::get(); - System::run_to_block::(now + unmet_delay); - assert_noop!( SubtensorModule::announce_coldkey_swap(RuntimeOrigin::signed(who), new_coldkey_2_hash,), Error::::ColdkeySwapReannouncedTooEarly @@ -321,9 +323,10 @@ fn test_swap_coldkey_announced_too_early_fails() { let who = U256::from(1); let new_coldkey = U256::from(2); let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); - let now = System::block_number(); - ColdkeySwapAnnouncements::::insert(who.clone(), (now, new_coldkey_hash)); + let now = System::block_number(); + let delay = ColdkeySwapAnnouncementDelay::::get(); + ColdkeySwapAnnouncements::::insert(who.clone(), (now + delay, new_coldkey_hash)); assert_noop!( SubtensorModule::swap_coldkey_announced( From 12eb085ec3c21269c3fa7132c858ced42b9bfb9b Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Thu, 11 Dec 2025 20:58:52 +0100 Subject: [PATCH 050/240] reannouncement delay --- chain-extensions/src/mock.rs | 2 ++ pallets/admin-utils/src/lib.rs | 19 +++++++++++++++++++ pallets/admin-utils/src/tests/mock.rs | 2 ++ pallets/subtensor/src/lib.rs | 11 +++++++++++ pallets/subtensor/src/macros/config.rs | 9 +++++---- pallets/subtensor/src/macros/dispatches.rs | 4 +++- pallets/subtensor/src/macros/events.rs | 2 ++ pallets/subtensor/src/tests/mock.rs | 2 ++ pallets/subtensor/src/tests/swap_coldkey.rs | 8 +++++--- pallets/subtensor/src/utils/misc.rs | 5 +++++ pallets/transaction-fee/src/tests/mock.rs | 2 ++ runtime/src/lib.rs | 2 ++ 12 files changed, 60 insertions(+), 8 deletions(-) diff --git a/chain-extensions/src/mock.rs b/chain-extensions/src/mock.rs index 8a38fea2a7..b94b6f27f6 100644 --- a/chain-extensions/src/mock.rs +++ b/chain-extensions/src/mock.rs @@ -326,6 +326,7 @@ parameter_types! { pub const InitialLiquidAlphaOn: bool = false; // Default value for LiquidAlphaOn pub const InitialYuma3On: bool = false; // Default value for Yuma3On pub const InitialColdkeySwapAnnouncementDelay: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days + pub const InitialColdkeySwapReannouncementDelay: u64 = 1 * 24 * 60 * 60 / 12; // Default as 1 day pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days pub const InitialTaoWeight: u64 = 0; // 100% global weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks @@ -396,6 +397,7 @@ impl pallet_subtensor::Config for Test { type Yuma3On = InitialYuma3On; type Preimages = Preimage; type InitialColdkeySwapAnnouncementDelay = InitialColdkeySwapAnnouncementDelay; + type InitialColdkeySwapReannouncementDelay = InitialColdkeySwapReannouncementDelay; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialTaoWeight = InitialTaoWeight; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 6321f2363f..502e7777bb 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -2202,6 +2202,25 @@ pub mod pallet { log::trace!("ColdkeySwapAnnouncementDelaySet( duration: {duration:?} )"); Ok(()) } + + /// Sets the reannouncement delay for coldkey swap. + #[pallet::call_index(85)] + #[pallet::weight(( + Weight::from_parts(5_000_000, 0) + .saturating_add(T::DbWeight::get().reads(0_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)), + DispatchClass::Operational, + Pays::Yes + ))] + pub fn sudo_set_coldkey_swap_reannouncement_delay( + origin: OriginFor, + duration: BlockNumberFor, + ) -> DispatchResult { + ensure_root(origin)?; + pallet_subtensor::Pallet::::set_coldkey_swap_reannouncement_delay(duration); + log::trace!("ColdkeySwapReannouncementDelaySet( duration: {duration:?} )"); + Ok(()) + } } } diff --git a/pallets/admin-utils/src/tests/mock.rs b/pallets/admin-utils/src/tests/mock.rs index e49afe124f..bbc5954b1f 100644 --- a/pallets/admin-utils/src/tests/mock.rs +++ b/pallets/admin-utils/src/tests/mock.rs @@ -138,6 +138,7 @@ parameter_types! { pub const InitialLiquidAlphaOn: bool = false; // Default value for LiquidAlphaOn pub const InitialYuma3On: bool = false; // Default value for Yuma3On pub const InitialColdkeySwapAnnouncementDelay: u64 = 5 * 24 * 60 * 60 / 12; // 5 days + pub const InitialColdkeySwapReannouncementDelay: u64 = 1 * 24 * 60 * 60 / 12; // 1 day pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // 5 days pub const InitialTaoWeight: u64 = u64::MAX/10; // 10% global weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks @@ -207,6 +208,7 @@ impl pallet_subtensor::Config for Test { type Yuma3On = InitialYuma3On; type Preimages = (); type InitialColdkeySwapAnnouncementDelay = InitialColdkeySwapAnnouncementDelay; + type InitialColdkeySwapReannouncementDelay = InitialColdkeySwapReannouncementDelay; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialTaoWeight = InitialTaoWeight; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 25f2f599a4..ef306927a5 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -950,6 +950,12 @@ pub mod pallet { T::InitialColdkeySwapAnnouncementDelay::get() } + /// Default value for coldkey swap reannouncement delay. + #[pallet::type_value] + pub fn DefaultColdkeySwapReannouncementDelay() -> BlockNumberFor { + T::InitialColdkeySwapReannouncementDelay::get() + } + /// Default value for applying pending items (e.g. childkeys). #[pallet::type_value] pub fn DefaultPendingCooldown() -> u64 { @@ -1338,6 +1344,11 @@ pub mod pallet { pub type ColdkeySwapAnnouncementDelay = StorageValue<_, BlockNumberFor, ValueQuery, DefaultColdkeySwapAnnouncementDelay>; + /// The delay after a reannouncement before a coldkey swap can be performed. + #[pallet::storage] + pub type ColdkeySwapReannouncementDelay = + StorageValue<_, BlockNumberFor, ValueQuery, DefaultColdkeySwapReannouncementDelay>; + /// A map of the coldkey swap announcements from a coldkey /// to the block number the coldkey swap can be performed. #[pallet::storage] diff --git a/pallets/subtensor/src/macros/config.rs b/pallets/subtensor/src/macros/config.rs index 6f8b5466ee..ee42feb117 100644 --- a/pallets/subtensor/src/macros/config.rs +++ b/pallets/subtensor/src/macros/config.rs @@ -214,13 +214,14 @@ mod config { #[pallet::constant] type LiquidAlphaOn: Get; /// A flag to indicate if Yuma3 is enabled. + #[pallet::constant] type Yuma3On: Get; - // /// Initial hotkey emission tempo. - // #[pallet::constant] - // type InitialHotkeyEmissionTempo: Get; - /// Coldkey swap schedule duartion. + /// Coldkey swap announcement delay. #[pallet::constant] type InitialColdkeySwapAnnouncementDelay: Get>; + /// Coldkey swap reannouncement delay. + #[pallet::constant] + type InitialColdkeySwapReannouncementDelay: Get>; /// Dissolve network schedule duration #[pallet::constant] type InitialDissolveNetworkScheduleDuration: Get>; diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 620cb05715..f23c78398d 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2376,7 +2376,9 @@ mod dispatches { let now = >::block_number(); if let Some((when, _)) = ColdkeySwapAnnouncements::::get(who.clone()) { - ensure!(now >= when, Error::::ColdkeySwapReannouncedTooEarly); + let reannouncement_delay = ColdkeySwapReannouncementDelay::::get(); + let new_when = when.saturating_add(reannouncement_delay); + ensure!(now >= new_when, Error::::ColdkeySwapReannouncedTooEarly); } else { let swap_cost = Self::get_key_swap_cost(); Self::charge_swap_cost(&who, swap_cost)?; diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index 793b30b217..d45effe738 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -230,6 +230,8 @@ mod events { }, /// The coldkey swap announcement delay has been set. ColdkeySwapAnnouncementDelaySet(BlockNumberFor), + /// The coldkey swap reannouncement delay has been set. + ColdkeySwapReannouncementDelaySet(BlockNumberFor), /// The duration of dissolve network has been set DissolveNetworkScheduleDurationSet(BlockNumberFor), /// Commit-reveal v3 weights have been successfully committed. diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index d0830198b9..f021d9d721 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -213,6 +213,7 @@ parameter_types! { 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 @@ -283,6 +284,7 @@ impl crate::Config for Test { type Yuma3On = InitialYuma3On; type Preimages = Preimage; type InitialColdkeySwapAnnouncementDelay = InitialColdkeySwapAnnouncementDelay; + type InitialColdkeySwapReannouncementDelay = InitialColdkeySwapReannouncementDelay; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialTaoWeight = InitialTaoWeight; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index 68b985b67f..a2ac22263e 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -95,7 +95,8 @@ fn test_announce_coldkey_swap_with_existing_announcement_past_delay_works() { vec![(who, (now + delay, new_coldkey_hash))] ); - System::run_to_block::(now + delay); + let reannouncement_delay = ColdkeySwapReannouncementDelay::::get(); + System::run_to_block::(now + delay + reannouncement_delay); assert_ok!(SubtensorModule::announce_coldkey_swap( RuntimeOrigin::signed(who), @@ -134,8 +135,9 @@ fn test_announce_coldkey_swap_only_pays_swap_cost_if_no_announcement_exists() { assert_eq!(SubtensorModule::get_coldkey_balance(&who), left_over); let now = System::block_number(); - let delay = ColdkeySwapAnnouncementDelay::::get(); - System::run_to_block::(now + delay); + let base_delay = ColdkeySwapAnnouncementDelay::::get(); + let reannouncement_delay = ColdkeySwapReannouncementDelay::::get(); + System::run_to_block::(now + base_delay + reannouncement_delay); assert_ok!(SubtensorModule::announce_coldkey_swap( RuntimeOrigin::signed(who), diff --git a/pallets/subtensor/src/utils/misc.rs b/pallets/subtensor/src/utils/misc.rs index 772d825541..4dad15d8fe 100644 --- a/pallets/subtensor/src/utils/misc.rs +++ b/pallets/subtensor/src/utils/misc.rs @@ -824,6 +824,11 @@ impl Pallet { Self::deposit_event(Event::ColdkeySwapAnnouncementDelaySet(duration)); } + pub fn set_coldkey_swap_reannouncement_delay(duration: BlockNumberFor) { + ColdkeySwapReannouncementDelay::::set(duration); + Self::deposit_event(Event::ColdkeySwapReannouncementDelaySet(duration)); + } + /// Set the duration for dissolve network /// /// # Arguments diff --git a/pallets/transaction-fee/src/tests/mock.rs b/pallets/transaction-fee/src/tests/mock.rs index 4ebef59c38..84c7eeee9f 100644 --- a/pallets/transaction-fee/src/tests/mock.rs +++ b/pallets/transaction-fee/src/tests/mock.rs @@ -203,6 +203,7 @@ parameter_types! { pub const InitialLiquidAlphaOn: bool = false; // Default value for LiquidAlphaOn pub const InitialYuma3On: bool = false; // Default value for Yuma3On pub const InitialColdkeySwapAnnouncementDelay: u64 = 5 * 24 * 60 * 60 / 12; // 5 days + pub const InitialColdkeySwapReannouncementDelay: u64 = 1 * 24 * 60 * 60 / 12; // 1 day pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // 5 days pub const InitialTaoWeight: u64 = u64::MAX/10; // 10% global weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks @@ -272,6 +273,7 @@ impl pallet_subtensor::Config for Test { type Yuma3On = InitialYuma3On; type Preimages = (); type InitialColdkeySwapAnnouncementDelay = InitialColdkeySwapAnnouncementDelay; + type InitialColdkeySwapReannouncementDelay = InitialColdkeySwapReannouncementDelay; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialTaoWeight = InitialTaoWeight; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 0ac0fb0787..3e721940d6 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -1047,6 +1047,7 @@ parameter_types! { pub const InitialLiquidAlphaOn: bool = false; // Default value for LiquidAlphaOn pub const InitialYuma3On: bool = false; // Default value for Yuma3On pub const InitialColdkeySwapAnnouncementDelay: BlockNumber = 5 * 24 * 60 * 60 / 12; // 5 days + pub const InitialColdkeySwapReannouncementDelay: BlockNumber = 1 * 24 * 60 * 60 / 12; // 1 day pub const InitialDissolveNetworkScheduleDuration: BlockNumber = 5 * 24 * 60 * 60 / 12; // 5 days pub const SubtensorInitialTaoWeight: u64 = 971_718_665_099_567_868; // 0.05267697438728329% tao weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks @@ -1117,6 +1118,7 @@ impl pallet_subtensor::Config for Runtime { type InitialTaoWeight = SubtensorInitialTaoWeight; type Preimages = Preimage; type InitialColdkeySwapAnnouncementDelay = InitialColdkeySwapAnnouncementDelay; + type InitialColdkeySwapReannouncementDelay = InitialColdkeySwapReannouncementDelay; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; type DurationOfStartCall = DurationOfStartCall; From 61acd0f1b7063e9b820800727509091c1f3d0105 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Thu, 11 Dec 2025 21:00:36 +0100 Subject: [PATCH 051/240] fix test --- pallets/admin-utils/src/tests/mod.rs | 36 ++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/pallets/admin-utils/src/tests/mod.rs b/pallets/admin-utils/src/tests/mod.rs index 5fcfeed083..a901070705 100644 --- a/pallets/admin-utils/src/tests/mod.rs +++ b/pallets/admin-utils/src/tests/mod.rs @@ -1417,6 +1417,42 @@ fn test_sudo_set_coldkey_swap_announcement_delay() { }); } +#[test] +fn test_sudo_set_coldkey_swap_reannouncement_delay() { + new_test_ext().execute_with(|| { + // Arrange + let root = RuntimeOrigin::root(); + let non_root = RuntimeOrigin::signed(U256::from(1)); + let new_delay = 100u32.into(); + + // Act & Assert: Non-root account should fail + assert_noop!( + AdminUtils::sudo_set_coldkey_swap_reannouncement_delay(non_root, new_delay), + DispatchError::BadOrigin + ); + + // Act: Root account should succeed + assert_ok!(AdminUtils::sudo_set_coldkey_swap_reannouncement_delay( + root.clone(), + new_delay + )); + + // Assert: Check if the delay was actually set + assert_eq!( + pallet_subtensor::ColdkeySwapReannouncementDelay::::get(), + new_delay + ); + + // Act & Assert: Setting the same value again should succeed (idempotent operation) + assert_ok!(AdminUtils::sudo_set_coldkey_swap_reannouncement_delay( + root, new_delay + )); + + // You might want to check for events here if your pallet emits them + System::assert_last_event(Event::ColdkeySwapReannouncementDelaySet(new_delay).into()); + }); +} + #[test] fn test_sudo_set_dissolve_network_schedule_duration() { new_test_ext().execute_with(|| { From fc4224075c0e9cfe54a30cbef6b8fb9183326d0c Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Fri, 12 Dec 2025 12:18:55 +0100 Subject: [PATCH 052/240] fix rust --- chain-extensions/src/mock.rs | 2 +- pallets/admin-utils/src/tests/mock.rs | 2 +- pallets/subtensor/src/tests/swap_coldkey.rs | 10 +++++----- pallets/transaction-fee/src/tests/mock.rs | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/chain-extensions/src/mock.rs b/chain-extensions/src/mock.rs index b94b6f27f6..3583169d91 100644 --- a/chain-extensions/src/mock.rs +++ b/chain-extensions/src/mock.rs @@ -326,7 +326,7 @@ parameter_types! { pub const InitialLiquidAlphaOn: bool = false; // Default value for LiquidAlphaOn pub const InitialYuma3On: bool = false; // Default value for Yuma3On pub const InitialColdkeySwapAnnouncementDelay: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days - pub const InitialColdkeySwapReannouncementDelay: u64 = 1 * 24 * 60 * 60 / 12; // Default as 1 day + pub const InitialColdkeySwapReannouncementDelay: u64 = 24 * 60 * 60 / 12; // Default as 1 day pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days pub const InitialTaoWeight: u64 = 0; // 100% global weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks diff --git a/pallets/admin-utils/src/tests/mock.rs b/pallets/admin-utils/src/tests/mock.rs index bbc5954b1f..7d61b55a5f 100644 --- a/pallets/admin-utils/src/tests/mock.rs +++ b/pallets/admin-utils/src/tests/mock.rs @@ -138,7 +138,7 @@ parameter_types! { pub const InitialLiquidAlphaOn: bool = false; // Default value for LiquidAlphaOn pub const InitialYuma3On: bool = false; // Default value for Yuma3On pub const InitialColdkeySwapAnnouncementDelay: u64 = 5 * 24 * 60 * 60 / 12; // 5 days - pub const InitialColdkeySwapReannouncementDelay: u64 = 1 * 24 * 60 * 60 / 12; // 1 day + pub const InitialColdkeySwapReannouncementDelay: u64 = 24 * 60 * 60 / 12; // 1 day pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // 5 days pub const InitialTaoWeight: u64 = u64::MAX/10; // 10% global weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index a2ac22263e..e9b6cf4c0b 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -214,7 +214,7 @@ fn test_swap_coldkey_announced_works() { let stake3 = min_stake * 30; let now = System::block_number(); - ColdkeySwapAnnouncements::::insert(who.clone(), (now, new_coldkey_hash)); + ColdkeySwapAnnouncements::::insert(who, (now, new_coldkey_hash)); // Run some blocks for the announcement to be past the delay let delay = ColdkeySwapAnnouncementDelay::::get() + 1; @@ -310,7 +310,7 @@ fn test_swap_coldkey_announced_with_mismatched_coldkey_hash_fails() { let other_coldkey = U256::from(3); let now = System::block_number(); - ColdkeySwapAnnouncements::::insert(who.clone(), (now, new_coldkey_hash)); + ColdkeySwapAnnouncements::::insert(who, (now, new_coldkey_hash)); assert_noop!( SubtensorModule::swap_coldkey_announced(RuntimeOrigin::signed(who), other_coldkey), @@ -328,7 +328,7 @@ fn test_swap_coldkey_announced_too_early_fails() { let now = System::block_number(); let delay = ColdkeySwapAnnouncementDelay::::get(); - ColdkeySwapAnnouncements::::insert(who.clone(), (now + delay, new_coldkey_hash)); + ColdkeySwapAnnouncements::::insert(who, (now + delay, new_coldkey_hash)); assert_noop!( SubtensorModule::swap_coldkey_announced( @@ -381,7 +381,7 @@ fn test_swap_coldkey_announced_with_hotkey_fails() { let hotkey_hash = ::Hashing::hash_of(&hotkey); let now = System::block_number(); - ColdkeySwapAnnouncements::::insert(who.clone(), (now, hotkey_hash.clone())); + ColdkeySwapAnnouncements::::insert(who, (now, hotkey_hash)); let now = System::block_number(); let delay = ColdkeySwapAnnouncementDelay::::get() + 1; @@ -1242,7 +1242,7 @@ fn test_remove_coldkey_swap_announcement_works() { let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); let now = System::block_number(); - ColdkeySwapAnnouncements::::insert(who.clone(), (now, new_coldkey_hash.clone())); + ColdkeySwapAnnouncements::::insert(who, (now, new_coldkey_hash)); assert_ok!(SubtensorModule::remove_coldkey_swap_announcement( RuntimeOrigin::root(), diff --git a/pallets/transaction-fee/src/tests/mock.rs b/pallets/transaction-fee/src/tests/mock.rs index 84c7eeee9f..d008c7fd0c 100644 --- a/pallets/transaction-fee/src/tests/mock.rs +++ b/pallets/transaction-fee/src/tests/mock.rs @@ -203,7 +203,7 @@ parameter_types! { pub const InitialLiquidAlphaOn: bool = false; // Default value for LiquidAlphaOn pub const InitialYuma3On: bool = false; // Default value for Yuma3On pub const InitialColdkeySwapAnnouncementDelay: u64 = 5 * 24 * 60 * 60 / 12; // 5 days - pub const InitialColdkeySwapReannouncementDelay: u64 = 1 * 24 * 60 * 60 / 12; // 1 day + pub const InitialColdkeySwapReannouncementDelay: u64 = 24 * 60 * 60 / 12; // 1 day pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // 5 days pub const InitialTaoWeight: u64 = u64::MAX/10; // 10% global weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks From c415d8bc63197a8000de0513d64b5126244c4244 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Fri, 12 Dec 2025 15:25:03 +0100 Subject: [PATCH 053/240] cargo clippy --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index a95ef01552..63cc67ca27 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -1047,7 +1047,7 @@ parameter_types! { pub const InitialLiquidAlphaOn: bool = false; // Default value for LiquidAlphaOn pub const InitialYuma3On: bool = false; // Default value for Yuma3On pub const InitialColdkeySwapAnnouncementDelay: BlockNumber = 5 * 24 * 60 * 60 / 12; // 5 days - pub const InitialColdkeySwapReannouncementDelay: BlockNumber = 1 * 24 * 60 * 60 / 12; // 1 day + pub const InitialColdkeySwapReannouncementDelay: BlockNumber = 24 * 60 * 60 / 12; // 1 day pub const InitialDissolveNetworkScheduleDuration: BlockNumber = 5 * 24 * 60 * 60 / 12; // 5 days pub const SubtensorInitialTaoWeight: u64 = 971_718_665_099_567_868; // 0.05267697438728329% tao weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks From a54ac00cedb36c0ba7cbd53a9ee524a555d9eb9b Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Fri, 12 Dec 2025 23:06:33 +0100 Subject: [PATCH 054/240] fix benchmark --- pallets/subtensor/src/benchmarks.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index a616211407..836801f764 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -413,6 +413,9 @@ mod pallet_benchmarks { let new_coldkey: T::AccountId = account("new_coldkey", 0, 0); let new_coldkey_hash: T::Hash = ::Hashing::hash_of(&new_coldkey); + let swap_cost = Subtensor::::get_key_swap_cost(); + Subtensor::::add_balance_to_coldkey_account(&coldkey, swap_cost.into()); + #[extrinsic_call] _(RawOrigin::Signed(coldkey), new_coldkey_hash); } @@ -429,9 +432,6 @@ mod pallet_benchmarks { ColdkeySwapAnnouncements::::insert(&old_coldkey, (now, new_coldkey_hash)); frame_system::Pallet::::set_block_number(now + delay + 1u32.into()); - let swap_cost = Subtensor::::get_key_swap_cost(); - Subtensor::::add_balance_to_coldkey_account(&old_coldkey, swap_cost.into()); - let netuid = NetUid::from(1); Subtensor::::init_new_network(netuid, 1); Subtensor::::set_network_registration_allowed(netuid, true); From 92365943a6d2a8a9981fd79ed5f20ee1ccb04017 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Sat, 13 Dec 2025 15:22:02 +0100 Subject: [PATCH 055/240] fix call indices --- pallets/admin-utils/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 3f4b11da34..34e6f4a66a 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -2204,7 +2204,7 @@ pub mod pallet { } /// Sets the announcement delay for coldkey swap. - #[pallet::call_index(84)] + #[pallet::call_index(85)] #[pallet::weight(( Weight::from_parts(5_000_000, 0) .saturating_add(T::DbWeight::get().reads(0_u64)) @@ -2223,7 +2223,7 @@ pub mod pallet { } /// Sets the reannouncement delay for coldkey swap. - #[pallet::call_index(85)] + #[pallet::call_index(86)] #[pallet::weight(( Weight::from_parts(5_000_000, 0) .saturating_add(T::DbWeight::get().reads(0_u64)) From 1079b3cbd3532a8557d826f0a15c942602dd0ef1 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Sun, 14 Dec 2025 20:05:19 +0100 Subject: [PATCH 056/240] fix missing return --- pallets/admin-utils/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 34e6f4a66a..875916ccb7 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -2238,6 +2238,7 @@ pub mod pallet { ensure_root(origin)?; pallet_subtensor::Pallet::::set_coldkey_swap_reannouncement_delay(duration); log::trace!("ColdkeySwapReannouncementDelaySet( duration: {duration:?} )"); + Ok(()) } } } From 46164e8950e1ed29cbbaaf9a14d0ebab946e22d1 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 15 Dec 2025 11:31:01 -0500 Subject: [PATCH 057/240] High level architecture for palswap in place, everything compiles --- chain-extensions/src/lib.rs | 4 +- chain-extensions/src/tests.rs | 4 +- .../subtensor/src/coinbase/block_emission.rs | 18 +- .../subtensor/src/coinbase/run_coinbase.rs | 2 +- pallets/subtensor/src/staking/helpers.rs | 14 +- pallets/subtensor/src/staking/remove_stake.rs | 2 +- pallets/subtensor/src/staking/stake_utils.rs | 6 +- pallets/subtensor/src/tests/claim_root.rs | 471 +- pallets/subtensor/src/tests/coinbase.rs | 1122 +++-- pallets/subtensor/src/tests/migration.rs | 284 +- pallets/subtensor/src/tests/move_stake.rs | 2 +- pallets/subtensor/src/tests/networks.rs | 905 ++-- pallets/subtensor/src/tests/staking.rs | 611 ++- pallets/swap-interface/src/lib.rs | 4 +- pallets/swap-interface/src/order.rs | 10 +- pallets/swap/src/benchmarking.rs | 56 +- pallets/swap/src/lib.rs | 5 - pallets/swap/src/mock.rs | 25 +- pallets/swap/src/pallet/impls.rs | 1072 ++-- pallets/swap/src/pallet/mod.rs | 144 +- pallets/swap/src/pallet/reserve_weights.rs | 57 + pallets/swap/src/pallet/swap_step.rs | 458 +- pallets/swap/src/pallet/tests.rs | 4479 ++++++++--------- pallets/swap/src/position.rs | 177 +- pallets/swap/src/tick.rs | 2198 -------- pallets/transaction-fee/src/lib.rs | 8 +- pallets/transaction-fee/src/tests/mod.rs | 149 +- precompiles/src/alpha.rs | 10 +- runtime/src/lib.rs | 4 +- 29 files changed, 4500 insertions(+), 7801 deletions(-) create mode 100644 pallets/swap/src/pallet/reserve_weights.rs delete mode 100644 pallets/swap/src/tick.rs diff --git a/chain-extensions/src/lib.rs b/chain-extensions/src/lib.rs index eaaee70d27..2e2eb807fa 100644 --- a/chain-extensions/src/lib.rs +++ b/chain-extensions/src/lib.rs @@ -18,7 +18,7 @@ use pallet_subtensor_proxy as pallet_proxy; use pallet_subtensor_proxy::WeightInfo; use sp_runtime::{DispatchError, Weight, traits::StaticLookup}; use sp_std::marker::PhantomData; -use substrate_fixed::types::U96F32; +use substrate_fixed::types::U64F64; use subtensor_runtime_common::{AlphaCurrency, NetUid, ProxyType, TaoCurrency}; use subtensor_swap_interface::SwapHandler; @@ -520,7 +520,7 @@ where netuid.into(), ); - let price = current_alpha_price.saturating_mul(U96F32::from_num(1_000_000_000)); + let price = current_alpha_price.saturating_mul(U64F64::from_num(1_000_000_000)); let price: u64 = price.saturating_to_num(); let encoded_result = price.encode(); diff --git a/chain-extensions/src/tests.rs b/chain-extensions/src/tests.rs index 094b0987e2..ed9adc45e4 100644 --- a/chain-extensions/src/tests.rs +++ b/chain-extensions/src/tests.rs @@ -10,7 +10,7 @@ use pallet_subtensor::DefaultMinStake; use sp_core::Get; use sp_core::U256; use sp_runtime::DispatchError; -use substrate_fixed::types::U96F32; +use substrate_fixed::types::U64F64; use subtensor_runtime_common::{AlphaCurrency, Currency as CurrencyTrait, NetUid, TaoCurrency}; use subtensor_swap_interface::SwapHandler; @@ -985,7 +985,7 @@ fn get_alpha_price_returns_encoded_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_scaled = expected_price.saturating_mul(U64F64::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()); diff --git a/pallets/subtensor/src/coinbase/block_emission.rs b/pallets/subtensor/src/coinbase/block_emission.rs index 064bab4d2a..6cdd8abf20 100644 --- a/pallets/subtensor/src/coinbase/block_emission.rs +++ b/pallets/subtensor/src/coinbase/block_emission.rs @@ -3,7 +3,7 @@ use frame_support::traits::Get; use safe_math::*; use substrate_fixed::{ transcendental::log2, - types::{I96F32, U96F32}, + types::{I96F32, U64F64}, }; use subtensor_runtime_common::{NetUid, TaoCurrency}; use subtensor_swap_interface::SwapHandler; @@ -36,15 +36,15 @@ impl Pallet { alpha_block_emission: u64, ) -> (u64, u64, u64) { // Init terms. - let mut tao_in_emission: U96F32 = U96F32::saturating_from_num(tao_emission); - let float_alpha_block_emission: U96F32 = U96F32::saturating_from_num(alpha_block_emission); + let mut tao_in_emission: U64F64 = U64F64::saturating_from_num(tao_emission); + let float_alpha_block_emission: U64F64 = U64F64::saturating_from_num(alpha_block_emission); // Get alpha price for subnet. let alpha_price = T::SwapInterface::current_alpha_price(netuid.into()); log::debug!("{netuid:?} - alpha_price: {alpha_price:?}"); // Get initial alpha_in - let mut alpha_in_emission: U96F32 = U96F32::saturating_from_num(tao_emission) + let mut alpha_in_emission: U64F64 = U64F64::saturating_from_num(tao_emission) .checked_div(alpha_price) .unwrap_or(float_alpha_block_emission); @@ -62,11 +62,13 @@ impl Pallet { } // Avoid rounding errors. - if tao_in_emission < U96F32::saturating_from_num(1) - || alpha_in_emission < U96F32::saturating_from_num(1) + let zero = U64F64::saturating_from_num(0); + let one = U64F64::saturating_from_num(1); + if tao_in_emission < one + || alpha_in_emission < one { - alpha_in_emission = U96F32::saturating_from_num(0); - tao_in_emission = U96F32::saturating_from_num(0); + alpha_in_emission = zero; + tao_in_emission = zero; } // Set Alpha in emission. diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 3b82ec86d5..b57cf0e48c 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -135,7 +135,7 @@ impl Pallet { log::debug!("alpha_emission_i: {alpha_emission_i:?}"); // Get subnet price. - let price_i: U96F32 = T::SwapInterface::current_alpha_price(netuid_i.into()); + let price_i: U96F32 = U96F32::saturating_from_num(T::SwapInterface::current_alpha_price(netuid_i.into())); log::debug!("price_i: {price_i:?}"); let mut tao_in_i: U96F32 = tao_emission_i; diff --git a/pallets/subtensor/src/staking/helpers.rs b/pallets/subtensor/src/staking/helpers.rs index 1176064e36..b16e3a79d6 100644 --- a/pallets/subtensor/src/staking/helpers.rs +++ b/pallets/subtensor/src/staking/helpers.rs @@ -6,7 +6,7 @@ use frame_support::traits::{ }, }; use safe_math::*; -use substrate_fixed::types::U96F32; +use substrate_fixed::types::{U64F64, U96F32}; use subtensor_runtime_common::{NetUid, TaoCurrency}; use subtensor_swap_interface::{Order, SwapHandler}; @@ -48,15 +48,13 @@ impl Pallet { Self::get_all_subnet_netuids() .into_iter() .map(|netuid| { - let alpha = U96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet( + let alpha = U64F64::saturating_from_num(Self::get_stake_for_hotkey_on_subnet( hotkey, netuid, )); - let alpha_price = U96F32::saturating_from_num( - T::SwapInterface::current_alpha_price(netuid.into()), - ); + let alpha_price = T::SwapInterface::current_alpha_price(netuid.into()); alpha.saturating_mul(alpha_price) }) - .sum::() + .sum::() .saturating_to_num::() .into() } @@ -76,7 +74,7 @@ impl Pallet { let order = GetTaoForAlpha::::with_amount(alpha_stake); T::SwapInterface::sim_swap(netuid.into(), order) .map(|r| { - let fee: u64 = U96F32::saturating_from_num(r.fee_paid) + let fee: u64 = U64F64::saturating_from_num(r.fee_paid) .saturating_mul(T::SwapInterface::current_alpha_price( netuid.into(), )) @@ -186,7 +184,7 @@ impl Pallet { let alpha_stake = Self::get_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid); let min_alpha_stake = - U96F32::saturating_from_num(Self::get_nominator_min_required_stake()) + U64F64::saturating_from_num(Self::get_nominator_min_required_stake()) .safe_div(T::SwapInterface::current_alpha_price(netuid)) .saturating_to_num::(); if alpha_stake > 0.into() && alpha_stake < min_alpha_stake.into() { diff --git a/pallets/subtensor/src/staking/remove_stake.rs b/pallets/subtensor/src/staking/remove_stake.rs index 423cb97493..7a7c1a2d79 100644 --- a/pallets/subtensor/src/staking/remove_stake.rs +++ b/pallets/subtensor/src/staking/remove_stake.rs @@ -463,7 +463,7 @@ impl Pallet { .saturating_to_num::(); owner_emission_tao = if owner_alpha_u64 > 0 { - let cur_price: U96F32 = T::SwapInterface::current_alpha_price(netuid.into()); + let cur_price: U96F32 = U96F32::saturating_from_num(T::SwapInterface::current_alpha_price(netuid.into())); let val_u64 = U96F32::from_num(owner_alpha_u64) .saturating_mul(cur_price) .floor() diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index d0f78551c1..50f8badfd4 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -64,8 +64,8 @@ impl Pallet { // We can use unsigned type here: U96F32 let one_minus_alpha: U96F32 = U96F32::saturating_from_num(1.0).saturating_sub(alpha); let current_price: U96F32 = alpha.saturating_mul( - T::SwapInterface::current_alpha_price(netuid.into()) - .min(U96F32::saturating_from_num(1.0)), + U96F32::saturating_from_num(T::SwapInterface::current_alpha_price(netuid.into()) + .min(U64F64::saturating_from_num(1.0))), ); let current_moving: U96F32 = one_minus_alpha.saturating_mul(Self::get_moving_alpha_price(netuid)); @@ -877,7 +877,7 @@ impl Pallet { let current_price = ::SwapInterface::current_alpha_price(netuid.into()); let tao_equivalent: TaoCurrency = current_price - .saturating_mul(U96F32::saturating_from_num(actual_alpha_moved)) + .saturating_mul(U64F64::saturating_from_num(actual_alpha_moved)) .saturating_to_num::() .into(); diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index b910fa1e83..cc69459233 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -6,9 +6,16 @@ use crate::tests::mock::{ }; use crate::{ DefaultMinRootClaimAmount, Error, MAX_NUM_ROOT_CLAIMS, MAX_ROOT_CLAIM_THRESHOLD, NetworksAdded, - NumRootClaim, NumStakingColdkeys, PendingRootAlphaDivs, RootClaimable, RootClaimableThreshold, - StakingColdkeys, StakingColdkeysByIndex, SubnetAlphaIn, SubnetMechanism, SubnetMovingPrice, - SubnetTAO, SubnetTaoFlow, SubtokenEnabled, Tempo, pallet, + NumRootClaim, NumStakingColdkeys, + //PendingRootAlphaDivs, + RootClaimable, RootClaimableThreshold, + StakingColdkeys, StakingColdkeysByIndex, SubnetAlphaIn, SubnetMechanism, + // SubnetMovingPrice, + SubnetTAO, + // SubnetTaoFlow, + SubtokenEnabled, + //Tempo, + pallet, }; use crate::{RootClaimType, RootClaimTypeEnum, RootClaimed}; use approx::assert_abs_diff_eq; @@ -19,7 +26,9 @@ 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, U96F32 +}; use subtensor_runtime_common::{AlphaCurrency, Currency, NetUid, TaoCurrency}; use subtensor_swap_interface::SwapHandler; @@ -760,79 +769,81 @@ fn test_claim_root_with_drain_emissions_and_swap_claim_type() { #[test] fn test_claim_root_with_run_coinbase() { - new_test_ext(1).execute_with(|| { - let owner_coldkey = U256::from(1001); - let hotkey = U256::from(1002); - let coldkey = U256::from(1003); - let netuid = add_dynamic_network(&hotkey, &owner_coldkey); - - Tempo::::insert(netuid, 1); - SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 - - let root_stake = 200_000_000u64; - SubnetTAO::::insert(NetUid::ROOT, TaoCurrency::from(root_stake)); - - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey, - &coldkey, - NetUid::ROOT, - root_stake.into(), - ); - - let initial_total_hotkey_alpha = 10_000_000u64; - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey, - &owner_coldkey, - netuid, - initial_total_hotkey_alpha.into(), - ); - - // Set moving price > 1.0 and price > 1.0 - // So we turn ON root sell - SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); - pallet_subtensor_swap::AlphaSqrtPrice::::insert( - netuid, - U64F64::saturating_from_num(10.0), - ); - - // Make sure we are root selling, so we have root alpha divs. - let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); - assert!(root_sell_flag, "Root sell flag should be true"); - - // Distribute pending root alpha - - let initial_stake: u64 = - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid) - .into(); - assert_eq!(initial_stake, 0u64); - - let block_emissions = 1_000_000u64; - SubtensorModule::run_coinbase(U96F32::from(block_emissions)); - - // Claim root alpha - - let initial_stake: u64 = - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid) - .into(); - assert_eq!(initial_stake, 0u64); - - assert_ok!(SubtensorModule::set_root_claim_type( - RuntimeOrigin::signed(coldkey), - RootClaimTypeEnum::Keep - ),); - assert_eq!(RootClaimType::::get(coldkey), RootClaimTypeEnum::Keep); - - assert_ok!(SubtensorModule::claim_root( - RuntimeOrigin::signed(coldkey), - BTreeSet::from([netuid]) - )); - - let new_stake: u64 = - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid) - .into(); - - assert!(new_stake > 0); - }); + todo!(); + + // new_test_ext(1).execute_with(|| { + // let owner_coldkey = U256::from(1001); + // let hotkey = U256::from(1002); + // let coldkey = U256::from(1003); + // let netuid = add_dynamic_network(&hotkey, &owner_coldkey); + + // Tempo::::insert(netuid, 1); + // SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 + + // let root_stake = 200_000_000u64; + // SubnetTAO::::insert(NetUid::ROOT, TaoCurrency::from(root_stake)); + + // SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + // &hotkey, + // &coldkey, + // NetUid::ROOT, + // root_stake.into(), + // ); + + // let initial_total_hotkey_alpha = 10_000_000u64; + // SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + // &hotkey, + // &owner_coldkey, + // netuid, + // initial_total_hotkey_alpha.into(), + // ); + + // // Set moving price > 1.0 and price > 1.0 + // // So we turn ON root sell + // SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); + // pallet_subtensor_swap::AlphaSqrtPrice::::insert( + // netuid, + // U64F64::saturating_from_num(10.0), + // ); + + // // Make sure we are root selling, so we have root alpha divs. + // let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); + // assert!(root_sell_flag, "Root sell flag should be true"); + + // // Distribute pending root alpha + + // let initial_stake: u64 = + // SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid) + // .into(); + // assert_eq!(initial_stake, 0u64); + + // let block_emissions = 1_000_000u64; + // SubtensorModule::run_coinbase(U96F32::from(block_emissions)); + + // // Claim root alpha + + // let initial_stake: u64 = + // SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid) + // .into(); + // assert_eq!(initial_stake, 0u64); + + // assert_ok!(SubtensorModule::set_root_claim_type( + // RuntimeOrigin::signed(coldkey), + // RootClaimTypeEnum::Keep + // ),); + // assert_eq!(RootClaimType::::get(coldkey), RootClaimTypeEnum::Keep); + + // assert_ok!(SubtensorModule::claim_root( + // RuntimeOrigin::signed(coldkey), + // BTreeSet::from([netuid]) + // )); + + // let new_stake: u64 = + // SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid) + // .into(); + + // assert!(new_stake > 0); + // }); } #[test] @@ -878,69 +889,71 @@ fn test_claim_root_block_hash_indices() { #[test] fn test_claim_root_with_block_emissions() { - new_test_ext(0).execute_with(|| { - let owner_coldkey = U256::from(1001); - let hotkey = U256::from(1002); - let coldkey = U256::from(1003); - let netuid = add_dynamic_network(&hotkey, &owner_coldkey); - - Tempo::::insert(netuid, 1); - SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 - - let root_stake = 200_000_000u64; - SubnetTAO::::insert(NetUid::ROOT, TaoCurrency::from(root_stake)); - - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey, - &coldkey, - NetUid::ROOT, - root_stake.into(), - ); - SubtensorModule::maybe_add_coldkey_index(&coldkey); - - // Set moving price > 1.0 and price > 1.0 - // So we turn ON root sell - SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); - pallet_subtensor_swap::AlphaSqrtPrice::::insert( - netuid, - U64F64::saturating_from_num(10.0), - ); - - // Make sure we are root selling, so we have root alpha divs. - let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); - assert!(root_sell_flag, "Root sell flag should be true"); - - let initial_total_hotkey_alpha = 10_000_000u64; - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey, - &owner_coldkey, - netuid, - initial_total_hotkey_alpha.into(), - ); - - assert_ok!(SubtensorModule::set_root_claim_type( - RuntimeOrigin::signed(coldkey), - RootClaimTypeEnum::Keep - ),); - assert_eq!(RootClaimType::::get(coldkey), RootClaimTypeEnum::Keep); - - // Distribute pending root alpha - - let initial_stake: u64 = - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid) - .into(); - assert_eq!(initial_stake, 0u64); - - run_to_block(2); - - // Check stake after block emissions - - let new_stake: u64 = - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid) - .into(); - - assert!(new_stake > 0); - }); + todo!(); + + // new_test_ext(0).execute_with(|| { + // let owner_coldkey = U256::from(1001); + // let hotkey = U256::from(1002); + // let coldkey = U256::from(1003); + // let netuid = add_dynamic_network(&hotkey, &owner_coldkey); + + // Tempo::::insert(netuid, 1); + // SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 + + // let root_stake = 200_000_000u64; + // SubnetTAO::::insert(NetUid::ROOT, TaoCurrency::from(root_stake)); + + // SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + // &hotkey, + // &coldkey, + // NetUid::ROOT, + // root_stake.into(), + // ); + // SubtensorModule::maybe_add_coldkey_index(&coldkey); + + // // Set moving price > 1.0 and price > 1.0 + // // So we turn ON root sell + // SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); + // pallet_subtensor_swap::AlphaSqrtPrice::::insert( + // netuid, + // U64F64::saturating_from_num(10.0), + // ); + + // // Make sure we are root selling, so we have root alpha divs. + // let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); + // assert!(root_sell_flag, "Root sell flag should be true"); + + // let initial_total_hotkey_alpha = 10_000_000u64; + // SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + // &hotkey, + // &owner_coldkey, + // netuid, + // initial_total_hotkey_alpha.into(), + // ); + + // assert_ok!(SubtensorModule::set_root_claim_type( + // RuntimeOrigin::signed(coldkey), + // RootClaimTypeEnum::Keep + // ),); + // assert_eq!(RootClaimType::::get(coldkey), RootClaimTypeEnum::Keep); + + // // Distribute pending root alpha + + // let initial_stake: u64 = + // SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid) + // .into(); + // assert_eq!(initial_stake, 0u64); + + // run_to_block(2); + + // // Check stake after block emissions + + // let new_stake: u64 = + // SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid) + // .into(); + + // assert!(new_stake > 0); + // }); } #[test] fn test_populate_staking_maps() { @@ -992,94 +1005,96 @@ fn test_populate_staking_maps() { #[test] fn test_claim_root_coinbase_distribution() { - new_test_ext(1).execute_with(|| { - let owner_coldkey = U256::from(1001); - let hotkey = U256::from(1002); - let coldkey = U256::from(1003); - let netuid = add_dynamic_network(&hotkey, &owner_coldkey); - - Tempo::::insert(netuid, 1); - SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 - - let root_stake = 200_000_000u64; - let initial_tao = 200_000_000u64; - SubnetTAO::::insert(NetUid::ROOT, TaoCurrency::from(initial_tao)); - - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey, - &coldkey, - NetUid::ROOT, - root_stake.into(), - ); - - let initial_total_hotkey_alpha = 10_000_000u64; - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey, - &owner_coldkey, - netuid, - initial_total_hotkey_alpha.into(), - ); - - let initial_alpha_issuance = SubtensorModule::get_alpha_issuance(netuid); - let alpha_emissions: AlphaCurrency = 1_000_000_000u64.into(); - - // Set moving price > 1.0 and price > 1.0 - // So we turn ON root sell - SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); - pallet_subtensor_swap::AlphaSqrtPrice::::insert( - netuid, - U64F64::saturating_from_num(10.0), - ); - - // Make sure we are root selling, so we have root alpha divs. - let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); - assert!(root_sell_flag, "Root sell flag should be true"); - - // Set TAOFlow > 0 - SubnetTaoFlow::::insert(netuid, 2222_i64); - - // Check total issuance (saved to pending alpha divs) - run_to_block(2); - - let alpha_issuance = SubtensorModule::get_alpha_issuance(netuid); - // We went two blocks so we should have 2x the alpha emissions - assert_eq!( - initial_alpha_issuance + alpha_emissions.saturating_mul(2.into()), - alpha_issuance - ); - - let root_prop = initial_tao as f64 / (u64::from(alpha_issuance) + initial_tao) as f64; - let root_validators_share = 0.5f64; - - let expected_pending_root_alpha_divs = - u64::from(alpha_emissions) as f64 * root_prop * root_validators_share; - assert_abs_diff_eq!( - u64::from(PendingRootAlphaDivs::::get(netuid)) as f64, - expected_pending_root_alpha_divs, - epsilon = 100f64 - ); - - // Epoch pending alphas divs is distributed - - run_to_block(3); - - assert_eq!(u64::from(PendingRootAlphaDivs::::get(netuid)), 0u64); - - let claimable = *RootClaimable::::get(hotkey) - .get(&netuid) - .expect("claimable must exist at this point"); - - let validator_take_percent = 0.18f64; - let calculated_rate = (expected_pending_root_alpha_divs * 2f64) - * (1f64 - validator_take_percent) - / (root_stake as f64); - - assert_abs_diff_eq!( - claimable.saturating_to_num::(), - calculated_rate, - epsilon = 0.001f64, - ); - }); + todo!(); + + // new_test_ext(1).execute_with(|| { + // let owner_coldkey = U256::from(1001); + // let hotkey = U256::from(1002); + // let coldkey = U256::from(1003); + // let netuid = add_dynamic_network(&hotkey, &owner_coldkey); + + // Tempo::::insert(netuid, 1); + // SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 + + // let root_stake = 200_000_000u64; + // let initial_tao = 200_000_000u64; + // SubnetTAO::::insert(NetUid::ROOT, TaoCurrency::from(initial_tao)); + + // SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + // &hotkey, + // &coldkey, + // NetUid::ROOT, + // root_stake.into(), + // ); + + // let initial_total_hotkey_alpha = 10_000_000u64; + // SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + // &hotkey, + // &owner_coldkey, + // netuid, + // initial_total_hotkey_alpha.into(), + // ); + + // let initial_alpha_issuance = SubtensorModule::get_alpha_issuance(netuid); + // let alpha_emissions: AlphaCurrency = 1_000_000_000u64.into(); + + // // Set moving price > 1.0 and price > 1.0 + // // So we turn ON root sell + // SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); + // pallet_subtensor_swap::AlphaSqrtPrice::::insert( + // netuid, + // U64F64::saturating_from_num(10.0), + // ); + + // // Make sure we are root selling, so we have root alpha divs. + // let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); + // assert!(root_sell_flag, "Root sell flag should be true"); + + // // Set TAOFlow > 0 + // SubnetTaoFlow::::insert(netuid, 2222_i64); + + // // Check total issuance (saved to pending alpha divs) + // run_to_block(2); + + // let alpha_issuance = SubtensorModule::get_alpha_issuance(netuid); + // // We went two blocks so we should have 2x the alpha emissions + // assert_eq!( + // initial_alpha_issuance + alpha_emissions.saturating_mul(2.into()), + // alpha_issuance + // ); + + // let root_prop = initial_tao as f64 / (u64::from(alpha_issuance) + initial_tao) as f64; + // let root_validators_share = 0.5f64; + + // let expected_pending_root_alpha_divs = + // u64::from(alpha_emissions) as f64 * root_prop * root_validators_share; + // assert_abs_diff_eq!( + // u64::from(PendingRootAlphaDivs::::get(netuid)) as f64, + // expected_pending_root_alpha_divs, + // epsilon = 100f64 + // ); + + // // Epoch pending alphas divs is distributed + + // run_to_block(3); + + // assert_eq!(u64::from(PendingRootAlphaDivs::::get(netuid)), 0u64); + + // let claimable = *RootClaimable::::get(hotkey) + // .get(&netuid) + // .expect("claimable must exist at this point"); + + // let validator_take_percent = 0.18f64; + // let calculated_rate = (expected_pending_root_alpha_divs * 2f64) + // * (1f64 - validator_take_percent) + // / (root_stake as f64); + + // assert_abs_diff_eq!( + // claimable.saturating_to_num::(), + // calculated_rate, + // epsilon = 0.001f64, + // ); + // }); } #[test] diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index c7ad008c04..4fe6af495e 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -2733,48 +2733,50 @@ fn test_run_coinbase_not_started_start_after() { /// cargo test --package pallet-subtensor --lib -- tests::coinbase::test_coinbase_v3_liquidity_update --exact --show-output #[test] fn test_coinbase_v3_liquidity_update() { - new_test_ext(1).execute_with(|| { - let owner_hotkey = U256::from(1); - let owner_coldkey = U256::from(2); - - // add network - let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); - - // Force the swap to initialize - SubtensorModule::swap_tao_for_alpha( - netuid, - TaoCurrency::ZERO, - 1_000_000_000_000.into(), - false, - ) - .unwrap(); - - let protocol_account_id = pallet_subtensor_swap::Pallet::::protocol_account_id(); - let position = pallet_subtensor_swap::Positions::::get(( - netuid, - protocol_account_id, - PositionId::from(1), - )) - .unwrap(); - let liquidity_before = position.liquidity; - - // Enable emissions and run coinbase (which will increase position liquidity) - let emission: u64 = 1_234_567; - // Set the TAO flow to non-zero - SubnetTaoFlow::::insert(netuid, 8348383_i64); - FirstEmissionBlockNumber::::insert(netuid, 0); - SubtensorModule::run_coinbase(U96F32::from_num(emission)); - - let position_after = pallet_subtensor_swap::Positions::::get(( - netuid, - protocol_account_id, - PositionId::from(1), - )) - .unwrap(); - let liquidity_after = position_after.liquidity; - - assert!(liquidity_before < liquidity_after); - }); + todo!(); + + // new_test_ext(1).execute_with(|| { + // let owner_hotkey = U256::from(1); + // let owner_coldkey = U256::from(2); + + // // add network + // let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); + + // // Force the swap to initialize + // SubtensorModule::swap_tao_for_alpha( + // netuid, + // TaoCurrency::ZERO, + // 1_000_000_000_000.into(), + // false, + // ) + // .unwrap(); + + // let protocol_account_id = pallet_subtensor_swap::Pallet::::protocol_account_id(); + // let position = pallet_subtensor_swap::Positions::::get(( + // netuid, + // protocol_account_id, + // PositionId::from(1), + // )) + // .unwrap(); + // let liquidity_before = position.liquidity; + + // // Enable emissions and run coinbase (which will increase position liquidity) + // let emission: u64 = 1_234_567; + // // Set the TAO flow to non-zero + // SubnetTaoFlow::::insert(netuid, 8348383_i64); + // FirstEmissionBlockNumber::::insert(netuid, 0); + // SubtensorModule::run_coinbase(U96F32::from_num(emission)); + + // let position_after = pallet_subtensor_swap::Positions::::get(( + // netuid, + // protocol_account_id, + // PositionId::from(1), + // )) + // .unwrap(); + // let liquidity_after = position_after.liquidity; + + // assert!(liquidity_before < liquidity_after); + // }); } // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_drain_alpha_childkey_parentkey_with_burn --exact --show-output --nocapture @@ -2978,368 +2980,372 @@ fn test_zero_shares_zero_emission() { #[test] fn test_mining_emission_distribution_with_no_root_sell() { - new_test_ext(1).execute_with(|| { - let validator_coldkey = U256::from(1); - let validator_hotkey = U256::from(2); - let validator_miner_coldkey = U256::from(3); - let validator_miner_hotkey = U256::from(4); - let miner_coldkey = U256::from(5); - let miner_hotkey = U256::from(6); - let netuid = NetUid::from(1); - let subnet_tempo = 10; - let stake: u64 = 100_000_000_000; - let root_stake: u64 = 200_000_000_000; // 200 TAO - - // Create root network - SubtensorModule::set_tao_weight(0); // Start tao weight at 0 - SubtokenEnabled::::insert(NetUid::ROOT, true); - NetworksAdded::::insert(NetUid::ROOT, true); - - // Add network, register hotkeys, and setup network parameters - add_network(netuid, subnet_tempo, 0); - SubnetMechanism::::insert(netuid, 1); // Set mechanism to 1 - - // Setup large LPs to prevent slippage - SubnetTAO::::insert(netuid, TaoCurrency::from(1_000_000_000_000_000)); - SubnetAlphaIn::::insert(netuid, AlphaCurrency::from(1_000_000_000_000_000)); - - register_ok_neuron(netuid, validator_hotkey, validator_coldkey, 0); - register_ok_neuron(netuid, validator_miner_hotkey, validator_miner_coldkey, 1); - register_ok_neuron(netuid, miner_hotkey, miner_coldkey, 2); - SubtensorModule::add_balance_to_coldkey_account( - &validator_coldkey, - stake + ExistentialDeposit::get(), - ); - SubtensorModule::add_balance_to_coldkey_account( - &validator_miner_coldkey, - stake + ExistentialDeposit::get(), - ); - SubtensorModule::add_balance_to_coldkey_account( - &miner_coldkey, - stake + ExistentialDeposit::get(), - ); - SubtensorModule::set_weights_set_rate_limit(netuid, 0); - step_block(subnet_tempo); - SubnetOwnerCut::::set(u16::MAX / 10); - // There are two validators and three neurons - MaxAllowedUids::::set(netuid, 3); - SubtensorModule::set_max_allowed_validators(netuid, 2); - - // Setup stakes: - // Stake from validator - // Stake from valiminer - assert_ok!(SubtensorModule::add_stake( - RuntimeOrigin::signed(validator_coldkey), - validator_hotkey, - netuid, - stake.into() - )); - assert_ok!(SubtensorModule::add_stake( - RuntimeOrigin::signed(validator_miner_coldkey), - validator_miner_hotkey, - netuid, - stake.into() - )); - - // Setup YUMA so that it creates emissions - Weights::::insert(NetUidStorageIndex::from(netuid), 0, vec![(1, 0xFFFF)]); - Weights::::insert(NetUidStorageIndex::from(netuid), 1, vec![(2, 0xFFFF)]); - BlockAtRegistration::::set(netuid, 0, 1); - BlockAtRegistration::::set(netuid, 1, 1); - BlockAtRegistration::::set(netuid, 2, 1); - LastUpdate::::set(NetUidStorageIndex::from(netuid), vec![2, 2, 2]); - Kappa::::set(netuid, u16::MAX / 5); - ActivityCutoff::::set(netuid, u16::MAX); // makes all stake active - ValidatorPermit::::insert(netuid, vec![true, true, false]); - - // Run run_coinbase until emissions are drained - step_block(subnet_tempo); - - // Add stake to validator so it has root stake - SubtensorModule::add_balance_to_coldkey_account(&validator_coldkey, root_stake.into()); - // init root - assert_ok!(SubtensorModule::add_stake( - RuntimeOrigin::signed(validator_coldkey), - validator_hotkey, - NetUid::ROOT, - root_stake.into() - )); - // Set tao weight non zero - SubtensorModule::set_tao_weight(u64::MAX / 10); - - // Make root sell NOT happen - // set price very low, e.g. a lot of alpha in - //SubnetAlphaIn::::insert(netuid, AlphaCurrency::from(1_000_000_000_000_000)); - pallet_subtensor_swap::AlphaSqrtPrice::::insert( - netuid, - U64F64::saturating_from_num(0.01), - ); - - // Make sure we ARE NOT root selling, so we do not have root alpha divs. - let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); - assert!(!root_sell_flag, "Root sell flag should be false"); - - // Run run_coinbase until emissions are drained - step_block(subnet_tempo); - - let old_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); - let per_block_emission = SubtensorModule::get_block_emission_for_issuance( - SubtensorModule::get_alpha_issuance(netuid).into(), - ) - .unwrap_or(0); - - // step by one block - step_block(1); - // Verify that root alpha divs - let new_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); - // Check that we are indeed NOT root selling, i.e. that root alpha divs are NOT increasing - assert_eq!( - new_root_alpha_divs, old_root_alpha_divs, - "Root alpha divs should not increase" - ); - // Check root divs are zero - assert_eq!( - new_root_alpha_divs, - AlphaCurrency::ZERO, - "Root alpha divs should be zero" - ); - let miner_stake_before_epoch = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &miner_hotkey, - &miner_coldkey, - netuid, - ); - // Run again but with some root stake - step_block(subnet_tempo - 2); - assert_abs_diff_eq!( - PendingServerEmission::::get(netuid).to_u64(), - U96F32::saturating_from_num(per_block_emission) - .saturating_mul(U96F32::saturating_from_num(subnet_tempo as u64)) - .saturating_mul(U96F32::saturating_from_num(0.5)) // miner cut - .saturating_mul(U96F32::saturating_from_num(0.90)) - .saturating_to_num::(), - epsilon = 100_000_u64.into() - ); - step_block(1); - assert!( - BlocksSinceLastStep::::get(netuid) == 0, - "Blocks since last step should be 0" - ); - - let miner_uid = Uids::::get(netuid, miner_hotkey).unwrap_or(0); - log::info!("Miner uid: {miner_uid:?}"); - let miner_incentive: AlphaCurrency = { - let miner_incentive = Incentive::::get(NetUidStorageIndex::from(netuid)) - .get(miner_uid as usize) - .copied(); - - assert!(miner_incentive.is_some()); - - (miner_incentive.unwrap_or_default() as u64).into() - }; - log::info!("Miner incentive: {miner_incentive:?}"); - - // Miner emissions - let miner_emission_1: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &miner_hotkey, - &miner_coldkey, - netuid, - ) - .to_u64() - - miner_stake_before_epoch.to_u64(); - - assert_abs_diff_eq!( - Incentive::::get(NetUidStorageIndex::from(netuid)) - .iter() - .sum::(), - u16::MAX, - epsilon = 10 - ); - - assert_abs_diff_eq!( - miner_emission_1, - U96F32::saturating_from_num(miner_incentive) - .saturating_div(u16::MAX.into()) - .saturating_mul(U96F32::saturating_from_num(per_block_emission)) - .saturating_mul(U96F32::saturating_from_num(subnet_tempo + 1)) - .saturating_mul(U96F32::saturating_from_num(0.45)) // miner cut - .saturating_to_num::(), - epsilon = 1_000_000_u64 - ); - }); + todo!(); + + // new_test_ext(1).execute_with(|| { + // let validator_coldkey = U256::from(1); + // let validator_hotkey = U256::from(2); + // let validator_miner_coldkey = U256::from(3); + // let validator_miner_hotkey = U256::from(4); + // let miner_coldkey = U256::from(5); + // let miner_hotkey = U256::from(6); + // let netuid = NetUid::from(1); + // let subnet_tempo = 10; + // let stake: u64 = 100_000_000_000; + // let root_stake: u64 = 200_000_000_000; // 200 TAO + + // // Create root network + // SubtensorModule::set_tao_weight(0); // Start tao weight at 0 + // SubtokenEnabled::::insert(NetUid::ROOT, true); + // NetworksAdded::::insert(NetUid::ROOT, true); + + // // Add network, register hotkeys, and setup network parameters + // add_network(netuid, subnet_tempo, 0); + // SubnetMechanism::::insert(netuid, 1); // Set mechanism to 1 + + // // Setup large LPs to prevent slippage + // SubnetTAO::::insert(netuid, TaoCurrency::from(1_000_000_000_000_000)); + // SubnetAlphaIn::::insert(netuid, AlphaCurrency::from(1_000_000_000_000_000)); + + // register_ok_neuron(netuid, validator_hotkey, validator_coldkey, 0); + // register_ok_neuron(netuid, validator_miner_hotkey, validator_miner_coldkey, 1); + // register_ok_neuron(netuid, miner_hotkey, miner_coldkey, 2); + // SubtensorModule::add_balance_to_coldkey_account( + // &validator_coldkey, + // stake + ExistentialDeposit::get(), + // ); + // SubtensorModule::add_balance_to_coldkey_account( + // &validator_miner_coldkey, + // stake + ExistentialDeposit::get(), + // ); + // SubtensorModule::add_balance_to_coldkey_account( + // &miner_coldkey, + // stake + ExistentialDeposit::get(), + // ); + // SubtensorModule::set_weights_set_rate_limit(netuid, 0); + // step_block(subnet_tempo); + // SubnetOwnerCut::::set(u16::MAX / 10); + // // There are two validators and three neurons + // MaxAllowedUids::::set(netuid, 3); + // SubtensorModule::set_max_allowed_validators(netuid, 2); + + // // Setup stakes: + // // Stake from validator + // // Stake from valiminer + // assert_ok!(SubtensorModule::add_stake( + // RuntimeOrigin::signed(validator_coldkey), + // validator_hotkey, + // netuid, + // stake.into() + // )); + // assert_ok!(SubtensorModule::add_stake( + // RuntimeOrigin::signed(validator_miner_coldkey), + // validator_miner_hotkey, + // netuid, + // stake.into() + // )); + + // // Setup YUMA so that it creates emissions + // Weights::::insert(NetUidStorageIndex::from(netuid), 0, vec![(1, 0xFFFF)]); + // Weights::::insert(NetUidStorageIndex::from(netuid), 1, vec![(2, 0xFFFF)]); + // BlockAtRegistration::::set(netuid, 0, 1); + // BlockAtRegistration::::set(netuid, 1, 1); + // BlockAtRegistration::::set(netuid, 2, 1); + // LastUpdate::::set(NetUidStorageIndex::from(netuid), vec![2, 2, 2]); + // Kappa::::set(netuid, u16::MAX / 5); + // ActivityCutoff::::set(netuid, u16::MAX); // makes all stake active + // ValidatorPermit::::insert(netuid, vec![true, true, false]); + + // // Run run_coinbase until emissions are drained + // step_block(subnet_tempo); + + // // Add stake to validator so it has root stake + // SubtensorModule::add_balance_to_coldkey_account(&validator_coldkey, root_stake.into()); + // // init root + // assert_ok!(SubtensorModule::add_stake( + // RuntimeOrigin::signed(validator_coldkey), + // validator_hotkey, + // NetUid::ROOT, + // root_stake.into() + // )); + // // Set tao weight non zero + // SubtensorModule::set_tao_weight(u64::MAX / 10); + + // // Make root sell NOT happen + // // set price very low, e.g. a lot of alpha in + // //SubnetAlphaIn::::insert(netuid, AlphaCurrency::from(1_000_000_000_000_000)); + // pallet_subtensor_swap::AlphaSqrtPrice::::insert( + // netuid, + // U64F64::saturating_from_num(0.01), + // ); + + // // Make sure we ARE NOT root selling, so we do not have root alpha divs. + // let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); + // assert!(!root_sell_flag, "Root sell flag should be false"); + + // // Run run_coinbase until emissions are drained + // step_block(subnet_tempo); + + // let old_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); + // let per_block_emission = SubtensorModule::get_block_emission_for_issuance( + // SubtensorModule::get_alpha_issuance(netuid).into(), + // ) + // .unwrap_or(0); + + // // step by one block + // step_block(1); + // // Verify that root alpha divs + // let new_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); + // // Check that we are indeed NOT root selling, i.e. that root alpha divs are NOT increasing + // assert_eq!( + // new_root_alpha_divs, old_root_alpha_divs, + // "Root alpha divs should not increase" + // ); + // // Check root divs are zero + // assert_eq!( + // new_root_alpha_divs, + // AlphaCurrency::ZERO, + // "Root alpha divs should be zero" + // ); + // let miner_stake_before_epoch = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + // &miner_hotkey, + // &miner_coldkey, + // netuid, + // ); + // // Run again but with some root stake + // step_block(subnet_tempo - 2); + // assert_abs_diff_eq!( + // PendingServerEmission::::get(netuid).to_u64(), + // U96F32::saturating_from_num(per_block_emission) + // .saturating_mul(U96F32::saturating_from_num(subnet_tempo as u64)) + // .saturating_mul(U96F32::saturating_from_num(0.5)) // miner cut + // .saturating_mul(U96F32::saturating_from_num(0.90)) + // .saturating_to_num::(), + // epsilon = 100_000_u64.into() + // ); + // step_block(1); + // assert!( + // BlocksSinceLastStep::::get(netuid) == 0, + // "Blocks since last step should be 0" + // ); + + // let miner_uid = Uids::::get(netuid, miner_hotkey).unwrap_or(0); + // log::info!("Miner uid: {miner_uid:?}"); + // let miner_incentive: AlphaCurrency = { + // let miner_incentive = Incentive::::get(NetUidStorageIndex::from(netuid)) + // .get(miner_uid as usize) + // .copied(); + + // assert!(miner_incentive.is_some()); + + // (miner_incentive.unwrap_or_default() as u64).into() + // }; + // log::info!("Miner incentive: {miner_incentive:?}"); + + // // Miner emissions + // let miner_emission_1: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + // &miner_hotkey, + // &miner_coldkey, + // netuid, + // ) + // .to_u64() + // - miner_stake_before_epoch.to_u64(); + + // assert_abs_diff_eq!( + // Incentive::::get(NetUidStorageIndex::from(netuid)) + // .iter() + // .sum::(), + // u16::MAX, + // epsilon = 10 + // ); + + // assert_abs_diff_eq!( + // miner_emission_1, + // U96F32::saturating_from_num(miner_incentive) + // .saturating_div(u16::MAX.into()) + // .saturating_mul(U96F32::saturating_from_num(per_block_emission)) + // .saturating_mul(U96F32::saturating_from_num(subnet_tempo + 1)) + // .saturating_mul(U96F32::saturating_from_num(0.45)) // miner cut + // .saturating_to_num::(), + // epsilon = 1_000_000_u64 + // ); + // }); } // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_mining_emission_distribution_with_root_sell --exact --show-output --nocapture #[test] fn test_mining_emission_distribution_with_root_sell() { - new_test_ext(1).execute_with(|| { - let validator_coldkey = U256::from(1); - let validator_hotkey = U256::from(2); - let validator_miner_coldkey = U256::from(3); - let validator_miner_hotkey = U256::from(4); - let miner_coldkey = U256::from(5); - let miner_hotkey = U256::from(6); - let subnet_tempo = 10; - let stake: u64 = 100_000_000_000; - let root_stake: u64 = 200_000_000_000; // 200 TAO - - // Create root network - SubtensorModule::set_tao_weight(0); // Start tao weight at 0 - SubtokenEnabled::::insert(NetUid::ROOT, true); - NetworksAdded::::insert(NetUid::ROOT, true); - - // Add network, register hotkeys, and setup network parameters - let owner_hotkey = U256::from(10); - let owner_coldkey = U256::from(11); - let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); - Tempo::::insert(netuid, 1); - FirstEmissionBlockNumber::::insert(netuid, 0); - - // Setup large LPs to prevent slippage - SubnetTAO::::insert(netuid, TaoCurrency::from(1_000_000_000_000_000)); - SubnetAlphaIn::::insert(netuid, AlphaCurrency::from(1_000_000_000_000_000)); - - register_ok_neuron(netuid, validator_hotkey, validator_coldkey, 0); - register_ok_neuron(netuid, validator_miner_hotkey, validator_miner_coldkey, 1); - register_ok_neuron(netuid, miner_hotkey, miner_coldkey, 2); - SubtensorModule::add_balance_to_coldkey_account( - &validator_coldkey, - stake + ExistentialDeposit::get(), - ); - SubtensorModule::add_balance_to_coldkey_account( - &validator_miner_coldkey, - stake + ExistentialDeposit::get(), - ); - SubtensorModule::add_balance_to_coldkey_account( - &miner_coldkey, - stake + ExistentialDeposit::get(), - ); - SubtensorModule::set_weights_set_rate_limit(netuid, 0); - step_block(subnet_tempo); - SubnetOwnerCut::::set(u16::MAX / 10); - // There are two validators and three neurons - MaxAllowedUids::::set(netuid, 3); - SubtensorModule::set_max_allowed_validators(netuid, 2); - - // Setup stakes: - // Stake from validator - // Stake from valiminer - assert_ok!(SubtensorModule::add_stake( - RuntimeOrigin::signed(validator_coldkey), - validator_hotkey, - netuid, - stake.into() - )); - assert_ok!(SubtensorModule::add_stake( - RuntimeOrigin::signed(validator_miner_coldkey), - validator_miner_hotkey, - netuid, - stake.into() - )); - - // Setup YUMA so that it creates emissions - Weights::::insert(NetUidStorageIndex::from(netuid), 0, vec![(1, 0xFFFF)]); - Weights::::insert(NetUidStorageIndex::from(netuid), 1, vec![(2, 0xFFFF)]); - BlockAtRegistration::::set(netuid, 0, 1); - BlockAtRegistration::::set(netuid, 1, 1); - BlockAtRegistration::::set(netuid, 2, 1); - LastUpdate::::set(NetUidStorageIndex::from(netuid), vec![2, 2, 2]); - Kappa::::set(netuid, u16::MAX / 5); - ActivityCutoff::::set(netuid, u16::MAX); // makes all stake active - ValidatorPermit::::insert(netuid, vec![true, true, false]); - - // Run run_coinbase until emissions are drained - step_block(subnet_tempo); - - // Add stake to validator so it has root stake - SubtensorModule::add_balance_to_coldkey_account(&validator_coldkey, root_stake.into()); - // init root - assert_ok!(SubtensorModule::add_stake( - RuntimeOrigin::signed(validator_coldkey), - validator_hotkey, - NetUid::ROOT, - root_stake.into() - )); - // Set tao weight non zero - SubtensorModule::set_tao_weight(u64::MAX / 10); - - // Make root sell happen - // Set moving price > 1.0 - // Set price > 1.0 - pallet_subtensor_swap::AlphaSqrtPrice::::insert( - netuid, - U64F64::saturating_from_num(10.0), - ); - - SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); - - // Make sure we are root selling, so we have root alpha divs. - let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); - assert!(root_sell_flag, "Root sell flag should be true"); - - // Run run_coinbase until emissions are drained - step_block(subnet_tempo); - - let old_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); - let miner_stake_before_epoch = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &miner_hotkey, - &miner_coldkey, - netuid, - ); - - // step by one block - step_block(1); - // Verify root alpha divs - let new_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); - // Check that we ARE root selling, i.e. that root alpha divs are changing - assert_ne!( - new_root_alpha_divs, old_root_alpha_divs, - "Root alpha divs should be changing" - ); - assert!( - new_root_alpha_divs > AlphaCurrency::ZERO, - "Root alpha divs should be greater than 0" - ); - - // Run again but with some root stake - step_block(subnet_tempo - 1); - - let miner_uid = Uids::::get(netuid, miner_hotkey).unwrap_or(0); - let miner_incentive: AlphaCurrency = { - let miner_incentive = Incentive::::get(NetUidStorageIndex::from(netuid)) - .get(miner_uid as usize) - .copied(); - - assert!(miner_incentive.is_some()); - - (miner_incentive.unwrap_or_default() as u64).into() - }; - log::info!("Miner incentive: {miner_incentive:?}"); - - let per_block_emission = SubtensorModule::get_block_emission_for_issuance( - SubtensorModule::get_alpha_issuance(netuid).into(), - ) - .unwrap_or(0); - - // Miner emissions - let miner_emission_1: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &miner_hotkey, - &miner_coldkey, - netuid, - ) - .to_u64() - - miner_stake_before_epoch.to_u64(); - - assert_abs_diff_eq!( - miner_emission_1, - U96F32::saturating_from_num(miner_incentive) - .saturating_div(u16::MAX.into()) - .saturating_mul(U96F32::saturating_from_num(per_block_emission)) - .saturating_mul(U96F32::saturating_from_num(subnet_tempo + 1)) - .saturating_mul(U96F32::saturating_from_num(0.45)) // miner cut - .saturating_to_num::(), - epsilon = 1_000_000_u64 - ); - }); + todo!(); + + // new_test_ext(1).execute_with(|| { + // let validator_coldkey = U256::from(1); + // let validator_hotkey = U256::from(2); + // let validator_miner_coldkey = U256::from(3); + // let validator_miner_hotkey = U256::from(4); + // let miner_coldkey = U256::from(5); + // let miner_hotkey = U256::from(6); + // let subnet_tempo = 10; + // let stake: u64 = 100_000_000_000; + // let root_stake: u64 = 200_000_000_000; // 200 TAO + + // // Create root network + // SubtensorModule::set_tao_weight(0); // Start tao weight at 0 + // SubtokenEnabled::::insert(NetUid::ROOT, true); + // NetworksAdded::::insert(NetUid::ROOT, true); + + // // Add network, register hotkeys, and setup network parameters + // let owner_hotkey = U256::from(10); + // let owner_coldkey = U256::from(11); + // let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); + // Tempo::::insert(netuid, 1); + // FirstEmissionBlockNumber::::insert(netuid, 0); + + // // Setup large LPs to prevent slippage + // SubnetTAO::::insert(netuid, TaoCurrency::from(1_000_000_000_000_000)); + // SubnetAlphaIn::::insert(netuid, AlphaCurrency::from(1_000_000_000_000_000)); + + // register_ok_neuron(netuid, validator_hotkey, validator_coldkey, 0); + // register_ok_neuron(netuid, validator_miner_hotkey, validator_miner_coldkey, 1); + // register_ok_neuron(netuid, miner_hotkey, miner_coldkey, 2); + // SubtensorModule::add_balance_to_coldkey_account( + // &validator_coldkey, + // stake + ExistentialDeposit::get(), + // ); + // SubtensorModule::add_balance_to_coldkey_account( + // &validator_miner_coldkey, + // stake + ExistentialDeposit::get(), + // ); + // SubtensorModule::add_balance_to_coldkey_account( + // &miner_coldkey, + // stake + ExistentialDeposit::get(), + // ); + // SubtensorModule::set_weights_set_rate_limit(netuid, 0); + // step_block(subnet_tempo); + // SubnetOwnerCut::::set(u16::MAX / 10); + // // There are two validators and three neurons + // MaxAllowedUids::::set(netuid, 3); + // SubtensorModule::set_max_allowed_validators(netuid, 2); + + // // Setup stakes: + // // Stake from validator + // // Stake from valiminer + // assert_ok!(SubtensorModule::add_stake( + // RuntimeOrigin::signed(validator_coldkey), + // validator_hotkey, + // netuid, + // stake.into() + // )); + // assert_ok!(SubtensorModule::add_stake( + // RuntimeOrigin::signed(validator_miner_coldkey), + // validator_miner_hotkey, + // netuid, + // stake.into() + // )); + + // // Setup YUMA so that it creates emissions + // Weights::::insert(NetUidStorageIndex::from(netuid), 0, vec![(1, 0xFFFF)]); + // Weights::::insert(NetUidStorageIndex::from(netuid), 1, vec![(2, 0xFFFF)]); + // BlockAtRegistration::::set(netuid, 0, 1); + // BlockAtRegistration::::set(netuid, 1, 1); + // BlockAtRegistration::::set(netuid, 2, 1); + // LastUpdate::::set(NetUidStorageIndex::from(netuid), vec![2, 2, 2]); + // Kappa::::set(netuid, u16::MAX / 5); + // ActivityCutoff::::set(netuid, u16::MAX); // makes all stake active + // ValidatorPermit::::insert(netuid, vec![true, true, false]); + + // // Run run_coinbase until emissions are drained + // step_block(subnet_tempo); + + // // Add stake to validator so it has root stake + // SubtensorModule::add_balance_to_coldkey_account(&validator_coldkey, root_stake.into()); + // // init root + // assert_ok!(SubtensorModule::add_stake( + // RuntimeOrigin::signed(validator_coldkey), + // validator_hotkey, + // NetUid::ROOT, + // root_stake.into() + // )); + // // Set tao weight non zero + // SubtensorModule::set_tao_weight(u64::MAX / 10); + + // // Make root sell happen + // // Set moving price > 1.0 + // // Set price > 1.0 + // pallet_subtensor_swap::AlphaSqrtPrice::::insert( + // netuid, + // U64F64::saturating_from_num(10.0), + // ); + + // SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); + + // // Make sure we are root selling, so we have root alpha divs. + // let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); + // assert!(root_sell_flag, "Root sell flag should be true"); + + // // Run run_coinbase until emissions are drained + // step_block(subnet_tempo); + + // let old_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); + // let miner_stake_before_epoch = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + // &miner_hotkey, + // &miner_coldkey, + // netuid, + // ); + + // // step by one block + // step_block(1); + // // Verify root alpha divs + // let new_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); + // // Check that we ARE root selling, i.e. that root alpha divs are changing + // assert_ne!( + // new_root_alpha_divs, old_root_alpha_divs, + // "Root alpha divs should be changing" + // ); + // assert!( + // new_root_alpha_divs > AlphaCurrency::ZERO, + // "Root alpha divs should be greater than 0" + // ); + + // // Run again but with some root stake + // step_block(subnet_tempo - 1); + + // let miner_uid = Uids::::get(netuid, miner_hotkey).unwrap_or(0); + // let miner_incentive: AlphaCurrency = { + // let miner_incentive = Incentive::::get(NetUidStorageIndex::from(netuid)) + // .get(miner_uid as usize) + // .copied(); + + // assert!(miner_incentive.is_some()); + + // (miner_incentive.unwrap_or_default() as u64).into() + // }; + // log::info!("Miner incentive: {miner_incentive:?}"); + + // let per_block_emission = SubtensorModule::get_block_emission_for_issuance( + // SubtensorModule::get_alpha_issuance(netuid).into(), + // ) + // .unwrap_or(0); + + // // Miner emissions + // let miner_emission_1: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + // &miner_hotkey, + // &miner_coldkey, + // netuid, + // ) + // .to_u64() + // - miner_stake_before_epoch.to_u64(); + + // assert_abs_diff_eq!( + // miner_emission_1, + // U96F32::saturating_from_num(miner_incentive) + // .saturating_div(u16::MAX.into()) + // .saturating_mul(U96F32::saturating_from_num(per_block_emission)) + // .saturating_mul(U96F32::saturating_from_num(subnet_tempo + 1)) + // .saturating_mul(U96F32::saturating_from_num(0.45)) // miner cut + // .saturating_to_num::(), + // epsilon = 1_000_000_u64 + // ); + // }); } #[test] @@ -3385,78 +3391,80 @@ fn test_coinbase_subnets_with_no_reg_get_no_emission() { // Tests for the excess TAO condition #[test] fn test_coinbase_subnet_terms_with_alpha_in_gt_alpha_emission() { - new_test_ext(1).execute_with(|| { - let zero = U96F32::saturating_from_num(0); - let netuid0 = add_dynamic_network(&U256::from(1), &U256::from(2)); - mock::setup_reserves( - netuid0, - TaoCurrency::from(1_000_000_000_000_000), - AlphaCurrency::from(1_000_000_000_000_000), - ); - // Initialize swap v3 - Swap::maybe_initialize_v3(netuid0); - - // Set netuid0 to have price tao_emission / price > alpha_emission - let alpha_emission = U96F32::saturating_from_num( - SubtensorModule::get_block_emission_for_issuance( - SubtensorModule::get_alpha_issuance(netuid0).into(), - ) - .unwrap_or(0), - ); - let price_to_set: U64F64 = U64F64::saturating_from_num(0.01); - let price_to_set_fixed: U96F32 = U96F32::saturating_from_num(price_to_set); - let sqrt_price_to_set: U64F64 = sqrt(price_to_set).unwrap(); - - let tao_emission: U96F32 = U96F32::saturating_from_num(alpha_emission) - .saturating_mul(price_to_set_fixed) - .saturating_add(U96F32::saturating_from_num(0.01)); - - // Set the price - pallet_subtensor_swap::AlphaSqrtPrice::::insert(netuid0, sqrt_price_to_set); - // Check the price is set - assert_abs_diff_eq!( - pallet_subtensor_swap::Pallet::::current_alpha_price(netuid0).to_num::(), - price_to_set.to_num::(), - epsilon = 0.001 - ); - - let subnet_emissions = BTreeMap::from([(netuid0, tao_emission)]); - - let (tao_in, alpha_in, alpha_out, excess_tao) = - SubtensorModule::get_subnet_terms(&subnet_emissions); - - // Check our condition is met - assert!(tao_emission / price_to_set_fixed > alpha_emission); - - // alpha_out should be the alpha_emission, always - assert_abs_diff_eq!( - alpha_out[&netuid0].to_num::(), - alpha_emission.to_num::(), - epsilon = 0.01 - ); - - // alpha_in should equal the alpha_emission - assert_abs_diff_eq!( - alpha_in[&netuid0].to_num::(), - alpha_emission.to_num::(), - epsilon = 0.01 - ); - // tao_in should be the alpha_in at the ratio of the price - assert_abs_diff_eq!( - tao_in[&netuid0].to_num::(), - alpha_in[&netuid0] - .saturating_mul(price_to_set_fixed) - .to_num::(), - epsilon = 0.01 - ); - - // excess_tao should be the difference between the tao_emission and the tao_in - assert_abs_diff_eq!( - excess_tao[&netuid0].to_num::(), - tao_emission.to_num::() - tao_in[&netuid0].to_num::(), - epsilon = 0.01 - ); - }); + todo!(); + + // new_test_ext(1).execute_with(|| { + // let zero = U96F32::saturating_from_num(0); + // let netuid0 = add_dynamic_network(&U256::from(1), &U256::from(2)); + // mock::setup_reserves( + // netuid0, + // TaoCurrency::from(1_000_000_000_000_000), + // AlphaCurrency::from(1_000_000_000_000_000), + // ); + // // Initialize swap v3 + // Swap::maybe_initialize_palswap(netuid0); + + // // Set netuid0 to have price tao_emission / price > alpha_emission + // let alpha_emission = U96F32::saturating_from_num( + // SubtensorModule::get_block_emission_for_issuance( + // SubtensorModule::get_alpha_issuance(netuid0).into(), + // ) + // .unwrap_or(0), + // ); + // let price_to_set: U64F64 = U64F64::saturating_from_num(0.01); + // let price_to_set_fixed: U96F32 = U96F32::saturating_from_num(price_to_set); + // let sqrt_price_to_set: U64F64 = sqrt(price_to_set).unwrap(); + + // let tao_emission: U96F32 = U96F32::saturating_from_num(alpha_emission) + // .saturating_mul(price_to_set_fixed) + // .saturating_add(U96F32::saturating_from_num(0.01)); + + // // Set the price + // pallet_subtensor_swap::AlphaSqrtPrice::::insert(netuid0, sqrt_price_to_set); + // // Check the price is set + // assert_abs_diff_eq!( + // pallet_subtensor_swap::Pallet::::current_alpha_price(netuid0).to_num::(), + // price_to_set.to_num::(), + // epsilon = 0.001 + // ); + + // let subnet_emissions = BTreeMap::from([(netuid0, tao_emission)]); + + // let (tao_in, alpha_in, alpha_out, excess_tao) = + // SubtensorModule::get_subnet_terms(&subnet_emissions); + + // // Check our condition is met + // assert!(tao_emission / price_to_set_fixed > alpha_emission); + + // // alpha_out should be the alpha_emission, always + // assert_abs_diff_eq!( + // alpha_out[&netuid0].to_num::(), + // alpha_emission.to_num::(), + // epsilon = 0.01 + // ); + + // // alpha_in should equal the alpha_emission + // assert_abs_diff_eq!( + // alpha_in[&netuid0].to_num::(), + // alpha_emission.to_num::(), + // epsilon = 0.01 + // ); + // // tao_in should be the alpha_in at the ratio of the price + // assert_abs_diff_eq!( + // tao_in[&netuid0].to_num::(), + // alpha_in[&netuid0] + // .saturating_mul(price_to_set_fixed) + // .to_num::(), + // epsilon = 0.01 + // ); + + // // excess_tao should be the difference between the tao_emission and the tao_in + // assert_abs_diff_eq!( + // excess_tao[&netuid0].to_num::(), + // tao_emission.to_num::() - tao_in[&netuid0].to_num::(), + // epsilon = 0.01 + // ); + // }); } #[test] @@ -3470,7 +3478,7 @@ fn test_coinbase_subnet_terms_with_alpha_in_lte_alpha_emission() { AlphaCurrency::from(1_000_000_000_000_000), ); // Initialize swap v3 - Swap::maybe_initialize_v3(netuid0); + Swap::maybe_initialize_palswap(netuid0); let alpha_emission = U96F32::saturating_from_num( SubtensorModule::get_block_emission_for_issuance( @@ -3480,7 +3488,7 @@ fn test_coinbase_subnet_terms_with_alpha_in_lte_alpha_emission() { ); let tao_emission = U96F32::saturating_from_num(34566756_u64); - let price: U96F32 = Swap::current_alpha_price(netuid0); + let price: U96F32 = U96F32::saturating_from_num(Swap::current_alpha_price(netuid0)); let subnet_emissions = BTreeMap::from([(netuid0, tao_emission)]); @@ -3533,7 +3541,7 @@ fn test_coinbase_inject_and_maybe_swap_does_not_skew_reserves() { AlphaCurrency::from(1_000_000_000_000_000), ); // Initialize swap v3 - Swap::maybe_initialize_v3(netuid0); + Swap::maybe_initialize_palswap(netuid0); let tao_in = BTreeMap::from([(netuid0, U96F32::saturating_from_num(123))]); let alpha_in = BTreeMap::from([(netuid0, U96F32::saturating_from_num(456))]); @@ -3667,7 +3675,7 @@ fn test_coinbase_emit_to_subnets_with_no_root_sell() { AlphaCurrency::from(1_000_000_000_000_000), ); // Initialize swap v3 - Swap::maybe_initialize_v3(netuid0); + Swap::maybe_initialize_palswap(netuid0); let tao_emission = U96F32::saturating_from_num(12345678); let subnet_emissions = BTreeMap::from([(netuid0, tao_emission)]); @@ -3681,7 +3689,7 @@ fn test_coinbase_emit_to_subnets_with_no_root_sell() { ) .unwrap_or(0), ); - let price: U96F32 = Swap::current_alpha_price(netuid0); + let price: U96F32 = U96F32::saturating_from_num(Swap::current_alpha_price(netuid0)); let (tao_in, alpha_in, alpha_out, excess_tao) = SubtensorModule::get_subnet_terms(&subnet_emissions); // Based on the price, we should have NO excess TAO @@ -3758,7 +3766,7 @@ fn test_coinbase_emit_to_subnets_with_root_sell() { AlphaCurrency::from(1_000_000_000_000_000), ); // Initialize swap v3 - Swap::maybe_initialize_v3(netuid0); + Swap::maybe_initialize_palswap(netuid0); let tao_emission = U96F32::saturating_from_num(12345678); let subnet_emissions = BTreeMap::from([(netuid0, tao_emission)]); @@ -3772,7 +3780,7 @@ fn test_coinbase_emit_to_subnets_with_root_sell() { ) .unwrap_or(0), ); - let price: U96F32 = Swap::current_alpha_price(netuid0); + let price: U96F32 = U96F32::saturating_from_num(Swap::current_alpha_price(netuid0)); let (tao_in, alpha_in, alpha_out, excess_tao) = SubtensorModule::get_subnet_terms(&subnet_emissions); // Based on the price, we should have NO excess TAO @@ -3840,82 +3848,84 @@ fn test_coinbase_emit_to_subnets_with_root_sell() { // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_pending_emission_start_call_not_done --exact --show-output --nocapture #[test] fn test_pending_emission_start_call_not_done() { - new_test_ext(1).execute_with(|| { - let validator_coldkey = U256::from(1); - let validator_hotkey = U256::from(2); - let subnet_tempo = 10; - let stake: u64 = 100_000_000_000; - let root_stake: u64 = 200_000_000_000; // 200 TAO - - // Create root network - NetworksAdded::::insert(NetUid::ROOT, true); - // enabled root - SubtokenEnabled::::insert(NetUid::ROOT, true); - - // Add network, register hotkeys, and setup network parameters - let owner_hotkey = U256::from(10); - let owner_coldkey = U256::from(11); - let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); - // Remove FirstEmissionBlockNumber - FirstEmissionBlockNumber::::remove(netuid); - Tempo::::insert(netuid, subnet_tempo); - - register_ok_neuron(netuid, validator_hotkey, validator_coldkey, 0); - SubtensorModule::add_balance_to_coldkey_account( - &validator_coldkey, - stake + ExistentialDeposit::get(), - ); - SubtensorModule::set_weights_set_rate_limit(netuid, 0); - step_block(subnet_tempo); - SubnetOwnerCut::::set(u16::MAX / 10); - // There are two validators and three neurons - MaxAllowedUids::::set(netuid, 3); - SubtensorModule::set_max_allowed_validators(netuid, 2); - - // Add stake to validator so it has root stake - SubtensorModule::add_balance_to_coldkey_account(&validator_coldkey, root_stake.into()); - // init root - assert_ok!(SubtensorModule::add_stake( - RuntimeOrigin::signed(validator_coldkey), - validator_hotkey, - NetUid::ROOT, - root_stake.into() - )); - // Set tao weight non zero - SubtensorModule::set_tao_weight(u64::MAX / 10); - - // Make root sell happen - // Set moving price > 1.0 - // Set price > 1.0 - pallet_subtensor_swap::AlphaSqrtPrice::::insert( - netuid, - U64F64::saturating_from_num(10.0), - ); - - SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); - - // Make sure we are root selling, so we have root alpha divs. - let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); - assert!(root_sell_flag, "Root sell flag should be true"); - - // !!! Check that the subnet FirstEmissionBlockNumber is None -- no entry - assert!(FirstEmissionBlockNumber::::get(netuid).is_none()); - - // Run run_coinbase until emissions are accumulated - step_block(subnet_tempo - 2); - - // Verify that all pending emissions are zero - assert_eq!( - PendingServerEmission::::get(netuid), - AlphaCurrency::ZERO - ); - assert_eq!( - PendingValidatorEmission::::get(netuid), - AlphaCurrency::ZERO - ); - assert_eq!( - PendingRootAlphaDivs::::get(netuid), - AlphaCurrency::ZERO - ); - }); + todo!(); + + // new_test_ext(1).execute_with(|| { + // let validator_coldkey = U256::from(1); + // let validator_hotkey = U256::from(2); + // let subnet_tempo = 10; + // let stake: u64 = 100_000_000_000; + // let root_stake: u64 = 200_000_000_000; // 200 TAO + + // // Create root network + // NetworksAdded::::insert(NetUid::ROOT, true); + // // enabled root + // SubtokenEnabled::::insert(NetUid::ROOT, true); + + // // Add network, register hotkeys, and setup network parameters + // let owner_hotkey = U256::from(10); + // let owner_coldkey = U256::from(11); + // let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); + // // Remove FirstEmissionBlockNumber + // FirstEmissionBlockNumber::::remove(netuid); + // Tempo::::insert(netuid, subnet_tempo); + + // register_ok_neuron(netuid, validator_hotkey, validator_coldkey, 0); + // SubtensorModule::add_balance_to_coldkey_account( + // &validator_coldkey, + // stake + ExistentialDeposit::get(), + // ); + // SubtensorModule::set_weights_set_rate_limit(netuid, 0); + // step_block(subnet_tempo); + // SubnetOwnerCut::::set(u16::MAX / 10); + // // There are two validators and three neurons + // MaxAllowedUids::::set(netuid, 3); + // SubtensorModule::set_max_allowed_validators(netuid, 2); + + // // Add stake to validator so it has root stake + // SubtensorModule::add_balance_to_coldkey_account(&validator_coldkey, root_stake.into()); + // // init root + // assert_ok!(SubtensorModule::add_stake( + // RuntimeOrigin::signed(validator_coldkey), + // validator_hotkey, + // NetUid::ROOT, + // root_stake.into() + // )); + // // Set tao weight non zero + // SubtensorModule::set_tao_weight(u64::MAX / 10); + + // // Make root sell happen + // // Set moving price > 1.0 + // // Set price > 1.0 + // pallet_subtensor_swap::AlphaSqrtPrice::::insert( + // netuid, + // U64F64::saturating_from_num(10.0), + // ); + + // SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); + + // // Make sure we are root selling, so we have root alpha divs. + // let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); + // assert!(root_sell_flag, "Root sell flag should be true"); + + // // !!! Check that the subnet FirstEmissionBlockNumber is None -- no entry + // assert!(FirstEmissionBlockNumber::::get(netuid).is_none()); + + // // Run run_coinbase until emissions are accumulated + // step_block(subnet_tempo - 2); + + // // Verify that all pending emissions are zero + // assert_eq!( + // PendingServerEmission::::get(netuid), + // AlphaCurrency::ZERO + // ); + // assert_eq!( + // PendingValidatorEmission::::get(netuid), + // AlphaCurrency::ZERO + // ); + // assert_eq!( + // PendingRootAlphaDivs::::get(netuid), + // AlphaCurrency::ZERO + // ); + // }); } diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index 8d439e4e8f..64aeabc75c 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -2542,147 +2542,149 @@ fn test_migrate_reset_unactive_sn_get_unactive_netuids() { #[test] fn test_migrate_reset_unactive_sn() { - new_test_ext(1).execute_with(|| { - let (active_netuids, inactive_netuids) = do_setup_unactive_sn(); - - let initial_tao = Pallet::::get_network_min_lock(); - let initial_alpha: AlphaCurrency = initial_tao.to_u64().into(); - - // Run the migration - let w = crate::migrations::migrate_reset_unactive_sn::migrate_reset_unactive_sn::(); - assert!(!w.is_zero(), "weight must be non-zero"); - - // Verify the results - for netuid in &inactive_netuids { - let actual_tao_lock_amount = SubnetLocked::::get(*netuid); - let actual_tao_lock_amount_less_pool_tao = if (actual_tao_lock_amount < initial_tao) { - TaoCurrency::ZERO - } else { - actual_tao_lock_amount - initial_tao - }; - assert_eq!( - PendingServerEmission::::get(netuid), - AlphaCurrency::ZERO - ); - assert_eq!( - PendingValidatorEmission::::get(netuid), - AlphaCurrency::ZERO - ); - assert_eq!( - PendingRootAlphaDivs::::get(netuid), - AlphaCurrency::ZERO - ); - assert_eq!( - // not modified - RAORecycledForRegistration::::get(netuid), - actual_tao_lock_amount_less_pool_tao - ); - assert!(pallet_subtensor_swap::AlphaSqrtPrice::::contains_key( - *netuid - )); - assert_eq!(PendingOwnerCut::::get(netuid), AlphaCurrency::ZERO); - assert_ne!(SubnetTAO::::get(netuid), initial_tao); - assert_ne!(SubnetAlphaIn::::get(netuid), initial_alpha); - assert_ne!(SubnetAlphaOut::::get(netuid), AlphaCurrency::ZERO); - assert_eq!(SubnetTaoInEmission::::get(netuid), TaoCurrency::ZERO); - assert_eq!( - SubnetAlphaInEmission::::get(netuid), - AlphaCurrency::ZERO - ); - assert_eq!( - SubnetAlphaOutEmission::::get(netuid), - AlphaCurrency::ZERO - ); - assert_ne!(SubnetVolume::::get(netuid), 0u128); - for hotkey in 0..10 { - let hk = U256::from(hotkey); - assert_ne!( - TotalHotkeyAlpha::::get(hk, netuid), - AlphaCurrency::ZERO - ); - assert_ne!( - TotalHotkeyShares::::get(hk, netuid), - U64F64::from_num(0.0) - ); - assert_ne!( - TotalHotkeyAlphaLastEpoch::::get(hk, netuid), - AlphaCurrency::ZERO - ); - assert_ne!(RootClaimable::::get(hk).get(netuid), None); - for coldkey in 0..10 { - let ck = U256::from(coldkey); - assert_ne!(Alpha::::get((hk, ck, netuid)), U64F64::from_num(0.0)); - assert_ne!(RootClaimed::::get((netuid, hk, ck)), 0u128); - } - } - - // Don't touch SubnetLocked - assert_ne!(SubnetLocked::::get(netuid), TaoCurrency::ZERO); - } - - // !!! Make sure the active subnets were not reset - for netuid in &active_netuids { - let actual_tao_lock_amount = SubnetLocked::::get(*netuid); - let actual_tao_lock_amount_less_pool_tao = actual_tao_lock_amount - initial_tao; - assert_ne!( - PendingServerEmission::::get(netuid), - AlphaCurrency::ZERO - ); - assert_ne!( - PendingValidatorEmission::::get(netuid), - AlphaCurrency::ZERO - ); - assert_ne!( - PendingRootAlphaDivs::::get(netuid), - AlphaCurrency::ZERO - ); - assert_eq!( - // not modified - RAORecycledForRegistration::::get(netuid), - actual_tao_lock_amount_less_pool_tao - ); - assert_ne!(SubnetTaoInEmission::::get(netuid), TaoCurrency::ZERO); - assert_ne!( - SubnetAlphaInEmission::::get(netuid), - AlphaCurrency::ZERO - ); - assert_ne!( - SubnetAlphaOutEmission::::get(netuid), - AlphaCurrency::ZERO - ); - assert!(pallet_subtensor_swap::AlphaSqrtPrice::::contains_key( - *netuid - )); - assert_ne!(PendingOwnerCut::::get(netuid), AlphaCurrency::ZERO); - assert_ne!(SubnetTAO::::get(netuid), initial_tao); - assert_ne!(SubnetAlphaIn::::get(netuid), initial_alpha); - assert_ne!(SubnetAlphaOut::::get(netuid), AlphaCurrency::ZERO); - assert_ne!(SubnetVolume::::get(netuid), 0u128); - for hotkey in 0..10 { - let hk = U256::from(hotkey); - assert_ne!( - TotalHotkeyAlpha::::get(hk, netuid), - AlphaCurrency::ZERO - ); - assert_ne!( - TotalHotkeyShares::::get(hk, netuid), - U64F64::from_num(0.0) - ); - assert_ne!( - TotalHotkeyAlphaLastEpoch::::get(hk, netuid), - AlphaCurrency::ZERO - ); - assert!(RootClaimable::::get(hk).contains_key(netuid)); - for coldkey in 0..10 { - let ck = U256::from(coldkey); - assert_ne!(Alpha::::get((hk, ck, netuid)), U64F64::from_num(0.0)); - assert_ne!(RootClaimed::::get((netuid, hk, ck)), 0u128); - } - } - // Don't touch SubnetLocked - assert_ne!(SubnetLocked::::get(netuid), TaoCurrency::ZERO); - } - }); + todo!(); + + // new_test_ext(1).execute_with(|| { + // let (active_netuids, inactive_netuids) = do_setup_unactive_sn(); + + // let initial_tao = Pallet::::get_network_min_lock(); + // let initial_alpha: AlphaCurrency = initial_tao.to_u64().into(); + + // // Run the migration + // let w = crate::migrations::migrate_reset_unactive_sn::migrate_reset_unactive_sn::(); + // assert!(!w.is_zero(), "weight must be non-zero"); + + // // Verify the results + // for netuid in &inactive_netuids { + // let actual_tao_lock_amount = SubnetLocked::::get(*netuid); + // let actual_tao_lock_amount_less_pool_tao = if (actual_tao_lock_amount < initial_tao) { + // TaoCurrency::ZERO + // } else { + // actual_tao_lock_amount - initial_tao + // }; + // assert_eq!( + // PendingServerEmission::::get(netuid), + // AlphaCurrency::ZERO + // ); + // assert_eq!( + // PendingValidatorEmission::::get(netuid), + // AlphaCurrency::ZERO + // ); + // assert_eq!( + // PendingRootAlphaDivs::::get(netuid), + // AlphaCurrency::ZERO + // ); + // assert_eq!( + // // not modified + // RAORecycledForRegistration::::get(netuid), + // actual_tao_lock_amount_less_pool_tao + // ); + // assert!(pallet_subtensor_swap::AlphaSqrtPrice::::contains_key( + // *netuid + // )); + // assert_eq!(PendingOwnerCut::::get(netuid), AlphaCurrency::ZERO); + // assert_ne!(SubnetTAO::::get(netuid), initial_tao); + // assert_ne!(SubnetAlphaIn::::get(netuid), initial_alpha); + // assert_ne!(SubnetAlphaOut::::get(netuid), AlphaCurrency::ZERO); + // assert_eq!(SubnetTaoInEmission::::get(netuid), TaoCurrency::ZERO); + // assert_eq!( + // SubnetAlphaInEmission::::get(netuid), + // AlphaCurrency::ZERO + // ); + // assert_eq!( + // SubnetAlphaOutEmission::::get(netuid), + // AlphaCurrency::ZERO + // ); + // assert_ne!(SubnetVolume::::get(netuid), 0u128); + // for hotkey in 0..10 { + // let hk = U256::from(hotkey); + // assert_ne!( + // TotalHotkeyAlpha::::get(hk, netuid), + // AlphaCurrency::ZERO + // ); + // assert_ne!( + // TotalHotkeyShares::::get(hk, netuid), + // U64F64::from_num(0.0) + // ); + // assert_ne!( + // TotalHotkeyAlphaLastEpoch::::get(hk, netuid), + // AlphaCurrency::ZERO + // ); + // assert_ne!(RootClaimable::::get(hk).get(netuid), None); + // for coldkey in 0..10 { + // let ck = U256::from(coldkey); + // assert_ne!(Alpha::::get((hk, ck, netuid)), U64F64::from_num(0.0)); + // assert_ne!(RootClaimed::::get((netuid, hk, ck)), 0u128); + // } + // } + + // // Don't touch SubnetLocked + // assert_ne!(SubnetLocked::::get(netuid), TaoCurrency::ZERO); + // } + + // // !!! Make sure the active subnets were not reset + // for netuid in &active_netuids { + // let actual_tao_lock_amount = SubnetLocked::::get(*netuid); + // let actual_tao_lock_amount_less_pool_tao = actual_tao_lock_amount - initial_tao; + // assert_ne!( + // PendingServerEmission::::get(netuid), + // AlphaCurrency::ZERO + // ); + // assert_ne!( + // PendingValidatorEmission::::get(netuid), + // AlphaCurrency::ZERO + // ); + // assert_ne!( + // PendingRootAlphaDivs::::get(netuid), + // AlphaCurrency::ZERO + // ); + // assert_eq!( + // // not modified + // RAORecycledForRegistration::::get(netuid), + // actual_tao_lock_amount_less_pool_tao + // ); + // assert_ne!(SubnetTaoInEmission::::get(netuid), TaoCurrency::ZERO); + // assert_ne!( + // SubnetAlphaInEmission::::get(netuid), + // AlphaCurrency::ZERO + // ); + // assert_ne!( + // SubnetAlphaOutEmission::::get(netuid), + // AlphaCurrency::ZERO + // ); + // assert!(pallet_subtensor_swap::AlphaSqrtPrice::::contains_key( + // *netuid + // )); + // assert_ne!(PendingOwnerCut::::get(netuid), AlphaCurrency::ZERO); + // assert_ne!(SubnetTAO::::get(netuid), initial_tao); + // assert_ne!(SubnetAlphaIn::::get(netuid), initial_alpha); + // assert_ne!(SubnetAlphaOut::::get(netuid), AlphaCurrency::ZERO); + // assert_ne!(SubnetVolume::::get(netuid), 0u128); + // for hotkey in 0..10 { + // let hk = U256::from(hotkey); + // assert_ne!( + // TotalHotkeyAlpha::::get(hk, netuid), + // AlphaCurrency::ZERO + // ); + // assert_ne!( + // TotalHotkeyShares::::get(hk, netuid), + // U64F64::from_num(0.0) + // ); + // assert_ne!( + // TotalHotkeyAlphaLastEpoch::::get(hk, netuid), + // AlphaCurrency::ZERO + // ); + // assert!(RootClaimable::::get(hk).contains_key(netuid)); + // for coldkey in 0..10 { + // let ck = U256::from(coldkey); + // assert_ne!(Alpha::::get((hk, ck, netuid)), U64F64::from_num(0.0)); + // assert_ne!(RootClaimed::::get((netuid, hk, ck)), 0u128); + // } + // } + // // Don't touch SubnetLocked + // assert_ne!(SubnetLocked::::get(netuid), TaoCurrency::ZERO); + // } + // }); } #[test] diff --git a/pallets/subtensor/src/tests/move_stake.rs b/pallets/subtensor/src/tests/move_stake.rs index dfd9927da4..419ecedbc3 100644 --- a/pallets/subtensor/src/tests/move_stake.rs +++ b/pallets/subtensor/src/tests/move_stake.rs @@ -620,7 +620,7 @@ fn test_do_move_event_emission() { // Move stake and capture events System::reset_events(); let current_price = - ::SwapInterface::current_alpha_price(netuid.into()); + U96F32::from_num(::SwapInterface::current_alpha_price(netuid.into())); let tao_equivalent = (current_price * U96F32::from_num(alpha)).to_num::(); // no fee conversion assert_ok!(SubtensorModule::do_move_stake( RuntimeOrigin::signed(coldkey), diff --git a/pallets/subtensor/src/tests/networks.rs b/pallets/subtensor/src/tests/networks.rs index 60637e0b1f..d02afb522a 100644 --- a/pallets/subtensor/src/tests/networks.rs +++ b/pallets/subtensor/src/tests/networks.rs @@ -6,10 +6,15 @@ use crate::*; use frame_support::{assert_err, assert_ok}; use frame_system::Config; use sp_core::U256; -use sp_std::collections::{btree_map::BTreeMap, vec_deque::VecDeque}; +use sp_std::collections::{ + //btree_map::BTreeMap, +vec_deque::VecDeque}; use substrate_fixed::types::{I96F32, U64F64, U96F32}; use subtensor_runtime_common::{MechId, NetUidStorageIndex, TaoCurrency}; -use subtensor_swap_interface::{Order, SwapHandler}; +use subtensor_swap_interface::{ + //Order, + SwapHandler +}; #[test] fn test_registration_ok() { @@ -248,7 +253,7 @@ fn dissolve_owner_cut_refund_logic() { // Use the current alpha price to estimate the TAO equivalent. let owner_emission_tao = { let price: U96F32 = - ::SwapInterface::current_alpha_price(net.into()); + U96F32::from_num(::SwapInterface::current_alpha_price(net.into())); U96F32::from_num(owner_alpha_u64) .saturating_mul(price) .floor() @@ -899,7 +904,7 @@ fn destroy_alpha_out_many_stakers_complex_distribution() { let owner_emission_tao: u64 = { // Fallback matches the pallet's fallback let price: U96F32 = - ::SwapInterface::current_alpha_price(netuid.into()); + U96F32::from_num(::SwapInterface::current_alpha_price(netuid.into())); U96F32::from_num(owner_alpha_u64) .saturating_mul(price) .floor() @@ -979,7 +984,7 @@ fn destroy_alpha_out_refund_gating_by_registration_block() { let owner_emission_tao_u64 = { let price: U96F32 = - ::SwapInterface::current_alpha_price(netuid.into()); + U96F32::from_num(::SwapInterface::current_alpha_price(netuid.into())); U96F32::from_num(owner_alpha_u64) .saturating_mul(price) .floor() @@ -1769,450 +1774,452 @@ fn test_tempo_greater_than_weight_set_rate_limit() { #[allow(clippy::indexing_slicing)] #[test] fn massive_dissolve_refund_and_reregistration_flow_is_lossless_and_cleans_state() { - new_test_ext(0).execute_with(|| { - // ──────────────────────────────────────────────────────────────────── - // 0) Constants and helpers (distinct hotkeys & coldkeys) - // ──────────────────────────────────────────────────────────────────── - const NUM_NETS: usize = 4; - - // Six LP coldkeys - let cold_lps: [U256; 6] = [ - U256::from(3001), - U256::from(3002), - U256::from(3003), - U256::from(3004), - U256::from(3005), - U256::from(3006), - ]; - - // For each coldkey, define two DISTINCT hotkeys it owns. - let mut cold_to_hots: BTreeMap = BTreeMap::new(); - for &c in cold_lps.iter() { - let h1 = U256::from(c.low_u64().saturating_add(100_000)); - let h2 = U256::from(c.low_u64().saturating_add(200_000)); - cold_to_hots.insert(c, [h1, h2]); - } - - // Distinct τ pot sizes per net. - let pots: [u64; NUM_NETS] = [12_345, 23_456, 34_567, 45_678]; - - let lp_sets_per_net: [&[U256]; NUM_NETS] = [ - &cold_lps[0..4], // net0: A,B,C,D - &cold_lps[2..6], // net1: C,D,E,F - &cold_lps[0..6], // net2: A..F - &cold_lps[1..5], // net3: B,C,D,E - ]; - - // Multiple bands/sizes → many positions per cold across nets, using mixed hotkeys. - let bands: [i32; 3] = [5, 13, 30]; - let liqs: [u64; 3] = [400_000, 700_000, 1_100_000]; - - // Helper: add a V3 position via a (hot, cold) pair. - let add_pos = |net: NetUid, hot: U256, cold: U256, band: i32, liq: u64| { - let ct = pallet_subtensor_swap::CurrentTick::::get(net); - let lo = ct.saturating_sub(band); - let hi = ct.saturating_add(band); - assert_ok!(pallet_subtensor_swap::Pallet::::add_liquidity( - RuntimeOrigin::signed(cold), - hot, - net, - lo, - hi, - liq - )); - }; - - // ──────────────────────────────────────────────────────────────────── - // 1) Create many subnets, enable V3, fix price at tick=0 (sqrt≈1) - // ──────────────────────────────────────────────────────────────────── - let mut nets: Vec = Vec::new(); - for i in 0..NUM_NETS { - let owner_hot = U256::from(10_000 + (i as u64)); - let owner_cold = U256::from(20_000 + (i as u64)); - let net = add_dynamic_network(&owner_hot, &owner_cold); - SubtensorModule::set_max_registrations_per_block(net, 1_000u16); - SubtensorModule::set_target_registrations_per_interval(net, 1_000u16); - Emission::::insert(net, Vec::::new()); - SubtensorModule::set_subnet_locked_balance(net, TaoCurrency::from(0)); - - assert_ok!( - pallet_subtensor_swap::Pallet::::toggle_user_liquidity( - RuntimeOrigin::root(), - net, - true - ) - ); - - // Price/tick pinned so LP math stays stable (sqrt(1)). - let ct0 = pallet_subtensor_swap::tick::TickIndex::new_unchecked(0); - let sqrt1 = ct0.try_to_sqrt_price().expect("sqrt(1) price"); - pallet_subtensor_swap::CurrentTick::::set(net, ct0); - pallet_subtensor_swap::AlphaSqrtPrice::::set(net, sqrt1); - - nets.push(net); - } - - // Map net → index for quick lookups. - let mut net_index: BTreeMap = BTreeMap::new(); - for (i, &n) in nets.iter().enumerate() { - net_index.insert(n, i); - } - - // ──────────────────────────────────────────────────────────────────── - // 2) Pre-create a handful of small (hot, cold) pairs so accounts exist - // ──────────────────────────────────────────────────────────────────── - for id in 0u64..10 { - let cold_acc = U256::from(1_000_000 + id); - let hot_acc = U256::from(2_000_000 + id); - for &net in nets.iter() { - register_ok_neuron(net, hot_acc, cold_acc, 100_000 + id); - } - } - - // ──────────────────────────────────────────────────────────────────── - // 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); - } - - // τ balances before LP adds (after staking): - let mut tao_before: BTreeMap = BTreeMap::new(); - - // Ordered α snapshot per net at **pair granularity** (pre‑LP): - let mut alpha_pairs_per_net: BTreeMap> = BTreeMap::new(); - - // Register both hotkeys for each participating cold on each net and stake τ→α. - for (ni, &net) in nets.iter().enumerate() { - let participants = lp_sets_per_net[ni]; - for &cold in participants.iter() { - let [hot1, hot2] = cold_to_hots[&cold]; - - // Ensure (hot, cold) neurons exist on this net. - register_ok_neuron( - net, - hot1, - cold, - (ni as u64) * 10_000 + (hot1.low_u64() % 10_000), - ); - register_ok_neuron( - net, - hot2, - cold, - (ni as u64) * 10_000 + (hot2.low_u64() % 10_000) + 1, - ); - - // Stake τ (split across the two hotkeys). - let base: u64 = - 5_000_000 + ((ni as u64) * 1_000_000) + ((cold.low_u64() % 10) * 250_000); - let stake1: u64 = base.saturating_mul(3) / 5; // 60% - let stake2: u64 = base.saturating_sub(stake1); // 40% - - assert_ok!(SubtensorModule::do_add_stake( - RuntimeOrigin::signed(cold), - hot1, - net, - stake1.into() - )); - assert_ok!(SubtensorModule::do_add_stake( - RuntimeOrigin::signed(cold), - hot2, - net, - stake2.into() - )); - } - } - - // Record τ balances now (post‑stake, pre‑LP). - for &cold in cold_lps.iter() { - tao_before.insert(cold, SubtensorModule::get_coldkey_balance(&cold)); - } - - // Capture **pair‑level** α snapshot per net (pre‑LP). - for ((hot, cold, net), amt) in Alpha::::iter() { - if let Some(&ni) = net_index.get(&net) { - if lp_sets_per_net[ni].contains(&cold) { - let a: u128 = amt.saturating_to_num(); - if a > 0 { - alpha_pairs_per_net - .entry(net) - .or_default() - .push(((hot, cold), a)); - } - } - } - } - - // ──────────────────────────────────────────────────────────────────── - // 4) Add many V3 positions per cold across nets, alternating hotkeys - // ──────────────────────────────────────────────────────────────────── - for (ni, &net) in nets.iter().enumerate() { - let participants = lp_sets_per_net[ni]; - for (pi, &cold) in participants.iter().enumerate() { - let [hot1, hot2] = cold_to_hots[&cold]; - let hots = [hot1, hot2]; - for k in 0..3 { - let band = bands[(pi + k) % bands.len()]; - let liq = liqs[(ni + k) % liqs.len()]; - let hot = hots[k % hots.len()]; - add_pos(net, hot, cold, band, liq); - } - } - } - - // Snapshot τ balances AFTER LP adds (to measure actual principal debit). - let mut tao_after_adds: BTreeMap = BTreeMap::new(); - for &cold in cold_lps.iter() { - tao_after_adds.insert(cold, SubtensorModule::get_coldkey_balance(&cold)); - } - - // ──────────────────────────────────────────────────────────────────── - // 5) Compute Hamilton-apportionment BASE shares per cold and total leftover - // from the **pair-level** pre‑LP α snapshot; also count pairs per cold. - // ──────────────────────────────────────────────────────────────────── - let mut base_share_cold: BTreeMap = - cold_lps.iter().copied().map(|c| (c, 0_u64)).collect(); - let mut pair_count_cold: BTreeMap = - cold_lps.iter().copied().map(|c| (c, 0_u32)).collect(); - - let mut leftover_total: u64 = 0; - - for (ni, &net) in nets.iter().enumerate() { - let pot = pots[ni]; - let pairs = alpha_pairs_per_net.get(&net).cloned().unwrap_or_default(); - if pot == 0 || pairs.is_empty() { - continue; - } - let total_alpha: u128 = pairs.iter().map(|(_, a)| *a).sum(); - if total_alpha == 0 { - continue; - } - - let mut base_sum_net: u64 = 0; - for ((_, cold), a) in pairs.iter().copied() { - // quota = a * pot / total_alpha - let prod: u128 = a.saturating_mul(pot as u128); - let base: u64 = (prod / total_alpha) as u64; - base_sum_net = base_sum_net.saturating_add(base); - *base_share_cold.entry(cold).or_default() = - base_share_cold[&cold].saturating_add(base); - *pair_count_cold.entry(cold).or_default() += 1; - } - let leftover_net = pot.saturating_sub(base_sum_net); - leftover_total = leftover_total.saturating_add(leftover_net); - } - - // ──────────────────────────────────────────────────────────────────── - // 6) Seed τ pots and dissolve *all* networks (liquidates LPs + refunds) - // ──────────────────────────────────────────────────────────────────── - for (ni, &net) in nets.iter().enumerate() { - SubnetTAO::::insert(net, TaoCurrency::from(pots[ni])); - } - for &net in nets.iter() { - assert_ok!(SubtensorModule::do_dissolve_network(net)); - } - - // ──────────────────────────────────────────────────────────────────── - // 7) Assertions: τ balances, α gone, nets removed, swap state clean - // (Hamilton invariants enforced at cold-level without relying on tie-break) - // ──────────────────────────────────────────────────────────────────── - // Collect actual pot credits per cold (principal cancels out against adds when comparing before→after). - let mut actual_pot_cold: BTreeMap = - cold_lps.iter().copied().map(|c| (c, 0_u64)).collect(); - for &cold in cold_lps.iter() { - let before = tao_before[&cold]; - let after = SubtensorModule::get_coldkey_balance(&cold); - actual_pot_cold.insert(cold, after.saturating_sub(before)); - } - - // (a) Sum of actual pot credits equals total pots. - let total_actual: u64 = actual_pot_cold.values().copied().sum(); - let total_pots: u64 = pots.iter().copied().sum(); - assert_eq!( - total_actual, total_pots, - "total τ pot credited across colds must equal sum of pots" - ); - - // (b) Each cold’s pot is within Hamilton bounds: base ≤ actual ≤ base + #pairs. - let mut extra_accum: u64 = 0; - for &cold in cold_lps.iter() { - let base = *base_share_cold.get(&cold).unwrap_or(&0); - let pairs = *pair_count_cold.get(&cold).unwrap_or(&0) as u64; - let actual = *actual_pot_cold.get(&cold).unwrap_or(&0); - - assert!( - actual >= base, - "cold {cold:?} actual pot {actual} is below base {base}" - ); - assert!( - actual <= base.saturating_add(pairs), - "cold {cold:?} actual pot {actual} exceeds base + pairs ({base} + {pairs})" - ); - - extra_accum = extra_accum.saturating_add(actual.saturating_sub(base)); - } - - // (c) The total “extra beyond base” equals the computed leftover_total across nets. - assert_eq!( - extra_accum, leftover_total, - "sum of extras beyond base must equal total leftover" - ); - - // (d) τ principal was fully refunded (compare after_adds → after). - for &cold in cold_lps.iter() { - let before = tao_before[&cold]; - let mid = tao_after_adds[&cold]; - let after = SubtensorModule::get_coldkey_balance(&cold); - let principal_actual = before.saturating_sub(mid); - let actual_pot = after.saturating_sub(before); - assert_eq!( - after.saturating_sub(mid), - principal_actual.saturating_add(actual_pot), - "cold {cold:?} τ balance incorrect vs 'after_adds'" - ); - } - - // For each dissolved net, check α ledgers gone, network removed, and swap state clean. - for &net in nets.iter() { - assert!( - Alpha::::iter().all(|((_h, _c, n), _)| n != net), - "alpha ledger not fully cleared for net {net:?}" - ); - assert!( - !SubtensorModule::if_subnet_exist(net), - "subnet {net:?} still exists" - ); - assert!( - pallet_subtensor_swap::Ticks::::iter_prefix(net) - .next() - .is_none(), - "ticks not cleared for net {net:?}" - ); - assert!( - !pallet_subtensor_swap::Positions::::iter() - .any(|((n, _owner, _pid), _)| n == net), - "swap positions not fully cleared for net {net:?}" - ); - assert_eq!( - pallet_subtensor_swap::FeeGlobalTao::::get(net).saturating_to_num::(), - 0, - "FeeGlobalTao nonzero for net {net:?}" - ); - assert_eq!( - pallet_subtensor_swap::FeeGlobalAlpha::::get(net).saturating_to_num::(), - 0, - "FeeGlobalAlpha nonzero for net {net:?}" - ); - assert_eq!( - pallet_subtensor_swap::CurrentLiquidity::::get(net), - 0, - "CurrentLiquidity not zero for net {net:?}" - ); - assert!( - !pallet_subtensor_swap::SwapV3Initialized::::get(net), - "SwapV3Initialized still set" - ); - assert!( - !pallet_subtensor_swap::EnabledUserLiquidity::::get(net), - "EnabledUserLiquidity still set" - ); - assert!( - pallet_subtensor_swap::TickIndexBitmapWords::::iter_prefix((net,)) - .next() - .is_none(), - "TickIndexBitmapWords not cleared for net {net:?}" - ); - } - - // ──────────────────────────────────────────────────────────────────── - // 8) Re-register a fresh subnet and re‑stake using the pallet’s min rule - // Assert αΔ equals the sim-swap result for the exact τ staked. - // ──────────────────────────────────────────────────────────────────── - let new_owner_hot = U256::from(99_000); - let new_owner_cold = U256::from(99_001); - let net_new = add_dynamic_network(&new_owner_hot, &new_owner_cold); - SubtensorModule::set_max_registrations_per_block(net_new, 1_000u16); - SubtensorModule::set_target_registrations_per_interval(net_new, 1_000u16); - Emission::::insert(net_new, Vec::::new()); - SubtensorModule::set_subnet_locked_balance(net_new, TaoCurrency::from(0)); - - assert_ok!( - pallet_subtensor_swap::Pallet::::toggle_user_liquidity( - RuntimeOrigin::root(), - net_new, - true - ) - ); - let ct0 = pallet_subtensor_swap::tick::TickIndex::new_unchecked(0); - let sqrt1 = ct0.try_to_sqrt_price().expect("sqrt(1)"); - pallet_subtensor_swap::CurrentTick::::set(net_new, ct0); - pallet_subtensor_swap::AlphaSqrtPrice::::set(net_new, sqrt1); - - // Compute the exact min stake per the pallet rule: DefaultMinStake + fee(DefaultMinStake). - let min_stake = DefaultMinStake::::get(); - let order = GetAlphaForTao::::with_amount(min_stake); - let fee_for_min = pallet_subtensor_swap::Pallet::::sim_swap( - net_new, - order, - ) - .map(|r| r.fee_paid) - .unwrap_or_else(|_e| { - as subtensor_swap_interface::SwapHandler>::approx_fee_amount(net_new, min_stake) - }); - let min_amount_required = min_stake.saturating_add(fee_for_min).to_u64(); - - // Re‑stake from three coldkeys; choose a specific DISTINCT hotkey per cold. - for &cold in &cold_lps[0..3] { - let [hot1, _hot2] = cold_to_hots[&cold]; - register_ok_neuron(net_new, hot1, cold, 7777); - - let before_tao = SubtensorModule::get_coldkey_balance(&cold); - let a_prev: u64 = Alpha::::get((hot1, cold, net_new)).saturating_to_num(); - - // Expected α for this exact τ, using the same sim path as the pallet. - let order = GetAlphaForTao::::with_amount(min_amount_required); - let expected_alpha_out = pallet_subtensor_swap::Pallet::::sim_swap( - net_new, - order, - ) - .map(|r| r.amount_paid_out) - .expect("sim_swap must succeed for fresh net and min amount"); - - assert_ok!(SubtensorModule::do_add_stake( - RuntimeOrigin::signed(cold), - hot1, - net_new, - min_amount_required.into() - )); - - let after_tao = SubtensorModule::get_coldkey_balance(&cold); - let a_new: u64 = Alpha::::get((hot1, cold, net_new)).saturating_to_num(); - let a_delta = a_new.saturating_sub(a_prev); - - // τ decreased by exactly the amount we sent. - assert_eq!( - after_tao, - before_tao.saturating_sub(min_amount_required), - "τ did not decrease by the min required restake amount for cold {cold:?}" - ); - - // α minted equals the simulated swap’s net out for that same τ. - assert_eq!( - a_delta, expected_alpha_out.to_u64(), - "α minted mismatch for cold {cold:?} (hot {hot1:?}) on new net (αΔ {a_delta}, expected {expected_alpha_out})" - ); - } - - // Ensure V3 still functional on new net: add a small position for the first cold using its hot1 - let who_cold = cold_lps[0]; - let [who_hot, _] = cold_to_hots[&who_cold]; - add_pos(net_new, who_hot, who_cold, 8, 123_456); - assert!( - pallet_subtensor_swap::Positions::::iter() - .any(|((n, owner, _pid), _)| n == net_new && owner == who_cold), - "new position not recorded on the re-registered net" - ); - }); + todo!(); + + // new_test_ext(0).execute_with(|| { + // // ──────────────────────────────────────────────────────────────────── + // // 0) Constants and helpers (distinct hotkeys & coldkeys) + // // ──────────────────────────────────────────────────────────────────── + // const NUM_NETS: usize = 4; + + // // Six LP coldkeys + // let cold_lps: [U256; 6] = [ + // U256::from(3001), + // U256::from(3002), + // U256::from(3003), + // U256::from(3004), + // U256::from(3005), + // U256::from(3006), + // ]; + + // // For each coldkey, define two DISTINCT hotkeys it owns. + // let mut cold_to_hots: BTreeMap = BTreeMap::new(); + // for &c in cold_lps.iter() { + // let h1 = U256::from(c.low_u64().saturating_add(100_000)); + // let h2 = U256::from(c.low_u64().saturating_add(200_000)); + // cold_to_hots.insert(c, [h1, h2]); + // } + + // // Distinct τ pot sizes per net. + // let pots: [u64; NUM_NETS] = [12_345, 23_456, 34_567, 45_678]; + + // let lp_sets_per_net: [&[U256]; NUM_NETS] = [ + // &cold_lps[0..4], // net0: A,B,C,D + // &cold_lps[2..6], // net1: C,D,E,F + // &cold_lps[0..6], // net2: A..F + // &cold_lps[1..5], // net3: B,C,D,E + // ]; + + // // Multiple bands/sizes → many positions per cold across nets, using mixed hotkeys. + // let bands: [i32; 3] = [5, 13, 30]; + // let liqs: [u64; 3] = [400_000, 700_000, 1_100_000]; + + // // Helper: add a V3 position via a (hot, cold) pair. + // let add_pos = |net: NetUid, hot: U256, cold: U256, band: i32, liq: u64| { + // let ct = pallet_subtensor_swap::CurrentTick::::get(net); + // let lo = ct.saturating_sub(band); + // let hi = ct.saturating_add(band); + // assert_ok!(pallet_subtensor_swap::Pallet::::add_liquidity( + // RuntimeOrigin::signed(cold), + // hot, + // net, + // lo, + // hi, + // liq + // )); + // }; + + // // ──────────────────────────────────────────────────────────────────── + // // 1) Create many subnets, enable V3, fix price at tick=0 (sqrt≈1) + // // ──────────────────────────────────────────────────────────────────── + // let mut nets: Vec = Vec::new(); + // for i in 0..NUM_NETS { + // let owner_hot = U256::from(10_000 + (i as u64)); + // let owner_cold = U256::from(20_000 + (i as u64)); + // let net = add_dynamic_network(&owner_hot, &owner_cold); + // SubtensorModule::set_max_registrations_per_block(net, 1_000u16); + // SubtensorModule::set_target_registrations_per_interval(net, 1_000u16); + // Emission::::insert(net, Vec::::new()); + // SubtensorModule::set_subnet_locked_balance(net, TaoCurrency::from(0)); + + // assert_ok!( + // pallet_subtensor_swap::Pallet::::toggle_user_liquidity( + // RuntimeOrigin::root(), + // net, + // true + // ) + // ); + + // // Price/tick pinned so LP math stays stable (sqrt(1)). + // let ct0 = pallet_subtensor_swap::tick::TickIndex::new_unchecked(0); + // let sqrt1 = ct0.try_to_sqrt_price().expect("sqrt(1) price"); + // pallet_subtensor_swap::CurrentTick::::set(net, ct0); + // pallet_subtensor_swap::AlphaSqrtPrice::::set(net, sqrt1); + + // nets.push(net); + // } + + // // Map net → index for quick lookups. + // let mut net_index: BTreeMap = BTreeMap::new(); + // for (i, &n) in nets.iter().enumerate() { + // net_index.insert(n, i); + // } + + // // ──────────────────────────────────────────────────────────────────── + // // 2) Pre-create a handful of small (hot, cold) pairs so accounts exist + // // ──────────────────────────────────────────────────────────────────── + // for id in 0u64..10 { + // let cold_acc = U256::from(1_000_000 + id); + // let hot_acc = U256::from(2_000_000 + id); + // for &net in nets.iter() { + // register_ok_neuron(net, hot_acc, cold_acc, 100_000 + id); + // } + // } + + // // ──────────────────────────────────────────────────────────────────── + // // 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); + // } + + // // τ balances before LP adds (after staking): + // let mut tao_before: BTreeMap = BTreeMap::new(); + + // // Ordered α snapshot per net at **pair granularity** (pre‑LP): + // let mut alpha_pairs_per_net: BTreeMap> = BTreeMap::new(); + + // // Register both hotkeys for each participating cold on each net and stake τ→α. + // for (ni, &net) in nets.iter().enumerate() { + // let participants = lp_sets_per_net[ni]; + // for &cold in participants.iter() { + // let [hot1, hot2] = cold_to_hots[&cold]; + + // // Ensure (hot, cold) neurons exist on this net. + // register_ok_neuron( + // net, + // hot1, + // cold, + // (ni as u64) * 10_000 + (hot1.low_u64() % 10_000), + // ); + // register_ok_neuron( + // net, + // hot2, + // cold, + // (ni as u64) * 10_000 + (hot2.low_u64() % 10_000) + 1, + // ); + + // // Stake τ (split across the two hotkeys). + // let base: u64 = + // 5_000_000 + ((ni as u64) * 1_000_000) + ((cold.low_u64() % 10) * 250_000); + // let stake1: u64 = base.saturating_mul(3) / 5; // 60% + // let stake2: u64 = base.saturating_sub(stake1); // 40% + + // assert_ok!(SubtensorModule::do_add_stake( + // RuntimeOrigin::signed(cold), + // hot1, + // net, + // stake1.into() + // )); + // assert_ok!(SubtensorModule::do_add_stake( + // RuntimeOrigin::signed(cold), + // hot2, + // net, + // stake2.into() + // )); + // } + // } + + // // Record τ balances now (post‑stake, pre‑LP). + // for &cold in cold_lps.iter() { + // tao_before.insert(cold, SubtensorModule::get_coldkey_balance(&cold)); + // } + + // // Capture **pair‑level** α snapshot per net (pre‑LP). + // for ((hot, cold, net), amt) in Alpha::::iter() { + // if let Some(&ni) = net_index.get(&net) { + // if lp_sets_per_net[ni].contains(&cold) { + // let a: u128 = amt.saturating_to_num(); + // if a > 0 { + // alpha_pairs_per_net + // .entry(net) + // .or_default() + // .push(((hot, cold), a)); + // } + // } + // } + // } + + // // ──────────────────────────────────────────────────────────────────── + // // 4) Add many V3 positions per cold across nets, alternating hotkeys + // // ──────────────────────────────────────────────────────────────────── + // for (ni, &net) in nets.iter().enumerate() { + // let participants = lp_sets_per_net[ni]; + // for (pi, &cold) in participants.iter().enumerate() { + // let [hot1, hot2] = cold_to_hots[&cold]; + // let hots = [hot1, hot2]; + // for k in 0..3 { + // let band = bands[(pi + k) % bands.len()]; + // let liq = liqs[(ni + k) % liqs.len()]; + // let hot = hots[k % hots.len()]; + // add_pos(net, hot, cold, band, liq); + // } + // } + // } + + // // Snapshot τ balances AFTER LP adds (to measure actual principal debit). + // let mut tao_after_adds: BTreeMap = BTreeMap::new(); + // for &cold in cold_lps.iter() { + // tao_after_adds.insert(cold, SubtensorModule::get_coldkey_balance(&cold)); + // } + + // // ──────────────────────────────────────────────────────────────────── + // // 5) Compute Hamilton-apportionment BASE shares per cold and total leftover + // // from the **pair-level** pre‑LP α snapshot; also count pairs per cold. + // // ──────────────────────────────────────────────────────────────────── + // let mut base_share_cold: BTreeMap = + // cold_lps.iter().copied().map(|c| (c, 0_u64)).collect(); + // let mut pair_count_cold: BTreeMap = + // cold_lps.iter().copied().map(|c| (c, 0_u32)).collect(); + + // let mut leftover_total: u64 = 0; + + // for (ni, &net) in nets.iter().enumerate() { + // let pot = pots[ni]; + // let pairs = alpha_pairs_per_net.get(&net).cloned().unwrap_or_default(); + // if pot == 0 || pairs.is_empty() { + // continue; + // } + // let total_alpha: u128 = pairs.iter().map(|(_, a)| *a).sum(); + // if total_alpha == 0 { + // continue; + // } + + // let mut base_sum_net: u64 = 0; + // for ((_, cold), a) in pairs.iter().copied() { + // // quota = a * pot / total_alpha + // let prod: u128 = a.saturating_mul(pot as u128); + // let base: u64 = (prod / total_alpha) as u64; + // base_sum_net = base_sum_net.saturating_add(base); + // *base_share_cold.entry(cold).or_default() = + // base_share_cold[&cold].saturating_add(base); + // *pair_count_cold.entry(cold).or_default() += 1; + // } + // let leftover_net = pot.saturating_sub(base_sum_net); + // leftover_total = leftover_total.saturating_add(leftover_net); + // } + + // // ──────────────────────────────────────────────────────────────────── + // // 6) Seed τ pots and dissolve *all* networks (liquidates LPs + refunds) + // // ──────────────────────────────────────────────────────────────────── + // for (ni, &net) in nets.iter().enumerate() { + // SubnetTAO::::insert(net, TaoCurrency::from(pots[ni])); + // } + // for &net in nets.iter() { + // assert_ok!(SubtensorModule::do_dissolve_network(net)); + // } + + // // ──────────────────────────────────────────────────────────────────── + // // 7) Assertions: τ balances, α gone, nets removed, swap state clean + // // (Hamilton invariants enforced at cold-level without relying on tie-break) + // // ──────────────────────────────────────────────────────────────────── + // // Collect actual pot credits per cold (principal cancels out against adds when comparing before→after). + // let mut actual_pot_cold: BTreeMap = + // cold_lps.iter().copied().map(|c| (c, 0_u64)).collect(); + // for &cold in cold_lps.iter() { + // let before = tao_before[&cold]; + // let after = SubtensorModule::get_coldkey_balance(&cold); + // actual_pot_cold.insert(cold, after.saturating_sub(before)); + // } + + // // (a) Sum of actual pot credits equals total pots. + // let total_actual: u64 = actual_pot_cold.values().copied().sum(); + // let total_pots: u64 = pots.iter().copied().sum(); + // assert_eq!( + // total_actual, total_pots, + // "total τ pot credited across colds must equal sum of pots" + // ); + + // // (b) Each cold’s pot is within Hamilton bounds: base ≤ actual ≤ base + #pairs. + // let mut extra_accum: u64 = 0; + // for &cold in cold_lps.iter() { + // let base = *base_share_cold.get(&cold).unwrap_or(&0); + // let pairs = *pair_count_cold.get(&cold).unwrap_or(&0) as u64; + // let actual = *actual_pot_cold.get(&cold).unwrap_or(&0); + + // assert!( + // actual >= base, + // "cold {cold:?} actual pot {actual} is below base {base}" + // ); + // assert!( + // actual <= base.saturating_add(pairs), + // "cold {cold:?} actual pot {actual} exceeds base + pairs ({base} + {pairs})" + // ); + + // extra_accum = extra_accum.saturating_add(actual.saturating_sub(base)); + // } + + // // (c) The total “extra beyond base” equals the computed leftover_total across nets. + // assert_eq!( + // extra_accum, leftover_total, + // "sum of extras beyond base must equal total leftover" + // ); + + // // (d) τ principal was fully refunded (compare after_adds → after). + // for &cold in cold_lps.iter() { + // let before = tao_before[&cold]; + // let mid = tao_after_adds[&cold]; + // let after = SubtensorModule::get_coldkey_balance(&cold); + // let principal_actual = before.saturating_sub(mid); + // let actual_pot = after.saturating_sub(before); + // assert_eq!( + // after.saturating_sub(mid), + // principal_actual.saturating_add(actual_pot), + // "cold {cold:?} τ balance incorrect vs 'after_adds'" + // ); + // } + + // // For each dissolved net, check α ledgers gone, network removed, and swap state clean. + // for &net in nets.iter() { + // assert!( + // Alpha::::iter().all(|((_h, _c, n), _)| n != net), + // "alpha ledger not fully cleared for net {net:?}" + // ); + // assert!( + // !SubtensorModule::if_subnet_exist(net), + // "subnet {net:?} still exists" + // ); + // assert!( + // pallet_subtensor_swap::Ticks::::iter_prefix(net) + // .next() + // .is_none(), + // "ticks not cleared for net {net:?}" + // ); + // assert!( + // !pallet_subtensor_swap::Positions::::iter() + // .any(|((n, _owner, _pid), _)| n == net), + // "swap positions not fully cleared for net {net:?}" + // ); + // assert_eq!( + // pallet_subtensor_swap::FeeGlobalTao::::get(net).saturating_to_num::(), + // 0, + // "FeeGlobalTao nonzero for net {net:?}" + // ); + // assert_eq!( + // pallet_subtensor_swap::FeeGlobalAlpha::::get(net).saturating_to_num::(), + // 0, + // "FeeGlobalAlpha nonzero for net {net:?}" + // ); + // assert_eq!( + // pallet_subtensor_swap::CurrentLiquidity::::get(net), + // 0, + // "CurrentLiquidity not zero for net {net:?}" + // ); + // assert!( + // !pallet_subtensor_swap::PalSwapInitialized::::get(net), + // "PalSwapInitialized still set" + // ); + // assert!( + // !pallet_subtensor_swap::EnabledUserLiquidity::::get(net), + // "EnabledUserLiquidity still set" + // ); + // assert!( + // pallet_subtensor_swap::TickIndexBitmapWords::::iter_prefix((net,)) + // .next() + // .is_none(), + // "TickIndexBitmapWords not cleared for net {net:?}" + // ); + // } + + // // ──────────────────────────────────────────────────────────────────── + // // 8) Re-register a fresh subnet and re‑stake using the pallet’s min rule + // // Assert αΔ equals the sim-swap result for the exact τ staked. + // // ──────────────────────────────────────────────────────────────────── + // let new_owner_hot = U256::from(99_000); + // let new_owner_cold = U256::from(99_001); + // let net_new = add_dynamic_network(&new_owner_hot, &new_owner_cold); + // SubtensorModule::set_max_registrations_per_block(net_new, 1_000u16); + // SubtensorModule::set_target_registrations_per_interval(net_new, 1_000u16); + // Emission::::insert(net_new, Vec::::new()); + // SubtensorModule::set_subnet_locked_balance(net_new, TaoCurrency::from(0)); + + // assert_ok!( + // pallet_subtensor_swap::Pallet::::toggle_user_liquidity( + // RuntimeOrigin::root(), + // net_new, + // true + // ) + // ); + // let ct0 = pallet_subtensor_swap::tick::TickIndex::new_unchecked(0); + // let sqrt1 = ct0.try_to_sqrt_price().expect("sqrt(1)"); + // pallet_subtensor_swap::CurrentTick::::set(net_new, ct0); + // pallet_subtensor_swap::AlphaSqrtPrice::::set(net_new, sqrt1); + + // // Compute the exact min stake per the pallet rule: DefaultMinStake + fee(DefaultMinStake). + // let min_stake = DefaultMinStake::::get(); + // let order = GetAlphaForTao::::with_amount(min_stake); + // let fee_for_min = pallet_subtensor_swap::Pallet::::sim_swap( + // net_new, + // order, + // ) + // .map(|r| r.fee_paid) + // .unwrap_or_else(|_e| { + // as subtensor_swap_interface::SwapHandler>::approx_fee_amount(net_new, min_stake) + // }); + // let min_amount_required = min_stake.saturating_add(fee_for_min).to_u64(); + + // // Re‑stake from three coldkeys; choose a specific DISTINCT hotkey per cold. + // for &cold in &cold_lps[0..3] { + // let [hot1, _hot2] = cold_to_hots[&cold]; + // register_ok_neuron(net_new, hot1, cold, 7777); + + // let before_tao = SubtensorModule::get_coldkey_balance(&cold); + // let a_prev: u64 = Alpha::::get((hot1, cold, net_new)).saturating_to_num(); + + // // Expected α for this exact τ, using the same sim path as the pallet. + // let order = GetAlphaForTao::::with_amount(min_amount_required); + // let expected_alpha_out = pallet_subtensor_swap::Pallet::::sim_swap( + // net_new, + // order, + // ) + // .map(|r| r.amount_paid_out) + // .expect("sim_swap must succeed for fresh net and min amount"); + + // assert_ok!(SubtensorModule::do_add_stake( + // RuntimeOrigin::signed(cold), + // hot1, + // net_new, + // min_amount_required.into() + // )); + + // let after_tao = SubtensorModule::get_coldkey_balance(&cold); + // let a_new: u64 = Alpha::::get((hot1, cold, net_new)).saturating_to_num(); + // let a_delta = a_new.saturating_sub(a_prev); + + // // τ decreased by exactly the amount we sent. + // assert_eq!( + // after_tao, + // before_tao.saturating_sub(min_amount_required), + // "τ did not decrease by the min required restake amount for cold {cold:?}" + // ); + + // // α minted equals the simulated swap’s net out for that same τ. + // assert_eq!( + // a_delta, expected_alpha_out.to_u64(), + // "α minted mismatch for cold {cold:?} (hot {hot1:?}) on new net (αΔ {a_delta}, expected {expected_alpha_out})" + // ); + // } + + // // Ensure V3 still functional on new net: add a small position for the first cold using its hot1 + // let who_cold = cold_lps[0]; + // let [who_hot, _] = cold_to_hots[&who_cold]; + // add_pos(net_new, who_hot, who_cold, 8, 123_456); + // assert!( + // pallet_subtensor_swap::Positions::::iter() + // .any(|((n, owner, _pid), _)| n == net_new && owner == who_cold), + // "new position not recorded on the re-registered net" + // ); + // }); } #[test] diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index 4146786709..6fa7b4e056 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -6,11 +6,10 @@ use frame_support::dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo, Pays use frame_support::sp_runtime::DispatchError; use frame_support::{assert_err, assert_noop, assert_ok, traits::Currency}; use frame_system::RawOrigin; -use pallet_subtensor_swap::Call as SwapCall; -use pallet_subtensor_swap::tick::TickIndex; +// use pallet_subtensor_swap::Call as SwapCall; use safe_math::FixedExt; use sp_core::{Get, H256, U256}; -use sp_runtime::traits::Dispatchable; +// use sp_runtime::traits::Dispatchable; use substrate_fixed::traits::FromFixed; use substrate_fixed::types::{I96F32, I110F18, U64F64, U96F32}; use subtensor_runtime_common::{ @@ -716,8 +715,8 @@ fn test_remove_stake_total_balance_no_change() { ); // Add subnet TAO for the equivalent amount added at price - let amount_tao = U96F32::saturating_from_num(amount) - * ::SwapInterface::current_alpha_price(netuid.into()); + let amount_tao = U96F32::from_num(amount) + * U96F32::from_num(::SwapInterface::current_alpha_price(netuid.into())); SubnetTAO::::mutate(netuid, |v| { *v += amount_tao.saturating_to_num::().into() }); @@ -2179,7 +2178,7 @@ fn test_get_total_delegated_stake_after_unstaking() { unstake_amount_alpha.into() )); let current_price = - ::SwapInterface::current_alpha_price(netuid.into()); + U96F32::from_num(::SwapInterface::current_alpha_price(netuid.into())); // Calculate the expected delegated stake let unstake_amount = @@ -4840,126 +4839,104 @@ fn test_unstake_full_amount() { }); } -fn price_to_tick(price: f64) -> TickIndex { - let price_sqrt: U64F64 = U64F64::from_num(price.sqrt()); - // Handle potential errors in the conversion - match TickIndex::try_from_sqrt_price(price_sqrt) { - Ok(mut tick) => { - // Ensure the tick is within bounds - if tick > TickIndex::MAX { - tick = TickIndex::MAX; - } else if tick < TickIndex::MIN { - tick = TickIndex::MIN; - } - tick - } - // Default to a reasonable value when conversion fails - Err(_) => { - if price > 1.0 { - TickIndex::MAX - } else { - TickIndex::MIN - } - } - } -} - /// Test correctness of swap fees: /// 1. TAO is not minted or burned /// 2. Fees match FeeRate /// #[test] fn test_swap_fees_tao_correctness() { - 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; - let owner_balance_before = amount * 10; - let user_balance_before = amount * 100; - - // 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); - let fee_rate = pallet_subtensor_swap::FeeRate::::get(NetUid::from(netuid)) as f64 - / u16::MAX as f64; - 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 = TaoCurrency::from(100_000_000_000); - let alpha_in = AlphaCurrency::from(400_000_000_000); - mock::setup_reserves(netuid, tao_reserve, alpha_in); - - // Check starting "total TAO" - let total_tao_before = - user_balance_before + owner_balance_before + SubnetTAO::::get(netuid).to_u64(); - - // Get alpha for owner - assert_ok!(SubtensorModule::add_stake( - RuntimeOrigin::signed(owner_coldkey), - owner_hotkey, - netuid, - amount.into(), - )); - let mut fees = (fee_rate * amount as f64) as u64; - - // 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.01; - let tick_low = price_to_tick(current_price); - let tick_high = price_to_tick(limit_price); - let liquidity = amount; - - assert_ok!(::SwapInterface::do_add_liquidity( - netuid.into(), - &owner_coldkey, - &owner_hotkey, - tick_low, - tick_high, - liquidity, - )); - - // Limit-buy and then sell all alpha for user to hit owner liquidity - assert_ok!(SubtensorModule::add_stake_limit( - RuntimeOrigin::signed(coldkey), - owner_hotkey, - netuid, - amount.into(), - ((limit_price * u64::MAX as f64) as u64).into(), - true - )); - fees += (fee_rate * amount as f64) as u64; - - let user_alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &owner_hotkey, - &coldkey, - netuid, - ); - remove_stake_rate_limit_for_tests(&owner_hotkey, &coldkey, netuid); - assert_ok!(SubtensorModule::remove_stake( - RuntimeOrigin::signed(coldkey), - owner_hotkey, - netuid, - user_alpha, - )); - // Do not add fees because selling fees are in alpha - - // Check ending "total TAO" - let owner_balance_after = SubtensorModule::get_coldkey_balance(&owner_coldkey); - let user_balance_after = SubtensorModule::get_coldkey_balance(&coldkey); - let total_tao_after = user_balance_after - + owner_balance_after - + SubnetTAO::::get(netuid).to_u64() - + fees; - - // Total TAO does not change, leave some epsilon for rounding - assert_abs_diff_eq!(total_tao_before, total_tao_after, epsilon = 2); - }); + todo!(); + + // 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; + // let owner_balance_before = amount * 10; + // let user_balance_before = amount * 100; + + // // 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); + // let fee_rate = pallet_subtensor_swap::FeeRate::::get(NetUid::from(netuid)) as f64 + // / u16::MAX as f64; + // 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 = TaoCurrency::from(100_000_000_000); + // let alpha_in = AlphaCurrency::from(400_000_000_000); + // mock::setup_reserves(netuid, tao_reserve, alpha_in); + + // // Check starting "total TAO" + // let total_tao_before = + // user_balance_before + owner_balance_before + SubnetTAO::::get(netuid).to_u64(); + + // // Get alpha for owner + // assert_ok!(SubtensorModule::add_stake( + // RuntimeOrigin::signed(owner_coldkey), + // owner_hotkey, + // netuid, + // amount.into(), + // )); + // let mut fees = (fee_rate * amount as f64) as u64; + + // // 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.01; + // let tick_low = price_to_tick(current_price); + // let tick_high = price_to_tick(limit_price); + // let liquidity = amount; + + // assert_ok!(::SwapInterface::do_add_liquidity( + // netuid.into(), + // &owner_coldkey, + // &owner_hotkey, + // tick_low, + // tick_high, + // liquidity, + // )); + + // // Limit-buy and then sell all alpha for user to hit owner liquidity + // assert_ok!(SubtensorModule::add_stake_limit( + // RuntimeOrigin::signed(coldkey), + // owner_hotkey, + // netuid, + // amount.into(), + // ((limit_price * u64::MAX as f64) as u64).into(), + // true + // )); + // fees += (fee_rate * amount as f64) as u64; + + // let user_alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + // &owner_hotkey, + // &coldkey, + // netuid, + // ); + // remove_stake_rate_limit_for_tests(&owner_hotkey, &coldkey, netuid); + // assert_ok!(SubtensorModule::remove_stake( + // RuntimeOrigin::signed(coldkey), + // owner_hotkey, + // netuid, + // user_alpha, + // )); + // // Do not add fees because selling fees are in alpha + + // // Check ending "total TAO" + // let owner_balance_after = SubtensorModule::get_coldkey_balance(&owner_coldkey); + // let user_balance_after = SubtensorModule::get_coldkey_balance(&coldkey); + // let total_tao_after = user_balance_after + // + owner_balance_after + // + SubnetTAO::::get(netuid).to_u64() + // + fees; + + // // Total TAO does not change, leave some epsilon for rounding + // assert_abs_diff_eq!(total_tao_before, total_tao_after, epsilon = 2); + // }); } #[test] @@ -5207,206 +5184,210 @@ fn test_default_min_stake_sufficiency() { /// 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); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, amount * 100); - 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 = TaoCurrency::from(100_000_000_000); - let alpha_in = AlphaCurrency::from(400_000_000_000); - mock::setup_reserves(netuid, tao_reserve, alpha_in); - - // 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 - let owner_tao_before = SubtensorModule::get_coldkey_balance(&owner_coldkey); - let owner_alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &owner_hotkey, - &owner_coldkey, - netuid, - ); - - // 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 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 > owner_tao_before); - assert!(owner_alpha_after_add > owner_alpha_before); // always greater because of claimed fees - - // 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); - } - }); - }); + todo!(); + + // // 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); + // SubtensorModule::add_balance_to_coldkey_account(&coldkey, amount * 100); + // 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 = TaoCurrency::from(100_000_000_000); + // let alpha_in = AlphaCurrency::from(400_000_000_000); + // mock::setup_reserves(netuid, tao_reserve, alpha_in); + + // // 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 + // let owner_tao_before = SubtensorModule::get_coldkey_balance(&owner_coldkey); + // let owner_alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + // &owner_hotkey, + // &owner_coldkey, + // netuid, + // ); + + // // 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 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 > owner_tao_before); + // assert!(owner_alpha_after_add > owner_alpha_before); // always greater because of claimed fees + + // // 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); + // } + // }); + // }); } -fn setup_positions(netuid: NetUid) { - for (coldkey, hotkey, low_price, high_price, liquidity) in [ - (2, 12, 0.1, 0.20, 1_000_000_000_000_u64), - (3, 13, 0.15, 0.25, 200_000_000_000_u64), - (4, 14, 0.25, 0.5, 3_000_000_000_000_u64), - (5, 15, 0.3, 0.6, 300_000_000_000_u64), - (6, 16, 0.4, 0.7, 8_000_000_000_000_u64), - (7, 17, 0.5, 0.8, 600_000_000_000_u64), - (8, 18, 0.6, 0.9, 700_000_000_000_u64), - (9, 19, 0.7, 1.0, 100_000_000_000_u64), - (10, 20, 0.8, 1.1, 300_000_000_000_u64), - ] { - SubtensorModule::create_account_if_non_existent(&U256::from(coldkey), &U256::from(hotkey)); - SubtensorModule::add_balance_to_coldkey_account( - &U256::from(coldkey), - 1_000_000_000_000_000, - ); - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - &U256::from(hotkey), - &U256::from(coldkey), - netuid.into(), - 1_000_000_000_000_000.into(), - ); - - let tick_low = price_to_tick(low_price); - let tick_high = price_to_tick(high_price); - let add_lq_call = SwapCall::::add_liquidity { - hotkey: U256::from(hotkey), - netuid: netuid.into(), - tick_low, - tick_high, - liquidity, - }; - assert_ok!( - RuntimeCall::Swap(add_lq_call).dispatch(RuntimeOrigin::signed(U256::from(coldkey))) - ); - } -} +// fn setup_positions(netuid: NetUid) { +// for (coldkey, hotkey, low_price, high_price, liquidity) in [ +// (2, 12, 0.1, 0.20, 1_000_000_000_000_u64), +// (3, 13, 0.15, 0.25, 200_000_000_000_u64), +// (4, 14, 0.25, 0.5, 3_000_000_000_000_u64), +// (5, 15, 0.3, 0.6, 300_000_000_000_u64), +// (6, 16, 0.4, 0.7, 8_000_000_000_000_u64), +// (7, 17, 0.5, 0.8, 600_000_000_000_u64), +// (8, 18, 0.6, 0.9, 700_000_000_000_u64), +// (9, 19, 0.7, 1.0, 100_000_000_000_u64), +// (10, 20, 0.8, 1.1, 300_000_000_000_u64), +// ] { +// SubtensorModule::create_account_if_non_existent(&U256::from(coldkey), &U256::from(hotkey)); +// SubtensorModule::add_balance_to_coldkey_account( +// &U256::from(coldkey), +// 1_000_000_000_000_000, +// ); +// SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( +// &U256::from(hotkey), +// &U256::from(coldkey), +// netuid.into(), +// 1_000_000_000_000_000.into(), +// ); + +// let tick_low = price_to_tick(low_price); +// let tick_high = price_to_tick(high_price); +// let add_lq_call = SwapCall::::add_liquidity { +// hotkey: U256::from(hotkey), +// netuid: netuid.into(), +// tick_low, +// tick_high, +// liquidity, +// }; +// assert_ok!( +// RuntimeCall::Swap(add_lq_call).dispatch(RuntimeOrigin::signed(U256::from(coldkey))) +// ); +// } +// } #[test] fn test_large_swap() { - new_test_ext(1).execute_with(|| { - let owner_hotkey = U256::from(1); - let owner_coldkey = U256::from(2); - let coldkey = U256::from(100); - - // add network - let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_000); - pallet_subtensor_swap::EnabledUserLiquidity::::insert(NetUid::from(netuid), true); - - // Force the swap to initialize - SubtensorModule::swap_tao_for_alpha( - netuid, - TaoCurrency::ZERO, - 1_000_000_000_000.into(), - false, - ) - .unwrap(); - - setup_positions(netuid.into()); - - let swap_amount = TaoCurrency::from(100_000_000_000_000); - assert_ok!(SubtensorModule::add_stake( - RuntimeOrigin::signed(coldkey), - owner_hotkey, - netuid, - swap_amount, - )); - }); + todo!(); + + // new_test_ext(1).execute_with(|| { + // let owner_hotkey = U256::from(1); + // let owner_coldkey = U256::from(2); + // let coldkey = U256::from(100); + + // // add network + // let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); + // SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_000); + // pallet_subtensor_swap::EnabledUserLiquidity::::insert(NetUid::from(netuid), true); + + // // Force the swap to initialize + // SubtensorModule::swap_tao_for_alpha( + // netuid, + // TaoCurrency::ZERO, + // 1_000_000_000_000.into(), + // false, + // ) + // .unwrap(); + + // setup_positions(netuid.into()); + + // let swap_amount = TaoCurrency::from(100_000_000_000_000); + // assert_ok!(SubtensorModule::add_stake( + // RuntimeOrigin::signed(coldkey), + // owner_hotkey, + // netuid, + // swap_amount, + // )); + // }); } #[test] diff --git a/pallets/swap-interface/src/lib.rs b/pallets/swap-interface/src/lib.rs index 19af1303c1..c99db6a475 100644 --- a/pallets/swap-interface/src/lib.rs +++ b/pallets/swap-interface/src/lib.rs @@ -2,7 +2,7 @@ use core::ops::Neg; use frame_support::pallet_prelude::*; -use substrate_fixed::types::U96F32; +use substrate_fixed::types::U64F64; use subtensor_macros::freeze_struct; use subtensor_runtime_common::{AlphaCurrency, Currency, NetUid, TaoCurrency}; @@ -38,7 +38,7 @@ pub trait SwapHandler { Self: SwapEngine; fn approx_fee_amount(netuid: NetUid, amount: T) -> T; - fn current_alpha_price(netuid: NetUid) -> U96F32; + fn current_alpha_price(netuid: NetUid) -> U64F64; fn get_protocol_tao(netuid: NetUid) -> TaoCurrency; fn max_price() -> C; fn min_price() -> C; diff --git a/pallets/swap-interface/src/order.rs b/pallets/swap-interface/src/order.rs index 1576283fd5..fc3f2acefe 100644 --- a/pallets/swap-interface/src/order.rs +++ b/pallets/swap-interface/src/order.rs @@ -11,7 +11,7 @@ pub trait Order: Clone { fn with_amount(amount: impl Into) -> Self; fn amount(&self) -> Self::PaidIn; - fn is_beyond_price_limit(&self, alpha_sqrt_price: U64F64, limit_sqrt_price: U64F64) -> bool; + fn is_beyond_price_limit(&self, current_price: U64F64, limit_price: U64F64) -> bool; } #[derive(Clone, Default)] @@ -45,8 +45,8 @@ where self.amount } - fn is_beyond_price_limit(&self, alpha_sqrt_price: U64F64, limit_sqrt_price: U64F64) -> bool { - alpha_sqrt_price < limit_sqrt_price + fn is_beyond_price_limit(&self, current_price: U64F64, limit_price: U64F64) -> bool { + current_price < limit_price } } @@ -81,7 +81,7 @@ where self.amount } - fn is_beyond_price_limit(&self, alpha_sqrt_price: U64F64, limit_sqrt_price: U64F64) -> bool { - alpha_sqrt_price > limit_sqrt_price + fn is_beyond_price_limit(&self, current_price: U64F64, limit_price: U64F64) -> bool { + current_price > limit_price } } diff --git a/pallets/swap/src/benchmarking.rs b/pallets/swap/src/benchmarking.rs index 66ff88fd31..a7fcfedb4e 100644 --- a/pallets/swap/src/benchmarking.rs +++ b/pallets/swap/src/benchmarking.rs @@ -3,22 +3,21 @@ #![allow(clippy::multiple_bound_locations)] use core::marker::PhantomData; - use frame_benchmarking::v2::*; -use frame_support::traits::Get; use frame_system::RawOrigin; -use substrate_fixed::types::{I64F64, U64F64}; use subtensor_runtime_common::NetUid; use crate::{ pallet::{ - AlphaSqrtPrice, Call, Config, CurrentLiquidity, CurrentTick, Pallet, Positions, - SwapV3Initialized, + Call, Config, Pallet, PositionsV2, }, position::{Position, PositionId}, - tick::TickIndex, }; +fn init_swap(netuid: NetUid) { + let _ = Pallet::::maybe_initialize_palswap(netuid); +} + #[benchmarks(where T: Config)] mod benchmarks { use super::*; @@ -36,25 +35,16 @@ mod benchmarks { fn add_liquidity() { let netuid = NetUid::from(1); - if !SwapV3Initialized::::get(netuid) { - SwapV3Initialized::::insert(netuid, true); - AlphaSqrtPrice::::insert(netuid, U64F64::from_num(1)); - CurrentTick::::insert(netuid, TickIndex::new(0).unwrap()); - CurrentLiquidity::::insert(netuid, T::MinimumLiquidity::get()); - } + init_swap::(netuid); let caller: T::AccountId = whitelisted_caller(); let hotkey: T::AccountId = account("hotkey", 0, 0); - let tick_low = TickIndex::new_unchecked(-1000); - let tick_high = TickIndex::new_unchecked(1000); #[extrinsic_call] add_liquidity( RawOrigin::Signed(caller), hotkey, netuid, - tick_low, - tick_high, 1000, ); } @@ -63,27 +53,20 @@ mod benchmarks { fn remove_liquidity() { let netuid = NetUid::from(1); - if !SwapV3Initialized::::get(netuid) { - SwapV3Initialized::::insert(netuid, true); - AlphaSqrtPrice::::insert(netuid, U64F64::from_num(1)); - CurrentTick::::insert(netuid, TickIndex::new(0).unwrap()); - CurrentLiquidity::::insert(netuid, T::MinimumLiquidity::get()); - } + init_swap::(netuid); let caller: T::AccountId = whitelisted_caller(); let hotkey: T::AccountId = account("hotkey", 0, 0); let id = PositionId::from(1u128); - Positions::::insert( + PositionsV2::::insert( (netuid, caller.clone(), id), Position { id, netuid, - tick_low: TickIndex::new(-10000).unwrap(), - tick_high: TickIndex::new(10000).unwrap(), - liquidity: 1000, - fees_tao: I64F64::from_num(0), - fees_alpha: I64F64::from_num(0), + // liquidity: 1000, + // fees_tao: I64F64::from_num(0), + // fees_alpha: I64F64::from_num(0), _phantom: PhantomData, }, ); @@ -96,27 +79,20 @@ mod benchmarks { fn modify_position() { let netuid = NetUid::from(1); - if !SwapV3Initialized::::get(netuid) { - SwapV3Initialized::::insert(netuid, true); - AlphaSqrtPrice::::insert(netuid, U64F64::from_num(1)); - CurrentTick::::insert(netuid, TickIndex::new(0).unwrap()); - CurrentLiquidity::::insert(netuid, T::MinimumLiquidity::get()); - } + init_swap::(netuid); let caller: T::AccountId = whitelisted_caller(); let hotkey: T::AccountId = account("hotkey", 0, 0); let id = PositionId::from(1u128); - Positions::::insert( + PositionsV2::::insert( (netuid, caller.clone(), id), Position { id, netuid, - tick_low: TickIndex::new(-10000).unwrap(), - tick_high: TickIndex::new(10000).unwrap(), - liquidity: 10000, - fees_tao: I64F64::from_num(0), - fees_alpha: I64F64::from_num(0), + // liquidity: 10000, + // fees_tao: I64F64::from_num(0), + // fees_alpha: I64F64::from_num(0), _phantom: PhantomData, }, ); diff --git a/pallets/swap/src/lib.rs b/pallets/swap/src/lib.rs index 6257df852b..6ce2d20e7e 100644 --- a/pallets/swap/src/lib.rs +++ b/pallets/swap/src/lib.rs @@ -1,10 +1,7 @@ #![cfg_attr(not(feature = "std"), no_std)] -use substrate_fixed::types::U64F64; - pub mod pallet; pub mod position; -pub mod tick; pub mod weights; pub use pallet::*; @@ -14,5 +11,3 @@ pub mod benchmarking; #[cfg(test)] pub(crate) mod mock; - -type SqrtPrice = U64F64; diff --git a/pallets/swap/src/mock.rs b/pallets/swap/src/mock.rs index aacdf90835..e5703c9d79 100644 --- a/pallets/swap/src/mock.rs +++ b/pallets/swap/src/mock.rs @@ -14,13 +14,15 @@ use sp_runtime::{ BuildStorage, Vec, traits::{BlakeTwo256, IdentityLookup}, }; -use substrate_fixed::types::U64F64; +// use substrate_fixed::types::U64F64; use subtensor_runtime_common::{ - AlphaCurrency, BalanceOps, Currency, CurrencyReserve, NetUid, SubnetInfo, TaoCurrency, + AlphaCurrency, BalanceOps, + // Currency, + CurrencyReserve, NetUid, SubnetInfo, TaoCurrency, }; use subtensor_swap_interface::Order; -use crate::pallet::{EnabledUserLiquidity, FeeGlobalAlpha, FeeGlobalTao}; +use crate::pallet::EnabledUserLiquidity; construct_runtime!( pub enum Test { @@ -123,22 +125,7 @@ impl CurrencyReserve for AlphaReserve { pub type GetAlphaForTao = subtensor_swap_interface::GetAlphaForTao; pub type GetTaoForAlpha = subtensor_swap_interface::GetTaoForAlpha; -pub(crate) trait GlobalFeeInfo: Currency { - fn global_fee(&self, netuid: NetUid) -> U64F64; -} - -impl GlobalFeeInfo for TaoCurrency { - fn global_fee(&self, netuid: NetUid) -> U64F64 { - FeeGlobalTao::::get(netuid) - } -} - -impl GlobalFeeInfo for AlphaCurrency { - fn global_fee(&self, netuid: NetUid) -> U64F64 { - FeeGlobalAlpha::::get(netuid) - } -} - +#[allow(dead_code)] pub(crate) trait TestExt { fn approx_expected_swap_output( sqrt_current_price: f64, diff --git a/pallets/swap/src/pallet/impls.rs b/pallets/swap/src/pallet/impls.rs index 9c27b82a02..d025ed9571 100644 --- a/pallets/swap/src/pallet/impls.rs +++ b/pallets/swap/src/pallet/impls.rs @@ -1,28 +1,24 @@ -use core::ops::Neg; - use frame_support::storage::{TransactionOutcome, transactional}; use frame_support::{ensure, pallet_prelude::DispatchError, traits::Get}; use safe_math::*; use sp_arithmetic::helpers_128bit; -use sp_runtime::{DispatchResult, Vec, traits::AccountIdConversion}; -use substrate_fixed::types::{I64F64, U64F64, U96F32}; +use sp_runtime::{DispatchResult, traits::AccountIdConversion}; +use substrate_fixed::types::U64F64; use subtensor_runtime_common::{ - AlphaCurrency, BalanceOps, Currency, CurrencyReserve, NetUid, SubnetInfo, TaoCurrency, + AlphaCurrency, + // BalanceOps, + Currency, CurrencyReserve, NetUid, SubnetInfo, TaoCurrency, }; use subtensor_swap_interface::{ DefaultPriceLimit, Order as OrderT, SwapEngine, SwapHandler, SwapResult, }; use super::pallet::*; -use super::swap_step::{BasicSwapStep, SwapStep, SwapStepAction}; +use super::swap_step::{BasicSwapStep, SwapStep}; use crate::{ - SqrtPrice, - position::{Position, PositionId}, - tick::{ActiveTickIndexManager, Tick, TickIndex}, + position::PositionId, }; -const MAX_SWAP_ITERATIONS: u16 = 1000; - #[derive(Debug, PartialEq)] pub struct UpdateLiquidityResult { pub tao: TaoCurrency, @@ -30,8 +26,6 @@ pub struct UpdateLiquidityResult { pub fee_tao: TaoCurrency, pub fee_alpha: AlphaCurrency, pub removed: bool, - pub tick_low: TickIndex, - pub tick_high: TickIndex, } #[derive(Debug, PartialEq)] @@ -40,177 +34,75 @@ pub struct RemoveLiquidityResult { pub alpha: AlphaCurrency, pub fee_tao: TaoCurrency, pub fee_alpha: AlphaCurrency, - pub tick_low: TickIndex, - pub tick_high: TickIndex, pub liquidity: u64, } impl Pallet { - pub fn current_price(netuid: NetUid) -> U96F32 { + pub fn current_price(netuid: NetUid) -> U64F64 { match T::SubnetInfo::mechanism(netuid.into()) { 1 => { - if SwapV3Initialized::::get(netuid) { - let sqrt_price = AlphaSqrtPrice::::get(netuid); - U96F32::saturating_from_num(sqrt_price.saturating_mul(sqrt_price)) + let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); + if !alpha_reserve.is_zero() { + let alpha_reserve = U64F64::saturating_from_num(alpha_reserve); + let tao_reserve = + U64F64::saturating_from_num(T::TaoReserve::reserve(netuid.into())); + let reserve_weight = SwapReserveWeight::::get(netuid); + let base_weight = + U64F64::saturating_from_num(reserve_weight.get_base_weight()); + let quote_weight = + U64F64::saturating_from_num(reserve_weight.get_quote_weight()); + let w1_div_w2 = base_weight.safe_div(quote_weight); + w1_div_w2.saturating_mul(tao_reserve.safe_div(alpha_reserve)) } else { - let tao_reserve = T::TaoReserve::reserve(netuid.into()); - let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); - if !alpha_reserve.is_zero() { - U96F32::saturating_from_num(tao_reserve) - .saturating_div(U96F32::saturating_from_num(alpha_reserve)) - } else { - U96F32::saturating_from_num(0) - } + U64F64::saturating_from_num(0) } } - _ => U96F32::saturating_from_num(1), + _ => U64F64::saturating_from_num(1), } } - // initializes V3 swap for a subnet if needed - pub fn maybe_initialize_v3(netuid: NetUid) -> Result<(), Error> { - if SwapV3Initialized::::get(netuid) { + // initializes pal-swap (balancer) for a subnet if needed + pub fn maybe_initialize_palswap(netuid: NetUid) -> Result<(), Error> { + if PalSwapInitialized::::get(netuid) { return Ok(()); } - // Initialize the v3: - // Reserves are re-purposed, nothing to set, just query values for liquidity and price - // calculation + // Initialize the pal-swap: + // Reserves are re-purposed, nothing to set, just query values for creation + // of protocol position let tao_reserve = T::TaoReserve::reserve(netuid.into()); let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); - // Set price - let price = U64F64::saturating_from_num(tao_reserve) - .safe_div(U64F64::saturating_from_num(alpha_reserve)); - - let epsilon = U64F64::saturating_from_num(0.000000000001); - - let current_sqrt_price = price.checked_sqrt(epsilon).unwrap_or(U64F64::from_num(0)); - AlphaSqrtPrice::::set(netuid, current_sqrt_price); - - // Set current tick - let current_tick = TickIndex::from_sqrt_price_bounded(current_sqrt_price); - CurrentTick::::set(netuid, current_tick); - // Set initial (protocol owned) liquidity and positions - // Protocol liquidity makes one position from TickIndex::MIN to TickIndex::MAX + // Protocol liquidity makes one position // We are using the sp_arithmetic sqrt here, which works for u128 - let liquidity = helpers_128bit::sqrt( + let _liquidity = helpers_128bit::sqrt( (tao_reserve.to_u64() as u128).saturating_mul(alpha_reserve.to_u64() as u128), ) as u64; - let protocol_account_id = Self::protocol_account_id(); + let _protocol_account_id = Self::protocol_account_id(); - let (position, _, _) = Self::add_liquidity_not_insert( - netuid, - &protocol_account_id, - TickIndex::MIN, - TickIndex::MAX, - liquidity, - )?; + // let (position, _, _) = Self::add_liquidity_not_insert( + // netuid, + // &protocol_account_id, + // TickIndex::MIN, + // TickIndex::MAX, + // liquidity, + // )?; - Positions::::insert(&(netuid, protocol_account_id, position.id), position); + // Positions::::insert(&(netuid, protocol_account_id, position.id), position); - Ok(()) - } + // Ok(()) - pub(crate) fn get_proportional_alpha_tao_and_remainders( - sqrt_alpha_price: U64F64, - amount_tao: TaoCurrency, - amount_alpha: AlphaCurrency, - ) -> (TaoCurrency, AlphaCurrency, TaoCurrency, AlphaCurrency) { - let price = sqrt_alpha_price.saturating_mul(sqrt_alpha_price); - let tao_equivalent: u64 = U64F64::saturating_from_num(u64::from(amount_alpha)) - .saturating_mul(price) - .saturating_to_num(); - let amount_tao_u64 = u64::from(amount_tao); - - if tao_equivalent <= amount_tao_u64 { - // Too much or just enough TAO - ( - tao_equivalent.into(), - amount_alpha, - amount_tao.saturating_sub(TaoCurrency::from(tao_equivalent)), - 0.into(), - ) - } else { - // Too much Alpha - let alpha_equivalent: u64 = U64F64::saturating_from_num(u64::from(amount_tao)) - .safe_div(price) - .saturating_to_num(); - ( - amount_tao, - alpha_equivalent.into(), - 0.into(), - u64::from(amount_alpha) - .saturating_sub(alpha_equivalent) - .into(), - ) - } + todo!() } /// Adjusts protocol liquidity with new values of TAO and Alpha reserve pub(super) fn adjust_protocol_liquidity( - netuid: NetUid, - tao_delta: TaoCurrency, - alpha_delta: AlphaCurrency, + _netuid: NetUid, + _tao_delta: TaoCurrency, + _alpha_delta: AlphaCurrency, ) { - // Update protocol position with new liquidity - let protocol_account_id = Self::protocol_account_id(); - let mut positions = - Positions::::iter_prefix_values((netuid, protocol_account_id.clone())) - .collect::>(); - - if let Some(position) = positions.get_mut(0) { - // Claim protocol fees and add them to liquidity - let (tao_fees, alpha_fees) = position.collect_fees(); - - // Add fee reservoirs and get proportional amounts - let current_sqrt_price = AlphaSqrtPrice::::get(netuid); - let tao_reservoir = ScrapReservoirTao::::get(netuid); - let alpha_reservoir = ScrapReservoirAlpha::::get(netuid); - let (corrected_tao_delta, corrected_alpha_delta, tao_scrap, alpha_scrap) = - Self::get_proportional_alpha_tao_and_remainders( - current_sqrt_price, - tao_delta - .saturating_add(TaoCurrency::from(tao_fees)) - .saturating_add(tao_reservoir), - alpha_delta - .saturating_add(AlphaCurrency::from(alpha_fees)) - .saturating_add(alpha_reservoir), - ); - - // Update scrap reservoirs - ScrapReservoirTao::::insert(netuid, tao_scrap); - ScrapReservoirAlpha::::insert(netuid, alpha_scrap); - - // Adjust liquidity - let maybe_token_amounts = position.to_token_amounts(current_sqrt_price); - if let Ok((tao, alpha)) = maybe_token_amounts { - // Get updated reserves, calculate liquidity - let new_tao_reserve = tao.saturating_add(corrected_tao_delta.to_u64()); - let new_alpha_reserve = alpha.saturating_add(corrected_alpha_delta.to_u64()); - let new_liquidity = helpers_128bit::sqrt( - (new_tao_reserve as u128).saturating_mul(new_alpha_reserve as u128), - ) as u64; - let liquidity_delta = new_liquidity.saturating_sub(position.liquidity); - - // Update current liquidity - CurrentLiquidity::::mutate(netuid, |current_liquidity| { - *current_liquidity = current_liquidity.saturating_add(liquidity_delta); - }); - - // Update protocol position - position.liquidity = new_liquidity; - Positions::::insert( - (netuid, protocol_account_id, position.id), - position.clone(), - ); - - // Update position ticks - Self::add_liquidity_at_index(netuid, position.tick_low, liquidity_delta, false); - Self::add_liquidity_at_index(netuid, position.tick_high, liquidity_delta, true); - } - } + todo!(); } /// Executes a token swap on the specified subnet. @@ -240,7 +132,7 @@ impl Pallet { pub(crate) fn do_swap( netuid: NetUid, order: Order, - limit_sqrt_price: SqrtPrice, + limit_price: U64F64, drop_fees: bool, simulate: bool, ) -> Result, DispatchError> @@ -251,7 +143,7 @@ impl Pallet { transactional::with_transaction(|| { let reserve = Order::ReserveOut::reserve(netuid.into()); - let result = Self::swap_inner::(netuid, order, limit_sqrt_price, drop_fees) + let result = Self::swap_inner::(netuid, order, limit_price, drop_fees) .map_err(Into::into); if simulate || result.is_err() { @@ -277,7 +169,7 @@ impl Pallet { fn swap_inner( netuid: NetUid, order: Order, - limit_sqrt_price: SqrtPrice, + limit_price: U64F64, drop_fees: bool, ) -> Result, Error> where @@ -289,70 +181,36 @@ impl Pallet { Error::::ReservesTooLow ); - Self::maybe_initialize_v3(netuid)?; + Self::maybe_initialize_palswap(netuid)?; // Because user specifies the limit price, check that it is in fact beoynd the current one ensure!( - order.is_beyond_price_limit(AlphaSqrtPrice::::get(netuid), limit_sqrt_price), + order.is_beyond_price_limit(Self::current_price(netuid), limit_price), Error::::PriceLimitExceeded ); - let mut amount_remaining = order.amount(); - let mut amount_paid_out = Order::PaidOut::ZERO; - let mut iteration_counter: u16 = 0; - let mut in_acc = Order::PaidIn::ZERO; - let mut fee_acc = Order::PaidIn::ZERO; - log::trace!("======== Start Swap ========"); - log::trace!("Amount Remaining: {amount_remaining}"); - - // Swap one tick at a time until we reach one of the stop conditions - while !amount_remaining.is_zero() { - log::trace!("\nIteration: {iteration_counter}"); - log::trace!( - "\tCurrent Liquidity: {}", - CurrentLiquidity::::get(netuid) - ); - - // Create and execute a swap step - let mut swap_step = BasicSwapStep::::new( - netuid, - amount_remaining, - limit_sqrt_price, - drop_fees, - ); - - let swap_result = swap_step.execute()?; - - in_acc = in_acc.saturating_add(swap_result.delta_in); - fee_acc = fee_acc.saturating_add(swap_result.fee_paid); - amount_remaining = amount_remaining.saturating_sub(swap_result.amount_to_take); - amount_paid_out = amount_paid_out.saturating_add(swap_result.delta_out); - - if swap_step.action() == SwapStepAction::Stop { - amount_remaining = Order::PaidIn::ZERO; - } + let amount_to_swap = order.amount(); + log::trace!("Amount to swap: {amount_to_swap}"); - // The swap step didn't exchange anything - if swap_result.amount_to_take.is_zero() { - amount_remaining = Order::PaidIn::ZERO; - } + // Create and execute a swap step + let mut swap_step = BasicSwapStep::::new( + netuid, + amount_to_swap, + limit_price, + drop_fees, + ); - iteration_counter = iteration_counter.saturating_add(1); + let swap_result = swap_step.execute()?; - ensure!( - iteration_counter <= MAX_SWAP_ITERATIONS, - Error::::TooManySwapSteps - ); - } - - log::trace!("\nAmount Paid Out: {amount_paid_out}"); + log::trace!("Delta out: {}", swap_result.delta_out); + log::trace!("Fees: {}", swap_result.fee_paid); log::trace!("======== End Swap ========"); Ok(SwapResult { - amount_paid_in: in_acc, - amount_paid_out, - fee_paid: fee_acc, + amount_paid_in: swap_result.delta_in, + amount_paid_out: swap_result.delta_out, + fee_paid: swap_result.fee_paid, }) } @@ -381,23 +239,6 @@ impl Pallet { } } - pub fn find_closest_lower_active_tick(netuid: NetUid, index: TickIndex) -> Option { - ActiveTickIndexManager::::find_closest_lower(netuid, index) - .and_then(|ti| Ticks::::get(netuid, ti)) - } - - pub fn find_closest_higher_active_tick(netuid: NetUid, index: TickIndex) -> Option { - ActiveTickIndexManager::::find_closest_higher(netuid, index) - .and_then(|ti| Ticks::::get(netuid, ti)) - } - - /// Here we subtract minimum safe liquidity from current liquidity to stay in the safe range - pub(crate) fn current_liquidity_safe(netuid: NetUid) -> U64F64 { - U64F64::saturating_from_num( - CurrentLiquidity::::get(netuid).saturating_sub(T::MinimumLiquidity::get()), - ) - } - /// Adds liquidity to the specified price range. /// /// This function allows an account to provide liquidity to a given range of price ticks. The @@ -429,86 +270,16 @@ impl Pallet { /// - Other [`SwapError`] variants as applicable. pub fn do_add_liquidity( netuid: NetUid, - coldkey_account_id: &T::AccountId, - hotkey_account_id: &T::AccountId, - tick_low: TickIndex, - tick_high: TickIndex, - liquidity: u64, + _coldkey_account_id: &T::AccountId, + _hotkey_account_id: &T::AccountId, + _liquidity: u64, ) -> Result<(PositionId, u64, u64), Error> { ensure!( EnabledUserLiquidity::::get(netuid), Error::::UserLiquidityDisabled ); - let (position, tao, alpha) = Self::add_liquidity_not_insert( - netuid, - coldkey_account_id, - tick_low, - tick_high, - liquidity, - )?; - let position_id = position.id; - - ensure!( - T::BalanceOps::tao_balance(coldkey_account_id) >= TaoCurrency::from(tao) - && T::BalanceOps::alpha_balance( - netuid.into(), - coldkey_account_id, - hotkey_account_id - ) >= AlphaCurrency::from(alpha), - Error::::InsufficientBalance - ); - - // Small delta is not allowed - ensure!( - liquidity >= T::MinimumLiquidity::get(), - Error::::InvalidLiquidityValue - ); - - Positions::::insert(&(netuid, coldkey_account_id, position.id), position); - - Ok((position_id, tao, alpha)) - } - - // add liquidity without inserting position into storage (used privately for v3 intiialization). - // unlike Self::add_liquidity it also doesn't perform account's balance check. - // - // the public interface is [`Self::add_liquidity`] - fn add_liquidity_not_insert( - netuid: NetUid, - coldkey_account_id: &T::AccountId, - tick_low: TickIndex, - tick_high: TickIndex, - liquidity: u64, - ) -> Result<(Position, u64, u64), Error> { - ensure!( - Self::count_positions(netuid, coldkey_account_id) < T::MaxPositions::get() as usize, - Error::::MaxPositionsExceeded - ); - - // Ensure that tick_high is actually higher than tick_low - ensure!(tick_high > tick_low, Error::::InvalidTickRange); - - // Add liquidity at tick - Self::add_liquidity_at_index(netuid, tick_low, liquidity, false); - Self::add_liquidity_at_index(netuid, tick_high, liquidity, true); - - // Update current tick liquidity - let current_tick_index = TickIndex::current_bounded::(netuid); - Self::clamp_sqrt_price(netuid, current_tick_index); - - Self::update_liquidity_if_needed(netuid, tick_low, tick_high, liquidity as i128); - - // New position - let position_id = PositionId::new::(); - let position = Position::new(position_id, netuid, tick_low, tick_high, liquidity); - - let current_price_sqrt = AlphaSqrtPrice::::get(netuid); - let (tao, alpha) = position.to_token_amounts(current_price_sqrt)?; - - SwapV3Initialized::::set(netuid, true); - - Ok((position, tao, alpha)) + todo!(); } /// Remove liquidity and credit balances back to (coldkey_account_id, hotkey_account_id) stake. @@ -516,291 +287,49 @@ impl Pallet { /// /// Account ID and Position ID identify position in the storage map pub fn do_remove_liquidity( - netuid: NetUid, - coldkey_account_id: &T::AccountId, - position_id: PositionId, + _netuid: NetUid, + _coldkey_account_id: &T::AccountId, + _position_id: PositionId, ) -> Result> { - let Some(mut position) = Positions::::get((netuid, coldkey_account_id, position_id)) - else { - return Err(Error::::LiquidityNotFound); - }; - - // Collect fees and get tao and alpha amounts - let (fee_tao, fee_alpha) = position.collect_fees(); - let current_price = AlphaSqrtPrice::::get(netuid); - let (tao, alpha) = position.to_token_amounts(current_price)?; - - // Update liquidity at position ticks - Self::remove_liquidity_at_index(netuid, position.tick_low, position.liquidity, false); - Self::remove_liquidity_at_index(netuid, position.tick_high, position.liquidity, true); - - // Update current tick liquidity - Self::update_liquidity_if_needed( - netuid, - position.tick_low, - position.tick_high, - (position.liquidity as i128).neg(), - ); - - // Remove user position - Positions::::remove((netuid, coldkey_account_id, position_id)); - - Ok(RemoveLiquidityResult { - tao: tao.into(), - alpha: alpha.into(), - fee_tao: fee_tao.into(), - fee_alpha: fee_alpha.into(), - tick_low: position.tick_low, - tick_high: position.tick_high, - liquidity: position.liquidity, - }) + todo!(); } pub fn do_modify_position( netuid: NetUid, - coldkey_account_id: &T::AccountId, - hotkey_account_id: &T::AccountId, - position_id: PositionId, - liquidity_delta: i64, + _coldkey_account_id: &T::AccountId, + _hotkey_account_id: &T::AccountId, + _position_id: PositionId, + _liquidity_delta: i64, ) -> Result> { ensure!( EnabledUserLiquidity::::get(netuid), Error::::UserLiquidityDisabled ); - // Find the position - let Some(mut position) = Positions::::get((netuid, coldkey_account_id, position_id)) - else { - return Err(Error::::LiquidityNotFound); - }; - - // Small delta is not allowed - ensure!( - liquidity_delta.abs() >= T::MinimumLiquidity::get() as i64, - Error::::InvalidLiquidityValue - ); - let mut delta_liquidity_abs = liquidity_delta.unsigned_abs(); - - // Determine the effective price for token calculations - let current_price_sqrt = AlphaSqrtPrice::::get(netuid); - let sqrt_pa: SqrtPrice = position - .tick_low - .try_to_sqrt_price() - .map_err(|_| Error::::InvalidTickRange)?; - let sqrt_pb: SqrtPrice = position - .tick_high - .try_to_sqrt_price() - .map_err(|_| Error::::InvalidTickRange)?; - let sqrt_price_box = if current_price_sqrt < sqrt_pa { - sqrt_pa - } else if current_price_sqrt > sqrt_pb { - sqrt_pb - } else { - // Update current liquidity if price is in range - let new_liquidity_curr = if liquidity_delta > 0 { - CurrentLiquidity::::get(netuid).saturating_add(delta_liquidity_abs) - } else { - CurrentLiquidity::::get(netuid).saturating_sub(delta_liquidity_abs) - }; - CurrentLiquidity::::set(netuid, new_liquidity_curr); - current_price_sqrt - }; - - // Calculate token amounts for the liquidity change - let mul = SqrtPrice::from_num(1) - .safe_div(sqrt_price_box) - .saturating_sub(SqrtPrice::from_num(1).safe_div(sqrt_pb)); - let alpha = SqrtPrice::saturating_from_num(delta_liquidity_abs).saturating_mul(mul); - let tao = SqrtPrice::saturating_from_num(delta_liquidity_abs) - .saturating_mul(sqrt_price_box.saturating_sub(sqrt_pa)); - - // Validate delta - if liquidity_delta > 0 { - // Check that user has enough balances - ensure!( - T::BalanceOps::tao_balance(coldkey_account_id) - >= TaoCurrency::from(tao.saturating_to_num::()) - && T::BalanceOps::alpha_balance(netuid, coldkey_account_id, hotkey_account_id) - >= AlphaCurrency::from(alpha.saturating_to_num::()), - Error::::InsufficientBalance - ); - } else { - // Check that position has enough liquidity - ensure!( - position.liquidity >= delta_liquidity_abs, - Error::::InsufficientLiquidity - ); - } - - // Collect fees - let (fee_tao, fee_alpha) = position.collect_fees(); - - // If delta brings the position liquidity below MinimumLiquidity, eliminate position and - // withdraw full amounts - let mut remove = false; - if (liquidity_delta < 0) - && (position.liquidity.saturating_sub(delta_liquidity_abs) < T::MinimumLiquidity::get()) - { - delta_liquidity_abs = position.liquidity; - remove = true; - } - - // Adjust liquidity at the ticks based on the delta sign - if liquidity_delta > 0 { - // Add liquidity at tick - Self::add_liquidity_at_index(netuid, position.tick_low, delta_liquidity_abs, false); - Self::add_liquidity_at_index(netuid, position.tick_high, delta_liquidity_abs, true); - - // Add liquidity to user position - position.liquidity = position.liquidity.saturating_add(delta_liquidity_abs); - } else { - // Remove liquidity at tick - Self::remove_liquidity_at_index(netuid, position.tick_low, delta_liquidity_abs, false); - Self::remove_liquidity_at_index(netuid, position.tick_high, delta_liquidity_abs, true); - - // Remove liquidity from user position - position.liquidity = position.liquidity.saturating_sub(delta_liquidity_abs); - } - - // Update or, in case if full liquidity is removed, remove the position - if remove { - Positions::::remove((netuid, coldkey_account_id, position_id)); - } else { - Positions::::insert(&(netuid, coldkey_account_id, position.id), position.clone()); - } - - Ok(UpdateLiquidityResult { - tao: tao.saturating_to_num::().into(), - alpha: alpha.saturating_to_num::().into(), - fee_tao: fee_tao.into(), - fee_alpha: fee_alpha.into(), - removed: remove, - tick_low: position.tick_low, - tick_high: position.tick_high, - }) + todo!(); } - /// Adds or updates liquidity at a specific tick index for a subnet - /// - /// # Arguments - /// * `netuid` - The subnet ID - /// * `tick_index` - The tick index to add liquidity to - /// * `liquidity` - The amount of liquidity to add - fn add_liquidity_at_index(netuid: NetUid, tick_index: TickIndex, liquidity: u64, upper: bool) { - // Convert liquidity to signed value, negating it for upper bounds - let net_liquidity_change = if upper { - (liquidity as i128).neg() - } else { - liquidity as i128 - }; - - Ticks::::mutate(netuid, tick_index, |maybe_tick| match maybe_tick { - Some(tick) => { - tick.liquidity_net = tick.liquidity_net.saturating_add(net_liquidity_change); - tick.liquidity_gross = tick.liquidity_gross.saturating_add(liquidity); - } - None => { - let current_tick = TickIndex::current_bounded::(netuid); - - let (fees_out_tao, fees_out_alpha) = if tick_index > current_tick { - ( - I64F64::saturating_from_num(FeeGlobalTao::::get(netuid)), - I64F64::saturating_from_num(FeeGlobalAlpha::::get(netuid)), - ) - } else { - ( - I64F64::saturating_from_num(0), - I64F64::saturating_from_num(0), - ) - }; - *maybe_tick = Some(Tick { - liquidity_net: net_liquidity_change, - liquidity_gross: liquidity, - fees_out_tao, - fees_out_alpha, - }); - } - }); - - // Update active ticks - ActiveTickIndexManager::::insert(netuid, tick_index); - } - - /// Remove liquidity at tick index. - fn remove_liquidity_at_index( - netuid: NetUid, - tick_index: TickIndex, - liquidity: u64, - upper: bool, - ) { - // Calculate net liquidity addition - let net_reduction = if upper { - (liquidity as i128).neg() - } else { - liquidity as i128 - }; - - Ticks::::mutate_exists(netuid, tick_index, |maybe_tick| { - if let Some(tick) = maybe_tick { - tick.liquidity_net = tick.liquidity_net.saturating_sub(net_reduction); - tick.liquidity_gross = tick.liquidity_gross.saturating_sub(liquidity); - - // If no liquidity is left at the tick, remove it - if tick.liquidity_gross == 0 { - *maybe_tick = None; - - // Update active ticks: Final liquidity is zero, remove this tick from active. - ActiveTickIndexManager::::remove(netuid, tick_index); - } - } - }); - } + // /// Returns the number of positions for an account in a specific subnet + // /// + // /// # Arguments + // /// * `netuid` - The subnet ID + // /// * `account_id` - The account ID + // /// + // /// # Returns + // /// The number of positions that the account has in the specified subnet + // pub(super) fn count_positions(netuid: NetUid, account_id: &T::AccountId) -> usize { + // PositionsV2::::iter_prefix_values((netuid, account_id.clone())).count() + // } - /// Updates the current liquidity for a subnet if the current tick index is within the specified - /// range - /// - /// This function handles both increasing and decreasing liquidity based on the sign of the - /// liquidity parameter. It uses i128 to safely handle values up to u64::MAX in both positive - /// and negative directions. - fn update_liquidity_if_needed( - netuid: NetUid, - tick_low: TickIndex, - tick_high: TickIndex, - liquidity: i128, - ) { - let current_tick_index = TickIndex::current_bounded::(netuid); - if (tick_low <= current_tick_index) && (current_tick_index < tick_high) { - CurrentLiquidity::::mutate(netuid, |current_liquidity| { - let is_neg = liquidity.is_negative(); - let liquidity = liquidity.abs().min(u64::MAX as i128) as u64; - if is_neg { - *current_liquidity = current_liquidity.saturating_sub(liquidity); - } else { - *current_liquidity = current_liquidity.saturating_add(liquidity); - } - }); - } - } - - /// Clamps the subnet's sqrt price when tick index is outside of valid bounds - fn clamp_sqrt_price(netuid: NetUid, tick_index: TickIndex) { - if tick_index >= TickIndex::MAX || tick_index <= TickIndex::MIN { - let corrected_price = tick_index.as_sqrt_price_bounded(); - AlphaSqrtPrice::::set(netuid, corrected_price); - } + pub(crate) fn min_price_inner() -> C { + u64::from(1_000_u64) + .into() } - /// Returns the number of positions for an account in a specific subnet - /// - /// # Arguments - /// * `netuid` - The subnet ID - /// * `account_id` - The account ID - /// - /// # Returns - /// The number of positions that the account has in the specified subnet - pub(super) fn count_positions(netuid: NetUid, account_id: &T::AccountId) -> usize { - Positions::::iter_prefix_values((netuid, account_id.clone())).count() - } + pub(crate) fn max_price_inner() -> C { + u64::from(1_000_000_000_000_000_u64) + .into() + } /// Returns the protocol account ID /// @@ -810,213 +339,200 @@ impl Pallet { T::ProtocolId::get().into_account_truncating() } - pub(crate) fn min_price_inner() -> C { - TickIndex::min_sqrt_price() - .saturating_mul(TickIndex::min_sqrt_price()) - .saturating_mul(SqrtPrice::saturating_from_num(1_000_000_000)) - .saturating_to_num::() - .into() - } - - pub(crate) fn max_price_inner() -> C { - TickIndex::max_sqrt_price() - .saturating_mul(TickIndex::max_sqrt_price()) - .saturating_mul(SqrtPrice::saturating_from_num(1_000_000_000)) - .saturating_round() - .saturating_to_num::() - .into() - } - /// Dissolve all LPs and clean state. - pub fn do_dissolve_all_liquidity_providers(netuid: NetUid) -> DispatchResult { - if SwapV3Initialized::::get(netuid) { - // 1) Snapshot only *non‑protocol* positions: (owner, position_id). - struct CloseItem { - owner: A, - pos_id: PositionId, - } - let protocol_account = Self::protocol_account_id(); - - let mut to_close: sp_std::vec::Vec> = sp_std::vec::Vec::new(); - for ((owner, pos_id), _pos) in Positions::::iter_prefix((netuid,)) { - 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(()); - } - - let mut user_refunded_tao = TaoCurrency::ZERO; - let mut user_staked_alpha = AlphaCurrency::ZERO; - - let trust: Vec = T::SubnetInfo::get_validator_trust(netuid.into()); - let permit: Vec = T::SubnetInfo::get_validator_permit(netuid.into()); - - // 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) => { - // α withdrawn from the pool = principal + accrued fees - let alpha_total_from_pool: AlphaCurrency = - rm.alpha.saturating_add(rm.fee_alpha); - - // ---------------- USER: refund τ and convert α → stake ---------------- - - // 1) Refund τ principal directly. - let tao_total_from_pool: TaoCurrency = rm.tao.saturating_add(rm.fee_tao); - if tao_total_from_pool > TaoCurrency::ZERO { - T::BalanceOps::increase_balance(&owner, tao_total_from_pool); - user_refunded_tao = - user_refunded_tao.saturating_add(tao_total_from_pool); - T::TaoReserve::decrease_provided(netuid, tao_total_from_pool); - } - - // 2) Stake ALL withdrawn α (principal + fees) to the best permitted validator. - if alpha_total_from_pool > AlphaCurrency::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", - ), - )?; - - // Stake α from LP owner (coldkey) to chosen validator (hotkey). - T::BalanceOps::increase_stake( - &owner, - &validator_hotkey, - netuid, - alpha_total_from_pool, - )?; - - 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); - } - } - Err(e) => { - log::debug!( - "dissolve_all_lp: force-close failed: netuid={netuid:?}, owner={owner:?}, pos_id={pos_id:?}, err={e:?}" - ); - 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(()); - } - - log::debug!( - "dissolve_all_liquidity_providers: netuid={netuid:?}, mode=V2-or-nonV3, leaving all liquidity/state intact" - ); - - Ok(()) + pub fn do_dissolve_all_liquidity_providers(_netuid: NetUid) -> DispatchResult { + // if PalSwapInitialized::::get(netuid) { + // // 1) Snapshot only *non‑protocol* positions: (owner, position_id). + // struct CloseItem { + // owner: A, + // pos_id: PositionId, + // } + // let protocol_account = Self::protocol_account_id(); + + // let mut to_close: sp_std::vec::Vec> = sp_std::vec::Vec::new(); + // for ((owner, pos_id), _pos) in Positions::::iter_prefix((netuid,)) { + // 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(()); + // } + + // let mut user_refunded_tao = TaoCurrency::ZERO; + // let mut user_staked_alpha = AlphaCurrency::ZERO; + + // let trust: Vec = T::SubnetInfo::get_validator_trust(netuid.into()); + // let permit: Vec = T::SubnetInfo::get_validator_permit(netuid.into()); + + // // 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) => { + // // α withdrawn from the pool = principal + accrued fees + // let alpha_total_from_pool: AlphaCurrency = + // rm.alpha.saturating_add(rm.fee_alpha); + + // // ---------------- USER: refund τ and convert α → stake ---------------- + + // // 1) Refund τ principal directly. + // let tao_total_from_pool: TaoCurrency = rm.tao.saturating_add(rm.fee_tao); + // if tao_total_from_pool > TaoCurrency::ZERO { + // T::BalanceOps::increase_balance(&owner, tao_total_from_pool); + // user_refunded_tao = + // user_refunded_tao.saturating_add(tao_total_from_pool); + // T::TaoReserve::decrease_provided(netuid, tao_total_from_pool); + // } + + // // 2) Stake ALL withdrawn α (principal + fees) to the best permitted validator. + // if alpha_total_from_pool > AlphaCurrency::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", + // ), + // )?; + + // // Stake α from LP owner (coldkey) to chosen validator (hotkey). + // T::BalanceOps::increase_stake( + // &owner, + // &validator_hotkey, + // netuid, + // alpha_total_from_pool, + // )?; + + // 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); + // } + // } + // Err(e) => { + // log::debug!( + // "dissolve_all_lp: force-close failed: netuid={netuid:?}, owner={owner:?}, pos_id={pos_id:?}, err={e:?}" + // ); + // 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(()); + // } + + // log::debug!( + // "dissolve_all_liquidity_providers: netuid={netuid:?}, mode=V2-or-nonV3, leaving all liquidity/state intact" + // ); + + // Ok(()) + + todo!(); } /// Clear **protocol-owned** liquidity and wipe all swap state for `netuid`. - pub fn do_clear_protocol_liquidity(netuid: NetUid) -> DispatchResult { - let protocol_account = Self::protocol_account_id(); - - // 1) Force-close only protocol positions, burning proceeds. - let mut burned_tao = TaoCurrency::ZERO; - let mut burned_alpha = AlphaCurrency::ZERO; - - // Collect protocol position IDs first to avoid mutating while iterating. - let protocol_pos_ids: sp_std::vec::Vec = Positions::::iter_prefix((netuid,)) - .filter_map(|((owner, pos_id), _)| { - if owner == protocol_account { - Some(pos_id) - } else { - None - } - }) - .collect(); - - for pos_id in protocol_pos_ids { - match Self::do_remove_liquidity(netuid, &protocol_account, pos_id) { - Ok(rm) => { - let alpha_total_from_pool: AlphaCurrency = - rm.alpha.saturating_add(rm.fee_alpha); - let tao_total_from_pool: TaoCurrency = rm.tao.saturating_add(rm.fee_tao); - - if tao_total_from_pool > TaoCurrency::ZERO { - burned_tao = burned_tao.saturating_add(tao_total_from_pool); - } - if alpha_total_from_pool > AlphaCurrency::ZERO { - burned_alpha = burned_alpha.saturating_add(alpha_total_from_pool); - } - - log::debug!( - "clear_protocol_liquidity: burned protocol pos: netuid={netuid:?}, pos_id={pos_id:?}, τ={tao_total_from_pool:?}, α_total={alpha_total_from_pool:?}" - ); - } - Err(e) => { - log::debug!( - "clear_protocol_liquidity: force-close failed: netuid={netuid:?}, pos_id={pos_id:?}, err={e:?}" - ); - continue; - } - } - } - - // 2) Clear active tick index entries, then all swap state (idempotent even if empty/non‑V3). - let active_ticks: sp_std::vec::Vec = - Ticks::::iter_prefix(netuid).map(|(ti, _)| ti).collect(); - for ti in active_ticks { - ActiveTickIndexManager::::remove(netuid, ti); - } - - let _ = Positions::::clear_prefix((netuid,), u32::MAX, None); - let _ = Ticks::::clear_prefix(netuid, u32::MAX, None); - - FeeGlobalTao::::remove(netuid); - FeeGlobalAlpha::::remove(netuid); - CurrentLiquidity::::remove(netuid); - CurrentTick::::remove(netuid); - AlphaSqrtPrice::::remove(netuid); - SwapV3Initialized::::remove(netuid); - - let _ = TickIndexBitmapWords::::clear_prefix((netuid,), u32::MAX, None); - FeeRate::::remove(netuid); - EnabledUserLiquidity::::remove(netuid); - - log::debug!( - "clear_protocol_liquidity: netuid={netuid:?}, protocol_burned: τ={burned_tao:?}, α={burned_alpha:?}; state cleared" - ); - - Ok(()) + pub fn do_clear_protocol_liquidity(_netuid: NetUid) -> DispatchResult { + // let protocol_account = Self::protocol_account_id(); + + // // 1) Force-close only protocol positions, burning proceeds. + // let mut burned_tao = TaoCurrency::ZERO; + // let mut burned_alpha = AlphaCurrency::ZERO; + + // // Collect protocol position IDs first to avoid mutating while iterating. + // let protocol_pos_ids: sp_std::vec::Vec = Positions::::iter_prefix((netuid,)) + // .filter_map(|((owner, pos_id), _)| { + // if owner == protocol_account { + // Some(pos_id) + // } else { + // None + // } + // }) + // .collect(); + + // for pos_id in protocol_pos_ids { + // match Self::do_remove_liquidity(netuid, &protocol_account, pos_id) { + // Ok(rm) => { + // let alpha_total_from_pool: AlphaCurrency = + // rm.alpha.saturating_add(rm.fee_alpha); + // let tao_total_from_pool: TaoCurrency = rm.tao.saturating_add(rm.fee_tao); + + // if tao_total_from_pool > TaoCurrency::ZERO { + // burned_tao = burned_tao.saturating_add(tao_total_from_pool); + // } + // if alpha_total_from_pool > AlphaCurrency::ZERO { + // burned_alpha = burned_alpha.saturating_add(alpha_total_from_pool); + // } + + // log::debug!( + // "clear_protocol_liquidity: burned protocol pos: netuid={netuid:?}, pos_id={pos_id:?}, τ={tao_total_from_pool:?}, α_total={alpha_total_from_pool:?}" + // ); + // } + // Err(e) => { + // log::debug!( + // "clear_protocol_liquidity: force-close failed: netuid={netuid:?}, pos_id={pos_id:?}, err={e:?}" + // ); + // continue; + // } + // } + // } + + // // 2) Clear active tick index entries, then all swap state (idempotent even if empty/non‑V3). + // let active_ticks: sp_std::vec::Vec = + // Ticks::::iter_prefix(netuid).map(|(ti, _)| ti).collect(); + // for ti in active_ticks { + // ActiveTickIndexManager::::remove(netuid, ti); + // } + + // let _ = Positions::::clear_prefix((netuid,), u32::MAX, None); + // let _ = Ticks::::clear_prefix(netuid, u32::MAX, None); + + // FeeGlobalTao::::remove(netuid); + // FeeGlobalAlpha::::remove(netuid); + // CurrentLiquidity::::remove(netuid); + // CurrentTick::::remove(netuid); + // AlphaSqrtPrice::::remove(netuid); + // PalSwapInitialized::::remove(netuid); + + // let _ = TickIndexBitmapWords::::clear_prefix((netuid,), u32::MAX, None); + // FeeRate::::remove(netuid); + // EnabledUserLiquidity::::remove(netuid); + + // log::debug!( + // "clear_protocol_liquidity: netuid={netuid:?}, protocol_burned: τ={burned_tao:?}, α={burned_alpha:?}; state cleared" + // ); + + // Ok(()) + + todo!(); } } @@ -1046,15 +562,13 @@ where drop_fees: bool, should_rollback: bool, ) -> Result, DispatchError> { - let limit_sqrt_price = SqrtPrice::saturating_from_num(price_limit.to_u64()) - .safe_div(SqrtPrice::saturating_from_num(1_000_000_000)) - .checked_sqrt(SqrtPrice::saturating_from_num(0.0000000001)) - .ok_or(Error::::PriceLimitExceeded)?; + let limit_price = U64F64::saturating_from_num(price_limit.to_u64()) + .safe_div(U64F64::saturating_from_num(1_000_000_000_u64)); Self::do_swap::( NetUid::from(netuid), order, - limit_sqrt_price, + limit_price, drop_fees, should_rollback, ) @@ -1110,20 +624,20 @@ impl SwapHandler for Pallet { Self::calculate_fee_amount(netuid, amount, false) } - fn current_alpha_price(netuid: NetUid) -> U96F32 { + fn current_alpha_price(netuid: NetUid) -> U64F64 { Self::current_price(netuid.into()) } fn get_protocol_tao(netuid: NetUid) -> TaoCurrency { let protocol_account_id = Self::protocol_account_id(); let mut positions = - Positions::::iter_prefix_values((netuid, protocol_account_id.clone())) + PositionsV2::::iter_prefix_values((netuid, protocol_account_id.clone())) .collect::>(); if let Some(position) = positions.get_mut(0) { - let current_sqrt_price = AlphaSqrtPrice::::get(netuid); + let price = Self::current_price(netuid); // Adjust liquidity - let maybe_token_amounts = position.to_token_amounts(current_sqrt_price); + let maybe_token_amounts = position.to_token_amounts(price); if let Ok((tao, _)) = maybe_token_amounts { return tao.into(); } diff --git a/pallets/swap/src/pallet/mod.rs b/pallets/swap/src/pallet/mod.rs index 97a25ec242..66f82ed4af 100644 --- a/pallets/swap/src/pallet/mod.rs +++ b/pallets/swap/src/pallet/mod.rs @@ -3,20 +3,21 @@ use core::ops::Neg; use frame_support::{PalletId, pallet_prelude::*, traits::Get}; use frame_system::pallet_prelude::*; -use substrate_fixed::types::U64F64; +// use safe_math::SafeDiv; use subtensor_runtime_common::{ AlphaCurrency, BalanceOps, Currency, CurrencyReserve, NetUid, SubnetInfo, TaoCurrency, }; use crate::{ position::{Position, PositionId}, - tick::{LayerLevel, Tick, TickIndex}, + pallet::reserve_weights::ReserveWeight, weights::WeightInfo, }; pub use pallet::*; mod impls; +mod reserve_weights; mod swap_step; #[cfg(test)] mod tests; @@ -82,33 +83,33 @@ mod pallet { #[pallet::storage] pub type FeeRate = StorageMap<_, Twox64Concat, NetUid, u16, ValueQuery, DefaultFeeRate>; - // Global accrued fees in tao per subnet - #[pallet::storage] - pub type FeeGlobalTao = StorageMap<_, Twox64Concat, NetUid, U64F64, ValueQuery>; + // // Global accrued fees in tao per subnet + // #[pallet::storage] + // pub type FeeGlobalTao = StorageMap<_, Twox64Concat, NetUid, U64F64, ValueQuery>; - // Global accrued fees in alpha per subnet - #[pallet::storage] - pub type FeeGlobalAlpha = StorageMap<_, Twox64Concat, NetUid, U64F64, ValueQuery>; + // // Global accrued fees in alpha per subnet + // #[pallet::storage] + // pub type FeeGlobalAlpha = StorageMap<_, Twox64Concat, NetUid, U64F64, ValueQuery>; - /// Storage for all ticks, using subnet ID as the primary key and tick index as the secondary key - #[pallet::storage] - pub type Ticks = StorageDoubleMap<_, Twox64Concat, NetUid, Twox64Concat, TickIndex, Tick>; + // /// Storage for all ticks, using subnet ID as the primary key and tick index as the secondary key + // #[pallet::storage] + // pub type Ticks = StorageDoubleMap<_, Twox64Concat, NetUid, Twox64Concat, TickIndex, Tick>; - /// Storage to determine whether swap V3 was initialized for a specific subnet. - #[pallet::storage] - pub type SwapV3Initialized = StorageMap<_, Twox64Concat, NetUid, bool, ValueQuery>; + // /// Storage to determine whether swap V3 was initialized for a specific subnet. + // #[pallet::storage] + // pub type PalSwapInitialized = StorageMap<_, Twox64Concat, NetUid, bool, ValueQuery>; - /// Storage for the square root price of Alpha token for each subnet. - #[pallet::storage] - pub type AlphaSqrtPrice = StorageMap<_, Twox64Concat, NetUid, U64F64, ValueQuery>; + // /// Storage for the square root price of Alpha token for each subnet. + // #[pallet::storage] + // pub type AlphaSqrtPrice = StorageMap<_, Twox64Concat, NetUid, U64F64, ValueQuery>; - /// Storage for the current price tick. - #[pallet::storage] - pub type CurrentTick = StorageMap<_, Twox64Concat, NetUid, TickIndex, ValueQuery>; + // /// Storage for the current price tick. + // #[pallet::storage] + // pub type CurrentTick = StorageMap<_, Twox64Concat, NetUid, TickIndex, ValueQuery>; - /// Storage for the current liquidity amount for each subnet. - #[pallet::storage] - pub type CurrentLiquidity = StorageMap<_, Twox64Concat, NetUid, u64, ValueQuery>; + // /// Storage for the current liquidity amount for each subnet. + // #[pallet::storage] + // pub type CurrentLiquidity = StorageMap<_, Twox64Concat, NetUid, u64, ValueQuery>; /// Indicates whether a subnet has been switched to V3 swap from V2. /// If `true`, the subnet is permanently on V3 swap mode allowing add/remove liquidity @@ -119,7 +120,7 @@ mod pallet { /// Storage for user positions, using subnet ID and account ID as keys /// The value is a bounded vector of Position structs with details about the liquidity positions #[pallet::storage] - pub type Positions = StorageNMap< + pub type PositionsV2 = StorageNMap< _, ( NMapKey, // Subnet ID @@ -134,27 +135,44 @@ mod pallet { #[pallet::storage] pub type LastPositionId = StorageValue<_, u128, ValueQuery>; - /// Tick index bitmap words storage - #[pallet::storage] - pub type TickIndexBitmapWords = StorageNMap< - _, - ( - NMapKey, // Subnet ID - NMapKey, // Layer level - NMapKey, // word index - ), - u128, - ValueQuery, - >; - - /// TAO reservoir for scraps of protocol claimed fees. + // /// Tick index bitmap words storage + // #[pallet::storage] + // pub type TickIndexBitmapWords = StorageNMap< + // _, + // ( + // NMapKey, // Subnet ID + // NMapKey, // Layer level + // NMapKey, // word index + // ), + // u128, + // ValueQuery, + // >; + + // /// TAO reservoir for scraps of protocol claimed fees. + // #[pallet::storage] + // pub type ScrapReservoirTao = StorageMap<_, Twox64Concat, NetUid, TaoCurrency, ValueQuery>; + + // /// Alpha reservoir for scraps of protocol claimed fees. + // #[pallet::storage] + // pub type ScrapReservoirAlpha = + // StorageMap<_, Twox64Concat, NetUid, AlphaCurrency, ValueQuery>; + + //////////////////////////////////////////////////// + // Balancer (PalSwap) maps and variables + + /// Default reserve weight + #[pallet::type_value] + pub fn DefaultReserveWeight() -> ReserveWeight { + ReserveWeight::default() + } + /// u64-normalized reserve weight #[pallet::storage] - pub type ScrapReservoirTao = StorageMap<_, Twox64Concat, NetUid, TaoCurrency, ValueQuery>; + pub type SwapReserveWeight = + StorageMap<_, Twox64Concat, NetUid, ReserveWeight, ValueQuery, DefaultReserveWeight>; - /// Alpha reservoir for scraps of protocol claimed fees. + /// Storage to determine whether balancer swap was initialized for a specific subnet. #[pallet::storage] - pub type ScrapReservoirAlpha = - StorageMap<_, Twox64Concat, NetUid, AlphaCurrency, ValueQuery>; + pub type PalSwapInitialized = StorageMap<_, Twox64Concat, NetUid, bool, ValueQuery>; #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] @@ -167,7 +185,7 @@ mod pallet { UserLiquidityToggled { netuid: NetUid, enable: bool }, /// Event emitted when a liquidity position is added to a subnet's liquidity pool. - LiquidityAdded { + LiquidityAddedV2 { /// The coldkey account that owns the position coldkey: T::AccountId, /// The hotkey account where Alpha comes from @@ -182,14 +200,10 @@ mod pallet { tao: TaoCurrency, /// The amount of Alpha tokens committed to the position alpha: AlphaCurrency, - /// the lower tick - tick_low: TickIndex, - /// the upper tick - tick_high: TickIndex, }, /// Event emitted when a liquidity position is removed from a subnet's liquidity pool. - LiquidityRemoved { + LiquidityRemovedV2 { /// The coldkey account that owns the position coldkey: T::AccountId, /// The hotkey account where Alpha goes to @@ -208,15 +222,11 @@ mod pallet { fee_tao: TaoCurrency, /// The amount of Alpha fees earned from the position fee_alpha: AlphaCurrency, - /// the lower tick - tick_low: TickIndex, - /// the upper tick - tick_high: TickIndex, }, /// Event emitted when a liquidity position is modified in a subnet's liquidity pool. /// Modifying causes the fees to be claimed. - LiquidityModified { + LiquidityModifiedV2 { /// The coldkey account that owns the position coldkey: T::AccountId, /// The hotkey account where Alpha comes from or goes to @@ -235,10 +245,6 @@ mod pallet { fee_tao: TaoCurrency, /// The amount of Alpha fees earned from the position fee_alpha: AlphaCurrency, - /// the lower tick - tick_low: TickIndex, - /// the upper tick - tick_high: TickIndex, }, } @@ -373,8 +379,6 @@ mod pallet { origin: OriginFor, hotkey: T::AccountId, netuid: NetUid, - tick_low: TickIndex, - tick_high: TickIndex, liquidity: u64, ) -> DispatchResult { let coldkey = ensure_signed(origin)?; @@ -394,8 +398,6 @@ mod pallet { netuid.into(), &coldkey, &hotkey, - tick_low, - tick_high, liquidity, )?; let alpha = AlphaCurrency::from(alpha); @@ -414,7 +416,7 @@ mod pallet { T::AlphaReserve::increase_provided(netuid.into(), alpha_provided); // Emit an event - Self::deposit_event(Event::LiquidityAdded { + Self::deposit_event(Event::LiquidityAddedV2 { coldkey, hotkey, netuid, @@ -422,8 +424,6 @@ mod pallet { liquidity, tao, alpha, - tick_low, - tick_high, }); Ok(()) @@ -470,7 +470,7 @@ mod pallet { T::AlphaReserve::decrease_provided(netuid.into(), result.alpha); // Emit an event - Self::deposit_event(Event::LiquidityRemoved { + Self::deposit_event(Event::LiquidityRemovedV2 { coldkey, hotkey, netuid: netuid.into(), @@ -480,8 +480,6 @@ mod pallet { 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(), }); Ok(()) @@ -535,7 +533,7 @@ mod pallet { ); // Emit an event - Self::deposit_event(Event::LiquidityModified { + Self::deposit_event(Event::LiquidityModifiedV2 { coldkey: coldkey.clone(), hotkey: hotkey.clone(), netuid, @@ -545,8 +543,6 @@ mod pallet { 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 @@ -555,7 +551,7 @@ mod pallet { // Emit an event if result.removed { - Self::deposit_event(Event::LiquidityRemoved { + Self::deposit_event(Event::LiquidityRemovedV2 { coldkey: coldkey.clone(), hotkey: hotkey.clone(), netuid, @@ -565,11 +561,9 @@ mod pallet { 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 { + Self::deposit_event(Event::LiquidityModifiedV2 { coldkey: coldkey.clone(), hotkey: hotkey.clone(), netuid, @@ -579,8 +573,6 @@ mod pallet { 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, }); } } diff --git a/pallets/swap/src/pallet/reserve_weights.rs b/pallets/swap/src/pallet/reserve_weights.rs new file mode 100644 index 0000000000..3b7fb89a6c --- /dev/null +++ b/pallets/swap/src/pallet/reserve_weights.rs @@ -0,0 +1,57 @@ +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::pallet_prelude::*; +use safe_math::SafeDiv; +use subtensor_macros::freeze_struct; + +#[freeze_struct("6382cc997c2e2049")] +#[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] +pub struct ReserveWeight { + quote: u64, +} + +// Lower imit of weights is 0.01 +const MIN_WEIGHT: u64 = 184467440737095516; + +#[derive(Debug)] +pub enum ReserveWeightError { + InvalidValue, +} + +impl Default for ReserveWeight { + fn default() -> Self { + Self { + quote: u64::MAX.safe_div(2_u64) + } + } +} + +impl ReserveWeight { + pub fn new(quote: u64) -> Result { + if Self::check_constraints(quote) { + Ok(ReserveWeight { quote }) + } else { + Err(ReserveWeightError::InvalidValue) + } + } + + fn check_constraints(quote: u64) -> bool { + let base = u64::MAX.saturating_sub(quote); + (base >= MIN_WEIGHT) && (quote >= MIN_WEIGHT) + } + + pub fn get_quote_weight(&self) -> u64 { + self.quote + } + + pub fn get_base_weight(&self) -> u64 { + u64::MAX.saturating_sub(self.quote) + } + + pub fn set_quote_weight(&self, new_value: u64) -> Result<(), ReserveWeightError> { + if Self::check_constraints(new_value) { + Ok(()) + } else { + Err(ReserveWeightError::InvalidValue) + } + } +} diff --git a/pallets/swap/src/pallet/swap_step.rs b/pallets/swap/src/pallet/swap_step.rs index 6791835b1a..c7abebbb29 100644 --- a/pallets/swap/src/pallet/swap_step.rs +++ b/pallets/swap/src/pallet/swap_step.rs @@ -1,14 +1,10 @@ use core::marker::PhantomData; use safe_math::*; -use substrate_fixed::types::{I64F64, U64F64}; +use substrate_fixed::types::U64F64; use subtensor_runtime_common::{AlphaCurrency, Currency, NetUid, TaoCurrency}; use super::pallet::*; -use crate::{ - SqrtPrice, - tick::{ActiveTickIndexManager, TickIndex}, -}; /// A struct representing a single swap step with all its parameters and state pub(crate) struct BasicSwapStep @@ -20,22 +16,16 @@ where // Input parameters netuid: NetUid, drop_fees: bool, + requested_delta_in: PaidIn, + limit_price: U64F64, - // Computed values - current_liquidity: U64F64, - possible_delta_in: PaidIn, - - // Ticks and prices (current, limit, edge, target) - target_sqrt_price: SqrtPrice, - limit_sqrt_price: SqrtPrice, - current_sqrt_price: SqrtPrice, - edge_sqrt_price: SqrtPrice, - edge_tick: TickIndex, + // Intermediate calculations + target_price: U64F64, + current_price: U64F64, // Result values - action: SwapStepAction, delta_in: PaidIn, - final_price: SqrtPrice, + final_price: U64F64, fee: PaidIn, _phantom: PhantomData<(T, PaidIn, PaidOut)>, @@ -52,36 +42,26 @@ where pub(crate) fn new( netuid: NetUid, amount_remaining: PaidIn, - limit_sqrt_price: SqrtPrice, + limit_price: U64F64, drop_fees: bool, ) -> Self { - // Calculate prices and ticks - let current_tick = CurrentTick::::get(netuid); - let current_sqrt_price = AlphaSqrtPrice::::get(netuid); - let edge_tick = Self::tick_edge(netuid, current_tick); - let edge_sqrt_price = edge_tick.as_sqrt_price_bounded(); - let fee = Pallet::::calculate_fee_amount(netuid, amount_remaining, drop_fees); - let possible_delta_in = amount_remaining.saturating_sub(fee); + let requested_delta_in = amount_remaining.saturating_sub(fee); - // Target price and quantities - let current_liquidity = U64F64::saturating_from_num(CurrentLiquidity::::get(netuid)); - let target_sqrt_price = - Self::sqrt_price_target(current_liquidity, current_sqrt_price, possible_delta_in); + // Target and current prices + let target_price = + Self::price_target(requested_delta_in); + let current_price = Pallet::::current_price(netuid); Self { netuid, drop_fees, - target_sqrt_price, - limit_sqrt_price, - current_sqrt_price, - edge_sqrt_price, - edge_tick, - possible_delta_in, - current_liquidity, - action: SwapStepAction::Stop, + requested_delta_in, + limit_price, + target_price, + current_price, delta_in: PaidIn::ZERO, - final_price: target_sqrt_price, + final_price: target_price, fee, _phantom: PhantomData, } @@ -98,64 +78,38 @@ where let mut recalculate_fee = false; // Calculate the stopping price: The price at which we either reach the limit price, - // exchange the full amount, or reach the edge price. - if Self::price_is_closer(&self.target_sqrt_price, &self.limit_sqrt_price) - && Self::price_is_closer(&self.target_sqrt_price, &self.edge_sqrt_price) - { - // Case 1. target_quantity is the lowest - // The trade completely happens within one tick, no tick crossing happens. - self.action = SwapStepAction::Stop; - self.final_price = self.target_sqrt_price; - self.delta_in = self.possible_delta_in; - } else if Self::price_is_closer(&self.limit_sqrt_price, &self.target_sqrt_price) - && Self::price_is_closer(&self.limit_sqrt_price, &self.edge_sqrt_price) + // or exchange the full amount. + if Self::price_is_closer(&self.target_price, &self.limit_price) { - // Case 2. lim_quantity is the lowest - // The trade also completely happens within one tick, no tick crossing happens. - self.action = SwapStepAction::Stop; - self.final_price = self.limit_sqrt_price; - self.delta_in = Self::delta_in( - self.current_liquidity, - self.current_sqrt_price, - self.limit_sqrt_price, - ); - recalculate_fee = true; + // Case 1. target_quantity is the lowest, execute in full + self.final_price = self.target_price; + self.delta_in = self.requested_delta_in; } else { - // Case 3. edge_quantity is the lowest - // Tick crossing is likely - self.action = SwapStepAction::Crossing; + // Case 2. lim_quantity is the lowest + self.final_price = self.limit_price; self.delta_in = Self::delta_in( - self.current_liquidity, - self.current_sqrt_price, - self.edge_sqrt_price, + self.current_price, + self.limit_price, ); - self.final_price = self.edge_sqrt_price; recalculate_fee = true; } - log::trace!("\tAction : {:?}", self.action); log::trace!( "\tCurrent Price : {}", - self.current_sqrt_price - .saturating_mul(self.current_sqrt_price) + self.current_price ); log::trace!( "\tTarget Price : {}", - self.target_sqrt_price - .saturating_mul(self.target_sqrt_price) + self.target_price ); log::trace!( "\tLimit Price : {}", - self.limit_sqrt_price.saturating_mul(self.limit_sqrt_price) - ); - log::trace!( - "\tEdge Price : {}", - self.edge_sqrt_price.saturating_mul(self.edge_sqrt_price) + self.limit_price ); log::trace!("\tDelta In : {}", self.delta_in); // Because on step creation we calculate fee off the total amount, we might need to - // recalculate it in case if we hit the limit price or the edge price. + // recalculate it in case if we hit the limit price. if recalculate_fee { let u16_max = U64F64::saturating_from_num(u16::MAX); let fee_rate = if self.drop_fees { @@ -169,19 +123,6 @@ where .saturating_to_num::() .into(); } - - // Now correct the action if we stopped exactly at the edge no matter what was the case - // above. Because order type buy moves the price up and tick semi-open interval doesn't - // include its right point, we cross on buys and stop on sells. - let natural_reason_stop_price = - if Self::price_is_closer(&self.limit_sqrt_price, &self.target_sqrt_price) { - self.limit_sqrt_price - } else { - self.target_sqrt_price - }; - if natural_reason_stop_price == self.edge_sqrt_price { - self.action = Self::action_on_edge_sqrt_price(); - } } /// Process a single step of a swap @@ -189,159 +130,47 @@ where // Hold the fees Self::add_fees( self.netuid, - Pallet::::current_liquidity_safe(self.netuid), self.fee, ); - let delta_out = Self::convert_deltas(self.netuid, self.delta_in); - // log::trace!("\tDelta Out : {delta_out:?}"); - - if self.action == SwapStepAction::Crossing { - let mut tick = Ticks::::get(self.netuid, self.edge_tick).unwrap_or_default(); - tick.fees_out_tao = I64F64::saturating_from_num(FeeGlobalTao::::get(self.netuid)) - .saturating_sub(tick.fees_out_tao); - tick.fees_out_alpha = - I64F64::saturating_from_num(FeeGlobalAlpha::::get(self.netuid)) - .saturating_sub(tick.fees_out_alpha); - Self::update_liquidity_at_crossing(self.netuid)?; - Ticks::::insert(self.netuid, self.edge_tick, tick); - } - - // Update current price - AlphaSqrtPrice::::set(self.netuid, self.final_price); - // Update current tick - let new_current_tick = TickIndex::from_sqrt_price_bounded(self.final_price); - CurrentTick::::set(self.netuid, new_current_tick); + // Convert amounts, actual swap happens here + let delta_out = Self::convert_deltas(self.netuid, self.delta_in); + log::trace!("\tDelta Out : {delta_out}"); Ok(SwapStepResult { - amount_to_take: self.delta_in.saturating_add(self.fee), fee_paid: self.fee, delta_in: self.delta_in, delta_out, }) } - - pub(crate) fn action(&self) -> SwapStepAction { - self.action - } } impl SwapStep for BasicSwapStep { fn delta_in( - liquidity_curr: U64F64, - sqrt_price_curr: SqrtPrice, - sqrt_price_target: SqrtPrice, + _price_curr: U64F64, + _price_target: U64F64, ) -> TaoCurrency { - liquidity_curr - .saturating_mul(sqrt_price_target.saturating_sub(sqrt_price_curr)) - .saturating_to_num::() - .into() + todo!(); } - fn tick_edge(netuid: NetUid, current_tick: TickIndex) -> TickIndex { - ActiveTickIndexManager::::find_closest_higher( - netuid, - current_tick.next().unwrap_or(TickIndex::MAX), - ) - .unwrap_or(TickIndex::MAX) - } - - fn sqrt_price_target( - liquidity_curr: U64F64, - sqrt_price_curr: SqrtPrice, - delta_in: TaoCurrency, - ) -> SqrtPrice { - let delta_fixed = U64F64::saturating_from_num(delta_in); - - // No liquidity means that price should go to the limit - if liquidity_curr == 0 { - return SqrtPrice::saturating_from_num( - Pallet::::max_price_inner::().to_u64(), - ); - } - - delta_fixed - .safe_div(liquidity_curr) - .saturating_add(sqrt_price_curr) + fn price_target( + _delta_in: TaoCurrency, + ) -> U64F64 { + todo!(); } - fn price_is_closer(sq_price1: &SqrtPrice, sq_price2: &SqrtPrice) -> bool { - sq_price1 <= sq_price2 + fn price_is_closer(price1: &U64F64, price2: &U64F64) -> bool { + price1 <= price2 } - fn action_on_edge_sqrt_price() -> SwapStepAction { - SwapStepAction::Crossing + fn add_fees(_netuid: NetUid, _fee: TaoCurrency) { + todo!(); } - fn add_fees(netuid: NetUid, current_liquidity: U64F64, fee: TaoCurrency) { - if current_liquidity == 0 { - return; - } - - let fee_fixed = U64F64::saturating_from_num(fee.to_u64()); - - FeeGlobalTao::::mutate(netuid, |value| { - *value = value.saturating_add(fee_fixed.safe_div(current_liquidity)) - }); - } - - fn convert_deltas(netuid: NetUid, delta_in: TaoCurrency) -> AlphaCurrency { - // Skip conversion if delta_in is zero - if delta_in.is_zero() { - return AlphaCurrency::ZERO; - } - - let liquidity_curr = SqrtPrice::saturating_from_num(CurrentLiquidity::::get(netuid)); - let sqrt_price_curr = AlphaSqrtPrice::::get(netuid); - let delta_fixed = SqrtPrice::saturating_from_num(delta_in.to_u64()); - - // Calculate result based on order type with proper fixed-point math - // Using safe math operations throughout to prevent overflows - let result = { - // (liquidity_curr * sqrt_price_curr + delta_fixed) * sqrt_price_curr; - let a = liquidity_curr - .saturating_mul(sqrt_price_curr) - .saturating_add(delta_fixed) - .saturating_mul(sqrt_price_curr); - // liquidity_curr / a; - let b = liquidity_curr.safe_div(a); - // b * delta_fixed; - b.saturating_mul(delta_fixed) - }; - - result.saturating_to_num::().into() - } - - fn update_liquidity_at_crossing(netuid: NetUid) -> Result<(), Error> { - let mut liquidity_curr = CurrentLiquidity::::get(netuid); - let current_tick_index = TickIndex::current_bounded::(netuid); - - // Find the appropriate tick based on order type - let tick = { - // Self::find_closest_higher_active_tick(netuid, current_tick_index), - let upper_tick = ActiveTickIndexManager::::find_closest_higher( - netuid, - current_tick_index.next().unwrap_or(TickIndex::MAX), - ) - .unwrap_or(TickIndex::MAX); - Ticks::::get(netuid, upper_tick) - } - .ok_or(Error::::InsufficientLiquidity)?; - - let liquidity_update_abs_u64 = tick.liquidity_net_as_u64(); - - // Update liquidity based on the sign of liquidity_net and the order type - liquidity_curr = if tick.liquidity_net >= 0 { - liquidity_curr.saturating_add(liquidity_update_abs_u64) - } else { - liquidity_curr.saturating_sub(liquidity_update_abs_u64) - }; - - CurrentLiquidity::::set(netuid, liquidity_curr); - - Ok(()) + fn convert_deltas(_netuid: NetUid, _delta_in: TaoCurrency) -> AlphaCurrency { + todo!(); } } @@ -349,145 +178,28 @@ impl SwapStep for BasicSwapStep { fn delta_in( - liquidity_curr: U64F64, - sqrt_price_curr: SqrtPrice, - sqrt_price_target: SqrtPrice, + _price_curr: U64F64, + _price_target: U64F64, ) -> AlphaCurrency { - let one = U64F64::saturating_from_num(1); - - liquidity_curr - .saturating_mul( - one.safe_div(sqrt_price_target.into()) - .saturating_sub(one.safe_div(sqrt_price_curr)), - ) - .saturating_to_num::() - .into() + todo!(); } - fn tick_edge(netuid: NetUid, current_tick: TickIndex) -> TickIndex { - let current_price: SqrtPrice = AlphaSqrtPrice::::get(netuid); - let current_tick_price = current_tick.as_sqrt_price_bounded(); - let is_active = ActiveTickIndexManager::::tick_is_active(netuid, current_tick); - - if is_active && current_price > current_tick_price { - return ActiveTickIndexManager::::find_closest_lower(netuid, current_tick) - .unwrap_or(TickIndex::MIN); - } - - ActiveTickIndexManager::::find_closest_lower( - netuid, - current_tick.prev().unwrap_or(TickIndex::MIN), - ) - .unwrap_or(TickIndex::MIN) + fn price_target( + _delta_in: AlphaCurrency, + ) -> U64F64 { + todo!(); } - fn sqrt_price_target( - liquidity_curr: U64F64, - sqrt_price_curr: SqrtPrice, - delta_in: AlphaCurrency, - ) -> SqrtPrice { - let delta_fixed = U64F64::saturating_from_num(delta_in); - let one = U64F64::saturating_from_num(1); - - // No liquidity means that price should go to the limit - if liquidity_curr == 0 { - return SqrtPrice::saturating_from_num( - Pallet::::min_price_inner::().to_u64(), - ); - } - - one.safe_div( - delta_fixed - .safe_div(liquidity_curr) - .saturating_add(one.safe_div(sqrt_price_curr)), - ) - } - - fn price_is_closer(sq_price1: &SqrtPrice, sq_price2: &SqrtPrice) -> bool { - sq_price1 >= sq_price2 - } - - fn action_on_edge_sqrt_price() -> SwapStepAction { - SwapStepAction::Stop - } - - fn add_fees(netuid: NetUid, current_liquidity: U64F64, fee: AlphaCurrency) { - if current_liquidity == 0 { - return; - } - - let fee_fixed = U64F64::saturating_from_num(fee.to_u64()); - - FeeGlobalAlpha::::mutate(netuid, |value| { - *value = value.saturating_add(fee_fixed.safe_div(current_liquidity)) - }); + fn price_is_closer(price1: &U64F64, price2: &U64F64) -> bool { + price1 >= price2 } - fn convert_deltas(netuid: NetUid, delta_in: AlphaCurrency) -> TaoCurrency { - // Skip conversion if delta_in is zero - if delta_in.is_zero() { - return TaoCurrency::ZERO; - } - - let liquidity_curr = SqrtPrice::saturating_from_num(CurrentLiquidity::::get(netuid)); - let sqrt_price_curr = AlphaSqrtPrice::::get(netuid); - let delta_fixed = SqrtPrice::saturating_from_num(delta_in.to_u64()); - - // Calculate result based on order type with proper fixed-point math - // Using safe math operations throughout to prevent overflows - let result = { - // liquidity_curr / (liquidity_curr / sqrt_price_curr + delta_fixed); - let denom = liquidity_curr - .safe_div(sqrt_price_curr) - .saturating_add(delta_fixed); - let a = liquidity_curr.safe_div(denom); - // a * sqrt_price_curr; - let b = a.saturating_mul(sqrt_price_curr); - - // delta_fixed * b; - delta_fixed.saturating_mul(b) - }; - - result.saturating_to_num::().into() + fn add_fees(_netuid: NetUid, _fee: AlphaCurrency) { + todo!(); } - fn update_liquidity_at_crossing(netuid: NetUid) -> Result<(), Error> { - let mut liquidity_curr = CurrentLiquidity::::get(netuid); - let current_tick_index = TickIndex::current_bounded::(netuid); - - // Find the appropriate tick based on order type - let tick = { - // Self::find_closest_lower_active_tick(netuid, current_tick_index) - let current_price = AlphaSqrtPrice::::get(netuid); - let current_tick_price = current_tick_index.as_sqrt_price_bounded(); - let is_active = ActiveTickIndexManager::::tick_is_active(netuid, current_tick_index); - - let lower_tick = if is_active && current_price > current_tick_price { - ActiveTickIndexManager::::find_closest_lower(netuid, current_tick_index) - .unwrap_or(TickIndex::MIN) - } else { - ActiveTickIndexManager::::find_closest_lower( - netuid, - current_tick_index.prev().unwrap_or(TickIndex::MIN), - ) - .unwrap_or(TickIndex::MIN) - }; - Ticks::::get(netuid, lower_tick) - } - .ok_or(Error::::InsufficientLiquidity)?; - - let liquidity_update_abs_u64 = tick.liquidity_net_as_u64(); - - // Update liquidity based on the sign of liquidity_net and the order type - liquidity_curr = if tick.liquidity_net >= 0 { - liquidity_curr.saturating_sub(liquidity_update_abs_u64) - } else { - liquidity_curr.saturating_add(liquidity_update_abs_u64) - }; - - CurrentLiquidity::::set(netuid, liquidity_curr); - - Ok(()) + fn convert_deltas(_netuid: NetUid, _delta_in: AlphaCurrency) -> TaoCurrency { + todo!(); } } @@ -499,48 +211,29 @@ where { /// Get the input amount needed to reach the target price fn delta_in( - liquidity_curr: U64F64, - sqrt_price_curr: SqrtPrice, - sqrt_price_target: SqrtPrice, + price_curr: U64F64, + price_target: U64F64, ) -> PaidIn; - /// Get the tick at the current tick edge. - /// - /// If anything is wrong with tick math and it returns Err, we just abort the deal, i.e. return - /// the edge that is impossible to execute - fn tick_edge(netuid: NetUid, current_tick: TickIndex) -> TickIndex; - - /// Get the target square root price based on the input amount - /// - /// This is the price that would be reached if - /// - There are no liquidity positions other than protocol liquidity - /// - Full delta_in amount is executed - fn sqrt_price_target( - liquidity_curr: U64F64, - sqrt_price_curr: SqrtPrice, + /// Get the target price based on the input amount + fn price_target( delta_in: PaidIn, - ) -> SqrtPrice; + ) -> U64F64; - /// Returns True if sq_price1 is closer to the current price than sq_price2 + /// Returns True if price1 is closer to the current price than price2 /// in terms of order direction. - /// For buying: sq_price1 <= sq_price2 - /// For selling: sq_price1 >= sq_price2 - fn price_is_closer(sq_price1: &SqrtPrice, sq_price2: &SqrtPrice) -> bool; - - /// Get swap step action on the edge sqrt price. - fn action_on_edge_sqrt_price() -> SwapStepAction; + /// For buying: price1 <= price2 + /// For selling: price1 >= price2 + fn price_is_closer(price1: &U64F64, price2: &U64F64) -> bool; /// Add fees to the global fee counters - fn add_fees(netuid: NetUid, current_liquidity: U64F64, fee: PaidIn); + fn add_fees(netuid: NetUid, fee: PaidIn); /// Convert input amount (delta_in) to output amount (delta_out) /// - /// This is the core method of uniswap V3 that tells how much output token is given for an - /// amount of input token within one price tick. + /// This is the core method of the swap that tells how much output token is given for an + /// amount of input token fn convert_deltas(netuid: NetUid, delta_in: PaidIn) -> PaidOut; - - /// Update liquidity when crossing a tick - fn update_liquidity_at_crossing(netuid: NetUid) -> Result<(), Error>; } #[derive(Debug, PartialEq)] @@ -549,14 +242,7 @@ where PaidIn: Currency, PaidOut: Currency, { - pub(crate) amount_to_take: PaidIn, pub(crate) fee_paid: PaidIn, pub(crate) delta_in: PaidIn, pub(crate) delta_out: PaidOut, } - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum SwapStepAction { - Crossing, - Stop, -} diff --git a/pallets/swap/src/pallet/tests.rs b/pallets/swap/src/pallet/tests.rs index 4013248abb..7d94b6083e 100644 --- a/pallets/swap/src/pallet/tests.rs +++ b/pallets/swap/src/pallet/tests.rs @@ -6,76 +6,28 @@ )] use approx::assert_abs_diff_eq; -use frame_support::{assert_err, assert_noop, assert_ok}; -use sp_arithmetic::helpers_128bit; +use frame_support::{ + //assert_err, + assert_noop, assert_ok +}; +// use sp_arithmetic::helpers_128bit; use sp_runtime::DispatchError; -use substrate_fixed::types::U96F32; +use substrate_fixed::types::U64F64; use subtensor_runtime_common::NetUid; use subtensor_swap_interface::Order as OrderT; use super::*; use crate::pallet::swap_step::*; -use crate::{SqrtPrice, mock::*}; - -// this function is used to convert price (NON-SQRT price!) to TickIndex. it's only utility for -// testing, all the implementation logic is based on sqrt prices -fn price_to_tick(price: f64) -> TickIndex { - let price_sqrt: SqrtPrice = SqrtPrice::from_num(price.sqrt()); - // Handle potential errors in the conversion - match TickIndex::try_from_sqrt_price(price_sqrt) { - Ok(mut tick) => { - // Ensure the tick is within bounds - if tick > TickIndex::MAX { - tick = TickIndex::MAX; - } else if tick < TickIndex::MIN { - tick = TickIndex::MIN; - } - tick - } - // Default to a reasonable value when conversion fails - Err(_) => { - if price > 1.0 { - TickIndex::MAX - } else { - TickIndex::MIN - } - } - } -} - -fn get_ticked_prices_around_current_price() -> (f64, f64) { - // Get current price, ticks around it, and prices on the tick edges for test cases - let netuid = NetUid::from(1); - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - let current_tick = CurrentTick::::get(netuid); +use crate::mock::*; - // Low and high prices that match to a lower and higher tick that doesn't contain the current price - let current_price_low_sqrt = current_tick.as_sqrt_price_bounded(); - let current_price_high_sqrt = current_tick.next().unwrap().as_sqrt_price_bounded(); - let current_price_low = U96F32::from_num(current_price_low_sqrt * current_price_low_sqrt); - let current_price_high = U96F32::from_num(current_price_high_sqrt * current_price_high_sqrt); - - ( - current_price_low.to_num::(), - current_price_high.to_num::() + 0.000000001, - ) +#[allow(dead_code)] +fn get_min_price() -> U64F64 { + U64F64::saturating_from_num(Pallet::::min_price_inner::()) } -// this function is used to convert tick index NON-SQRT (!) price. it's only utility for -// testing, all the implementation logic is based on sqrt prices -fn tick_to_price(tick: TickIndex) -> f64 { - // Handle errors gracefully - match tick.try_to_sqrt_price() { - Ok(price_sqrt) => (price_sqrt * price_sqrt).to_num::(), - Err(_) => { - // Return a sensible default based on whether the tick is above or below the valid range - if tick > TickIndex::MAX { - tick_to_price(TickIndex::MAX) // Use the max valid tick price - } else { - tick_to_price(TickIndex::MIN) // Use the min valid tick price - } - } - } +#[allow(dead_code)] +fn get_max_price() -> U64F64 { + U64F64::saturating_from_num(Pallet::::max_price_inner::()) } mod dispatchables { @@ -158,1129 +110,1130 @@ fn test_swap_initialization() { let netuid = NetUid::from(1); // Get reserves from the mock provider - let tao = TaoReserve::reserve(netuid.into()); - let alpha = AlphaReserve::reserve(netuid.into()); + // let tao = TaoReserve::reserve(netuid.into()); + // let alpha = AlphaReserve::reserve(netuid.into()); - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - assert!(SwapV3Initialized::::get(netuid)); + assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + assert!(PalSwapInitialized::::get(netuid)); // Verify current price is set - let sqrt_price = AlphaSqrtPrice::::get(netuid); - let expected_sqrt_price = U64F64::from_num(0.5_f64); + let price = Pallet::::current_price(netuid); + let expected_price = U64F64::from_num(0.5_f64); assert_abs_diff_eq!( - sqrt_price.to_num::(), - expected_sqrt_price.to_num::(), + price.to_num::(), + expected_price.to_num::(), epsilon = 0.000000001 ); - // Verify that current tick is set - let current_tick = CurrentTick::::get(netuid); - let expected_current_tick = TickIndex::from_sqrt_price_bounded(expected_sqrt_price); - assert_eq!(current_tick, expected_current_tick); - - // Calculate expected liquidity - let expected_liquidity = - helpers_128bit::sqrt((tao.to_u64() as u128).saturating_mul(alpha.to_u64() as u128)) - as u64; - - // Get the protocol account - let protocol_account_id = Pallet::::protocol_account_id(); - - // Verify position created for protocol account - let positions = Positions::::iter_prefix_values((netuid, protocol_account_id)) - .collect::>(); - assert_eq!(positions.len(), 1); - - let position = &positions[0]; - assert_eq!(position.liquidity, expected_liquidity); - assert_eq!(position.tick_low, TickIndex::MIN); - assert_eq!(position.tick_high, TickIndex::MAX); - assert_eq!(position.fees_tao, 0); - assert_eq!(position.fees_alpha, 0); - - // Verify ticks were created - let tick_low = Ticks::::get(netuid, TickIndex::MIN).unwrap(); - let tick_high = Ticks::::get(netuid, TickIndex::MAX).unwrap(); - - // Check liquidity values - assert_eq!(tick_low.liquidity_net, expected_liquidity as i128); - assert_eq!(tick_low.liquidity_gross, expected_liquidity); - assert_eq!(tick_high.liquidity_net, -(expected_liquidity as i128)); - assert_eq!(tick_high.liquidity_gross, expected_liquidity); - - // Verify current liquidity is set - assert_eq!(CurrentLiquidity::::get(netuid), expected_liquidity); + // // Calculate expected liquidity + // let expected_liquidity = + // helpers_128bit::sqrt((tao.to_u64() as u128).saturating_mul(alpha.to_u64() as u128)) + // as u64; + + // // Get the protocol account + // let protocol_account_id = Pallet::::protocol_account_id(); + + // // Verify position created for protocol account + // let positions = Positions::::iter_prefix_values((netuid, protocol_account_id)) + // .collect::>(); + // assert_eq!(positions.len(), 1); + + // let position = &positions[0]; + // assert_eq!(position.liquidity, expected_liquidity); + // assert_eq!(position.fees_tao, 0); + // assert_eq!(position.fees_alpha, 0); + + todo!(); }); } // Test adding liquidity on top of the existing protocol liquidity #[test] fn test_add_liquidity_basic() { - new_test_ext().execute_with(|| { - let min_price = tick_to_price(TickIndex::MIN); - let max_price = tick_to_price(TickIndex::MAX); - let max_tick = price_to_tick(max_price); - assert_eq!(max_tick, TickIndex::MAX); - - assert_ok!(Pallet::::maybe_initialize_v3(NetUid::from(1))); - let current_price = Pallet::::current_price(NetUid::from(1)).to_num::(); - let (current_price_low, current_price_high) = get_ticked_prices_around_current_price(); - - // As a user add liquidity with all possible corner cases - // - Initial price is 0.25 - // - liquidity is expressed in RAO units - // Test case is (price_low, price_high, liquidity, tao, alpha) - [ - // Repeat the protocol liquidity at maximum range: Expect all the same values - ( - min_price, - max_price, - 2_000_000_000_u64, - 1_000_000_000_u64, - 4_000_000_000_u64, - ), - // Repeat the protocol liquidity at current to max range: Expect the same alpha - ( - current_price_high, - max_price, - 2_000_000_000_u64, - 0, - 4_000_000_000, - ), - // Repeat the protocol liquidity at min to current range: Expect all the same tao - ( - min_price, - current_price_low, - 2_000_000_000_u64, - 1_000_000_000, - 0, - ), - // Half to double price - just some sane wothdraw amounts - (0.125, 0.5, 2_000_000_000_u64, 293_000_000, 1_171_000_000), - // Both below price - tao is non-zero, alpha is zero - (0.12, 0.13, 2_000_000_000_u64, 28_270_000, 0), - // Both above price - tao is zero, alpha is non-zero - (0.3, 0.4, 2_000_000_000_u64, 0, 489_200_000), - ] - .into_iter() - .enumerate() - .map(|(n, v)| (NetUid::from(n as u16 + 1), v.0, v.1, v.2, v.3, v.4)) - .for_each( - |(netuid, price_low, price_high, liquidity, expected_tao, expected_alpha)| { - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - // Calculate ticks (assuming tick math is tested separately) - let tick_low = price_to_tick(price_low); - let tick_high = price_to_tick(price_high); - - // Get tick infos and liquidity before adding (to account for protocol liquidity) - let tick_low_info_before = Ticks::::get(netuid, tick_low).unwrap_or_default(); - let tick_high_info_before = - Ticks::::get(netuid, tick_high).unwrap_or_default(); - let liquidity_before = CurrentLiquidity::::get(netuid); - - // Add liquidity - let (position_id, tao, alpha) = Pallet::::do_add_liquidity( - netuid, - &OK_COLDKEY_ACCOUNT_ID, - &OK_HOTKEY_ACCOUNT_ID, - tick_low, - tick_high, - liquidity, - ) - .unwrap(); - - assert_abs_diff_eq!(tao, expected_tao, epsilon = tao / 1000); - assert_abs_diff_eq!(alpha, expected_alpha, epsilon = alpha / 1000); - - // Check that low and high ticks appear in the state and are properly updated - let tick_low_info = Ticks::::get(netuid, tick_low).unwrap(); - let tick_high_info = Ticks::::get(netuid, tick_high).unwrap(); - let expected_liquidity_net_low = liquidity as i128; - let expected_liquidity_gross_low = liquidity; - let expected_liquidity_net_high = -(liquidity as i128); - let expected_liquidity_gross_high = liquidity; - - assert_eq!( - tick_low_info.liquidity_net - tick_low_info_before.liquidity_net, - expected_liquidity_net_low, - ); - assert_eq!( - tick_low_info.liquidity_gross - tick_low_info_before.liquidity_gross, - expected_liquidity_gross_low, - ); - assert_eq!( - tick_high_info.liquidity_net - tick_high_info_before.liquidity_net, - expected_liquidity_net_high, - ); - assert_eq!( - tick_high_info.liquidity_gross - tick_high_info_before.liquidity_gross, - expected_liquidity_gross_high, - ); - - // Liquidity position at correct ticks - assert_eq!( - Pallet::::count_positions(netuid, &OK_COLDKEY_ACCOUNT_ID), - 1 - ); - - let position = - Positions::::get((netuid, OK_COLDKEY_ACCOUNT_ID, position_id)).unwrap(); - assert_eq!(position.liquidity, liquidity); - assert_eq!(position.tick_low, tick_low); - assert_eq!(position.tick_high, tick_high); - assert_eq!(position.fees_alpha, 0); - assert_eq!(position.fees_tao, 0); - - // Current liquidity is updated only when price range includes the current price - let expected_liquidity = - if (price_high > current_price) && (price_low <= current_price) { - liquidity_before + liquidity - } else { - liquidity_before - }; - - assert_eq!(CurrentLiquidity::::get(netuid), expected_liquidity) - }, - ); - }); + todo!(); + + // new_test_ext().execute_with(|| { + // let min_price = tick_to_price(TickIndex::MIN); + // let max_price = tick_to_price(TickIndex::MAX); + // let max_tick = price_to_tick(max_price); + // assert_eq!(max_tick, TickIndex::MAX); + + // assert_ok!(Pallet::::maybe_initialize_palswap(NetUid::from(1))); + // let current_price = Pallet::::current_price(NetUid::from(1)).to_num::(); + // let (current_price_low, current_price_high) = get_ticked_prices_around_current_price(); + + // // As a user add liquidity with all possible corner cases + // // - Initial price is 0.25 + // // - liquidity is expressed in RAO units + // // Test case is (price_low, price_high, liquidity, tao, alpha) + // [ + // // Repeat the protocol liquidity at maximum range: Expect all the same values + // ( + // min_price, + // max_price, + // 2_000_000_000_u64, + // 1_000_000_000_u64, + // 4_000_000_000_u64, + // ), + // // Repeat the protocol liquidity at current to max range: Expect the same alpha + // ( + // current_price_high, + // max_price, + // 2_000_000_000_u64, + // 0, + // 4_000_000_000, + // ), + // // Repeat the protocol liquidity at min to current range: Expect all the same tao + // ( + // min_price, + // current_price_low, + // 2_000_000_000_u64, + // 1_000_000_000, + // 0, + // ), + // // Half to double price - just some sane wothdraw amounts + // (0.125, 0.5, 2_000_000_000_u64, 293_000_000, 1_171_000_000), + // // Both below price - tao is non-zero, alpha is zero + // (0.12, 0.13, 2_000_000_000_u64, 28_270_000, 0), + // // Both above price - tao is zero, alpha is non-zero + // (0.3, 0.4, 2_000_000_000_u64, 0, 489_200_000), + // ] + // .into_iter() + // .enumerate() + // .map(|(n, v)| (NetUid::from(n as u16 + 1), v.0, v.1, v.2, v.3, v.4)) + // .for_each( + // |(netuid, price_low, price_high, liquidity, expected_tao, expected_alpha)| { + // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + + // // Calculate ticks (assuming tick math is tested separately) + // let tick_low = price_to_tick(price_low); + // let tick_high = price_to_tick(price_high); + + // // Get tick infos and liquidity before adding (to account for protocol liquidity) + // let tick_low_info_before = Ticks::::get(netuid, tick_low).unwrap_or_default(); + // let tick_high_info_before = + // Ticks::::get(netuid, tick_high).unwrap_or_default(); + // let liquidity_before = CurrentLiquidity::::get(netuid); + + // // Add liquidity + // let (position_id, tao, alpha) = Pallet::::do_add_liquidity( + // netuid, + // &OK_COLDKEY_ACCOUNT_ID, + // &OK_HOTKEY_ACCOUNT_ID, + // tick_low, + // tick_high, + // liquidity, + // ) + // .unwrap(); + + // assert_abs_diff_eq!(tao, expected_tao, epsilon = tao / 1000); + // assert_abs_diff_eq!(alpha, expected_alpha, epsilon = alpha / 1000); + + // // Check that low and high ticks appear in the state and are properly updated + // let tick_low_info = Ticks::::get(netuid, tick_low).unwrap(); + // let tick_high_info = Ticks::::get(netuid, tick_high).unwrap(); + // let expected_liquidity_net_low = liquidity as i128; + // let expected_liquidity_gross_low = liquidity; + // let expected_liquidity_net_high = -(liquidity as i128); + // let expected_liquidity_gross_high = liquidity; + + // assert_eq!( + // tick_low_info.liquidity_net - tick_low_info_before.liquidity_net, + // expected_liquidity_net_low, + // ); + // assert_eq!( + // tick_low_info.liquidity_gross - tick_low_info_before.liquidity_gross, + // expected_liquidity_gross_low, + // ); + // assert_eq!( + // tick_high_info.liquidity_net - tick_high_info_before.liquidity_net, + // expected_liquidity_net_high, + // ); + // assert_eq!( + // tick_high_info.liquidity_gross - tick_high_info_before.liquidity_gross, + // expected_liquidity_gross_high, + // ); + + // // Liquidity position at correct ticks + // assert_eq!( + // Pallet::::count_positions(netuid, &OK_COLDKEY_ACCOUNT_ID), + // 1 + // ); + + // let position = + // Positions::::get((netuid, OK_COLDKEY_ACCOUNT_ID, position_id)).unwrap(); + // assert_eq!(position.liquidity, liquidity); + // assert_eq!(position.tick_low, tick_low); + // assert_eq!(position.tick_high, tick_high); + // assert_eq!(position.fees_alpha, 0); + // assert_eq!(position.fees_tao, 0); + + // // Current liquidity is updated only when price range includes the current price + // let expected_liquidity = + // if (price_high > current_price) && (price_low <= current_price) { + // liquidity_before + liquidity + // } else { + // liquidity_before + // }; + + // assert_eq!(CurrentLiquidity::::get(netuid), expected_liquidity) + // }, + // ); + // }); } #[test] fn test_add_liquidity_max_limit_enforced() { - new_test_ext().execute_with(|| { - let netuid = NetUid::from(1); - let liquidity = 2_000_000_000_u64; - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - let limit = MaxPositions::get() as usize; - - for _ in 0..limit { - Pallet::::do_add_liquidity( - netuid, - &OK_COLDKEY_ACCOUNT_ID, - &OK_HOTKEY_ACCOUNT_ID, - TickIndex::MIN, - TickIndex::MAX, - liquidity, - ) - .unwrap(); - } - - let test_result = Pallet::::do_add_liquidity( - netuid, - &OK_COLDKEY_ACCOUNT_ID, - &OK_HOTKEY_ACCOUNT_ID, - TickIndex::MIN, - TickIndex::MAX, - liquidity, - ); - - assert_err!(test_result, Error::::MaxPositionsExceeded); - }); + todo!(); + + // new_test_ext().execute_with(|| { + // let netuid = NetUid::from(1); + // let liquidity = 2_000_000_000_u64; + // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + + // let limit = MaxPositions::get() as usize; + + // for _ in 0..limit { + // Pallet::::do_add_liquidity( + // netuid, + // &OK_COLDKEY_ACCOUNT_ID, + // &OK_HOTKEY_ACCOUNT_ID, + // TickIndex::MIN, + // TickIndex::MAX, + // liquidity, + // ) + // .unwrap(); + // } + + // let test_result = Pallet::::do_add_liquidity( + // netuid, + // &OK_COLDKEY_ACCOUNT_ID, + // &OK_HOTKEY_ACCOUNT_ID, + // TickIndex::MIN, + // TickIndex::MAX, + // liquidity, + // ); + + // assert_err!(test_result, Error::::MaxPositionsExceeded); + // }); } #[test] fn test_add_liquidity_out_of_bounds() { - new_test_ext().execute_with(|| { - [ - // For our tests, we'll construct TickIndex values that are intentionally - // outside the valid range for testing purposes only - ( - TickIndex::new_unchecked(TickIndex::MIN.get() - 1), - TickIndex::MAX, - 1_000_000_000_u64, - ), - ( - TickIndex::MIN, - TickIndex::new_unchecked(TickIndex::MAX.get() + 1), - 1_000_000_000_u64, - ), - ( - TickIndex::new_unchecked(TickIndex::MIN.get() - 1), - TickIndex::new_unchecked(TickIndex::MAX.get() + 1), - 1_000_000_000_u64, - ), - ( - TickIndex::new_unchecked(TickIndex::MIN.get() - 100), - TickIndex::new_unchecked(TickIndex::MAX.get() + 100), - 1_000_000_000_u64, - ), - // Inverted ticks: high < low - ( - TickIndex::new_unchecked(-900), - TickIndex::new_unchecked(-1000), - 1_000_000_000_u64, - ), - // Equal ticks: high == low - ( - TickIndex::new_unchecked(-10_000), - TickIndex::new_unchecked(-10_000), - 1_000_000_000_u64, - ), - ] - .into_iter() - .enumerate() - .map(|(n, v)| (NetUid::from(n as u16 + 1), v.0, v.1, v.2)) - .for_each(|(netuid, tick_low, tick_high, liquidity)| { - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - // Add liquidity - assert_err!( - Swap::do_add_liquidity( - netuid, - &OK_COLDKEY_ACCOUNT_ID, - &OK_HOTKEY_ACCOUNT_ID, - tick_low, - tick_high, - liquidity - ), - Error::::InvalidTickRange, - ); - }); - }); + todo!(); + + // new_test_ext().execute_with(|| { + // [ + // // For our tests, we'll construct TickIndex values that are intentionally + // // outside the valid range for testing purposes only + // ( + // TickIndex::new_unchecked(TickIndex::MIN.get() - 1), + // TickIndex::MAX, + // 1_000_000_000_u64, + // ), + // ( + // TickIndex::MIN, + // TickIndex::new_unchecked(TickIndex::MAX.get() + 1), + // 1_000_000_000_u64, + // ), + // ( + // TickIndex::new_unchecked(TickIndex::MIN.get() - 1), + // TickIndex::new_unchecked(TickIndex::MAX.get() + 1), + // 1_000_000_000_u64, + // ), + // ( + // TickIndex::new_unchecked(TickIndex::MIN.get() - 100), + // TickIndex::new_unchecked(TickIndex::MAX.get() + 100), + // 1_000_000_000_u64, + // ), + // // Inverted ticks: high < low + // ( + // TickIndex::new_unchecked(-900), + // TickIndex::new_unchecked(-1000), + // 1_000_000_000_u64, + // ), + // // Equal ticks: high == low + // ( + // TickIndex::new_unchecked(-10_000), + // TickIndex::new_unchecked(-10_000), + // 1_000_000_000_u64, + // ), + // ] + // .into_iter() + // .enumerate() + // .map(|(n, v)| (NetUid::from(n as u16 + 1), v.0, v.1, v.2)) + // .for_each(|(netuid, tick_low, tick_high, liquidity)| { + // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + + // // Add liquidity + // assert_err!( + // Swap::do_add_liquidity( + // netuid, + // &OK_COLDKEY_ACCOUNT_ID, + // &OK_HOTKEY_ACCOUNT_ID, + // tick_low, + // tick_high, + // liquidity + // ), + // Error::::InvalidTickRange, + // ); + // }); + // }); } #[test] fn test_add_liquidity_over_balance() { - new_test_ext().execute_with(|| { - let coldkey_account_id = 3; - let hotkey_account_id = 1002; - - [ - // Lower than price (not enough tao) - (0.1, 0.2, 100_000_000_000_u64), - // Higher than price (not enough alpha) - (0.3, 0.4, 100_000_000_000_u64), - // Around the price (not enough both) - (0.1, 0.4, 100_000_000_000_u64), - ] - .into_iter() - .enumerate() - .map(|(n, v)| (NetUid::from(n as u16 + 1), v.0, v.1, v.2)) - .for_each(|(netuid, price_low, price_high, liquidity)| { - // Calculate ticks - let tick_low = price_to_tick(price_low); - let tick_high = price_to_tick(price_high); - - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - // Add liquidity - assert_err!( - Pallet::::do_add_liquidity( - netuid, - &coldkey_account_id, - &hotkey_account_id, - tick_low, - tick_high, - liquidity - ), - Error::::InsufficientBalance, - ); - }); - }); + todo!(); + + // new_test_ext().execute_with(|| { + // let coldkey_account_id = 3; + // let hotkey_account_id = 1002; + + // [ + // // Lower than price (not enough tao) + // (0.1, 0.2, 100_000_000_000_u64), + // // Higher than price (not enough alpha) + // (0.3, 0.4, 100_000_000_000_u64), + // // Around the price (not enough both) + // (0.1, 0.4, 100_000_000_000_u64), + // ] + // .into_iter() + // .enumerate() + // .map(|(n, v)| (NetUid::from(n as u16 + 1), v.0, v.1, v.2)) + // .for_each(|(netuid, price_low, price_high, liquidity)| { + // // Calculate ticks + // let tick_low = price_to_tick(price_low); + // let tick_high = price_to_tick(price_high); + + // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + + // // Add liquidity + // assert_err!( + // Pallet::::do_add_liquidity( + // netuid, + // &coldkey_account_id, + // &hotkey_account_id, + // tick_low, + // tick_high, + // liquidity + // ), + // Error::::InsufficientBalance, + // ); + // }); + // }); } // cargo test --package pallet-subtensor-swap --lib -- pallet::impls::tests::test_remove_liquidity_basic --exact --show-output #[test] fn test_remove_liquidity_basic() { - new_test_ext().execute_with(|| { - let min_price = tick_to_price(TickIndex::MIN); - let max_price = tick_to_price(TickIndex::MAX); - let max_tick = price_to_tick(max_price); - assert_eq!(max_tick, TickIndex::MAX); - - let (current_price_low, current_price_high) = get_ticked_prices_around_current_price(); - - // As a user add liquidity with all possible corner cases - // - Initial price is 0.25 - // - liquidity is expressed in RAO units - // Test case is (price_low, price_high, liquidity, tao, alpha) - [ - // Repeat the protocol liquidity at maximum range: Expect all the same values - ( - min_price, - max_price, - 2_000_000_000_u64, - 1_000_000_000_u64, - 4_000_000_000_u64, - ), - // Repeat the protocol liquidity at current to max range: Expect the same alpha - ( - current_price_high, - max_price, - 2_000_000_000_u64, - 0, - 4_000_000_000, - ), - // Repeat the protocol liquidity at min to current range: Expect all the same tao - ( - min_price, - current_price_low, - 2_000_000_000_u64, - 1_000_000_000, - 0, - ), - // Half to double price - just some sane wothdraw amounts - (0.125, 0.5, 2_000_000_000_u64, 293_000_000, 1_171_000_000), - // Both below price - tao is non-zero, alpha is zero - (0.12, 0.13, 2_000_000_000_u64, 28_270_000, 0), - // Both above price - tao is zero, alpha is non-zero - (0.3, 0.4, 2_000_000_000_u64, 0, 489_200_000), - ] - .into_iter() - .enumerate() - .map(|(n, v)| (NetUid::from(n as u16 + 1), v.0, v.1, v.2, v.3, v.4)) - .for_each(|(netuid, price_low, price_high, liquidity, tao, alpha)| { - // Calculate ticks (assuming tick math is tested separately) - let tick_low = price_to_tick(price_low); - let tick_high = price_to_tick(price_high); - - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - let liquidity_before = CurrentLiquidity::::get(netuid); - - // Add liquidity - let (position_id, _, _) = Pallet::::do_add_liquidity( - netuid, - &OK_COLDKEY_ACCOUNT_ID, - &OK_HOTKEY_ACCOUNT_ID, - tick_low, - tick_high, - liquidity, - ) - .unwrap(); - - // Remove liquidity - let remove_result = - Pallet::::do_remove_liquidity(netuid, &OK_COLDKEY_ACCOUNT_ID, position_id) - .unwrap(); - assert_abs_diff_eq!(remove_result.tao.to_u64(), tao, epsilon = tao / 1000); - assert_abs_diff_eq!( - u64::from(remove_result.alpha), - alpha, - epsilon = alpha / 1000 - ); - assert_eq!(remove_result.fee_tao, TaoCurrency::ZERO); - assert_eq!(remove_result.fee_alpha, AlphaCurrency::ZERO); + todo!(); + + // new_test_ext().execute_with(|| { + // let min_price = tick_to_price(TickIndex::MIN); + // let max_price = tick_to_price(TickIndex::MAX); + // let max_tick = price_to_tick(max_price); + // assert_eq!(max_tick, TickIndex::MAX); + + // let (current_price_low, current_price_high) = get_ticked_prices_around_current_price(); + + // // As a user add liquidity with all possible corner cases + // // - Initial price is 0.25 + // // - liquidity is expressed in RAO units + // // Test case is (price_low, price_high, liquidity, tao, alpha) + // [ + // // Repeat the protocol liquidity at maximum range: Expect all the same values + // ( + // min_price, + // max_price, + // 2_000_000_000_u64, + // 1_000_000_000_u64, + // 4_000_000_000_u64, + // ), + // // Repeat the protocol liquidity at current to max range: Expect the same alpha + // ( + // current_price_high, + // max_price, + // 2_000_000_000_u64, + // 0, + // 4_000_000_000, + // ), + // // Repeat the protocol liquidity at min to current range: Expect all the same tao + // ( + // min_price, + // current_price_low, + // 2_000_000_000_u64, + // 1_000_000_000, + // 0, + // ), + // // Half to double price - just some sane wothdraw amounts + // (0.125, 0.5, 2_000_000_000_u64, 293_000_000, 1_171_000_000), + // // Both below price - tao is non-zero, alpha is zero + // (0.12, 0.13, 2_000_000_000_u64, 28_270_000, 0), + // // Both above price - tao is zero, alpha is non-zero + // (0.3, 0.4, 2_000_000_000_u64, 0, 489_200_000), + // ] + // .into_iter() + // .enumerate() + // .map(|(n, v)| (NetUid::from(n as u16 + 1), v.0, v.1, v.2, v.3, v.4)) + // .for_each(|(netuid, price_low, price_high, liquidity, tao, alpha)| { + // // Calculate ticks (assuming tick math is tested separately) + // let tick_low = price_to_tick(price_low); + // let tick_high = price_to_tick(price_high); + + // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + // let liquidity_before = CurrentLiquidity::::get(netuid); + + // // Add liquidity + // let (position_id, _, _) = Pallet::::do_add_liquidity( + // netuid, + // &OK_COLDKEY_ACCOUNT_ID, + // &OK_HOTKEY_ACCOUNT_ID, + // tick_low, + // tick_high, + // liquidity, + // ) + // .unwrap(); + + // // Remove liquidity + // let remove_result = + // Pallet::::do_remove_liquidity(netuid, &OK_COLDKEY_ACCOUNT_ID, position_id) + // .unwrap(); + // assert_abs_diff_eq!(remove_result.tao.to_u64(), tao, epsilon = tao / 1000); + // assert_abs_diff_eq!( + // u64::from(remove_result.alpha), + // alpha, + // epsilon = alpha / 1000 + // ); + // assert_eq!(remove_result.fee_tao, TaoCurrency::ZERO); + // assert_eq!(remove_result.fee_alpha, AlphaCurrency::ZERO); - // Liquidity position is removed - assert_eq!( - Pallet::::count_positions(netuid, &OK_COLDKEY_ACCOUNT_ID), - 0 - ); - assert!(Positions::::get((netuid, OK_COLDKEY_ACCOUNT_ID, position_id)).is_none()); + // // Liquidity position is removed + // assert_eq!( + // Pallet::::count_positions(netuid, &OK_COLDKEY_ACCOUNT_ID), + // 0 + // ); + // assert!(Positions::::get((netuid, OK_COLDKEY_ACCOUNT_ID, position_id)).is_none()); - // Current liquidity is updated (back where it was) - assert_eq!(CurrentLiquidity::::get(netuid), liquidity_before); - }); - }); + // // Current liquidity is updated (back where it was) + // assert_eq!(CurrentLiquidity::::get(netuid), liquidity_before); + // }); + // }); } #[test] fn test_remove_liquidity_nonexisting_position() { - new_test_ext().execute_with(|| { - let min_price = tick_to_price(TickIndex::MIN); - let max_price = tick_to_price(TickIndex::MAX); - let max_tick = price_to_tick(max_price); - assert_eq!(max_tick.get(), TickIndex::MAX.get()); - - let liquidity = 2_000_000_000_u64; - let netuid = NetUid::from(1); - - // Calculate ticks (assuming tick math is tested separately) - let tick_low = price_to_tick(min_price); - let tick_high = price_to_tick(max_price); - - // Setup swap - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - // Add liquidity - assert_ok!(Pallet::::do_add_liquidity( - netuid, - &OK_COLDKEY_ACCOUNT_ID, - &OK_HOTKEY_ACCOUNT_ID, - tick_low, - tick_high, - liquidity, - )); - - assert!(Pallet::::count_positions(netuid, &OK_COLDKEY_ACCOUNT_ID) > 0); - - // Remove liquidity - assert_err!( - Pallet::::do_remove_liquidity( - netuid, - &OK_COLDKEY_ACCOUNT_ID, - PositionId::new::() - ), - Error::::LiquidityNotFound, - ); - }); + todo!(); + + // new_test_ext().execute_with(|| { + // let min_price = tick_to_price(TickIndex::MIN); + // let max_price = tick_to_price(TickIndex::MAX); + // let max_tick = price_to_tick(max_price); + // assert_eq!(max_tick.get(), TickIndex::MAX.get()); + + // let liquidity = 2_000_000_000_u64; + // let netuid = NetUid::from(1); + + // // Calculate ticks (assuming tick math is tested separately) + // let tick_low = price_to_tick(min_price); + // let tick_high = price_to_tick(max_price); + + // // Setup swap + // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + + // // Add liquidity + // assert_ok!(Pallet::::do_add_liquidity( + // netuid, + // &OK_COLDKEY_ACCOUNT_ID, + // &OK_HOTKEY_ACCOUNT_ID, + // tick_low, + // tick_high, + // liquidity, + // )); + + // assert!(Pallet::::count_positions(netuid, &OK_COLDKEY_ACCOUNT_ID) > 0); + + // // Remove liquidity + // assert_err!( + // Pallet::::do_remove_liquidity( + // netuid, + // &OK_COLDKEY_ACCOUNT_ID, + // PositionId::new::() + // ), + // Error::::LiquidityNotFound, + // ); + // }); } // cargo test --package pallet-subtensor-swap --lib -- pallet::tests::test_modify_position_basic --exact --show-output #[test] fn test_modify_position_basic() { - new_test_ext().execute_with(|| { - let max_price = tick_to_price(TickIndex::MAX); - let max_tick = price_to_tick(max_price); - let limit_price = 1000.0_f64; - assert_eq!(max_tick, TickIndex::MAX); - let (current_price_low, _current_price_high) = get_ticked_prices_around_current_price(); - - // As a user add liquidity with all possible corner cases - // - Initial price is 0.25 - // - liquidity is expressed in RAO units - // Test case is (price_low, price_high, liquidity, tao, alpha) - [ - // Repeat the protocol liquidity at current to max range: Expect the same alpha - ( - current_price_low, - max_price, - 2_000_000_000_u64, - 4_000_000_000, - ), - ] - .into_iter() - .enumerate() - .map(|(n, v)| (NetUid::from(n as u16 + 1), v.0, v.1, v.2, v.3)) - .for_each(|(netuid, price_low, price_high, liquidity, alpha)| { - // Calculate ticks (assuming tick math is tested separately) - let tick_low = price_to_tick(price_low); - let tick_high = price_to_tick(price_high); - - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - // Add liquidity - let (position_id, _, _) = Pallet::::do_add_liquidity( - netuid, - &OK_COLDKEY_ACCOUNT_ID, - &OK_HOTKEY_ACCOUNT_ID, - tick_low, - tick_high, - liquidity, - ) - .unwrap(); - - // Get tick infos before the swap/update - let tick_low_info_before = Ticks::::get(netuid, tick_low).unwrap(); - let tick_high_info_before = Ticks::::get(netuid, tick_high).unwrap(); - - // Swap to create fees on the position - let sqrt_limit_price = SqrtPrice::from_num((limit_price).sqrt()); - let order = GetAlphaForTao::with_amount(liquidity / 10); - Pallet::::do_swap(netuid, order, sqrt_limit_price, false, false).unwrap(); - - // Modify liquidity (also causes claiming of fees) - let liquidity_before = CurrentLiquidity::::get(netuid); - let modify_result = Pallet::::do_modify_position( - netuid, - &OK_COLDKEY_ACCOUNT_ID, - &OK_HOTKEY_ACCOUNT_ID, - position_id, - -((liquidity / 10) as i64), - ) - .unwrap(); - assert_abs_diff_eq!( - u64::from(modify_result.alpha), - alpha / 10, - epsilon = alpha / 1000 - ); - assert!(modify_result.fee_tao > TaoCurrency::ZERO); - assert_eq!(modify_result.fee_alpha, AlphaCurrency::ZERO); - - // Liquidity position is reduced - assert_eq!( - Pallet::::count_positions(netuid, &OK_COLDKEY_ACCOUNT_ID), - 1 - ); + todo!(); + + // new_test_ext().execute_with(|| { + // let max_price = tick_to_price(TickIndex::MAX); + // let max_tick = price_to_tick(max_price); + // let limit_price = 1000.0_f64; + // assert_eq!(max_tick, TickIndex::MAX); + // let (current_price_low, _current_price_high) = get_ticked_prices_around_current_price(); + + // // As a user add liquidity with all possible corner cases + // // - Initial price is 0.25 + // // - liquidity is expressed in RAO units + // // Test case is (price_low, price_high, liquidity, tao, alpha) + // [ + // // Repeat the protocol liquidity at current to max range: Expect the same alpha + // ( + // current_price_low, + // max_price, + // 2_000_000_000_u64, + // 4_000_000_000, + // ), + // ] + // .into_iter() + // .enumerate() + // .map(|(n, v)| (NetUid::from(n as u16 + 1), v.0, v.1, v.2, v.3)) + // .for_each(|(netuid, price_low, price_high, liquidity, alpha)| { + // // Calculate ticks (assuming tick math is tested separately) + // let tick_low = price_to_tick(price_low); + // let tick_high = price_to_tick(price_high); + + // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + + // // Add liquidity + // let (position_id, _, _) = Pallet::::do_add_liquidity( + // netuid, + // &OK_COLDKEY_ACCOUNT_ID, + // &OK_HOTKEY_ACCOUNT_ID, + // tick_low, + // tick_high, + // liquidity, + // ) + // .unwrap(); + + // // Get tick infos before the swap/update + // let tick_low_info_before = Ticks::::get(netuid, tick_low).unwrap(); + // let tick_high_info_before = Ticks::::get(netuid, tick_high).unwrap(); + + // // Swap to create fees on the position + // let sqrt_limit_price = SqrtPrice::from_num((limit_price).sqrt()); + // let order = GetAlphaForTao::with_amount(liquidity / 10); + // Pallet::::do_swap(netuid, order, sqrt_limit_price, false, false).unwrap(); + + // // Modify liquidity (also causes claiming of fees) + // let liquidity_before = CurrentLiquidity::::get(netuid); + // let modify_result = Pallet::::do_modify_position( + // netuid, + // &OK_COLDKEY_ACCOUNT_ID, + // &OK_HOTKEY_ACCOUNT_ID, + // position_id, + // -((liquidity / 10) as i64), + // ) + // .unwrap(); + // assert_abs_diff_eq!( + // u64::from(modify_result.alpha), + // alpha / 10, + // epsilon = alpha / 1000 + // ); + // assert!(modify_result.fee_tao > TaoCurrency::ZERO); + // assert_eq!(modify_result.fee_alpha, AlphaCurrency::ZERO); - // Current liquidity is reduced with modify_position - assert!(CurrentLiquidity::::get(netuid) < liquidity_before); + // // Liquidity position is reduced + // assert_eq!( + // Pallet::::count_positions(netuid, &OK_COLDKEY_ACCOUNT_ID), + // 1 + // ); - // Position liquidity is reduced - let position = - Positions::::get((netuid, OK_COLDKEY_ACCOUNT_ID, position_id)).unwrap(); - assert_eq!(position.liquidity, liquidity * 9 / 10); - assert_eq!(position.tick_low, tick_low); - assert_eq!(position.tick_high, tick_high); + // // Current liquidity is reduced with modify_position + // assert!(CurrentLiquidity::::get(netuid) < liquidity_before); - // Tick liquidity is updated properly for low and high position ticks - let tick_low_info_after = Ticks::::get(netuid, tick_low).unwrap(); - let tick_high_info_after = Ticks::::get(netuid, tick_high).unwrap(); + // // Position liquidity is reduced + // let position = + // Positions::::get((netuid, OK_COLDKEY_ACCOUNT_ID, position_id)).unwrap(); + // assert_eq!(position.liquidity, liquidity * 9 / 10); + // assert_eq!(position.tick_low, tick_low); + // assert_eq!(position.tick_high, tick_high); - assert_eq!( - tick_low_info_before.liquidity_net - (liquidity / 10) as i128, - tick_low_info_after.liquidity_net, - ); - assert_eq!( - tick_low_info_before.liquidity_gross - (liquidity / 10), - tick_low_info_after.liquidity_gross, - ); - assert_eq!( - tick_high_info_before.liquidity_net + (liquidity / 10) as i128, - tick_high_info_after.liquidity_net, - ); - assert_eq!( - tick_high_info_before.liquidity_gross - (liquidity / 10), - tick_high_info_after.liquidity_gross, - ); + // // Tick liquidity is updated properly for low and high position ticks + // let tick_low_info_after = Ticks::::get(netuid, tick_low).unwrap(); + // let tick_high_info_after = Ticks::::get(netuid, tick_high).unwrap(); - // Modify liquidity again (ensure fees aren't double-collected) - let modify_result = Pallet::::do_modify_position( - netuid, - &OK_COLDKEY_ACCOUNT_ID, - &OK_HOTKEY_ACCOUNT_ID, - position_id, - -((liquidity / 100) as i64), - ) - .unwrap(); + // assert_eq!( + // tick_low_info_before.liquidity_net - (liquidity / 10) as i128, + // tick_low_info_after.liquidity_net, + // ); + // assert_eq!( + // tick_low_info_before.liquidity_gross - (liquidity / 10), + // tick_low_info_after.liquidity_gross, + // ); + // assert_eq!( + // tick_high_info_before.liquidity_net + (liquidity / 10) as i128, + // tick_high_info_after.liquidity_net, + // ); + // assert_eq!( + // tick_high_info_before.liquidity_gross - (liquidity / 10), + // tick_high_info_after.liquidity_gross, + // ); - assert_abs_diff_eq!( - u64::from(modify_result.alpha), - alpha / 100, - epsilon = alpha / 1000 - ); - assert_eq!(modify_result.fee_tao, TaoCurrency::ZERO); - assert_eq!(modify_result.fee_alpha, AlphaCurrency::ZERO); - }); - }); + // // Modify liquidity again (ensure fees aren't double-collected) + // let modify_result = Pallet::::do_modify_position( + // netuid, + // &OK_COLDKEY_ACCOUNT_ID, + // &OK_HOTKEY_ACCOUNT_ID, + // position_id, + // -((liquidity / 100) as i64), + // ) + // .unwrap(); + + // assert_abs_diff_eq!( + // u64::from(modify_result.alpha), + // alpha / 100, + // epsilon = alpha / 1000 + // ); + // assert_eq!(modify_result.fee_tao, TaoCurrency::ZERO); + // assert_eq!(modify_result.fee_alpha, AlphaCurrency::ZERO); + // }); + // }); } // cargo test --package pallet-subtensor-swap --lib -- pallet::impls::tests::test_swap_basic --exact --show-output #[test] fn test_swap_basic() { - new_test_ext().execute_with(|| { - fn perform_test( - netuid: NetUid, - order: Order, - limit_price: f64, - output_amount: u64, - price_should_grow: bool, - ) where - Order: OrderT, - Order::PaidIn: GlobalFeeInfo, - BasicSwapStep: - SwapStep, - { - // Consumed liquidity ticks - let tick_low = TickIndex::MIN; - let tick_high = TickIndex::MAX; - let liquidity = order.amount().to_u64(); - - // Setup swap - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - // Get tick infos before the swap - let tick_low_info_before = Ticks::::get(netuid, tick_low).unwrap_or_default(); - let tick_high_info_before = Ticks::::get(netuid, tick_high).unwrap_or_default(); - let liquidity_before = CurrentLiquidity::::get(netuid); - - // Get current price - let current_price = Pallet::::current_price(netuid); - - // Swap - let sqrt_limit_price = SqrtPrice::from_num((limit_price).sqrt()); - let swap_result = - Pallet::::do_swap(netuid, order.clone(), sqrt_limit_price, false, false) - .unwrap(); - assert_abs_diff_eq!( - swap_result.amount_paid_out.to_u64(), - output_amount, - epsilon = output_amount / 100 - ); - - assert_abs_diff_eq!( - swap_result.paid_in_reserve_delta() as u64, - liquidity, - epsilon = liquidity / 10 - ); - assert_abs_diff_eq!( - swap_result.paid_out_reserve_delta() as i64, - -(output_amount as i64), - epsilon = output_amount as i64 / 10 - ); + todo!(); + + // new_test_ext().execute_with(|| { + // fn perform_test( + // netuid: NetUid, + // order: Order, + // limit_price: f64, + // output_amount: u64, + // price_should_grow: bool, + // ) where + // Order: OrderT, + // Order::PaidIn: GlobalFeeInfo, + // BasicSwapStep: + // SwapStep, + // { + // // Consumed liquidity ticks + // let tick_low = TickIndex::MIN; + // let tick_high = TickIndex::MAX; + // let liquidity = order.amount().to_u64(); + + // // Setup swap + // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + + // // Get tick infos before the swap + // let tick_low_info_before = Ticks::::get(netuid, tick_low).unwrap_or_default(); + // let tick_high_info_before = Ticks::::get(netuid, tick_high).unwrap_or_default(); + // let liquidity_before = CurrentLiquidity::::get(netuid); + + // // Get current price + // let current_price = Pallet::::current_price(netuid); + + // // Swap + // let sqrt_limit_price = SqrtPrice::from_num((limit_price).sqrt()); + // let swap_result = + // Pallet::::do_swap(netuid, order.clone(), sqrt_limit_price, false, false) + // .unwrap(); + // assert_abs_diff_eq!( + // swap_result.amount_paid_out.to_u64(), + // output_amount, + // epsilon = output_amount / 100 + // ); - // Check that low and high ticks' fees were updated properly, and liquidity values were not updated - let tick_low_info = Ticks::::get(netuid, tick_low).unwrap(); - let tick_high_info = Ticks::::get(netuid, tick_high).unwrap(); - let expected_liquidity_net_low = tick_low_info_before.liquidity_net; - let expected_liquidity_gross_low = tick_low_info_before.liquidity_gross; - let expected_liquidity_net_high = tick_high_info_before.liquidity_net; - let expected_liquidity_gross_high = tick_high_info_before.liquidity_gross; - assert_eq!(tick_low_info.liquidity_net, expected_liquidity_net_low,); - assert_eq!(tick_low_info.liquidity_gross, expected_liquidity_gross_low,); - assert_eq!(tick_high_info.liquidity_net, expected_liquidity_net_high,); - assert_eq!( - tick_high_info.liquidity_gross, - expected_liquidity_gross_high, - ); + // assert_abs_diff_eq!( + // swap_result.paid_in_reserve_delta() as u64, + // liquidity, + // epsilon = liquidity / 10 + // ); + // assert_abs_diff_eq!( + // swap_result.paid_out_reserve_delta() as i64, + // -(output_amount as i64), + // epsilon = output_amount as i64 / 10 + // ); - // Expected fee amount - let fee_rate = FeeRate::::get(netuid) as f64 / u16::MAX as f64; - let expected_fee = (liquidity as f64 * fee_rate) as u64; + // // Check that low and high ticks' fees were updated properly, and liquidity values were not updated + // let tick_low_info = Ticks::::get(netuid, tick_low).unwrap(); + // let tick_high_info = Ticks::::get(netuid, tick_high).unwrap(); + // let expected_liquidity_net_low = tick_low_info_before.liquidity_net; + // let expected_liquidity_gross_low = tick_low_info_before.liquidity_gross; + // let expected_liquidity_net_high = tick_high_info_before.liquidity_net; + // let expected_liquidity_gross_high = tick_high_info_before.liquidity_gross; + // assert_eq!(tick_low_info.liquidity_net, expected_liquidity_net_low,); + // assert_eq!(tick_low_info.liquidity_gross, expected_liquidity_gross_low,); + // assert_eq!(tick_high_info.liquidity_net, expected_liquidity_net_high,); + // assert_eq!( + // tick_high_info.liquidity_gross, + // expected_liquidity_gross_high, + // ); - // Global fees should be updated - let actual_global_fee = (order.amount().global_fee(netuid).to_num::() - * (liquidity_before as f64)) as u64; + // // Expected fee amount + // let fee_rate = FeeRate::::get(netuid) as f64 / u16::MAX as f64; + // let expected_fee = (liquidity as f64 * fee_rate) as u64; - assert!((swap_result.fee_paid.to_u64() as i64 - expected_fee as i64).abs() <= 1); - assert!((actual_global_fee as i64 - expected_fee as i64).abs() <= 1); + // // Global fees should be updated + // let actual_global_fee = (order.amount().global_fee(netuid).to_num::() + // * (liquidity_before as f64)) as u64; - // Tick fees should be updated + // assert!((swap_result.fee_paid.to_u64() as i64 - expected_fee as i64).abs() <= 1); + // assert!((actual_global_fee as i64 - expected_fee as i64).abs() <= 1); - // Liquidity position should not be updated - let protocol_id = Pallet::::protocol_account_id(); - let positions = - Positions::::iter_prefix_values((netuid, protocol_id)).collect::>(); - let position = positions.first().unwrap(); + // // Tick fees should be updated - assert_eq!( - position.liquidity, - helpers_128bit::sqrt( - TaoReserve::reserve(netuid.into()).to_u64() as u128 - * AlphaReserve::reserve(netuid.into()).to_u64() as u128 - ) as u64 - ); - assert_eq!(position.tick_low, tick_low); - assert_eq!(position.tick_high, tick_high); - assert_eq!(position.fees_alpha, 0); - assert_eq!(position.fees_tao, 0); - - // Current liquidity is not updated - assert_eq!(CurrentLiquidity::::get(netuid), liquidity_before); - - // Assert that price movement is in correct direction - let sqrt_current_price_after = AlphaSqrtPrice::::get(netuid); - let current_price_after = Pallet::::current_price(netuid); - assert_eq!(current_price_after >= current_price, price_should_grow); - - // Assert that current tick is updated - let current_tick = CurrentTick::::get(netuid); - let expected_current_tick = - TickIndex::from_sqrt_price_bounded(sqrt_current_price_after); - assert_eq!(current_tick, expected_current_tick); - } + // // Liquidity position should not be updated + // let protocol_id = Pallet::::protocol_account_id(); + // let positions = + // Positions::::iter_prefix_values((netuid, protocol_id)).collect::>(); + // let position = positions.first().unwrap(); - // Current price is 0.25 - // Test case is (order_type, liquidity, limit_price, output_amount) - perform_test( - 1.into(), - GetAlphaForTao::with_amount(1_000), - 1000.0, - 3990, - true, - ); - perform_test( - 2.into(), - GetTaoForAlpha::with_amount(1_000), - 0.0001, - 250, - false, - ); - perform_test( - 3.into(), - GetAlphaForTao::with_amount(500_000_000), - 1000.0, - 2_000_000_000, - true, - ); - }); + // assert_eq!( + // position.liquidity, + // helpers_128bit::sqrt( + // TaoReserve::reserve(netuid.into()).to_u64() as u128 + // * AlphaReserve::reserve(netuid.into()).to_u64() as u128 + // ) as u64 + // ); + // assert_eq!(position.tick_low, tick_low); + // assert_eq!(position.tick_high, tick_high); + // assert_eq!(position.fees_alpha, 0); + // assert_eq!(position.fees_tao, 0); + + // // Current liquidity is not updated + // assert_eq!(CurrentLiquidity::::get(netuid), liquidity_before); + + // // Assert that price movement is in correct direction + // let sqrt_current_price_after = AlphaSqrtPrice::::get(netuid); + // let current_price_after = Pallet::::current_price(netuid); + // assert_eq!(current_price_after >= current_price, price_should_grow); + + // // Assert that current tick is updated + // let current_tick = CurrentTick::::get(netuid); + // let expected_current_tick = + // TickIndex::from_sqrt_price_bounded(sqrt_current_price_after); + // assert_eq!(current_tick, expected_current_tick); + // } + + // // Current price is 0.25 + // // Test case is (order_type, liquidity, limit_price, output_amount) + // perform_test( + // 1.into(), + // GetAlphaForTao::with_amount(1_000), + // 1000.0, + // 3990, + // true, + // ); + // perform_test( + // 2.into(), + // GetTaoForAlpha::with_amount(1_000), + // 0.0001, + // 250, + // false, + // ); + // perform_test( + // 3.into(), + // GetAlphaForTao::with_amount(500_000_000), + // 1000.0, + // 2_000_000_000, + // true, + // ); + // }); } // In this test the swap starts and ends within one (large liquidity) position // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor-swap --lib -- pallet::tests::test_swap_single_position --exact --show-output #[test] fn test_swap_single_position() { - let min_price = tick_to_price(TickIndex::MIN); - let max_price = tick_to_price(TickIndex::MAX); - let max_tick = price_to_tick(max_price); - let netuid = NetUid::from(1); - assert_eq!(max_tick, TickIndex::MAX); - - let mut current_price_low = 0_f64; - let mut current_price_high = 0_f64; - let mut current_price = 0_f64; - new_test_ext().execute_with(|| { - let (low, high) = get_ticked_prices_around_current_price(); - current_price_low = low; - current_price_high = high; - current_price = Pallet::::current_price(netuid).to_num::(); - }); - - macro_rules! perform_test { - ($order_t:ident, - $price_low_offset:expr, - $price_high_offset:expr, - $position_liquidity:expr, - $liquidity_fraction:expr, - $limit_price:expr, - $price_should_grow:expr - ) => { - new_test_ext().execute_with(|| { - let price_low_offset = $price_low_offset; - let price_high_offset = $price_high_offset; - let position_liquidity = $position_liquidity; - let order_liquidity_fraction = $liquidity_fraction; - let limit_price = $limit_price; - let price_should_grow = $price_should_grow; - - ////////////////////////////////////////////// - // Initialize pool and add the user position - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - let tao_reserve = TaoReserve::reserve(netuid.into()).to_u64(); - let alpha_reserve = AlphaReserve::reserve(netuid.into()).to_u64(); - let protocol_liquidity = (tao_reserve as f64 * alpha_reserve as f64).sqrt(); - - // Add liquidity - let current_price = Pallet::::current_price(netuid).to_num::(); - let sqrt_current_price = AlphaSqrtPrice::::get(netuid).to_num::(); - - let price_low = price_low_offset + current_price; - let price_high = price_high_offset + current_price; - let tick_low = price_to_tick(price_low); - let tick_high = price_to_tick(price_high); - let (_position_id, _tao, _alpha) = Pallet::::do_add_liquidity( - netuid, - &OK_COLDKEY_ACCOUNT_ID, - &OK_HOTKEY_ACCOUNT_ID, - tick_low, - tick_high, - position_liquidity, - ) - .unwrap(); - - // Liquidity position at correct ticks - assert_eq!( - Pallet::::count_positions(netuid, &OK_COLDKEY_ACCOUNT_ID), - 1 - ); - - // Get tick infos before the swap - let tick_low_info_before = Ticks::::get(netuid, tick_low).unwrap_or_default(); - let tick_high_info_before = - Ticks::::get(netuid, tick_high).unwrap_or_default(); - let liquidity_before = CurrentLiquidity::::get(netuid); - assert_abs_diff_eq!( - liquidity_before as f64, - protocol_liquidity + position_liquidity as f64, - epsilon = liquidity_before as f64 / 1000. - ); - - ////////////////////////////////////////////// - // Swap - - // Calculate the expected output amount for the cornercase of one step - let order_liquidity = order_liquidity_fraction * position_liquidity as f64; - - let output_amount = >::approx_expected_swap_output( - sqrt_current_price, - liquidity_before as f64, - order_liquidity, - ); - - // Do the swap - let sqrt_limit_price = SqrtPrice::from_num((limit_price).sqrt()); - let order = $order_t::with_amount(order_liquidity as u64); - let swap_result = - Pallet::::do_swap(netuid, order, sqrt_limit_price, false, false).unwrap(); - assert_abs_diff_eq!( - swap_result.amount_paid_out.to_u64() as f64, - output_amount, - epsilon = output_amount / 10. - ); - - if order_liquidity_fraction <= 0.001 { - assert_abs_diff_eq!( - swap_result.paid_in_reserve_delta() as i64, - order_liquidity as i64, - epsilon = order_liquidity as i64 / 10 - ); - assert_abs_diff_eq!( - swap_result.paid_out_reserve_delta() as i64, - -(output_amount as i64), - epsilon = output_amount as i64 / 10 - ); - } - - // Assert that price movement is in correct direction - let current_price_after = Pallet::::current_price(netuid); - assert_eq!(price_should_grow, current_price_after > current_price); - - // Assert that for small amounts price stays within the user position - if (order_liquidity_fraction <= 0.001) - && (price_low_offset > 0.0001) - && (price_high_offset > 0.0001) - { - assert!(current_price_after <= price_high); - assert!(current_price_after >= price_low); - } - - // Check that low and high ticks' fees were updated properly - let tick_low_info = Ticks::::get(netuid, tick_low).unwrap(); - let tick_high_info = Ticks::::get(netuid, tick_high).unwrap(); - let expected_liquidity_net_low = tick_low_info_before.liquidity_net; - let expected_liquidity_gross_low = tick_low_info_before.liquidity_gross; - let expected_liquidity_net_high = tick_high_info_before.liquidity_net; - let expected_liquidity_gross_high = tick_high_info_before.liquidity_gross; - assert_eq!(tick_low_info.liquidity_net, expected_liquidity_net_low,); - assert_eq!(tick_low_info.liquidity_gross, expected_liquidity_gross_low,); - assert_eq!(tick_high_info.liquidity_net, expected_liquidity_net_high,); - assert_eq!( - tick_high_info.liquidity_gross, - expected_liquidity_gross_high, - ); - - // Expected fee amount - let fee_rate = FeeRate::::get(netuid) as f64 / u16::MAX as f64; - let expected_fee = (order_liquidity - order_liquidity / (1.0 + fee_rate)) as u64; - - // // Global fees should be updated - let actual_global_fee = ($order_t::with_amount(0) - .amount() - .global_fee(netuid) - .to_num::() - * (liquidity_before as f64)) as u64; - - assert_abs_diff_eq!( - swap_result.fee_paid.to_u64(), - expected_fee, - epsilon = expected_fee / 10 - ); - assert_abs_diff_eq!(actual_global_fee, expected_fee, epsilon = expected_fee / 10); - - // Tick fees should be updated - - // Liquidity position should not be updated - let positions = - Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) - .collect::>(); - let position = positions.first().unwrap(); - - assert_eq!(position.liquidity, position_liquidity,); - assert_eq!(position.tick_low, tick_low); - assert_eq!(position.tick_high, tick_high); - assert_eq!(position.fees_alpha, 0); - assert_eq!(position.fees_tao, 0); - }); - }; - } + todo!(); + + // let min_price = tick_to_price(TickIndex::MIN); + // let max_price = tick_to_price(TickIndex::MAX); + // let max_tick = price_to_tick(max_price); + // let netuid = NetUid::from(1); + // assert_eq!(max_tick, TickIndex::MAX); + + // let mut current_price_low = 0_f64; + // let mut current_price_high = 0_f64; + // let mut current_price = 0_f64; + // new_test_ext().execute_with(|| { + // let (low, high) = get_ticked_prices_around_current_price(); + // current_price_low = low; + // current_price_high = high; + // current_price = Pallet::::current_price(netuid).to_num::(); + // }); + + // macro_rules! perform_test { + // ($order_t:ident, + // $price_low_offset:expr, + // $price_high_offset:expr, + // $position_liquidity:expr, + // $liquidity_fraction:expr, + // $limit_price:expr, + // $price_should_grow:expr + // ) => { + // new_test_ext().execute_with(|| { + // let price_low_offset = $price_low_offset; + // let price_high_offset = $price_high_offset; + // let position_liquidity = $position_liquidity; + // let order_liquidity_fraction = $liquidity_fraction; + // let limit_price = $limit_price; + // let price_should_grow = $price_should_grow; + + // ////////////////////////////////////////////// + // // Initialize pool and add the user position + // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + // let tao_reserve = TaoReserve::reserve(netuid.into()).to_u64(); + // let alpha_reserve = AlphaReserve::reserve(netuid.into()).to_u64(); + // let protocol_liquidity = (tao_reserve as f64 * alpha_reserve as f64).sqrt(); + + // // Add liquidity + // let current_price = Pallet::::current_price(netuid).to_num::(); + // let sqrt_current_price = AlphaSqrtPrice::::get(netuid).to_num::(); + + // let price_low = price_low_offset + current_price; + // let price_high = price_high_offset + current_price; + // let tick_low = price_to_tick(price_low); + // let tick_high = price_to_tick(price_high); + // let (_position_id, _tao, _alpha) = Pallet::::do_add_liquidity( + // netuid, + // &OK_COLDKEY_ACCOUNT_ID, + // &OK_HOTKEY_ACCOUNT_ID, + // tick_low, + // tick_high, + // position_liquidity, + // ) + // .unwrap(); + + // // Liquidity position at correct ticks + // assert_eq!( + // Pallet::::count_positions(netuid, &OK_COLDKEY_ACCOUNT_ID), + // 1 + // ); + + // // Get tick infos before the swap + // let tick_low_info_before = Ticks::::get(netuid, tick_low).unwrap_or_default(); + // let tick_high_info_before = + // Ticks::::get(netuid, tick_high).unwrap_or_default(); + // let liquidity_before = CurrentLiquidity::::get(netuid); + // assert_abs_diff_eq!( + // liquidity_before as f64, + // protocol_liquidity + position_liquidity as f64, + // epsilon = liquidity_before as f64 / 1000. + // ); + + // ////////////////////////////////////////////// + // // Swap + + // // Calculate the expected output amount for the cornercase of one step + // let order_liquidity = order_liquidity_fraction * position_liquidity as f64; + + // let output_amount = >::approx_expected_swap_output( + // sqrt_current_price, + // liquidity_before as f64, + // order_liquidity, + // ); + + // // Do the swap + // let sqrt_limit_price = SqrtPrice::from_num((limit_price).sqrt()); + // let order = $order_t::with_amount(order_liquidity as u64); + // let swap_result = + // Pallet::::do_swap(netuid, order, sqrt_limit_price, false, false).unwrap(); + // assert_abs_diff_eq!( + // swap_result.amount_paid_out.to_u64() as f64, + // output_amount, + // epsilon = output_amount / 10. + // ); + + // if order_liquidity_fraction <= 0.001 { + // assert_abs_diff_eq!( + // swap_result.paid_in_reserve_delta() as i64, + // order_liquidity as i64, + // epsilon = order_liquidity as i64 / 10 + // ); + // assert_abs_diff_eq!( + // swap_result.paid_out_reserve_delta() as i64, + // -(output_amount as i64), + // epsilon = output_amount as i64 / 10 + // ); + // } + + // // Assert that price movement is in correct direction + // let current_price_after = Pallet::::current_price(netuid); + // assert_eq!(price_should_grow, current_price_after > current_price); + + // // Assert that for small amounts price stays within the user position + // if (order_liquidity_fraction <= 0.001) + // && (price_low_offset > 0.0001) + // && (price_high_offset > 0.0001) + // { + // assert!(current_price_after <= price_high); + // assert!(current_price_after >= price_low); + // } + + // // Check that low and high ticks' fees were updated properly + // let tick_low_info = Ticks::::get(netuid, tick_low).unwrap(); + // let tick_high_info = Ticks::::get(netuid, tick_high).unwrap(); + // let expected_liquidity_net_low = tick_low_info_before.liquidity_net; + // let expected_liquidity_gross_low = tick_low_info_before.liquidity_gross; + // let expected_liquidity_net_high = tick_high_info_before.liquidity_net; + // let expected_liquidity_gross_high = tick_high_info_before.liquidity_gross; + // assert_eq!(tick_low_info.liquidity_net, expected_liquidity_net_low,); + // assert_eq!(tick_low_info.liquidity_gross, expected_liquidity_gross_low,); + // assert_eq!(tick_high_info.liquidity_net, expected_liquidity_net_high,); + // assert_eq!( + // tick_high_info.liquidity_gross, + // expected_liquidity_gross_high, + // ); + + // // Expected fee amount + // let fee_rate = FeeRate::::get(netuid) as f64 / u16::MAX as f64; + // let expected_fee = (order_liquidity - order_liquidity / (1.0 + fee_rate)) as u64; + + // // // Global fees should be updated + // let actual_global_fee = ($order_t::with_amount(0) + // .amount() + // .global_fee(netuid) + // .to_num::() + // * (liquidity_before as f64)) as u64; + + // assert_abs_diff_eq!( + // swap_result.fee_paid.to_u64(), + // expected_fee, + // epsilon = expected_fee / 10 + // ); + // assert_abs_diff_eq!(actual_global_fee, expected_fee, epsilon = expected_fee / 10); + + // // Tick fees should be updated + + // // Liquidity position should not be updated + // let positions = + // Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) + // .collect::>(); + // let position = positions.first().unwrap(); + + // assert_eq!(position.liquidity, position_liquidity,); + // assert_eq!(position.tick_low, tick_low); + // assert_eq!(position.tick_high, tick_high); + // assert_eq!(position.fees_alpha, 0); + // assert_eq!(position.fees_tao, 0); + // }); + // }; + // } - // Current price is 0.25 - // The test case is based on the current price and position prices are defined as a price - // offset from the current price - // Outer part of test case is Position: (price_low_offset, price_high_offset, liquidity) - [ - // Very localized position at the current price - (-0.1, 0.1, 500_000_000_000_u64), - // Repeat the protocol liquidity at maximum range - ( - min_price - current_price, - max_price - current_price, - 2_000_000_000_u64, - ), - // Repeat the protocol liquidity at current to max range - ( - current_price_high - current_price, - max_price - current_price, - 2_000_000_000_u64, - ), - // Repeat the protocol liquidity at min to current range - ( - min_price - current_price, - current_price_low - current_price, - 2_000_000_000_u64, - ), - // Half to double price - (-0.125, 0.25, 2_000_000_000_u64), - // A few other price ranges and liquidity volumes - (-0.1, 0.1, 2_000_000_000_u64), - (-0.1, 0.1, 10_000_000_000_u64), - (-0.1, 0.1, 100_000_000_000_u64), - (-0.01, 0.01, 100_000_000_000_u64), - (-0.001, 0.001, 100_000_000_000_u64), - ] - .into_iter() - .for_each( - |(price_low_offset, price_high_offset, position_liquidity)| { - // Inner part of test case is Order: (order_type, order_liquidity, limit_price) - // order_liquidity is represented as a fraction of position_liquidity - for liquidity_fraction in [0.0001, 0.001, 0.01, 0.1, 0.2, 0.5] { - perform_test!( - GetAlphaForTao, - price_low_offset, - price_high_offset, - position_liquidity, - liquidity_fraction, - 1000.0_f64, - true - ); - perform_test!( - GetTaoForAlpha, - price_low_offset, - price_high_offset, - position_liquidity, - liquidity_fraction, - 0.0001_f64, - false - ); - } - }, - ); + // // Current price is 0.25 + // // The test case is based on the current price and position prices are defined as a price + // // offset from the current price + // // Outer part of test case is Position: (price_low_offset, price_high_offset, liquidity) + // [ + // // Very localized position at the current price + // (-0.1, 0.1, 500_000_000_000_u64), + // // Repeat the protocol liquidity at maximum range + // ( + // min_price - current_price, + // max_price - current_price, + // 2_000_000_000_u64, + // ), + // // Repeat the protocol liquidity at current to max range + // ( + // current_price_high - current_price, + // max_price - current_price, + // 2_000_000_000_u64, + // ), + // // Repeat the protocol liquidity at min to current range + // ( + // min_price - current_price, + // current_price_low - current_price, + // 2_000_000_000_u64, + // ), + // // Half to double price + // (-0.125, 0.25, 2_000_000_000_u64), + // // A few other price ranges and liquidity volumes + // (-0.1, 0.1, 2_000_000_000_u64), + // (-0.1, 0.1, 10_000_000_000_u64), + // (-0.1, 0.1, 100_000_000_000_u64), + // (-0.01, 0.01, 100_000_000_000_u64), + // (-0.001, 0.001, 100_000_000_000_u64), + // ] + // .into_iter() + // .for_each( + // |(price_low_offset, price_high_offset, position_liquidity)| { + // // Inner part of test case is Order: (order_type, order_liquidity, limit_price) + // // order_liquidity is represented as a fraction of position_liquidity + // for liquidity_fraction in [0.0001, 0.001, 0.01, 0.1, 0.2, 0.5] { + // perform_test!( + // GetAlphaForTao, + // price_low_offset, + // price_high_offset, + // position_liquidity, + // liquidity_fraction, + // 1000.0_f64, + // true + // ); + // perform_test!( + // GetTaoForAlpha, + // price_low_offset, + // price_high_offset, + // position_liquidity, + // liquidity_fraction, + // 0.0001_f64, + // false + // ); + // } + // }, + // ); } // This test is a sanity check for swap and multiple positions // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor-swap --lib -- pallet::impls::tests::test_swap_multiple_positions --exact --show-output --nocapture #[test] fn test_swap_multiple_positions() { - new_test_ext().execute_with(|| { - let min_price = tick_to_price(TickIndex::MIN); - let max_price = tick_to_price(TickIndex::MAX); - let max_tick = price_to_tick(max_price); - let netuid = NetUid::from(1); - assert_eq!(max_tick, TickIndex::MAX); - - ////////////////////////////////////////////// - // Initialize pool and add the user position - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - // Add liquidity - let current_price = Pallet::::current_price(netuid).to_num::(); - - // Current price is 0.25 - // All positions below are placed at once - [ - // Very localized position at the current price - (-0.1, 0.1, 500_000_000_000_u64), - // Repeat the protocol liquidity at maximum range - ( - min_price - current_price, - max_price - current_price, - 2_000_000_000_u64, - ), - // Repeat the protocol liquidity at current to max range - (0.0, max_price - current_price, 2_000_000_000_u64), - // Repeat the protocol liquidity at min to current range - (min_price - current_price, 0.0, 2_000_000_000_u64), - // Half to double price - (-0.125, 0.25, 2_000_000_000_u64), - // A few other price ranges and liquidity volumes - (-0.1, 0.1, 2_000_000_000_u64), - (-0.1, 0.1, 10_000_000_000_u64), - (-0.1, 0.1, 100_000_000_000_u64), - (-0.01, 0.01, 100_000_000_000_u64), - (-0.001, 0.001, 100_000_000_000_u64), - // A few (overlapping) positions up the range - (0.01, 0.02, 100_000_000_000_u64), - (0.02, 0.03, 100_000_000_000_u64), - (0.03, 0.04, 100_000_000_000_u64), - (0.03, 0.05, 100_000_000_000_u64), - // A few (overlapping) positions down the range - (-0.02, -0.01, 100_000_000_000_u64), - (-0.03, -0.02, 100_000_000_000_u64), - (-0.04, -0.03, 100_000_000_000_u64), - (-0.05, -0.03, 100_000_000_000_u64), - ] - .into_iter() - .for_each( - |(price_low_offset, price_high_offset, position_liquidity)| { - let price_low = price_low_offset + current_price; - let price_high = price_high_offset + current_price; - let tick_low = price_to_tick(price_low); - let tick_high = price_to_tick(price_high); - let (_position_id, _tao, _alpha) = Pallet::::do_add_liquidity( - netuid, - &OK_COLDKEY_ACCOUNT_ID, - &OK_HOTKEY_ACCOUNT_ID, - tick_low, - tick_high, - position_liquidity, - ) - .unwrap(); - }, - ); - - macro_rules! perform_test { - ($order_t:ident, $order_liquidity:expr, $limit_price:expr, $should_price_grow:expr) => { - ////////////////////////////////////////////// - // Swap - let order_liquidity = $order_liquidity; - let limit_price = $limit_price; - let should_price_grow = $should_price_grow; - - let sqrt_current_price = AlphaSqrtPrice::::get(netuid); - let current_price = (sqrt_current_price * sqrt_current_price).to_num::(); - let liquidity_before = CurrentLiquidity::::get(netuid); - let output_amount = >::approx_expected_swap_output( - sqrt_current_price.to_num(), - liquidity_before as f64, - order_liquidity as f64, - ); - - // Do the swap - let sqrt_limit_price = SqrtPrice::from_num((limit_price).sqrt()); - let order = $order_t::with_amount(order_liquidity); - let swap_result = - Pallet::::do_swap(netuid, order, sqrt_limit_price, false, false).unwrap(); - assert_abs_diff_eq!( - swap_result.amount_paid_out.to_u64() as f64, - output_amount, - epsilon = output_amount / 10. - ); - - let tao_reserve = TaoReserve::reserve(netuid.into()).to_u64(); - let alpha_reserve = AlphaReserve::reserve(netuid.into()).to_u64(); - let output_amount = output_amount as u64; - - assert!(output_amount > 0); - - if alpha_reserve > order_liquidity && tao_reserve > order_liquidity { - assert_abs_diff_eq!( - swap_result.paid_in_reserve_delta() as i64, - order_liquidity as i64, - epsilon = order_liquidity as i64 / 100 - ); - assert_abs_diff_eq!( - swap_result.paid_out_reserve_delta() as i64, - -(output_amount as i64), - epsilon = output_amount as i64 / 100 - ); - } - - // Assert that price movement is in correct direction - let sqrt_current_price_after = AlphaSqrtPrice::::get(netuid); - let current_price_after = - (sqrt_current_price_after * sqrt_current_price_after).to_num::(); - assert_eq!(should_price_grow, current_price_after > current_price); - }; - } - - // All these orders are executed without swap reset - for order_liquidity in [ - (100_000_u64), - (1_000_000), - (10_000_000), - (100_000_000), - (200_000_000), - (500_000_000), - (1_000_000_000), - (10_000_000_000), - ] { - perform_test!(GetAlphaForTao, order_liquidity, 1000.0_f64, true); - perform_test!(GetTaoForAlpha, order_liquidity, 0.0001_f64, false); - } - - // Current price shouldn't be much different from the original - let sqrt_current_price_after = AlphaSqrtPrice::::get(netuid); - let current_price_after = - (sqrt_current_price_after * sqrt_current_price_after).to_num::(); - assert_abs_diff_eq!( - current_price, - current_price_after, - epsilon = current_price / 10. - ) - }); + todo!(); + + // new_test_ext().execute_with(|| { + // let min_price = tick_to_price(TickIndex::MIN); + // let max_price = tick_to_price(TickIndex::MAX); + // let max_tick = price_to_tick(max_price); + // let netuid = NetUid::from(1); + // assert_eq!(max_tick, TickIndex::MAX); + + // ////////////////////////////////////////////// + // // Initialize pool and add the user position + // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + + // // Add liquidity + // let current_price = Pallet::::current_price(netuid).to_num::(); + + // // Current price is 0.25 + // // All positions below are placed at once + // [ + // // Very localized position at the current price + // (-0.1, 0.1, 500_000_000_000_u64), + // // Repeat the protocol liquidity at maximum range + // ( + // min_price - current_price, + // max_price - current_price, + // 2_000_000_000_u64, + // ), + // // Repeat the protocol liquidity at current to max range + // (0.0, max_price - current_price, 2_000_000_000_u64), + // // Repeat the protocol liquidity at min to current range + // (min_price - current_price, 0.0, 2_000_000_000_u64), + // // Half to double price + // (-0.125, 0.25, 2_000_000_000_u64), + // // A few other price ranges and liquidity volumes + // (-0.1, 0.1, 2_000_000_000_u64), + // (-0.1, 0.1, 10_000_000_000_u64), + // (-0.1, 0.1, 100_000_000_000_u64), + // (-0.01, 0.01, 100_000_000_000_u64), + // (-0.001, 0.001, 100_000_000_000_u64), + // // A few (overlapping) positions up the range + // (0.01, 0.02, 100_000_000_000_u64), + // (0.02, 0.03, 100_000_000_000_u64), + // (0.03, 0.04, 100_000_000_000_u64), + // (0.03, 0.05, 100_000_000_000_u64), + // // A few (overlapping) positions down the range + // (-0.02, -0.01, 100_000_000_000_u64), + // (-0.03, -0.02, 100_000_000_000_u64), + // (-0.04, -0.03, 100_000_000_000_u64), + // (-0.05, -0.03, 100_000_000_000_u64), + // ] + // .into_iter() + // .for_each( + // |(price_low_offset, price_high_offset, position_liquidity)| { + // let price_low = price_low_offset + current_price; + // let price_high = price_high_offset + current_price; + // let tick_low = price_to_tick(price_low); + // let tick_high = price_to_tick(price_high); + // let (_position_id, _tao, _alpha) = Pallet::::do_add_liquidity( + // netuid, + // &OK_COLDKEY_ACCOUNT_ID, + // &OK_HOTKEY_ACCOUNT_ID, + // tick_low, + // tick_high, + // position_liquidity, + // ) + // .unwrap(); + // }, + // ); + + // macro_rules! perform_test { + // ($order_t:ident, $order_liquidity:expr, $limit_price:expr, $should_price_grow:expr) => { + // ////////////////////////////////////////////// + // // Swap + // let order_liquidity = $order_liquidity; + // let limit_price = $limit_price; + // let should_price_grow = $should_price_grow; + + // let sqrt_current_price = AlphaSqrtPrice::::get(netuid); + // let current_price = (sqrt_current_price * sqrt_current_price).to_num::(); + // let liquidity_before = CurrentLiquidity::::get(netuid); + // let output_amount = >::approx_expected_swap_output( + // sqrt_current_price.to_num(), + // liquidity_before as f64, + // order_liquidity as f64, + // ); + + // // Do the swap + // let sqrt_limit_price = SqrtPrice::from_num((limit_price).sqrt()); + // let order = $order_t::with_amount(order_liquidity); + // let swap_result = + // Pallet::::do_swap(netuid, order, sqrt_limit_price, false, false).unwrap(); + // assert_abs_diff_eq!( + // swap_result.amount_paid_out.to_u64() as f64, + // output_amount, + // epsilon = output_amount / 10. + // ); + + // let tao_reserve = TaoReserve::reserve(netuid.into()).to_u64(); + // let alpha_reserve = AlphaReserve::reserve(netuid.into()).to_u64(); + // let output_amount = output_amount as u64; + + // assert!(output_amount > 0); + + // if alpha_reserve > order_liquidity && tao_reserve > order_liquidity { + // assert_abs_diff_eq!( + // swap_result.paid_in_reserve_delta() as i64, + // order_liquidity as i64, + // epsilon = order_liquidity as i64 / 100 + // ); + // assert_abs_diff_eq!( + // swap_result.paid_out_reserve_delta() as i64, + // -(output_amount as i64), + // epsilon = output_amount as i64 / 100 + // ); + // } + + // // Assert that price movement is in correct direction + // let sqrt_current_price_after = AlphaSqrtPrice::::get(netuid); + // let current_price_after = + // (sqrt_current_price_after * sqrt_current_price_after).to_num::(); + // assert_eq!(should_price_grow, current_price_after > current_price); + // }; + // } + + // // All these orders are executed without swap reset + // for order_liquidity in [ + // (100_000_u64), + // (1_000_000), + // (10_000_000), + // (100_000_000), + // (200_000_000), + // (500_000_000), + // (1_000_000_000), + // (10_000_000_000), + // ] { + // perform_test!(GetAlphaForTao, order_liquidity, 1000.0_f64, true); + // perform_test!(GetTaoForAlpha, order_liquidity, 0.0001_f64, false); + // } + + // // Current price shouldn't be much different from the original + // let sqrt_current_price_after = AlphaSqrtPrice::::get(netuid); + // let current_price_after = + // (sqrt_current_price_after * sqrt_current_price_after).to_num::(); + // assert_abs_diff_eq!( + // current_price, + // current_price_after, + // epsilon = current_price / 10. + // ) + // }); } // cargo test --package pallet-subtensor-swap --lib -- pallet::impls::tests::test_swap_precision_edge_case --exact --show-output @@ -1289,94 +1242,73 @@ fn test_swap_precision_edge_case() { new_test_ext().execute_with(|| { let netuid = NetUid::from(123); // 123 is netuid with low edge case liquidity let order = GetTaoForAlpha::with_amount(1_000_000_000_000_000_000); - let tick_low = TickIndex::MIN; - - let sqrt_limit_price: SqrtPrice = tick_low.try_to_sqrt_price().unwrap(); // Setup swap - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); + assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + + // Minimum possible limit price + let limit_price: U64F64 = get_min_price(); // Swap let swap_result = - Pallet::::do_swap(netuid, order, sqrt_limit_price, false, true).unwrap(); + Pallet::::do_swap(netuid, order, limit_price, false, true).unwrap(); assert!(swap_result.amount_paid_out > TaoCurrency::ZERO); }); } -// cargo test --package pallet-subtensor-swap --lib -- pallet::impls::tests::test_price_tick_price_roundtrip --exact --show-output -#[test] -fn test_price_tick_price_roundtrip() { - new_test_ext().execute_with(|| { - let netuid = NetUid::from(1); - - // Setup swap - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - let current_price = SqrtPrice::from_num(0.500_000_512_192_122_7); - let tick = TickIndex::try_from_sqrt_price(current_price).unwrap(); - - let round_trip_price = TickIndex::try_to_sqrt_price(&tick).unwrap(); - assert!(round_trip_price <= current_price); - - let roundtrip_tick = TickIndex::try_from_sqrt_price(round_trip_price).unwrap(); - assert!(tick == roundtrip_tick); - }); -} - #[test] fn test_convert_deltas() { new_test_ext().execute_with(|| { let netuid = NetUid::from(1); - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); + assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - for (sqrt_price, delta_in, expected_buy, expected_sell) in [ - (SqrtPrice::from_num(1.5), 1, 0, 2), - (SqrtPrice::from_num(1.5), 10000, 4444, 22500), - (SqrtPrice::from_num(1.5), 1000000, 444444, 2250000), + // TODO: Add more test cases with different weights and edge cases for reserves + for (_tao, _alpha, _w1, _w2, delta_in, expected_buy, expected_sell) in [ + (1500, 1000, 0.5, 0.5, 1, 0, 2), + (1500, 1000, 0.5, 0.5, 10000, 4444, 22500), + (1500, 1000, 0.5, 0.5, 1000000, 444444, 2250000), ( - SqrtPrice::from_num(1.5), + 1500, 1000, 0.5, 0.5, u64::MAX, 2000000000000, 3000000000000, ), ( - TickIndex::MIN.as_sqrt_price_bounded(), + 1, 1000000, 0.5, 0.5, 1, 18406523739291577836, 465, ), - (TickIndex::MIN.as_sqrt_price_bounded(), 10000, u64::MAX, 465), + (1, 1000000, 0.5, 0.5, 10000, u64::MAX, 465), ( - TickIndex::MIN.as_sqrt_price_bounded(), + 1, 1000000, 0.5, 0.5, 1000000, u64::MAX, 465, ), ( - TickIndex::MIN.as_sqrt_price_bounded(), + 1, 1000000, 0.5, 0.5, u64::MAX, u64::MAX, 464, ), ( - TickIndex::MAX.as_sqrt_price_bounded(), + 1000000, 1, 0.5, 0.5, 1, 0, 18406523745214495085, ), - (TickIndex::MAX.as_sqrt_price_bounded(), 10000, 0, u64::MAX), - (TickIndex::MAX.as_sqrt_price_bounded(), 1000000, 0, u64::MAX), + (1000000, 1, 0.5, 0.5, 10000, 0, u64::MAX), + (1000000, 1, 0.5, 0.5, 1000000, 0, u64::MAX), ( - TickIndex::MAX.as_sqrt_price_bounded(), + 1000000, 1, 0.5, 0.5, u64::MAX, 2000000000000, u64::MAX, ), ] { { - AlphaSqrtPrice::::insert(netuid, sqrt_price); - assert_abs_diff_eq!( BasicSwapStep::::convert_deltas( netuid, @@ -1477,129 +1409,62 @@ fn test_convert_deltas() { /// #[test] fn test_swap_fee_correctness() { - new_test_ext().execute_with(|| { - let min_price = tick_to_price(TickIndex::MIN); - let max_price = tick_to_price(TickIndex::MAX); - let netuid = NetUid::from(1); - - // Provide very spread liquidity at the range from min to max that matches protocol liquidity - let liquidity = 2_000_000_000_000_u64; // 1x of protocol liquidity - - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - // Calculate ticks - let tick_low = price_to_tick(min_price); - let tick_high = price_to_tick(max_price); - - // Add user liquidity - let (position_id, _tao, _alpha) = Pallet::::do_add_liquidity( - netuid, - &OK_COLDKEY_ACCOUNT_ID, - &OK_HOTKEY_ACCOUNT_ID, - tick_low, - tick_high, - liquidity, - ) - .unwrap(); - - // Swap buy and swap sell - Pallet::::do_swap( - netuid, - GetAlphaForTao::with_amount(liquidity / 10), - u64::MAX.into(), - false, - false, - ) - .unwrap(); - Pallet::::do_swap( - netuid, - GetTaoForAlpha::with_amount(liquidity / 10), - 0_u64.into(), - false, - false, - ) - .unwrap(); - - // Get user position - let mut position = - Positions::::get((netuid, OK_COLDKEY_ACCOUNT_ID, position_id)).unwrap(); - assert_eq!(position.liquidity, liquidity); - assert_eq!(position.tick_low, tick_low); - assert_eq!(position.tick_high, tick_high); - - // Check that 50% of fees were credited to the position - let fee_rate = FeeRate::::get(NetUid::from(netuid)) as f64 / u16::MAX as f64; - let (actual_fee_tao, actual_fee_alpha) = position.collect_fees(); - let expected_fee = (fee_rate * (liquidity / 10) as f64 * 0.5) as u64; - - assert_abs_diff_eq!(actual_fee_tao, expected_fee, epsilon = 1,); - assert_abs_diff_eq!(actual_fee_alpha, expected_fee, epsilon = 1,); - }); -} - -#[test] -fn test_current_liquidity_updates() { - let netuid = NetUid::from(1); - let liquidity = 1_000_000_000; - - // Get current price - let (current_price, current_price_low, current_price_high) = - new_test_ext().execute_with(|| { - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - let sqrt_current_price = AlphaSqrtPrice::::get(netuid); - let current_price = (sqrt_current_price * sqrt_current_price).to_num::(); - let (current_price_low, current_price_high) = get_ticked_prices_around_current_price(); - (current_price, current_price_low, current_price_high) - }); - - // Test case: (price_low, price_high, expect_to_update) - [ - // Current price is out of position range (lower), no current lq update - (current_price * 2., current_price * 3., false), - // Current price is out of position range (higher), no current lq update - (current_price / 3., current_price / 2., false), - // Current price is just below position range, no current lq update - (current_price_high, current_price * 3., false), - // Position lower edge is just below the current price, current lq updates - (current_price_low, current_price * 3., true), - // Current price is exactly at lower edge of position range, current lq updates - (current_price, current_price * 3., true), - // Current price is exactly at higher edge of position range, no current lq update - (current_price / 2., current_price, false), - ] - .into_iter() - .for_each(|(price_low, price_high, expect_to_update)| { - new_test_ext().execute_with(|| { - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - // Calculate ticks (assuming tick math is tested separately) - let tick_low = price_to_tick(price_low); - let tick_high = price_to_tick(price_high); - let liquidity_before = CurrentLiquidity::::get(netuid); - - // Add liquidity - assert_ok!(Pallet::::do_add_liquidity( - netuid, - &OK_COLDKEY_ACCOUNT_ID, - &OK_HOTKEY_ACCOUNT_ID, - tick_low, - tick_high, - liquidity, - )); - - // Current liquidity is updated only when price range includes the current price - let expected_liquidity = if (price_high > current_price) && (price_low <= current_price) - { - assert!(expect_to_update); - liquidity_before + liquidity - } else { - assert!(!expect_to_update); - liquidity_before - }; - - assert_eq!(CurrentLiquidity::::get(netuid), expected_liquidity) - }); - }); + todo!(); + + // new_test_ext().execute_with(|| { + // let min_price = get_min_price(); + // let max_price = get_max_price(); + // let netuid = NetUid::from(1); + + // // Provide very spread liquidity at the range from min to max that matches protocol liquidity + // let liquidity = 2_000_000_000_000_u64; // 1x of protocol liquidity + + // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + + // // Add user liquidity + // let (position_id, _tao, _alpha) = Pallet::::do_add_liquidity( + // netuid, + // &OK_COLDKEY_ACCOUNT_ID, + // &OK_HOTKEY_ACCOUNT_ID, + // tick_low, + // tick_high, + // liquidity, + // ) + // .unwrap(); + + // // Swap buy and swap sell + // Pallet::::do_swap( + // netuid, + // GetAlphaForTao::with_amount(liquidity / 10), + // u64::MAX.into(), + // false, + // false, + // ) + // .unwrap(); + // Pallet::::do_swap( + // netuid, + // GetTaoForAlpha::with_amount(liquidity / 10), + // 0_u64.into(), + // false, + // false, + // ) + // .unwrap(); + + // // Get user position + // let mut position = + // Positions::::get((netuid, OK_COLDKEY_ACCOUNT_ID, position_id)).unwrap(); + // assert_eq!(position.liquidity, liquidity); + // assert_eq!(position.tick_low, tick_low); + // assert_eq!(position.tick_high, tick_high); + + // // Check that 50% of fees were credited to the position + // let fee_rate = FeeRate::::get(NetUid::from(netuid)) as f64 / u16::MAX as f64; + // let (actual_fee_tao, actual_fee_alpha) = position.collect_fees(); + // let expected_fee = (fee_rate * (liquidity / 10) as f64 * 0.5) as u64; + + // assert_abs_diff_eq!(actual_fee_tao, expected_fee, epsilon = 1,); + // assert_abs_diff_eq!(actual_fee_alpha, expected_fee, epsilon = 1,); + // }); } #[test] @@ -1634,74 +1499,77 @@ fn test_rollback_works() { /// cargo test --package pallet-subtensor-swap --lib -- pallet::tests::test_new_lp_doesnt_get_old_fees --exact --show-output #[test] fn test_new_lp_doesnt_get_old_fees() { - new_test_ext().execute_with(|| { - let min_price = tick_to_price(TickIndex::MIN); - let max_price = tick_to_price(TickIndex::MAX); - let netuid = NetUid::from(1); - - // Provide very spread liquidity at the range from min to max that matches protocol liquidity - let liquidity = 2_000_000_000_000_u64; // 1x of protocol liquidity - - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - // Calculate ticks - let tick_low = price_to_tick(min_price); - let tick_high = price_to_tick(max_price); - - // Add user liquidity - Pallet::::do_add_liquidity( - netuid, - &OK_COLDKEY_ACCOUNT_ID, - &OK_HOTKEY_ACCOUNT_ID, - tick_low, - tick_high, - liquidity, - ) - .unwrap(); - - // Swap buy and swap sell - Pallet::::do_swap( - netuid, - GetAlphaForTao::with_amount(liquidity / 10), - u64::MAX.into(), - false, - false, - ) - .unwrap(); - Pallet::::do_swap( - netuid, - GetTaoForAlpha::with_amount(liquidity / 10), - 0_u64.into(), - false, - false, - ) - .unwrap(); - - // Add liquidity from a different user to a new tick - let (position_id_2, _tao, _alpha) = Pallet::::do_add_liquidity( - netuid, - &OK_COLDKEY_ACCOUNT_ID_2, - &OK_HOTKEY_ACCOUNT_ID_2, - tick_low.next().unwrap(), - tick_high.prev().unwrap(), - liquidity, - ) - .unwrap(); - - // Get user position - let mut position = - Positions::::get((netuid, OK_COLDKEY_ACCOUNT_ID_2, position_id_2)).unwrap(); - assert_eq!(position.liquidity, liquidity); - assert_eq!(position.tick_low, tick_low.next().unwrap()); - assert_eq!(position.tick_high, tick_high.prev().unwrap()); - - // Check that collected fees are 0 - let (actual_fee_tao, actual_fee_alpha) = position.collect_fees(); - assert_abs_diff_eq!(actual_fee_tao, 0, epsilon = 1); - assert_abs_diff_eq!(actual_fee_alpha, 0, epsilon = 1); - }); + todo!(); + + // new_test_ext().execute_with(|| { + // let min_price = tick_to_price(TickIndex::MIN); + // let max_price = tick_to_price(TickIndex::MAX); + // let netuid = NetUid::from(1); + + // // Provide very spread liquidity at the range from min to max that matches protocol liquidity + // let liquidity = 2_000_000_000_000_u64; // 1x of protocol liquidity + + // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + + // // Calculate ticks + // let tick_low = price_to_tick(min_price); + // let tick_high = price_to_tick(max_price); + + // // Add user liquidity + // Pallet::::do_add_liquidity( + // netuid, + // &OK_COLDKEY_ACCOUNT_ID, + // &OK_HOTKEY_ACCOUNT_ID, + // tick_low, + // tick_high, + // liquidity, + // ) + // .unwrap(); + + // // Swap buy and swap sell + // Pallet::::do_swap( + // netuid, + // GetAlphaForTao::with_amount(liquidity / 10), + // u64::MAX.into(), + // false, + // false, + // ) + // .unwrap(); + // Pallet::::do_swap( + // netuid, + // GetTaoForAlpha::with_amount(liquidity / 10), + // 0_u64.into(), + // false, + // false, + // ) + // .unwrap(); + + // // Add liquidity from a different user to a new tick + // let (position_id_2, _tao, _alpha) = Pallet::::do_add_liquidity( + // netuid, + // &OK_COLDKEY_ACCOUNT_ID_2, + // &OK_HOTKEY_ACCOUNT_ID_2, + // tick_low.next().unwrap(), + // tick_high.prev().unwrap(), + // liquidity, + // ) + // .unwrap(); + + // // Get user position + // let mut position = + // Positions::::get((netuid, OK_COLDKEY_ACCOUNT_ID_2, position_id_2)).unwrap(); + // assert_eq!(position.liquidity, liquidity); + // assert_eq!(position.tick_low, tick_low.next().unwrap()); + // assert_eq!(position.tick_high, tick_high.prev().unwrap()); + + // // Check that collected fees are 0 + // let (actual_fee_tao, actual_fee_alpha) = position.collect_fees(); + // assert_abs_diff_eq!(actual_fee_tao, 0, epsilon = 1); + // assert_abs_diff_eq!(actual_fee_alpha, 0, epsilon = 1); + // }); } +#[allow(dead_code)] fn bbox(t: U64F64, a: U64F64, b: U64F64) -> U64F64 { if t < a { a @@ -1712,214 +1580,212 @@ fn bbox(t: U64F64, a: U64F64, b: U64F64) -> U64F64 { } } +#[allow(dead_code)] fn print_current_price(netuid: NetUid) { - let current_sqrt_price = AlphaSqrtPrice::::get(netuid).to_num::(); - let current_price = current_sqrt_price * current_sqrt_price; + let current_price = Pallet::::current_price(netuid); log::trace!("Current price: {current_price:.6}"); } /// RUST_LOG=pallet_subtensor_swap=trace cargo test --package pallet-subtensor-swap --lib -- pallet::tests::test_wrapping_fees --exact --show-output --nocapture #[test] fn test_wrapping_fees() { - new_test_ext().execute_with(|| { - let netuid = NetUid::from(WRAPPING_FEES_NETUID); - let position_1_low_price = 0.20; - let position_1_high_price = 0.255; - let position_2_low_price = 0.255; - let position_2_high_price = 0.257; - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - Pallet::::do_add_liquidity( - netuid, - &OK_COLDKEY_ACCOUNT_ID_RICH, - &OK_COLDKEY_ACCOUNT_ID_RICH, - price_to_tick(position_1_low_price), - price_to_tick(position_1_high_price), - 1_000_000_000_u64, - ) - .unwrap(); - - print_current_price(netuid); - - let order = GetTaoForAlpha::with_amount(800_000_000); - let sqrt_limit_price = SqrtPrice::from_num(0.000001); - Pallet::::do_swap(netuid, order, sqrt_limit_price, false, false).unwrap(); - - let order = GetAlphaForTao::with_amount(1_850_000_000); - let sqrt_limit_price = SqrtPrice::from_num(1_000_000.0); - - print_current_price(netuid); - - Pallet::::do_swap(netuid, order, sqrt_limit_price, false, false).unwrap(); - - print_current_price(netuid); - - let add_liquidity_result = Pallet::::do_add_liquidity( - netuid, - &OK_COLDKEY_ACCOUNT_ID_RICH, - &OK_COLDKEY_ACCOUNT_ID_RICH, - price_to_tick(position_2_low_price), - price_to_tick(position_2_high_price), - 1_000_000_000_u64, - ) - .unwrap(); - - let order = GetTaoForAlpha::with_amount(1_800_000_000); - let sqrt_limit_price = SqrtPrice::from_num(0.000001); - - let initial_sqrt_price = AlphaSqrtPrice::::get(netuid); - Pallet::::do_swap(netuid, order, sqrt_limit_price, false, false).unwrap(); - let final_sqrt_price = AlphaSqrtPrice::::get(netuid); - - print_current_price(netuid); - - let mut position = - Positions::::get((netuid, &OK_COLDKEY_ACCOUNT_ID_RICH, add_liquidity_result.0)) - .unwrap(); - - let initial_box_price = bbox( - initial_sqrt_price, - position.tick_low.try_to_sqrt_price().unwrap(), - position.tick_high.try_to_sqrt_price().unwrap(), - ); - - let final_box_price = bbox( - final_sqrt_price, - position.tick_low.try_to_sqrt_price().unwrap(), - position.tick_high.try_to_sqrt_price().unwrap(), - ); - - let fee_rate = FeeRate::::get(netuid) as f64 / u16::MAX as f64; - - log::trace!("fee_rate: {fee_rate:.6}"); - log::trace!("position.liquidity: {}", position.liquidity); - log::trace!( - "initial_box_price: {:.6}", - initial_box_price.to_num::() - ); - log::trace!("final_box_price: {:.6}", final_box_price.to_num::()); - - let expected_fee_tao = ((fee_rate / (1.0 - fee_rate)) - * (position.liquidity as f64) - * (final_box_price.to_num::() - initial_box_price.to_num::())) - as u64; - - let expected_fee_alpha = ((fee_rate / (1.0 - fee_rate)) - * (position.liquidity as f64) - * ((1.0 / final_box_price.to_num::()) - (1.0 / initial_box_price.to_num::()))) - as u64; - - log::trace!("Expected ALPHA fee: {:.6}", expected_fee_alpha as f64); - - let (fee_tao, fee_alpha) = position.collect_fees(); - - log::trace!("Collected fees: TAO: {fee_tao}, ALPHA: {fee_alpha}"); - - assert_abs_diff_eq!(fee_tao, expected_fee_tao, epsilon = 1); - assert_abs_diff_eq!(fee_alpha, expected_fee_alpha, epsilon = 1); - }); + todo!(); + + // new_test_ext().execute_with(|| { + // let netuid = NetUid::from(WRAPPING_FEES_NETUID); + // let position_1_low_price = 0.20; + // let position_1_high_price = 0.255; + // let position_2_low_price = 0.255; + // let position_2_high_price = 0.257; + // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + + // Pallet::::do_add_liquidity( + // netuid, + // &OK_COLDKEY_ACCOUNT_ID_RICH, + // &OK_COLDKEY_ACCOUNT_ID_RICH, + // price_to_tick(position_1_low_price), + // price_to_tick(position_1_high_price), + // 1_000_000_000_u64, + // ) + // .unwrap(); + + // print_current_price(netuid); + + // let order = GetTaoForAlpha::with_amount(800_000_000); + // let sqrt_limit_price = SqrtPrice::from_num(0.000001); + // Pallet::::do_swap(netuid, order, sqrt_limit_price, false, false).unwrap(); + + // let order = GetAlphaForTao::with_amount(1_850_000_000); + // let sqrt_limit_price = SqrtPrice::from_num(1_000_000.0); + + // print_current_price(netuid); + + // Pallet::::do_swap(netuid, order, sqrt_limit_price, false, false).unwrap(); + + // print_current_price(netuid); + + // let add_liquidity_result = Pallet::::do_add_liquidity( + // netuid, + // &OK_COLDKEY_ACCOUNT_ID_RICH, + // &OK_COLDKEY_ACCOUNT_ID_RICH, + // price_to_tick(position_2_low_price), + // price_to_tick(position_2_high_price), + // 1_000_000_000_u64, + // ) + // .unwrap(); + + // let order = GetTaoForAlpha::with_amount(1_800_000_000); + // let sqrt_limit_price = SqrtPrice::from_num(0.000001); + + // let initial_sqrt_price = AlphaSqrtPrice::::get(netuid); + // Pallet::::do_swap(netuid, order, sqrt_limit_price, false, false).unwrap(); + // let final_sqrt_price = AlphaSqrtPrice::::get(netuid); + + // print_current_price(netuid); + + // let mut position = + // Positions::::get((netuid, &OK_COLDKEY_ACCOUNT_ID_RICH, add_liquidity_result.0)) + // .unwrap(); + + // let initial_box_price = bbox( + // initial_sqrt_price, + // position.tick_low.try_to_sqrt_price().unwrap(), + // position.tick_high.try_to_sqrt_price().unwrap(), + // ); + + // let final_box_price = bbox( + // final_sqrt_price, + // position.tick_low.try_to_sqrt_price().unwrap(), + // position.tick_high.try_to_sqrt_price().unwrap(), + // ); + + // let fee_rate = FeeRate::::get(netuid) as f64 / u16::MAX as f64; + + // log::trace!("fee_rate: {fee_rate:.6}"); + // log::trace!("position.liquidity: {}", position.liquidity); + // log::trace!( + // "initial_box_price: {:.6}", + // initial_box_price.to_num::() + // ); + // log::trace!("final_box_price: {:.6}", final_box_price.to_num::()); + + // let expected_fee_tao = ((fee_rate / (1.0 - fee_rate)) + // * (position.liquidity as f64) + // * (final_box_price.to_num::() - initial_box_price.to_num::())) + // as u64; + + // let expected_fee_alpha = ((fee_rate / (1.0 - fee_rate)) + // * (position.liquidity as f64) + // * ((1.0 / final_box_price.to_num::()) - (1.0 / initial_box_price.to_num::()))) + // as u64; + + // log::trace!("Expected ALPHA fee: {:.6}", expected_fee_alpha as f64); + + // let (fee_tao, fee_alpha) = position.collect_fees(); + + // log::trace!("Collected fees: TAO: {fee_tao}, ALPHA: {fee_alpha}"); + + // assert_abs_diff_eq!(fee_tao, expected_fee_tao, epsilon = 1); + // assert_abs_diff_eq!(fee_alpha, expected_fee_alpha, epsilon = 1); + // }); } /// Test that price moves less with provided liquidity /// cargo test --package pallet-subtensor-swap --lib -- pallet::tests::test_less_price_movement --exact --show-output #[test] fn test_less_price_movement() { - let netuid = NetUid::from(1); - let mut last_end_price = U96F32::from_num(0); - let initial_stake_liquidity = 1_000_000_000; - let swapped_liquidity = 1_000_000; - - // Test case is (order_type, provided_liquidity) - // Testing algorithm: - // - Stake initial_stake_liquidity - // - Provide liquidity if iteration provides lq - // - Buy or sell - // - Save end price if iteration doesn't provide lq - macro_rules! perform_test { - ($order_t:ident, $provided_liquidity:expr, $limit_price:expr, $should_price_shrink:expr) => { - let provided_liquidity = $provided_liquidity; - let should_price_shrink = $should_price_shrink; - let limit_price = $limit_price; - new_test_ext().execute_with(|| { - // Setup swap - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - // Buy Alpha - assert_ok!(Pallet::::do_swap( - netuid, - GetAlphaForTao::with_amount(initial_stake_liquidity), - SqrtPrice::from_num(10_000_000_000_u64), - false, - false - )); - - // Get current price - let start_price = Pallet::::current_price(netuid); - - // Add liquidity if this test iteration provides - if provided_liquidity > 0 { - let tick_low = price_to_tick(start_price.to_num::() * 0.5); - let tick_high = price_to_tick(start_price.to_num::() * 1.5); - assert_ok!(Pallet::::do_add_liquidity( - netuid, - &OK_COLDKEY_ACCOUNT_ID, - &OK_HOTKEY_ACCOUNT_ID, - tick_low, - tick_high, - provided_liquidity, - )); - } - - // Swap - let sqrt_limit_price = SqrtPrice::from_num(limit_price); - assert_ok!(Pallet::::do_swap( - netuid, - $order_t::with_amount(swapped_liquidity), - sqrt_limit_price, - false, - false - )); - - let end_price = Pallet::::current_price(netuid); - - // Save end price if iteration doesn't provide or compare with previous end price if - // it does - if provided_liquidity > 0 { - assert_eq!(should_price_shrink, end_price < last_end_price); - } else { - last_end_price = end_price; - } - }); - }; - } + todo!(); + + // let netuid = NetUid::from(1); + // let mut last_end_price = U64F64::from_num(0); + // let initial_stake_liquidity = 1_000_000_000; + // let swapped_liquidity = 1_000_000; + + // // Test case is (order_type, provided_liquidity) + // // Testing algorithm: + // // - Stake initial_stake_liquidity + // // - Provide liquidity if iteration provides lq + // // - Buy or sell + // // - Save end price if iteration doesn't provide lq + // macro_rules! perform_test { + // ($order_t:ident, $provided_liquidity:expr, $limit_price:expr, $should_price_shrink:expr) => { + // let provided_liquidity = $provided_liquidity; + // let should_price_shrink = $should_price_shrink; + // let limit_price = $limit_price; + // new_test_ext().execute_with(|| { + // // Setup swap + // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + + // // Buy Alpha + // assert_ok!(Pallet::::do_swap( + // netuid, + // GetAlphaForTao::with_amount(initial_stake_liquidity), + // SqrtPrice::from_num(10_000_000_000_u64), + // false, + // false + // )); + + // // Get current price + // let start_price = Pallet::::current_price(netuid); + + // // Add liquidity if this test iteration provides + // if provided_liquidity > 0 { + // let tick_low = price_to_tick(start_price.to_num::() * 0.5); + // let tick_high = price_to_tick(start_price.to_num::() * 1.5); + // assert_ok!(Pallet::::do_add_liquidity( + // netuid, + // &OK_COLDKEY_ACCOUNT_ID, + // &OK_HOTKEY_ACCOUNT_ID, + // tick_low, + // tick_high, + // provided_liquidity, + // )); + // } + + // // Swap + // let sqrt_limit_price = SqrtPrice::from_num(limit_price); + // assert_ok!(Pallet::::do_swap( + // netuid, + // $order_t::with_amount(swapped_liquidity), + // sqrt_limit_price, + // false, + // false + // )); + + // let end_price = Pallet::::current_price(netuid); + + // // Save end price if iteration doesn't provide or compare with previous end price if + // // it does + // if provided_liquidity > 0 { + // assert_eq!(should_price_shrink, end_price < last_end_price); + // } else { + // last_end_price = end_price; + // } + // }); + // }; + // } - for provided_liquidity in [0, 1_000_000_000_000_u64] { - perform_test!(GetAlphaForTao, provided_liquidity, 1000.0_f64, true); - } - for provided_liquidity in [0, 1_000_000_000_000_u64] { - perform_test!(GetTaoForAlpha, provided_liquidity, 0.001_f64, false); - } + // for provided_liquidity in [0, 1_000_000_000_000_u64] { + // perform_test!(GetAlphaForTao, provided_liquidity, 1000.0_f64, true); + // } + // for provided_liquidity in [0, 1_000_000_000_000_u64] { + // perform_test!(GetTaoForAlpha, provided_liquidity, 0.001_f64, false); + // } } #[test] fn test_swap_subtoken_disabled() { new_test_ext().execute_with(|| { let netuid = NetUid::from(SUBTOKEN_DISABLED_NETUID); // Use a netuid not used elsewhere - let price_low = 0.1; - let price_high = 0.2; - let tick_low = price_to_tick(price_low); - let tick_high = price_to_tick(price_high); let liquidity = 1_000_000_u64; - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); + assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); assert_noop!( Pallet::::add_liquidity( RuntimeOrigin::signed(OK_COLDKEY_ACCOUNT_ID), OK_HOTKEY_ACCOUNT_ID, netuid, - tick_low, - tick_high, liquidity, ), Error::::SubtokenDisabled @@ -1940,109 +1806,107 @@ fn test_swap_subtoken_disabled() { #[test] fn test_liquidate_v3_removes_positions_ticks_and_state() { - new_test_ext().execute_with(|| { - let netuid = NetUid::from(1); - - // Initialize V3 (creates protocol position, ticks, price, liquidity) - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - assert!(SwapV3Initialized::::get(netuid)); - - // Enable user LP - assert_ok!(Swap::toggle_user_liquidity( - RuntimeOrigin::root(), - netuid.into(), - true - )); - - // Add a user position across the full range to ensure ticks/bitmap are populated. - let min_price = tick_to_price(TickIndex::MIN); - let max_price = tick_to_price(TickIndex::MAX); - let tick_low = price_to_tick(min_price); - let tick_high = price_to_tick(max_price); - let liquidity = 2_000_000_000_u64; - - let (_pos_id, _tao, _alpha) = Pallet::::do_add_liquidity( - netuid, - &OK_COLDKEY_ACCOUNT_ID, - &OK_HOTKEY_ACCOUNT_ID, - tick_low, - tick_high, - liquidity, - ) - .expect("add liquidity"); - - // Accrue some global fees so we can verify fee storage is cleared later. - let sqrt_limit_price = SqrtPrice::from_num(1_000_000.0); - assert_ok!(Pallet::::do_swap( - netuid, - GetAlphaForTao::with_amount(1_000_000), - sqrt_limit_price, - false, - false - )); - - // Sanity: protocol & user positions exist, ticks exist, liquidity > 0 - let protocol_id = Pallet::::protocol_account_id(); - let prot_positions = - Positions::::iter_prefix_values((netuid, protocol_id)).collect::>(); - assert!(!prot_positions.is_empty()); - - let user_positions = Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) - .collect::>(); - assert_eq!(user_positions.len(), 1); - - assert!(Ticks::::get(netuid, TickIndex::MIN).is_some()); - assert!(Ticks::::get(netuid, TickIndex::MAX).is_some()); - assert!(CurrentLiquidity::::get(netuid) > 0); - - let had_bitmap_words = TickIndexBitmapWords::::iter_prefix((netuid,)) - .next() - .is_some(); - assert!(had_bitmap_words); - - // ACT: users-only liquidation then protocol clear - assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); - - // ASSERT: positions cleared (both user and protocol) - assert_eq!( - Pallet::::count_positions(netuid, &OK_COLDKEY_ACCOUNT_ID), - 0 - ); - let prot_positions_after = - Positions::::iter_prefix_values((netuid, protocol_id)).collect::>(); - assert!(prot_positions_after.is_empty()); - let user_positions_after = - Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) - .collect::>(); - assert!(user_positions_after.is_empty()); - - // ASSERT: ticks cleared - 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: fee globals cleared - assert!(!FeeGlobalTao::::contains_key(netuid)); - assert!(!FeeGlobalAlpha::::contains_key(netuid)); - - // ASSERT: price/tick/liquidity flags cleared - assert!(!AlphaSqrtPrice::::contains_key(netuid)); - assert!(!CurrentTick::::contains_key(netuid)); - assert!(!CurrentLiquidity::::contains_key(netuid)); - assert!(!SwapV3Initialized::::contains_key(netuid)); - - // ASSERT: active tick bitmap cleared - assert!( - TickIndexBitmapWords::::iter_prefix((netuid,)) - .next() - .is_none() - ); - - // ASSERT: knobs removed on dereg - assert!(!FeeRate::::contains_key(netuid)); - assert!(!EnabledUserLiquidity::::contains_key(netuid)); - }); + todo!(); + + // new_test_ext().execute_with(|| { + // let netuid = NetUid::from(1); + + // // Initialize V3 (creates protocol position, ticks, price, liquidity) + // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + // assert!(PalSwapInitialized::::get(netuid)); + + // // Enable user LP + // assert_ok!(Swap::toggle_user_liquidity( + // RuntimeOrigin::root(), + // netuid.into(), + // true + // )); + + // // Add a user position across the full range to ensure ticks/bitmap are populated. + // let min_price = get_min_price(); + // let max_price = get_max_price(); + // let liquidity = 2_000_000_000_u64; + + // let (_pos_id, _tao, _alpha) = Pallet::::do_add_liquidity( + // netuid, + // &OK_COLDKEY_ACCOUNT_ID, + // &OK_HOTKEY_ACCOUNT_ID, + // liquidity, + // ) + // .expect("add liquidity"); + + // // Accrue some global fees so we can verify fee storage is cleared later. + // let sqrt_limit_price = SqrtPrice::from_num(1_000_000.0); + // assert_ok!(Pallet::::do_swap( + // netuid, + // GetAlphaForTao::with_amount(1_000_000), + // sqrt_limit_price, + // false, + // false + // )); + + // // Sanity: protocol & user positions exist, ticks exist, liquidity > 0 + // let protocol_id = Pallet::::protocol_account_id(); + // let prot_positions = + // Positions::::iter_prefix_values((netuid, protocol_id)).collect::>(); + // assert!(!prot_positions.is_empty()); + + // let user_positions = Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) + // .collect::>(); + // assert_eq!(user_positions.len(), 1); + + // assert!(Ticks::::get(netuid, TickIndex::MIN).is_some()); + // assert!(Ticks::::get(netuid, TickIndex::MAX).is_some()); + // assert!(CurrentLiquidity::::get(netuid) > 0); + + // let had_bitmap_words = TickIndexBitmapWords::::iter_prefix((netuid,)) + // .next() + // .is_some(); + // assert!(had_bitmap_words); + + // // ACT: users-only liquidation then protocol clear + // assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); + // assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); + + // // ASSERT: positions cleared (both user and protocol) + // assert_eq!( + // Pallet::::count_positions(netuid, &OK_COLDKEY_ACCOUNT_ID), + // 0 + // ); + // let prot_positions_after = + // Positions::::iter_prefix_values((netuid, protocol_id)).collect::>(); + // assert!(prot_positions_after.is_empty()); + // let user_positions_after = + // Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) + // .collect::>(); + // assert!(user_positions_after.is_empty()); + + // // ASSERT: ticks cleared + // 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: fee globals cleared + // assert!(!FeeGlobalTao::::contains_key(netuid)); + // assert!(!FeeGlobalAlpha::::contains_key(netuid)); + + // // ASSERT: price/tick/liquidity flags cleared + // assert!(!AlphaSqrtPrice::::contains_key(netuid)); + // assert!(!CurrentTick::::contains_key(netuid)); + // assert!(!CurrentLiquidity::::contains_key(netuid)); + // assert!(!PalSwapInitialized::::contains_key(netuid)); + + // // ASSERT: active tick bitmap cleared + // assert!( + // TickIndexBitmapWords::::iter_prefix((netuid,)) + // .next() + // .is_none() + // ); + + // // ASSERT: knobs removed on dereg + // assert!(!FeeRate::::contains_key(netuid)); + // assert!(!EnabledUserLiquidity::::contains_key(netuid)); + // }); } // V3 path with user liquidity disabled at teardown: @@ -2052,8 +1916,8 @@ fn test_liquidate_v3_removes_positions_ticks_and_state() { // new_test_ext().execute_with(|| { // let netuid = NetUid::from(101); -// assert_ok!(Pallet::::maybe_initialize_v3(netuid)); -// assert!(SwapV3Initialized::::get(netuid)); +// assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); +// assert!(PalSwapInitialized::::get(netuid)); // // Enable temporarily to add a user position // assert_ok!(Swap::toggle_user_liquidity( @@ -2105,7 +1969,7 @@ fn test_liquidate_v3_removes_positions_ticks_and_state() { // .next() // .is_none() // ); -// assert!(!SwapV3Initialized::::contains_key(netuid)); +// assert!(!PalSwapInitialized::::contains_key(netuid)); // assert!(!AlphaSqrtPrice::::contains_key(netuid)); // assert!(!CurrentTick::::contains_key(netuid)); // assert!(!CurrentLiquidity::::contains_key(netuid)); @@ -2120,631 +1984,648 @@ fn test_liquidate_v3_removes_positions_ticks_and_state() { /// Non‑V3 path: V3 not initialized (no positions); function must still clear any residual storages and succeed. #[test] fn test_liquidate_non_v3_uninitialized_ok_and_clears() { - new_test_ext().execute_with(|| { - let netuid = NetUid::from(202); - - // Sanity: V3 is not initialized - assert!(!SwapV3Initialized::::get(netuid)); - assert!( - Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) - .next() - .is_none() - ); - - // ACT - assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - - // ASSERT: Defensive clears leave no residues and do not panic - assert!( - Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) - .next() - .is_none() - ); - assert!(Ticks::::iter_prefix(netuid).next().is_none()); - assert!( - TickIndexBitmapWords::::iter_prefix((netuid,)) - .next() - .is_none() - ); - - // All single-key maps should not have the key after liquidation - assert!(!FeeGlobalTao::::contains_key(netuid)); - assert!(!FeeGlobalAlpha::::contains_key(netuid)); - assert!(!CurrentLiquidity::::contains_key(netuid)); - assert!(!CurrentTick::::contains_key(netuid)); - assert!(!AlphaSqrtPrice::::contains_key(netuid)); - assert!(!SwapV3Initialized::::contains_key(netuid)); - assert!(!FeeRate::::contains_key(netuid)); - assert!(!EnabledUserLiquidity::::contains_key(netuid)); - }); + todo!(); + + // new_test_ext().execute_with(|| { + // let netuid = NetUid::from(202); + + // // Sanity: V3 is not initialized + // assert!(!PalSwapInitialized::::get(netuid)); + // assert!( + // Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) + // .next() + // .is_none() + // ); + + // // ACT + // assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); + + // // ASSERT: Defensive clears leave no residues and do not panic + // assert!( + // Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) + // .next() + // .is_none() + // ); + // assert!(Ticks::::iter_prefix(netuid).next().is_none()); + // assert!( + // TickIndexBitmapWords::::iter_prefix((netuid,)) + // .next() + // .is_none() + // ); + + // // All single-key maps should not have the key after liquidation + // assert!(!FeeGlobalTao::::contains_key(netuid)); + // assert!(!FeeGlobalAlpha::::contains_key(netuid)); + // assert!(!CurrentLiquidity::::contains_key(netuid)); + // assert!(!CurrentTick::::contains_key(netuid)); + // assert!(!AlphaSqrtPrice::::contains_key(netuid)); + // assert!(!PalSwapInitialized::::contains_key(netuid)); + // assert!(!FeeRate::::contains_key(netuid)); + // assert!(!EnabledUserLiquidity::::contains_key(netuid)); + // }); } #[test] fn test_liquidate_idempotent() { - // V3 flavor - new_test_ext().execute_with(|| { - let netuid = NetUid::from(7); - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - // Add a small user position - assert_ok!(Swap::toggle_user_liquidity( - RuntimeOrigin::root(), - netuid.into(), - true - )); - let tick_low = price_to_tick(0.2); - let tick_high = price_to_tick(0.3); - assert_ok!(Pallet::::do_add_liquidity( - netuid, - &OK_COLDKEY_ACCOUNT_ID, - &OK_HOTKEY_ACCOUNT_ID, - tick_low, - tick_high, - 123_456_789 - )); - - // Users-only liquidations are idempotent. - assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - - // Now clear protocol liquidity/state—also idempotent. - assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); - assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); - - // State remains empty - assert!( - Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) - .next() - .is_none() - ); - assert!(Ticks::::iter_prefix(netuid).next().is_none()); - assert!( - TickIndexBitmapWords::::iter_prefix((netuid,)) - .next() - .is_none() - ); - assert!(!SwapV3Initialized::::contains_key(netuid)); - }); - - // Non‑V3 flavor - new_test_ext().execute_with(|| { - let netuid = NetUid::from(8); - - // Never initialize V3; both calls no-op and succeed. - assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - - assert!( - Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) - .next() - .is_none() - ); - assert!(Ticks::::iter_prefix(netuid).next().is_none()); - assert!( - TickIndexBitmapWords::::iter_prefix((netuid,)) - .next() - .is_none() - ); - assert!(!SwapV3Initialized::::contains_key(netuid)); - }); + todo!(); + + // // V3 flavor + // new_test_ext().execute_with(|| { + // let netuid = NetUid::from(7); + // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + + // // Add a small user position + // assert_ok!(Swap::toggle_user_liquidity( + // RuntimeOrigin::root(), + // netuid.into(), + // true + // )); + // let tick_low = price_to_tick(0.2); + // let tick_high = price_to_tick(0.3); + // assert_ok!(Pallet::::do_add_liquidity( + // netuid, + // &OK_COLDKEY_ACCOUNT_ID, + // &OK_HOTKEY_ACCOUNT_ID, + // tick_low, + // tick_high, + // 123_456_789 + // )); + + // // Users-only liquidations are idempotent. + // assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); + // assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); + + // // Now clear protocol liquidity/state—also idempotent. + // assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); + // assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); + + // // State remains empty + // assert!( + // Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) + // .next() + // .is_none() + // ); + // assert!(Ticks::::iter_prefix(netuid).next().is_none()); + // assert!( + // TickIndexBitmapWords::::iter_prefix((netuid,)) + // .next() + // .is_none() + // ); + // assert!(!PalSwapInitialized::::contains_key(netuid)); + // }); + + // // Non‑V3 flavor + // new_test_ext().execute_with(|| { + // let netuid = NetUid::from(8); + + // // Never initialize V3; both calls no-op and succeed. + // assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); + // assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); + + // assert!( + // Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) + // .next() + // .is_none() + // ); + // assert!(Ticks::::iter_prefix(netuid).next().is_none()); + // assert!( + // TickIndexBitmapWords::::iter_prefix((netuid,)) + // .next() + // .is_none() + // ); + // assert!(!PalSwapInitialized::::contains_key(netuid)); + // }); } #[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"); - let alpha_taken = ::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(), alpha_taken); - - // 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)); - }); + todo!(); + + // 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_palswap(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"); + // let alpha_taken = ::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(), alpha_taken); + + // // 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!(!PalSwapInitialized::::contains_key(netuid)); + // }); } #[test] fn refund_alpha_single_provider_exact() { - new_test_ext().execute_with(|| { - let netuid = NetUid::from(11); - let cold = OK_COLDKEY_ACCOUNT_ID; - let hot = OK_HOTKEY_ACCOUNT_ID; - - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - // --- Create an alpha‑only position (range entirely above current tick → TAO = 0, ALPHA > 0). - let ct = CurrentTick::::get(netuid); - let tick_low = ct.next().expect("current tick should not be MAX in tests"); - let tick_high = TickIndex::MAX; - - let liquidity = 1_000_000_u64; - let (_pos_id, tao_needed, alpha_needed) = - Pallet::::do_add_liquidity(netuid, &cold, &hot, tick_low, tick_high, liquidity) - .expect("add alpha-only liquidity"); - assert_eq!(tao_needed, 0, "alpha-only position must not require TAO"); - assert!(alpha_needed > 0, "alpha-only position must require ALPHA"); - - // --- Snapshot BEFORE we withdraw funds (baseline for conservation). - 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; - - // --- Mimic extrinsic bookkeeping: withdraw α and record provided reserve. - let alpha_taken = ::BalanceOps::decrease_stake( - &cold, - &hot, - netuid.into(), - alpha_needed.into(), - ) - .expect("decrease ALPHA"); - AlphaReserve::increase_provided(netuid.into(), alpha_taken); - - // --- Act: users‑only dissolve. - assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - - // --- Assert: total α conserved to owner (may be staked to validator). - 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 conserved to the account" - ); - - // Clear protocol liquidity and V3 state now. - assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); - - // --- State is cleared. - assert!(Ticks::::iter_prefix(netuid).next().is_none()); - assert_eq!(Pallet::::count_positions(netuid, &cold), 0); - assert!(!SwapV3Initialized::::contains_key(netuid)); - }); + todo!(); + + // new_test_ext().execute_with(|| { + // let netuid = NetUid::from(11); + // let cold = OK_COLDKEY_ACCOUNT_ID; + // let hot = OK_HOTKEY_ACCOUNT_ID; + + // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + + // // --- Create an alpha‑only position (range entirely above current tick → TAO = 0, ALPHA > 0). + // let ct = CurrentTick::::get(netuid); + // let tick_low = ct.next().expect("current tick should not be MAX in tests"); + // let tick_high = TickIndex::MAX; + + // let liquidity = 1_000_000_u64; + // let (_pos_id, tao_needed, alpha_needed) = + // Pallet::::do_add_liquidity(netuid, &cold, &hot, tick_low, tick_high, liquidity) + // .expect("add alpha-only liquidity"); + // assert_eq!(tao_needed, 0, "alpha-only position must not require TAO"); + // assert!(alpha_needed > 0, "alpha-only position must require ALPHA"); + + // // --- Snapshot BEFORE we withdraw funds (baseline for conservation). + // 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; + + // // --- Mimic extrinsic bookkeeping: withdraw α and record provided reserve. + // let alpha_taken = ::BalanceOps::decrease_stake( + // &cold, + // &hot, + // netuid.into(), + // alpha_needed.into(), + // ) + // .expect("decrease ALPHA"); + // AlphaReserve::increase_provided(netuid.into(), alpha_taken); + + // // --- Act: users‑only dissolve. + // assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); + + // // --- Assert: total α conserved to owner (may be staked to validator). + // 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 conserved to the account" + // ); + + // // Clear protocol liquidity and V3 state now. + // assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); + + // // --- State is cleared. + // assert!(Ticks::::iter_prefix(netuid).next().is_none()); + // assert_eq!(Pallet::::count_positions(netuid, &cold), 0); + // assert!(!PalSwapInitialized::::contains_key(netuid)); + // }); } #[test] fn refund_alpha_multiple_providers_proportional_to_principal() { - new_test_ext().execute_with(|| { - let netuid = NetUid::from(12); - let c1 = OK_COLDKEY_ACCOUNT_ID; - let h1 = OK_HOTKEY_ACCOUNT_ID; - let c2 = OK_COLDKEY_ACCOUNT_ID_2; - let h2 = OK_HOTKEY_ACCOUNT_ID_2; - - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - // Use the same "above current tick" trick for alpha‑only positions. - let ct = CurrentTick::::get(netuid); - let tick_low = ct.next().expect("current tick should not be MAX in tests"); - let tick_high = TickIndex::MAX; - - // Provider #1 (smaller α) - let liq1 = 700_000_u64; - let (_p1, t1, a1) = - Pallet::::do_add_liquidity(netuid, &c1, &h1, tick_low, tick_high, liq1) - .expect("add alpha-only liquidity #1"); - assert_eq!(t1, 0); - assert!(a1 > 0); - - // Provider #2 (larger α) - let liq2 = 2_100_000_u64; - let (_p2, t2, a2) = - Pallet::::do_add_liquidity(netuid, &c2, &h2, tick_low, tick_high, liq2) - .expect("add alpha-only liquidity #2"); - assert_eq!(t2, 0); - assert!(a2 > 0); - - // Baselines BEFORE withdrawing - let a1_before_hot = ::BalanceOps::alpha_balance(netuid.into(), &c1, &h1); - let a1_before_owner = ::BalanceOps::alpha_balance(netuid.into(), &c1, &c1); - let a1_before = a1_before_hot + a1_before_owner; - - let a2_before_hot = ::BalanceOps::alpha_balance(netuid.into(), &c2, &h2); - let a2_before_owner = ::BalanceOps::alpha_balance(netuid.into(), &c2, &c2); - let a2_before = a2_before_hot + a2_before_owner; - - // Withdraw α and account reserves for each provider. - let a1_taken = - ::BalanceOps::decrease_stake(&c1, &h1, netuid.into(), a1.into()) - .expect("decrease α #1"); - AlphaReserve::increase_provided(netuid.into(), a1_taken); - - let a2_taken = - ::BalanceOps::decrease_stake(&c2, &h2, netuid.into(), a2.into()) - .expect("decrease α #2"); - AlphaReserve::increase_provided(netuid.into(), a2_taken); - - // Act - assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - - // Each owner is restored to their exact baseline. - let a1_after_hot = ::BalanceOps::alpha_balance(netuid.into(), &c1, &h1); - let a1_after_owner = ::BalanceOps::alpha_balance(netuid.into(), &c1, &c1); - let a1_after = a1_after_hot + a1_after_owner; - assert_eq!( - a1_after, a1_before, - "owner #1 must receive their α principal back" - ); - - let a2_after_hot = ::BalanceOps::alpha_balance(netuid.into(), &c2, &h2); - let a2_after_owner = ::BalanceOps::alpha_balance(netuid.into(), &c2, &c2); - let a2_after = a2_after_hot + a2_after_owner; - assert_eq!( - a2_after, a2_before, - "owner #2 must receive their α principal back" - ); - }); + todo!(); + + // new_test_ext().execute_with(|| { + // let netuid = NetUid::from(12); + // let c1 = OK_COLDKEY_ACCOUNT_ID; + // let h1 = OK_HOTKEY_ACCOUNT_ID; + // let c2 = OK_COLDKEY_ACCOUNT_ID_2; + // let h2 = OK_HOTKEY_ACCOUNT_ID_2; + + // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + + // // Use the same "above current tick" trick for alpha‑only positions. + // let ct = CurrentTick::::get(netuid); + // let tick_low = ct.next().expect("current tick should not be MAX in tests"); + // let tick_high = TickIndex::MAX; + + // // Provider #1 (smaller α) + // let liq1 = 700_000_u64; + // let (_p1, t1, a1) = + // Pallet::::do_add_liquidity(netuid, &c1, &h1, tick_low, tick_high, liq1) + // .expect("add alpha-only liquidity #1"); + // assert_eq!(t1, 0); + // assert!(a1 > 0); + + // // Provider #2 (larger α) + // let liq2 = 2_100_000_u64; + // let (_p2, t2, a2) = + // Pallet::::do_add_liquidity(netuid, &c2, &h2, tick_low, tick_high, liq2) + // .expect("add alpha-only liquidity #2"); + // assert_eq!(t2, 0); + // assert!(a2 > 0); + + // // Baselines BEFORE withdrawing + // let a1_before_hot = ::BalanceOps::alpha_balance(netuid.into(), &c1, &h1); + // let a1_before_owner = ::BalanceOps::alpha_balance(netuid.into(), &c1, &c1); + // let a1_before = a1_before_hot + a1_before_owner; + + // let a2_before_hot = ::BalanceOps::alpha_balance(netuid.into(), &c2, &h2); + // let a2_before_owner = ::BalanceOps::alpha_balance(netuid.into(), &c2, &c2); + // let a2_before = a2_before_hot + a2_before_owner; + + // // Withdraw α and account reserves for each provider. + // let a1_taken = + // ::BalanceOps::decrease_stake(&c1, &h1, netuid.into(), a1.into()) + // .expect("decrease α #1"); + // AlphaReserve::increase_provided(netuid.into(), a1_taken); + + // let a2_taken = + // ::BalanceOps::decrease_stake(&c2, &h2, netuid.into(), a2.into()) + // .expect("decrease α #2"); + // AlphaReserve::increase_provided(netuid.into(), a2_taken); + + // // Act + // assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); + + // // Each owner is restored to their exact baseline. + // let a1_after_hot = ::BalanceOps::alpha_balance(netuid.into(), &c1, &h1); + // let a1_after_owner = ::BalanceOps::alpha_balance(netuid.into(), &c1, &c1); + // let a1_after = a1_after_hot + a1_after_owner; + // assert_eq!( + // a1_after, a1_before, + // "owner #1 must receive their α principal back" + // ); + + // let a2_after_hot = ::BalanceOps::alpha_balance(netuid.into(), &c2, &h2); + // let a2_after_owner = ::BalanceOps::alpha_balance(netuid.into(), &c2, &c2); + // let a2_after = a2_after_hot + a2_after_owner; + // assert_eq!( + // a2_after, a2_before, + // "owner #2 must receive their α principal back" + // ); + // }); } #[test] fn refund_alpha_same_cold_multiple_hotkeys_conserved_to_owner() { - new_test_ext().execute_with(|| { - let netuid = NetUid::from(13); - let cold = OK_COLDKEY_ACCOUNT_ID; - let hot1 = OK_HOTKEY_ACCOUNT_ID; - let hot2 = OK_HOTKEY_ACCOUNT_ID_2; - - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - // Two alpha‑only positions on different hotkeys of the same owner. - let ct = CurrentTick::::get(netuid); - let tick_low = ct.next().expect("current tick should not be MAX in tests"); - let tick_high = TickIndex::MAX; - - let (_p1, _t1, a1) = - Pallet::::do_add_liquidity(netuid, &cold, &hot1, tick_low, tick_high, 900_000) - .expect("add alpha-only pos (hot1)"); - let (_p2, _t2, a2) = - Pallet::::do_add_liquidity(netuid, &cold, &hot2, tick_low, tick_high, 1_500_000) - .expect("add alpha-only pos (hot2)"); - assert!(a1 > 0 && a2 > 0); - - // Baseline BEFORE: sum over (cold,hot1) + (cold,hot2) + (cold,cold). - let before_hot1 = ::BalanceOps::alpha_balance(netuid.into(), &cold, &hot1); - let before_hot2 = ::BalanceOps::alpha_balance(netuid.into(), &cold, &hot2); - let before_owner = ::BalanceOps::alpha_balance(netuid.into(), &cold, &cold); - let before_total = before_hot1 + before_hot2 + before_owner; - - // Withdraw α from both hotkeys; track provided‑reserve. - let t1 = - ::BalanceOps::decrease_stake(&cold, &hot1, netuid.into(), a1.into()) - .expect("decr α #hot1"); - AlphaReserve::increase_provided(netuid.into(), t1); - - let t2 = - ::BalanceOps::decrease_stake(&cold, &hot2, netuid.into(), a2.into()) - .expect("decr α #hot2"); - AlphaReserve::increase_provided(netuid.into(), t2); - - // Act - assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - - // The total α "owned" by the coldkey is conserved (credit may land on (cold,cold)). - let after_hot1 = ::BalanceOps::alpha_balance(netuid.into(), &cold, &hot1); - let after_hot2 = ::BalanceOps::alpha_balance(netuid.into(), &cold, &hot2); - let after_owner = ::BalanceOps::alpha_balance(netuid.into(), &cold, &cold); - let after_total = after_hot1 + after_hot2 + after_owner; - - assert_eq!( - after_total, before_total, - "owner’s α must be conserved across hot ledgers + (owner,owner)" - ); - }); + todo!(); + + // new_test_ext().execute_with(|| { + // let netuid = NetUid::from(13); + // let cold = OK_COLDKEY_ACCOUNT_ID; + // let hot1 = OK_HOTKEY_ACCOUNT_ID; + // let hot2 = OK_HOTKEY_ACCOUNT_ID_2; + + // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + + // // Two alpha‑only positions on different hotkeys of the same owner. + // let ct = CurrentTick::::get(netuid); + // let tick_low = ct.next().expect("current tick should not be MAX in tests"); + // let tick_high = TickIndex::MAX; + + // let (_p1, _t1, a1) = + // Pallet::::do_add_liquidity(netuid, &cold, &hot1, tick_low, tick_high, 900_000) + // .expect("add alpha-only pos (hot1)"); + // let (_p2, _t2, a2) = + // Pallet::::do_add_liquidity(netuid, &cold, &hot2, tick_low, tick_high, 1_500_000) + // .expect("add alpha-only pos (hot2)"); + // assert!(a1 > 0 && a2 > 0); + + // // Baseline BEFORE: sum over (cold,hot1) + (cold,hot2) + (cold,cold). + // let before_hot1 = ::BalanceOps::alpha_balance(netuid.into(), &cold, &hot1); + // let before_hot2 = ::BalanceOps::alpha_balance(netuid.into(), &cold, &hot2); + // let before_owner = ::BalanceOps::alpha_balance(netuid.into(), &cold, &cold); + // let before_total = before_hot1 + before_hot2 + before_owner; + + // // Withdraw α from both hotkeys; track provided‑reserve. + // let t1 = + // ::BalanceOps::decrease_stake(&cold, &hot1, netuid.into(), a1.into()) + // .expect("decr α #hot1"); + // AlphaReserve::increase_provided(netuid.into(), t1); + + // let t2 = + // ::BalanceOps::decrease_stake(&cold, &hot2, netuid.into(), a2.into()) + // .expect("decr α #hot2"); + // AlphaReserve::increase_provided(netuid.into(), t2); + + // // Act + // assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); + + // // The total α "owned" by the coldkey is conserved (credit may land on (cold,cold)). + // let after_hot1 = ::BalanceOps::alpha_balance(netuid.into(), &cold, &hot1); + // let after_hot2 = ::BalanceOps::alpha_balance(netuid.into(), &cold, &hot2); + // let after_owner = ::BalanceOps::alpha_balance(netuid.into(), &cold, &cold); + // let after_total = after_hot1 + after_hot2 + after_owner; + + // assert_eq!( + // after_total, before_total, + // "owner’s α must be conserved across hot ledgers + (owner,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"); - let alpha_taken = ::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_taken); - - // --- 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" - ); + todo!(); + + // 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_palswap(netuid)); + // assert!(PalSwapInitialized::::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"); + // let alpha_taken = ::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_taken); + + // // --- 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" + // ); - 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" - ); + // 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" + // ); - assert!(!FeeRate::::contains_key(netuid)); - assert!(!EnabledUserLiquidity::::contains_key(netuid)); - }); + // 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!(!PalSwapInitialized::::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(|| { - // --- Arrange --- - let netuid = NetUid::from(55); - - // Ensure the "user liquidity enabled" flag exists so we can verify it's removed later. - assert_ok!(Pallet::::toggle_user_liquidity( - RuntimeOrigin::root(), - netuid, - true - )); - - // Initialize V3 state; this should set price/tick flags and create a protocol position. - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - assert!( - SwapV3Initialized::::get(netuid), - "V3 must be initialized" - ); - - // Sanity: protocol positions exist before clearing. - let protocol_id = Pallet::::protocol_account_id(); - let prot_positions_before = - Positions::::iter_prefix_values((netuid, protocol_id)).collect::>(); - assert!( - !prot_positions_before.is_empty(), - "protocol positions should exist after V3 init" - ); - - // --- Act --- - // Green path: just clear protocol liquidity and wipe all V3 state. - assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); - - // --- Assert: all protocol positions removed --- - let prot_positions_after = - Positions::::iter_prefix_values((netuid, protocol_id)).collect::>(); - assert!( - prot_positions_after.is_empty(), - "protocol positions must be removed by do_clear_protocol_liquidity" - ); - - // --- Assert: V3 data wiped (idempotent even if some maps were empty) --- - // Ticks / active tick bitmap - assert!(Ticks::::iter_prefix(netuid).next().is_none()); - assert!( - TickIndexBitmapWords::::iter_prefix((netuid,)) - .next() - .is_none(), - "active tick bitmap words must be cleared" - ); - - // Fee globals - assert!(!FeeGlobalTao::::contains_key(netuid)); - assert!(!FeeGlobalAlpha::::contains_key(netuid)); - - // Price / tick / liquidity / flags - assert!(!AlphaSqrtPrice::::contains_key(netuid)); - assert!(!CurrentTick::::contains_key(netuid)); - assert!(!CurrentLiquidity::::contains_key(netuid)); - assert!(!SwapV3Initialized::::contains_key(netuid)); - - // Knobs removed - assert!(!FeeRate::::contains_key(netuid)); - assert!(!EnabledUserLiquidity::::contains_key(netuid)); - - // --- And it's idempotent --- - assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); - assert!( - Positions::::iter_prefix_values((netuid, protocol_id)) - .next() - .is_none() - ); - assert!(Ticks::::iter_prefix(netuid).next().is_none()); - assert!( - TickIndexBitmapWords::::iter_prefix((netuid,)) - .next() - .is_none() - ); - assert!(!SwapV3Initialized::::contains_key(netuid)); - }); + todo!(); + + // new_test_ext().execute_with(|| { + // // --- Arrange --- + // let netuid = NetUid::from(55); + + // // Ensure the "user liquidity enabled" flag exists so we can verify it's removed later. + // assert_ok!(Pallet::::toggle_user_liquidity( + // RuntimeOrigin::root(), + // netuid, + // true + // )); + + // // Initialize V3 state; this should set price/tick flags and create a protocol position. + // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + // assert!( + // PalSwapInitialized::::get(netuid), + // "V3 must be initialized" + // ); + + // // Sanity: protocol positions exist before clearing. + // let protocol_id = Pallet::::protocol_account_id(); + // let prot_positions_before = + // Positions::::iter_prefix_values((netuid, protocol_id)).collect::>(); + // assert!( + // !prot_positions_before.is_empty(), + // "protocol positions should exist after V3 init" + // ); + + // // --- Act --- + // // Green path: just clear protocol liquidity and wipe all V3 state. + // assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); + + // // --- Assert: all protocol positions removed --- + // let prot_positions_after = + // Positions::::iter_prefix_values((netuid, protocol_id)).collect::>(); + // assert!( + // prot_positions_after.is_empty(), + // "protocol positions must be removed by do_clear_protocol_liquidity" + // ); + + // // --- Assert: V3 data wiped (idempotent even if some maps were empty) --- + // // Ticks / active tick bitmap + // assert!(Ticks::::iter_prefix(netuid).next().is_none()); + // assert!( + // TickIndexBitmapWords::::iter_prefix((netuid,)) + // .next() + // .is_none(), + // "active tick bitmap words must be cleared" + // ); + + // // Fee globals + // assert!(!FeeGlobalTao::::contains_key(netuid)); + // assert!(!FeeGlobalAlpha::::contains_key(netuid)); + + // // Price / tick / liquidity / flags + // assert!(!AlphaSqrtPrice::::contains_key(netuid)); + // assert!(!CurrentTick::::contains_key(netuid)); + // assert!(!CurrentLiquidity::::contains_key(netuid)); + // assert!(!PalSwapInitialized::::contains_key(netuid)); + + // // Knobs removed + // assert!(!FeeRate::::contains_key(netuid)); + // assert!(!EnabledUserLiquidity::::contains_key(netuid)); + + // // --- And it's idempotent --- + // assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); + // assert!( + // Positions::::iter_prefix_values((netuid, protocol_id)) + // .next() + // .is_none() + // ); + // assert!(Ticks::::iter_prefix(netuid).next().is_none()); + // assert!( + // TickIndexBitmapWords::::iter_prefix((netuid,)) + // .next() + // .is_none() + // ); + // assert!(!PalSwapInitialized::::contains_key(netuid)); + // }); } +#[allow(dead_code)] fn as_tuple( (t_used, a_used, t_rem, a_rem): (TaoCurrency, AlphaCurrency, TaoCurrency, AlphaCurrency), ) -> (u64, u64, u64, u64) { @@ -2755,161 +2636,3 @@ fn as_tuple( u64::from(a_rem), ) } - -#[test] -fn proportional_when_price_is_one_and_tao_is_plenty() { - // sqrt_price = 1.0 => price = 1.0 - let sqrt = U64F64::from_num(1u64); - let amount_tao: TaoCurrency = 10u64.into(); - let amount_alpha: AlphaCurrency = 3u64.into(); - - // alpha * price = 3 * 1 = 3 <= amount_tao(10) - let out = - Pallet::::get_proportional_alpha_tao_and_remainders(sqrt, amount_tao, amount_alpha); - assert_eq!(as_tuple(out), (3, 3, 7, 0)); -} - -#[test] -fn proportional_when_price_is_one_and_alpha_is_excess() { - // sqrt_price = 1.0 => price = 1.0 - let sqrt = U64F64::from_num(1u64); - let amount_tao: TaoCurrency = 5u64.into(); - let amount_alpha: AlphaCurrency = 10u64.into(); - - // tao is limiting: alpha_equiv = floor(5 / 1) = 5 - let out = - Pallet::::get_proportional_alpha_tao_and_remainders(sqrt, amount_tao, amount_alpha); - assert_eq!(as_tuple(out), (5, 5, 0, 5)); -} - -#[test] -fn proportional_with_higher_price_and_alpha_limiting() { - // Choose sqrt_price = 2.0 => price = 4.0 (since implementation squares it) - let sqrt = U64F64::from_num(2u64); - let amount_tao: TaoCurrency = 85u64.into(); - let amount_alpha: AlphaCurrency = 20u64.into(); - - // tao_equivalent = alpha * price = 20 * 4 = 80 < 85 => alpha limits tao - // remainders: tao 5, alpha 0 - let out = - Pallet::::get_proportional_alpha_tao_and_remainders(sqrt, amount_tao, amount_alpha); - assert_eq!(as_tuple(out), (80, 20, 5, 0)); -} - -#[test] -fn proportional_with_higher_price_and_tao_limiting() { - // Choose sqrt_price = 2.0 => price = 4.0 (since implementation squares it) - let sqrt = U64F64::from_num(2u64); - let amount_tao: TaoCurrency = 50u64.into(); - let amount_alpha: AlphaCurrency = 20u64.into(); - - // tao_equivalent = alpha * price = 20 * 4 = 80 > 50 => tao limits alpha - // alpha_equivalent = floor(50 / 4) = 12 - // remainders: tao 0, alpha 20 - 12 = 8 - let out = - Pallet::::get_proportional_alpha_tao_and_remainders(sqrt, amount_tao, amount_alpha); - assert_eq!(as_tuple(out), (50, 12, 0, 8)); -} - -#[test] -fn zero_price_uses_no_tao_and_all_alpha() { - // sqrt_price = 0 => price = 0 - let sqrt = U64F64::from_num(0u64); - let amount_tao: TaoCurrency = 42u64.into(); - let amount_alpha: AlphaCurrency = 17u64.into(); - - // tao_equivalent = 17 * 0 = 0 <= 42 - let out = - Pallet::::get_proportional_alpha_tao_and_remainders(sqrt, amount_tao, amount_alpha); - assert_eq!(as_tuple(out), (0, 17, 42, 0)); -} - -#[test] -fn rounding_down_behavior_when_dividing_by_price() { - // sqrt_price = 2.0 => price = 4.0 - let sqrt = U64F64::from_num(2u64); - let amount_tao: TaoCurrency = 13u64.into(); - let amount_alpha: AlphaCurrency = 100u64.into(); - - // tao is limiting; alpha_equiv = floor(13 / 4) = 3 - // remainders: tao 0, alpha 100 - 3 = 97 - let out = - Pallet::::get_proportional_alpha_tao_and_remainders(sqrt, amount_tao, amount_alpha); - assert_eq!(as_tuple(out), (13, 3, 0, 97)); -} - -#[test] -fn exact_fit_when_tao_matches_alpha_times_price() { - // sqrt_price = 1.0 => price = 1.0 - let sqrt = U64F64::from_num(1u64); - let amount_tao: TaoCurrency = 9u64.into(); - let amount_alpha: AlphaCurrency = 9u64.into(); - - let out = - Pallet::::get_proportional_alpha_tao_and_remainders(sqrt, amount_tao, amount_alpha); - assert_eq!(as_tuple(out), (9, 9, 0, 0)); -} - -#[test] -fn handles_zero_balances() { - let sqrt = U64F64::from_num(1u64); - - // Zero TAO, some alpha - let out = - Pallet::::get_proportional_alpha_tao_and_remainders(sqrt, 0u64.into(), 7u64.into()); - // tao limits; alpha_equiv = floor(0 / 1) = 0 - assert_eq!(as_tuple(out), (0, 0, 0, 7)); - - // Some TAO, zero alpha - let out = - Pallet::::get_proportional_alpha_tao_and_remainders(sqrt, 7u64.into(), 0u64.into()); - // tao_equiv = 0 * 1 = 0 <= 7 - assert_eq!(as_tuple(out), (0, 0, 7, 0)); - - // Both zero - let out = - Pallet::::get_proportional_alpha_tao_and_remainders(sqrt, 0u64.into(), 0u64.into()); - assert_eq!(as_tuple(out), (0, 0, 0, 0)); -} - -#[test] -fn adjust_protocol_liquidity_uses_and_sets_scrap_reservoirs() { - new_test_ext().execute_with(|| { - // --- Arrange - let netuid: NetUid = 1u16.into(); - // Price = 1.0 (since sqrt_price^2 = 1), so proportional match is 1:1 - AlphaSqrtPrice::::insert(netuid, U64F64::saturating_from_num(1u64)); - - // Start with some non-zero scrap reservoirs - ScrapReservoirTao::::insert(netuid, TaoCurrency::from(7u64)); - ScrapReservoirAlpha::::insert(netuid, AlphaCurrency::from(5u64)); - - // Create a minimal protocol position so the function’s body executes. - let protocol = Pallet::::protocol_account_id(); - let position = Position::new( - PositionId::from(0), - netuid, - TickIndex::MIN, - TickIndex::MAX, - 0, - ); - // Ensure collect_fees() returns (0,0) via zeroed fees in `position` (default). - Positions::::insert((netuid, protocol, position.id), position.clone()); - - // --- Act - // No external deltas or fees; only reservoirs should be considered. - // With price=1, the exact proportional pair uses 5 alpha and 5 tao, - // leaving tao scrap = 7 - 5 = 2, alpha scrap = 5 - 5 = 0. - Pallet::::adjust_protocol_liquidity(netuid, 0u64.into(), 0u64.into()); - - // --- Assert: reservoirs were READ (used in proportional calc) and then SET (updated) - assert_eq!( - ScrapReservoirTao::::get(netuid), - TaoCurrency::from(2u64) - ); - assert_eq!( - ScrapReservoirAlpha::::get(netuid), - AlphaCurrency::from(0u64) - ); - }); -} diff --git a/pallets/swap/src/position.rs b/pallets/swap/src/position.rs index 5a57928a93..a514e60e8f 100644 --- a/pallets/swap/src/position.rs +++ b/pallets/swap/src/position.rs @@ -1,21 +1,19 @@ use core::marker::PhantomData; -use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen}; +use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::pallet_prelude::*; -use safe_math::*; -use substrate_fixed::types::{I64F64, U64F64}; +// use safe_math::*; +use substrate_fixed::types::U64F64; use subtensor_macros::freeze_struct; use subtensor_runtime_common::NetUid; -use crate::SqrtPrice; -use crate::pallet::{Config, Error, FeeGlobalAlpha, FeeGlobalTao, LastPositionId}; -use crate::tick::TickIndex; +use crate::pallet::{Config, Error, LastPositionId}; /// Position designates one liquidity position. /// /// Alpha price is expressed in rao units per one 10^9 unit. For example, /// price 1_000_000 is equal to 0.001 TAO per Alpha. -#[freeze_struct("27a1bf8c59480f0")] +#[freeze_struct("3f68e54e8969f976")] #[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen, Default)] #[scale_info(skip_type_params(T))] pub struct Position { @@ -23,16 +21,12 @@ pub struct Position { pub id: PositionId, /// Network identifier pub netuid: NetUid, - /// Tick index for lower boundary of price - pub tick_low: TickIndex, - /// Tick index for higher boundary of price - pub tick_high: TickIndex, - /// Position liquidity - pub liquidity: u64, - /// Fees accrued by the position in quote currency (TAO) relative to global fees - pub fees_tao: I64F64, - /// Fees accrued by the position in base currency (Alpha) relative to global fees - pub fees_alpha: I64F64, + // /// Position liquidity + // pub liquidity: u64, + // /// Fees accrued by the position in quote currency (TAO) relative to global fees + // pub fees_tao: I64F64, + // /// Fees accrued by the position in base currency (Alpha) relative to global fees + // pub fees_alpha: I64F64, /// Phantom marker for generic Config type pub _phantom: PhantomData, } @@ -41,23 +35,19 @@ impl Position { pub fn new( id: PositionId, netuid: NetUid, - tick_low: TickIndex, - tick_high: TickIndex, - liquidity: u64, + // liquidity: u64, ) -> Self { - let mut position = Position { + let position = Position { id, netuid, - tick_low, - tick_high, - liquidity, - fees_tao: I64F64::saturating_from_num(0), - fees_alpha: I64F64::saturating_from_num(0), + // liquidity, + // fees_tao: I64F64::saturating_from_num(0), + // fees_alpha: I64F64::saturating_from_num(0), _phantom: PhantomData, }; - position.fees_tao = position.fees_in_range(true); - position.fees_alpha = position.fees_in_range(false); + // position.fees_tao = position.fees_in_range(true); + // position.fees_alpha = position.fees_in_range(false); position } @@ -66,96 +56,57 @@ impl Position { /// /// returns tuple of (TAO, Alpha) /// - /// Pseudocode: - /// if self.sqrt_price_curr < sqrt_pa: - /// tao = 0 - /// alpha = L * (1 / sqrt_pa - 1 / sqrt_pb) - /// elif self.sqrt_price_curr > sqrt_pb: - /// tao = L * (sqrt_pb - sqrt_pa) - /// alpha = 0 - /// else: - /// tao = L * (self.sqrt_price_curr - sqrt_pa) - /// alpha = L * (1 / self.sqrt_price_curr - 1 / sqrt_pb) - /// - pub fn to_token_amounts(&self, sqrt_price_curr: SqrtPrice) -> Result<(u64, u64), Error> { - let one = U64F64::saturating_from_num(1); - - let sqrt_price_low = self - .tick_low - .try_to_sqrt_price() - .map_err(|_| Error::::InvalidTickRange)?; - let sqrt_price_high = self - .tick_high - .try_to_sqrt_price() - .map_err(|_| Error::::InvalidTickRange)?; - let liquidity_fixed = U64F64::saturating_from_num(self.liquidity); - - Ok(if sqrt_price_curr < sqrt_price_low { - ( - 0, - liquidity_fixed - .saturating_mul( - one.safe_div(sqrt_price_low) - .saturating_sub(one.safe_div(sqrt_price_high)), - ) - .saturating_to_num::(), - ) - } else if sqrt_price_curr > sqrt_price_high { - ( - liquidity_fixed - .saturating_mul(sqrt_price_high.saturating_sub(sqrt_price_low)) - .saturating_to_num::(), - 0, - ) - } else { - ( - liquidity_fixed - .saturating_mul(sqrt_price_curr.saturating_sub(sqrt_price_low)) - .saturating_to_num::(), - liquidity_fixed - .saturating_mul( - one.safe_div(sqrt_price_curr) - .saturating_sub(one.safe_div(sqrt_price_high)), - ) - .saturating_to_num::(), - ) - }) + pub fn to_token_amounts(&self, _price_curr: U64F64) -> Result<(u64, u64), Error> { + // let one = U64F64::saturating_from_num(1); + + // let sqrt_price_low = self + // .tick_low + // .try_to_sqrt_price() + // .map_err(|_| Error::::InvalidTickRange)?; + // let sqrt_price_high = self + // .tick_high + // .try_to_sqrt_price() + // .map_err(|_| Error::::InvalidTickRange)?; + // let liquidity_fixed = U64F64::saturating_from_num(self.liquidity); + + // Ok(if sqrt_price_curr < sqrt_price_low { + // ( + // 0, + // liquidity_fixed + // .saturating_mul( + // one.safe_div(sqrt_price_low) + // .saturating_sub(one.safe_div(sqrt_price_high)), + // ) + // .saturating_to_num::(), + // ) + // } else if sqrt_price_curr > sqrt_price_high { + // ( + // liquidity_fixed + // .saturating_mul(sqrt_price_high.saturating_sub(sqrt_price_low)) + // .saturating_to_num::(), + // 0, + // ) + // } else { + // ( + // liquidity_fixed + // .saturating_mul(sqrt_price_curr.saturating_sub(sqrt_price_low)) + // .saturating_to_num::(), + // liquidity_fixed + // .saturating_mul( + // one.safe_div(sqrt_price_curr) + // .saturating_sub(one.safe_div(sqrt_price_high)), + // ) + // .saturating_to_num::(), + // ) + // }) + + todo!() } /// Collect fees for a position /// Updates the position pub fn collect_fees(&mut self) -> (u64, u64) { - let fee_tao_agg = self.fees_in_range(true); - let fee_alpha_agg = self.fees_in_range(false); - - let mut fee_tao = fee_tao_agg.saturating_sub(self.fees_tao); - let mut fee_alpha = fee_alpha_agg.saturating_sub(self.fees_alpha); - - self.fees_tao = fee_tao_agg; - self.fees_alpha = fee_alpha_agg; - - let liquidity_frac = I64F64::saturating_from_num(self.liquidity); - - fee_tao = liquidity_frac.saturating_mul(fee_tao); - fee_alpha = liquidity_frac.saturating_mul(fee_alpha); - - ( - fee_tao.saturating_to_num::(), - fee_alpha.saturating_to_num::(), - ) - } - - /// Get fees in a position's range - /// - /// If quote flag is true, Tao is returned, otherwise alpha. - fn fees_in_range(&self, quote: bool) -> I64F64 { - if quote { - I64F64::saturating_from_num(FeeGlobalTao::::get(self.netuid)) - } else { - I64F64::saturating_from_num(FeeGlobalAlpha::::get(self.netuid)) - } - .saturating_sub(self.tick_low.fees_below::(self.netuid, quote)) - .saturating_sub(self.tick_high.fees_above::(self.netuid, quote)) + todo!() } } diff --git a/pallets/swap/src/tick.rs b/pallets/swap/src/tick.rs deleted file mode 100644 index d3493fde45..0000000000 --- a/pallets/swap/src/tick.rs +++ /dev/null @@ -1,2198 +0,0 @@ -//! The math is adapted from github.com/0xKitsune/uniswap-v3-math -use core::cmp::Ordering; -use core::convert::TryFrom; -use core::error::Error; -use core::fmt; -use core::hash::Hash; -use core::ops::{Add, AddAssign, BitOr, Deref, Neg, Shl, Shr, Sub, SubAssign}; - -use alloy_primitives::{I256, U256}; -use codec::{Decode, DecodeWithMemTracking, Encode, Error as CodecError, Input, MaxEncodedLen}; -use frame_support::pallet_prelude::*; -use safe_math::*; -use sp_std::vec; -use sp_std::vec::Vec; -use substrate_fixed::types::{I64F64, U64F64}; -use subtensor_macros::freeze_struct; -use subtensor_runtime_common::NetUid; - -use crate::SqrtPrice; -use crate::pallet::{ - Config, CurrentTick, FeeGlobalAlpha, FeeGlobalTao, TickIndexBitmapWords, Ticks, -}; - -const U256_1: U256 = U256::from_limbs([1, 0, 0, 0]); -const U256_2: U256 = U256::from_limbs([2, 0, 0, 0]); -const U256_3: U256 = U256::from_limbs([3, 0, 0, 0]); -const U256_4: U256 = U256::from_limbs([4, 0, 0, 0]); -const U256_5: U256 = U256::from_limbs([5, 0, 0, 0]); -const U256_6: U256 = U256::from_limbs([6, 0, 0, 0]); -const U256_7: U256 = U256::from_limbs([7, 0, 0, 0]); -const U256_8: U256 = U256::from_limbs([8, 0, 0, 0]); -const U256_15: U256 = U256::from_limbs([15, 0, 0, 0]); -const U256_16: U256 = U256::from_limbs([16, 0, 0, 0]); -const U256_32: U256 = U256::from_limbs([32, 0, 0, 0]); -const U256_64: U256 = U256::from_limbs([64, 0, 0, 0]); -const U256_127: U256 = U256::from_limbs([127, 0, 0, 0]); -const U256_128: U256 = U256::from_limbs([128, 0, 0, 0]); -const U256_255: U256 = U256::from_limbs([255, 0, 0, 0]); - -const U256_256: U256 = U256::from_limbs([256, 0, 0, 0]); -const U256_512: U256 = U256::from_limbs([512, 0, 0, 0]); -const U256_1024: U256 = U256::from_limbs([1024, 0, 0, 0]); -const U256_2048: U256 = U256::from_limbs([2048, 0, 0, 0]); -const U256_4096: U256 = U256::from_limbs([4096, 0, 0, 0]); -const U256_8192: U256 = U256::from_limbs([8192, 0, 0, 0]); -const U256_16384: U256 = U256::from_limbs([16384, 0, 0, 0]); -const U256_32768: U256 = U256::from_limbs([32768, 0, 0, 0]); -const U256_65536: U256 = U256::from_limbs([65536, 0, 0, 0]); -const U256_131072: U256 = U256::from_limbs([131072, 0, 0, 0]); -const U256_262144: U256 = U256::from_limbs([262144, 0, 0, 0]); -const U256_524288: U256 = U256::from_limbs([524288, 0, 0, 0]); - -const U256_MAX_TICK: U256 = U256::from_limbs([887272, 0, 0, 0]); - -const MIN_TICK: i32 = -887272; -const MAX_TICK: i32 = -MIN_TICK; - -const MIN_SQRT_RATIO: U256 = U256::from_limbs([4295128739, 0, 0, 0]); -const MAX_SQRT_RATIO: U256 = - U256::from_limbs([6743328256752651558, 17280870778742802505, 4294805859, 0]); - -const SQRT_10001: I256 = I256::from_raw(U256::from_limbs([11745905768312294533, 13863, 0, 0])); -const TICK_LOW: I256 = I256::from_raw(U256::from_limbs([ - 6552757943157144234, - 184476617836266586, - 0, - 0, -])); -const TICK_HIGH: I256 = I256::from_raw(U256::from_limbs([ - 4998474450511881007, - 15793544031827761793, - 0, - 0, -])); - -/// Tick is the price range determined by tick index (not part of this struct, but is the key at -/// which the Tick is stored in state hash maps). Tick struct stores liquidity and fee information. -/// -/// - Net liquidity -/// - Gross liquidity -/// - Fees (above global) in both currencies -#[freeze_struct("ff1bce826e64c4aa")] -#[derive(Debug, Default, Clone, Encode, Decode, TypeInfo, MaxEncodedLen, PartialEq, Eq)] -pub struct Tick { - pub liquidity_net: i128, - pub liquidity_gross: u64, - pub fees_out_tao: I64F64, - pub fees_out_alpha: I64F64, -} - -impl Tick { - pub fn liquidity_net_as_u64(&self) -> u64 { - self.liquidity_net.abs().min(u64::MAX as i128) as u64 - } -} - -/// Struct representing a tick index -#[freeze_struct("13c1f887258657f2")] -#[derive( - Debug, - Default, - Clone, - Copy, - Encode, - DecodeWithMemTracking, - TypeInfo, - MaxEncodedLen, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, -)] -pub struct TickIndex(i32); - -impl Decode for TickIndex { - fn decode(input: &mut I) -> Result { - let raw = i32::decode(input)?; - TickIndex::new(raw).map_err(|_| "TickIndex out of bounds".into()) - } -} - -impl Add for TickIndex { - type Output = Self; - - #[allow(clippy::arithmetic_side_effects)] - fn add(self, rhs: Self) -> Self::Output { - // Note: This assumes the result is within bounds. - // For a safer implementation, consider using checked_add. - Self::new_unchecked(self.get() + rhs.get()) - } -} - -impl Sub for TickIndex { - type Output = Self; - - #[allow(clippy::arithmetic_side_effects)] - fn sub(self, rhs: Self) -> Self::Output { - // Note: This assumes the result is within bounds. - // For a safer implementation, consider using checked_sub. - Self::new_unchecked(self.get() - rhs.get()) - } -} - -impl AddAssign for TickIndex { - #[allow(clippy::arithmetic_side_effects)] - fn add_assign(&mut self, rhs: Self) { - *self = Self::new_unchecked(self.get() + rhs.get()); - } -} - -impl SubAssign for TickIndex { - #[allow(clippy::arithmetic_side_effects)] - fn sub_assign(&mut self, rhs: Self) { - *self = Self::new_unchecked(self.get() - rhs.get()); - } -} - -impl TryFrom for TickIndex { - type Error = TickMathError; - - fn try_from(value: i32) -> Result { - Self::new(value) - } -} - -impl Deref for TickIndex { - type Target = i32; - - fn deref(&self) -> &Self::Target { - // Using get() would create an infinite recursion, so this is one place where we need direct - // field access. This is safe because Self::Target is i32, which is exactly what we're - // storing - &self.0 - } -} - -/// Extension trait to make working with TryFrom more ergonomic -pub trait TryIntoTickIndex { - /// Convert an i32 into a TickIndex, with bounds checking - fn into_tick_index(self) -> Result; -} - -impl TryIntoTickIndex for i32 { - fn into_tick_index(self) -> Result { - TickIndex::try_from(self) - } -} - -impl TickIndex { - /// Minimum value of the tick index - /// The tick_math library uses different bitness, so we have to divide by 2. - /// It's unsafe to change this value to something else. - pub const MIN: Self = Self(MIN_TICK.saturating_div(2)); - - /// Maximum value of the tick index - /// The tick_math library uses different bitness, so we have to divide by 2. - /// It's unsafe to change this value to something else. - pub const MAX: Self = Self(MAX_TICK.saturating_div(2)); - - /// All tick indexes are offset by this value for storage needs - /// so that tick indexes are positive, which simplifies bit logic - const OFFSET: Self = Self(MAX_TICK); - - /// The MIN sqrt price, which is caclculated at Self::MIN - pub fn min_sqrt_price() -> SqrtPrice { - SqrtPrice::saturating_from_num(0.0000000002328350195) - } - - /// The MAX sqrt price, which is calculated at Self::MAX - #[allow(clippy::excessive_precision)] - pub fn max_sqrt_price() -> SqrtPrice { - SqrtPrice::saturating_from_num(4294886577.20989222513899790805) - } - - /// Get fees above a tick - pub fn fees_above(&self, netuid: NetUid, quote: bool) -> I64F64 { - let current_tick = Self::current_bounded::(netuid); - - let tick = Ticks::::get(netuid, *self).unwrap_or_default(); - if *self <= current_tick { - if quote { - I64F64::saturating_from_num(FeeGlobalTao::::get(netuid)) - .saturating_sub(tick.fees_out_tao) - } else { - I64F64::saturating_from_num(FeeGlobalAlpha::::get(netuid)) - .saturating_sub(tick.fees_out_alpha) - } - } else if quote { - tick.fees_out_tao - } else { - tick.fees_out_alpha - } - } - - /// Get fees below a tick - pub fn fees_below(&self, netuid: NetUid, quote: bool) -> I64F64 { - let current_tick = Self::current_bounded::(netuid); - - let tick = Ticks::::get(netuid, *self).unwrap_or_default(); - if *self <= current_tick { - if quote { - tick.fees_out_tao - } else { - tick.fees_out_alpha - } - } else if quote { - I64F64::saturating_from_num(FeeGlobalTao::::get(netuid)) - .saturating_sub(tick.fees_out_tao) - } else { - I64F64::saturating_from_num(FeeGlobalAlpha::::get(netuid)) - .saturating_sub(tick.fees_out_alpha) - } - } - - /// Get the current tick index for a subnet, ensuring it's within valid bounds - pub fn current_bounded(netuid: NetUid) -> Self { - let current_tick = CurrentTick::::get(netuid); - if current_tick > Self::MAX { - Self::MAX - } else if current_tick < Self::MIN { - Self::MIN - } else { - current_tick - } - } - - /// Converts a sqrt price to a tick index, ensuring it's within valid bounds - /// - /// If the price is outside the valid range, this function will return the appropriate boundary - /// tick index (MIN or MAX) instead of an error. - /// - /// # Arguments - /// * `sqrt_price` - The square root price to convert to a tick index - /// - /// # Returns - /// * `TickIndex` - A tick index that is guaranteed to be within valid bounds - pub fn from_sqrt_price_bounded(sqrt_price: SqrtPrice) -> Self { - match Self::try_from_sqrt_price(sqrt_price) { - Ok(index) => index, - Err(_) => { - let max_price = Self::MAX.as_sqrt_price_bounded(); - - if sqrt_price > max_price { - Self::MAX - } else { - Self::MIN - } - } - } - } - - /// Converts a tick index to a sqrt price, ensuring it's within valid bounds - /// - /// Unlike try_to_sqrt_price which returns an error for boundary indices, this function - /// guarantees a valid sqrt price by using fallback values if conversion fails. - /// - /// # Returns - /// * `SqrtPrice` - A sqrt price that is guaranteed to be a valid value - pub fn as_sqrt_price_bounded(&self) -> SqrtPrice { - self.try_to_sqrt_price().unwrap_or_else(|_| { - if *self >= Self::MAX { - Self::max_sqrt_price() - } else { - Self::min_sqrt_price() - } - }) - } - - /// Creates a new TickIndex instance with bounds checking - pub fn new(value: i32) -> Result { - if !(Self::MIN.0..=Self::MAX.0).contains(&value) { - Err(TickMathError::TickOutOfBounds) - } else { - Ok(Self(value)) - } - } - - /// Creates a new TickIndex without bounds checking - /// Use this function with caution, only when you're certain the value is valid - pub fn new_unchecked(value: i32) -> Self { - Self(value) - } - - /// Get the inner value - pub fn get(&self) -> i32 { - self.0 - } - - /// Creates a TickIndex from an offset representation (u32) - /// - /// # Arguments - /// * `offset_index` - An offset index (u32 value) representing a tick index - /// - /// # Returns - /// * `Result` - The corresponding TickIndex if within valid bounds - pub fn from_offset_index(offset_index: u32) -> Result { - // while it's safe, we use saturating math to mute the linter and just in case - let signed_index = ((offset_index as i64).saturating_sub(Self::OFFSET.get() as i64)) as i32; - Self::new(signed_index) - } - - /// Get the next tick index (incrementing by 1) - pub fn next(&self) -> Result { - Self::new(self.0.saturating_add(1)) - } - - /// Get the previous tick index (decrementing by 1) - pub fn prev(&self) -> Result { - Self::new(self.0.saturating_sub(1)) - } - - /// Add a value to this tick index with bounds checking - pub fn checked_add(&self, value: i32) -> Result { - Self::new(self.0.saturating_add(value)) - } - - /// Subtract a value from this tick index with bounds checking - pub fn checked_sub(&self, value: i32) -> Result { - Self::new(self.0.saturating_sub(value)) - } - - /// Add a value to this tick index, saturating at the bounds instead of overflowing - pub fn saturating_add(&self, value: i32) -> Self { - match self.checked_add(value) { - Ok(result) => result, - Err(_) => { - if value > 0 { - Self::MAX - } else { - Self::MIN - } - } - } - } - - /// Subtract a value from this tick index, saturating at the bounds instead of overflowing - pub fn saturating_sub(&self, value: i32) -> Self { - match self.checked_sub(value) { - Ok(result) => result, - Err(_) => { - if value > 0 { - Self::MIN - } else { - Self::MAX - } - } - } - } - - /// Divide the tick index by a value with bounds checking - #[allow(clippy::arithmetic_side_effects)] - pub fn checked_div(&self, value: i32) -> Result { - if value == 0 { - return Err(TickMathError::DivisionByZero); - } - Self::new(self.0.saturating_div(value)) - } - - /// Divide the tick index by a value, saturating at the bounds - pub fn saturating_div(&self, value: i32) -> Self { - if value == 0 { - return Self::MAX; // Return MAX for division by zero - } - match self.checked_div(value) { - Ok(result) => result, - Err(_) => { - if (self.0 < 0 && value > 0) || (self.0 > 0 && value < 0) { - Self::MIN - } else { - Self::MAX - } - } - } - } - - /// Multiply the tick index by a value with bounds checking - pub fn checked_mul(&self, value: i32) -> Result { - // Check for potential overflow - match self.0.checked_mul(value) { - Some(result) => Self::new(result), - None => Err(TickMathError::Overflow), - } - } - - /// Multiply the tick index by a value, saturating at the bounds - pub fn saturating_mul(&self, value: i32) -> Self { - match self.checked_mul(value) { - Ok(result) => result, - Err(_) => { - if (self.0 < 0 && value > 0) || (self.0 > 0 && value < 0) { - Self::MIN - } else { - Self::MAX - } - } - } - } - - /// Converts tick index into SQRT of lower price of this tick In order to find the higher price - /// of this tick, call tick_index_to_sqrt_price(tick_idx + 1) - pub fn try_to_sqrt_price(&self) -> Result { - // because of u256->u128 conversion we have twice less values for min/max ticks - if !(Self::MIN..=Self::MAX).contains(self) { - return Err(TickMathError::TickOutOfBounds); - } - get_sqrt_ratio_at_tick(self.0).and_then(u256_q64_96_to_u64f64) - } - - /// Converts SQRT price to tick index - /// Because the tick is the range of prices [sqrt_lower_price, sqrt_higher_price), the resulting - /// tick index matches the price by the following inequality: - /// sqrt_lower_price <= sqrt_price < sqrt_higher_price - pub fn try_from_sqrt_price(sqrt_price: SqrtPrice) -> Result { - // price in the native Q64.96 integer format - let price_x96 = u64f64_to_u256_q64_96(sqrt_price); - - // first‑pass estimate from the log calculation - let mut tick = get_tick_at_sqrt_ratio(price_x96)?; - - // post‑verification, *both* directions - let price_at_tick = get_sqrt_ratio_at_tick(tick)?; - if price_at_tick > price_x96 { - tick = tick.saturating_sub(1); // estimate was too high - } else { - // it may still be one too low - let price_at_tick_plus = get_sqrt_ratio_at_tick(tick.saturating_add(1))?; - if price_at_tick_plus <= price_x96 { - tick = tick.saturating_add(1); // step up when required - } - } - - tick.into_tick_index() - } -} - -pub struct ActiveTickIndexManager(PhantomData); - -impl ActiveTickIndexManager { - pub fn insert(netuid: NetUid, index: TickIndex) { - // Check the range - if (index < TickIndex::MIN) || (index > TickIndex::MAX) { - return; - } - - // Convert to bitmap representation - let bitmap = TickIndexBitmap::from(index); - - // Update layer words - let mut word0_value = TickIndexBitmapWords::::get(( - netuid, - LayerLevel::Top, - bitmap.word_at(LayerLevel::Top), - )); - let mut word1_value = TickIndexBitmapWords::::get(( - netuid, - LayerLevel::Middle, - bitmap.word_at(LayerLevel::Middle), - )); - let mut word2_value = TickIndexBitmapWords::::get(( - netuid, - LayerLevel::Bottom, - bitmap.word_at(LayerLevel::Bottom), - )); - - // Set bits in each layer - word0_value |= bitmap.bit_mask(LayerLevel::Top); - word1_value |= bitmap.bit_mask(LayerLevel::Middle); - word2_value |= bitmap.bit_mask(LayerLevel::Bottom); - - // Update the storage - TickIndexBitmapWords::::set( - (netuid, LayerLevel::Top, bitmap.word_at(LayerLevel::Top)), - word0_value, - ); - TickIndexBitmapWords::::set( - ( - netuid, - LayerLevel::Middle, - bitmap.word_at(LayerLevel::Middle), - ), - word1_value, - ); - TickIndexBitmapWords::::set( - ( - netuid, - LayerLevel::Bottom, - bitmap.word_at(LayerLevel::Bottom), - ), - word2_value, - ); - } - - pub fn remove(netuid: NetUid, index: TickIndex) { - // Check the range - if (index < TickIndex::MIN) || (index > TickIndex::MAX) { - return; - } - - // Convert to bitmap representation - let bitmap = TickIndexBitmap::from(index); - - // Update layer words - let mut word0_value = TickIndexBitmapWords::::get(( - netuid, - LayerLevel::Top, - bitmap.word_at(LayerLevel::Top), - )); - let mut word1_value = TickIndexBitmapWords::::get(( - netuid, - LayerLevel::Middle, - bitmap.word_at(LayerLevel::Middle), - )); - let mut word2_value = TickIndexBitmapWords::::get(( - netuid, - LayerLevel::Bottom, - bitmap.word_at(LayerLevel::Bottom), - )); - - // Turn the bit off (& !bit) and save as needed - word2_value &= !bitmap.bit_mask(LayerLevel::Bottom); - TickIndexBitmapWords::::set( - ( - netuid, - LayerLevel::Bottom, - bitmap.word_at(LayerLevel::Bottom), - ), - word2_value, - ); - - if word2_value == 0 { - word1_value &= !bitmap.bit_mask(LayerLevel::Middle); - TickIndexBitmapWords::::set( - ( - netuid, - LayerLevel::Middle, - bitmap.word_at(LayerLevel::Middle), - ), - word1_value, - ); - } - - if word1_value == 0 { - word0_value &= !bitmap.bit_mask(LayerLevel::Top); - TickIndexBitmapWords::::set( - (netuid, LayerLevel::Top, bitmap.word_at(LayerLevel::Top)), - word0_value, - ); - } - } - - pub fn find_closest_lower(netuid: NetUid, index: TickIndex) -> Option { - Self::find_closest(netuid, index, true) - } - - pub fn find_closest_higher(netuid: NetUid, index: TickIndex) -> Option { - Self::find_closest(netuid, index, false) - } - - fn find_closest(netuid: NetUid, index: TickIndex, lower: bool) -> Option { - // Check the range - if (index < TickIndex::MIN) || (index > TickIndex::MAX) { - return None; - } - - // Convert to bitmap representation - let bitmap = TickIndexBitmap::from(index); - let mut found = false; - let mut result: u32 = 0; - - // Layer positions from bitmap - let layer0_word = bitmap.word_at(LayerLevel::Top); - let layer0_bit = bitmap.bit_at(LayerLevel::Top); - let layer1_word = bitmap.word_at(LayerLevel::Middle); - let layer1_bit = bitmap.bit_at(LayerLevel::Middle); - let layer2_word = bitmap.word_at(LayerLevel::Bottom); - let layer2_bit = bitmap.bit_at(LayerLevel::Bottom); - - // Find the closest active bits in layer 0, then 1, then 2 - - /////////////// - // Level 0 - let word0 = TickIndexBitmapWords::::get((netuid, LayerLevel::Top, layer0_word)); - let closest_bits_l0 = - TickIndexBitmap::find_closest_active_bit_candidates(word0, layer0_bit, lower); - - for closest_bit_l0 in closest_bits_l0.iter() { - /////////////// - // Level 1 - let word1_index = TickIndexBitmap::layer_to_index(BitmapLayer::new(0, *closest_bit_l0)); - - // Layer 1 words are different, shift the bit to the word edge - let start_from_l1_bit = match word1_index.cmp(&layer1_word) { - Ordering::Less => 127, - Ordering::Greater => 0, - _ => layer1_bit, - }; - let word1_value = - TickIndexBitmapWords::::get((netuid, LayerLevel::Middle, word1_index)); - let closest_bits_l1 = TickIndexBitmap::find_closest_active_bit_candidates( - word1_value, - start_from_l1_bit, - lower, - ); - - for closest_bit_l1 in closest_bits_l1.iter() { - /////////////// - // Level 2 - let word2_index = - TickIndexBitmap::layer_to_index(BitmapLayer::new(word1_index, *closest_bit_l1)); - - // Layer 2 words are different, shift the bit to the word edge - let start_from_l2_bit = match word2_index.cmp(&layer2_word) { - Ordering::Less => 127, - Ordering::Greater => 0, - _ => layer2_bit, - }; - - let word2_value = - TickIndexBitmapWords::::get((netuid, LayerLevel::Bottom, word2_index)); - - let closest_bits_l2 = TickIndexBitmap::find_closest_active_bit_candidates( - word2_value, - start_from_l2_bit, - lower, - ); - - if !closest_bits_l2.is_empty() { - // The active tick is found, restore its full index and return - let offset_found_index = TickIndexBitmap::layer_to_index(BitmapLayer::new( - word2_index, - // it's safe to unwrap, because the len is > 0, but to prevent errors in - // refactoring, we use default fallback here for extra safety - closest_bits_l2.first().copied().unwrap_or_default(), - )); - - if lower { - if (offset_found_index > result) || (!found) { - result = offset_found_index; - found = true; - } - } else if (offset_found_index < result) || (!found) { - result = offset_found_index; - found = true; - } - } - } - } - - if !found { - return None; - } - - // Convert the result offset_index back to a tick index - TickIndex::from_offset_index(result).ok() - } - - pub fn tick_is_active(netuid: NetUid, tick: TickIndex) -> bool { - Self::find_closest_lower(netuid, tick).unwrap_or(TickIndex::MAX) == tick - } -} - -/// Represents the three layers in the Uniswap V3 bitmap structure -#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode, TypeInfo, MaxEncodedLen)] -pub enum LayerLevel { - /// Top layer (highest level of the hierarchy) - Top = 0, - /// Middle layer - Middle = 1, - /// Bottom layer (contains the actual ticks) - Bottom = 2, -} - -#[freeze_struct("4015a04919eb5e2e")] -#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode, TypeInfo, MaxEncodedLen)] -pub(crate) struct BitmapLayer { - word: u32, - bit: u32, -} - -impl BitmapLayer { - pub fn new(word: u32, bit: u32) -> Self { - Self { word, bit } - } -} - -/// A bitmap representation of a tick index position across the three-layer structure -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) struct TickIndexBitmap { - /// The position in layer 0 (top layer) - layer0: BitmapLayer, - /// The position in layer 1 (middle layer) - layer1: BitmapLayer, - /// The position in layer 2 (bottom layer) - layer2: BitmapLayer, -} - -impl TickIndexBitmap { - /// Helper function to convert a bitmap index to a (word, bit) tuple in a bitmap layer using - /// safe methods - /// - /// Note: This function operates on bitmap navigation indices, NOT tick indices. - /// It converts a flat index within the bitmap structure to a (word, bit) position. - fn index_to_layer(index: u32) -> BitmapLayer { - let word = index.safe_div(128); - let bit = index.checked_rem(128).unwrap_or_default(); - BitmapLayer { word, bit } - } - - /// Converts a position (word, bit) within a layer to a word index in the next layer down - /// Note: This returns a bitmap navigation index, NOT a tick index - pub(crate) fn layer_to_index(layer: BitmapLayer) -> u32 { - layer.word.saturating_mul(128).saturating_add(layer.bit) - } - - /// Get the mask for a bit in the specified layer - pub(crate) fn bit_mask(&self, layer: LayerLevel) -> u128 { - match layer { - LayerLevel::Top => 1u128 << self.layer0.bit, - LayerLevel::Middle => 1u128 << self.layer1.bit, - LayerLevel::Bottom => 1u128 << self.layer2.bit, - } - } - - /// Get the word for the specified layer - pub(crate) fn word_at(&self, layer: LayerLevel) -> u32 { - match layer { - LayerLevel::Top => self.layer0.word, - LayerLevel::Middle => self.layer1.word, - LayerLevel::Bottom => self.layer2.word, - } - } - - /// Get the bit for the specified layer - pub(crate) fn bit_at(&self, layer: LayerLevel) -> u32 { - match layer { - LayerLevel::Top => self.layer0.bit, - LayerLevel::Middle => self.layer1.bit, - LayerLevel::Bottom => self.layer2.bit, - } - } - - /// Finds the closest active bit in a bitmap word, and if the active bit exactly matches the - /// requested bit, then it finds the next one as well - /// - /// # Arguments - /// * `word` - The bitmap word to search within - /// * `bit` - The bit position to start searching from - /// * `lower` - If true, search for lower bits (decreasing bit position), if false, search for - /// higher bits (increasing bit position) - /// - /// # Returns - /// * Exact match: Vec with [next_bit, bit] - /// * Non-exact match: Vec with [closest_bit] - /// * No match: Empty Vec - pub(crate) fn find_closest_active_bit_candidates( - word: u128, - bit: u32, - lower: bool, - ) -> Vec { - let mut result = vec![]; - let mut mask: u128 = 1_u128.wrapping_shl(bit); - let mut active_bit: u32 = bit; - - while mask > 0 { - if mask & word != 0 { - result.push(active_bit); - if active_bit != bit { - break; - } - } - - mask = if lower { - active_bit = active_bit.saturating_sub(1); - mask.wrapping_shr(1) - } else { - active_bit = active_bit.saturating_add(1); - mask.wrapping_shl(1) - }; - } - - result - } -} - -impl From for TickIndexBitmap { - fn from(tick_index: TickIndex) -> Self { - // Convert to offset index (internal operation only) - let offset_index = (tick_index.get().saturating_add(TickIndex::OFFSET.get())) as u32; - - // Calculate layer positions - let layer2 = Self::index_to_layer(offset_index); - let layer1 = Self::index_to_layer(layer2.word); - let layer0 = Self::index_to_layer(layer1.word); - - Self { - layer0, - layer1, - layer2, - } - } -} - -#[allow(clippy::arithmetic_side_effects)] -fn get_sqrt_ratio_at_tick(tick: i32) -> Result { - let abs_tick = if tick < 0 { - U256::from(tick.neg()) - } else { - U256::from(tick) - }; - - if abs_tick > U256_MAX_TICK { - return Err(TickMathError::TickOutOfBounds); - } - - let mut ratio = if abs_tick & (U256_1) != U256::ZERO { - U256::from_limbs([12262481743371124737, 18445821805675392311, 0, 0]) - } else { - U256::from_limbs([0, 0, 1, 0]) - }; - - if !(abs_tick & U256_2).is_zero() { - ratio = (ratio.saturating_mul(U256::from_limbs([ - 6459403834229662010, - 18444899583751176498, - 0, - 0, - ]))) >> 128 - } - if !(abs_tick & U256_4).is_zero() { - ratio = (ratio.saturating_mul(U256::from_limbs([ - 17226890335427755468, - 18443055278223354162, - 0, - 0, - ]))) >> 128 - } - if !(abs_tick & U256_8).is_zero() { - ratio = (ratio.saturating_mul(U256::from_limbs([ - 2032852871939366096, - 18439367220385604838, - 0, - 0, - ]))) >> 128 - } - if !(abs_tick & U256_16).is_zero() { - ratio = (ratio.saturating_mul(U256::from_limbs([ - 14545316742740207172, - 18431993317065449817, - 0, - 0, - ]))) >> 128 - } - if !(abs_tick & U256_32).is_zero() { - ratio = (ratio.saturating_mul(U256::from_limbs([ - 5129152022828963008, - 18417254355718160513, - 0, - 0, - ]))) >> 128 - } - if !(abs_tick & U256_64).is_zero() { - ratio = (ratio.saturating_mul(U256::from_limbs([ - 4894419605888772193, - 18387811781193591352, - 0, - 0, - ]))) >> 128 - } - if !(abs_tick & U256_128).is_zero() { - ratio = (ratio.saturating_mul(U256::from_limbs([ - 1280255884321894483, - 18329067761203520168, - 0, - 0, - ]))) >> 128 - } - if !(abs_tick & U256_256).is_zero() { - ratio = (ratio.saturating_mul(U256::from_limbs([ - 15924666964335305636, - 18212142134806087854, - 0, - 0, - ]))) >> 128 - } - if !(abs_tick & U256_512).is_zero() { - ratio = (ratio.saturating_mul(U256::from_limbs([ - 8010504389359918676, - 17980523815641551639, - 0, - 0, - ]))) >> 128 - } - if !(abs_tick & U256_1024).is_zero() { - ratio = (ratio.saturating_mul(U256::from_limbs([ - 10668036004952895731, - 17526086738831147013, - 0, - 0, - ]))) >> 128 - } - if !(abs_tick & U256_2048).is_zero() { - ratio = (ratio.saturating_mul(U256::from_limbs([ - 4878133418470705625, - 16651378430235024244, - 0, - 0, - ]))) >> 128 - } - if !(abs_tick & U256_4096).is_zero() { - ratio = (ratio.saturating_mul(U256::from_limbs([ - 9537173718739605541, - 15030750278693429944, - 0, - 0, - ]))) >> 128 - } - if !(abs_tick & U256_8192).is_zero() { - ratio = (ratio.saturating_mul(U256::from_limbs([ - 9972618978014552549, - 12247334978882834399, - 0, - 0, - ]))) >> 128 - } - if !(abs_tick & U256_16384).is_zero() { - ratio = (ratio.saturating_mul(U256::from_limbs([ - 10428997489610666743, - 8131365268884726200, - 0, - 0, - ]))) >> 128 - } - if !(abs_tick & U256_32768).is_zero() { - ratio = (ratio.saturating_mul(U256::from_limbs([ - 9305304367709015974, - 3584323654723342297, - 0, - 0, - ]))) >> 128 - } - if !(abs_tick & U256_65536).is_zero() { - ratio = (ratio.saturating_mul(U256::from_limbs([ - 14301143598189091785, - 696457651847595233, - 0, - 0, - ]))) >> 128 - } - if !(abs_tick & U256_131072).is_zero() { - ratio = (ratio.saturating_mul(U256::from_limbs([ - 7393154844743099908, - 26294789957452057, - 0, - 0, - ]))) >> 128 - } - if !(abs_tick & U256_262144).is_zero() { - ratio = (ratio.saturating_mul(U256::from_limbs([ - 2209338891292245656, - 37481735321082, - 0, - 0, - ]))) >> 128 - } - if !(abs_tick & U256_524288).is_zero() { - ratio = - (ratio.saturating_mul(U256::from_limbs([10518117631919034274, 76158723, 0, 0]))) >> 128 - } - - if tick > 0 { - ratio = U256::MAX / ratio; - } - - let shifted: U256 = ratio >> 32; - let ceil = if ratio & U256::from((1u128 << 32) - 1) != U256::ZERO { - shifted.saturating_add(U256_1) - } else { - shifted - }; - Ok(ceil) -} - -#[allow(clippy::arithmetic_side_effects)] -fn get_tick_at_sqrt_ratio(sqrt_price_x_96: U256) -> Result { - if !(sqrt_price_x_96 >= MIN_SQRT_RATIO && sqrt_price_x_96 < MAX_SQRT_RATIO) { - return Err(TickMathError::SqrtPriceOutOfBounds); - } - - let ratio: U256 = sqrt_price_x_96.shl(32); - let mut r = ratio; - let mut msb = U256::ZERO; - - let mut f = if r > U256::from_limbs([18446744073709551615, 18446744073709551615, 0, 0]) { - U256_1.shl(U256_7) - } else { - U256::ZERO - }; - msb = msb.bitor(f); - r = r.shr(f); - - f = if r > U256::from_limbs([18446744073709551615, 0, 0, 0]) { - U256_1.shl(U256_6) - } else { - U256::ZERO - }; - msb = msb.bitor(f); - r = r.shr(f); - - f = if r > U256::from_limbs([4294967295, 0, 0, 0]) { - U256_1.shl(U256_5) - } else { - U256::ZERO - }; - msb = msb.bitor(f); - r = r.shr(f); - - f = if r > U256::from_limbs([65535, 0, 0, 0]) { - U256_1.shl(U256_4) - } else { - U256::ZERO - }; - msb = msb.bitor(f); - r = r.shr(f); - - f = if r > U256_255 { - U256_1.shl(U256_3) - } else { - U256::ZERO - }; - msb = msb.bitor(f); - r = r.shr(f); - - f = if r > U256_15 { - U256_1.shl(U256_2) - } else { - U256::ZERO - }; - msb = msb.bitor(f); - r = r.shr(f); - - f = if r > U256_3 { - U256_1.shl(U256_1) - } else { - U256::ZERO - }; - msb = msb.bitor(f); - r = r.shr(f); - - f = if r > U256_1 { U256_1 } else { U256::ZERO }; - - msb = msb.bitor(f); - - r = if msb >= U256_128 { - ratio.shr(msb.saturating_sub(U256_127)) - } else { - ratio.shl(U256_127.saturating_sub(msb)) - }; - - let mut log_2: I256 = - (I256::from_raw(msb).saturating_sub(I256::from_limbs([128, 0, 0, 0]))).shl(64); - - for i in (51..=63).rev() { - r = r.overflowing_mul(r).0.shr(U256_127); - let f: U256 = r.shr(128); - log_2 = log_2.bitor(I256::from_raw(f.shl(i))); - - r = r.shr(f); - } - - r = r.overflowing_mul(r).0.shr(U256_127); - let f: U256 = r.shr(128); - log_2 = log_2.bitor(I256::from_raw(f.shl(50))); - - let log_sqrt10001 = log_2.wrapping_mul(SQRT_10001); - - let tick_low = (log_sqrt10001.saturating_sub(TICK_LOW) >> 128_u8).low_i32(); - - let tick_high = (log_sqrt10001.saturating_add(TICK_HIGH) >> 128_u8).low_i32(); - - let tick = if tick_low == tick_high { - tick_low - } else if get_sqrt_ratio_at_tick(tick_high)? <= sqrt_price_x_96 { - tick_high - } else { - tick_low - }; - - Ok(tick) -} - -// Convert U64F64 to U256 in Q64.96 format (Uniswap's sqrt price format) -fn u64f64_to_u256_q64_96(value: U64F64) -> U256 { - u64f64_to_u256(value, 96) -} - -/// Convert U64F64 to U256 -/// -/// # Arguments -/// * `value` - The U64F64 value to convert -/// * `target_fractional_bits` - Number of fractional bits in the target U256 format -/// -/// # Returns -/// * `U256` - Converted value -#[allow(clippy::arithmetic_side_effects)] -fn u64f64_to_u256(value: U64F64, target_fractional_bits: u32) -> U256 { - let raw = U256::from(value.to_bits()); - - match target_fractional_bits.cmp(&64) { - Ordering::Less => raw >> (64 - target_fractional_bits), - Ordering::Greater => raw.saturating_shl((target_fractional_bits - 64) as usize), - Ordering::Equal => raw, - } -} - -/// Convert U256 in Q64.96 format (Uniswap's sqrt price format) to U64F64 -fn u256_q64_96_to_u64f64(value: U256) -> Result { - q_to_u64f64(value, 96) -} - -#[allow(clippy::arithmetic_side_effects)] -fn q_to_u64f64(x: U256, frac_bits: u32) -> Result { - let diff = frac_bits.saturating_sub(64) as usize; - - // 1. shift right diff bits - let shifted = if diff != 0 { x >> diff } else { x }; - - // 2. **round up** if we threw away any 1‑bits - let mask = if diff != 0 { - (U256_1.saturating_shl(diff)).saturating_sub(U256_1) - } else { - U256::ZERO - }; - let rounded = if diff != 0 && (x & mask) != U256::ZERO { - shifted.saturating_add(U256_1) - } else { - shifted - }; - - // 3. check that it fits in 128 bits and transmute - if (rounded >> 128) != U256::ZERO { - return Err(TickMathError::Overflow); - } - Ok(U64F64::from_bits(rounded.to::())) -} - -#[derive(Debug, PartialEq, Eq)] -pub enum TickMathError { - TickOutOfBounds, - SqrtPriceOutOfBounds, - ConversionError, - Overflow, - DivisionByZero, -} - -impl fmt::Display for TickMathError { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::TickOutOfBounds => f.write_str("The given tick is outside of the minimum/maximum values."), - Self::SqrtPriceOutOfBounds =>f.write_str("Second inequality must be < because the price can never reach the price at the max tick"), - Self::ConversionError => f.write_str("Error converting from one number type into another"), - Self::Overflow => f.write_str("Number overflow in arithmetic operation"), - Self::DivisionByZero => f.write_str("Division by zero is not allowed") - } - } -} - -impl Error for TickMathError {} - -#[allow(clippy::unwrap_used)] -#[cfg(test)] -mod tests { - use safe_math::FixedExt; - use std::{ops::Sub, str::FromStr}; - - use super::*; - use crate::mock::*; - - #[test] - fn test_get_sqrt_ratio_at_tick_bounds() { - // the function should return an error if the tick is out of bounds - if let Err(err) = get_sqrt_ratio_at_tick(MIN_TICK - 1) { - assert!(matches!(err, TickMathError::TickOutOfBounds)); - } else { - panic!("get_qrt_ratio_at_tick did not respect lower tick bound") - } - if let Err(err) = get_sqrt_ratio_at_tick(MAX_TICK + 1) { - assert!(matches!(err, TickMathError::TickOutOfBounds)); - } else { - panic!("get_qrt_ratio_at_tick did not respect upper tick bound") - } - } - - #[test] - fn test_get_sqrt_ratio_at_tick_values() { - // test individual values for correct results - assert_eq!( - get_sqrt_ratio_at_tick(MIN_TICK).unwrap(), - U256::from(4295128739u64), - "sqrt ratio at min incorrect" - ); - assert_eq!( - get_sqrt_ratio_at_tick(MIN_TICK + 1).unwrap(), - U256::from(4295343490u64), - "sqrt ratio at min + 1 incorrect" - ); - assert_eq!( - get_sqrt_ratio_at_tick(MAX_TICK - 1).unwrap(), - U256::from_str("1461373636630004318706518188784493106690254656249").unwrap(), - "sqrt ratio at max - 1 incorrect" - ); - assert_eq!( - get_sqrt_ratio_at_tick(MAX_TICK).unwrap(), - U256::from_str("1461446703485210103287273052203988822378723970342").unwrap(), - "sqrt ratio at max incorrect" - ); - // checking hard coded values against solidity results - assert_eq!( - get_sqrt_ratio_at_tick(50).unwrap(), - U256::from(79426470787362580746886972461u128), - "sqrt ratio at 50 incorrect" - ); - assert_eq!( - get_sqrt_ratio_at_tick(100).unwrap(), - U256::from(79625275426524748796330556128u128), - "sqrt ratio at 100 incorrect" - ); - assert_eq!( - get_sqrt_ratio_at_tick(250).unwrap(), - U256::from(80224679980005306637834519095u128), - "sqrt ratio at 250 incorrect" - ); - assert_eq!( - get_sqrt_ratio_at_tick(500).unwrap(), - U256::from(81233731461783161732293370115u128), - "sqrt ratio at 500 incorrect" - ); - assert_eq!( - get_sqrt_ratio_at_tick(1000).unwrap(), - U256::from(83290069058676223003182343270u128), - "sqrt ratio at 1000 incorrect" - ); - assert_eq!( - get_sqrt_ratio_at_tick(2500).unwrap(), - U256::from(89776708723587163891445672585u128), - "sqrt ratio at 2500 incorrect" - ); - assert_eq!( - get_sqrt_ratio_at_tick(3000).unwrap(), - U256::from(92049301871182272007977902845u128), - "sqrt ratio at 3000 incorrect" - ); - assert_eq!( - get_sqrt_ratio_at_tick(4000).unwrap(), - U256::from(96768528593268422080558758223u128), - "sqrt ratio at 4000 incorrect" - ); - assert_eq!( - get_sqrt_ratio_at_tick(5000).unwrap(), - U256::from(101729702841318637793976746270u128), - "sqrt ratio at 5000 incorrect" - ); - assert_eq!( - get_sqrt_ratio_at_tick(50000).unwrap(), - U256::from(965075977353221155028623082916u128), - "sqrt ratio at 50000 incorrect" - ); - assert_eq!( - get_sqrt_ratio_at_tick(150000).unwrap(), - U256::from(143194173941309278083010301478497u128), - "sqrt ratio at 150000 incorrect" - ); - assert_eq!( - get_sqrt_ratio_at_tick(250000).unwrap(), - U256::from(21246587762933397357449903968194344u128), - "sqrt ratio at 250000 incorrect" - ); - assert_eq!( - get_sqrt_ratio_at_tick(500000).unwrap(), - U256::from_str("5697689776495288729098254600827762987878").unwrap(), - "sqrt ratio at 500000 incorrect" - ); - assert_eq!( - get_sqrt_ratio_at_tick(738203).unwrap(), - U256::from_str("847134979253254120489401328389043031315994541").unwrap(), - "sqrt ratio at 738203 incorrect" - ); - } - - #[test] - fn test_get_tick_at_sqrt_ratio() { - //throws for too low - let result = get_tick_at_sqrt_ratio(MIN_SQRT_RATIO.sub(U256_1)); - assert_eq!( - result.unwrap_err().to_string(), - "Second inequality must be < because the price can never reach the price at the max tick" - ); - - //throws for too high - let result = get_tick_at_sqrt_ratio(MAX_SQRT_RATIO); - assert_eq!( - result.unwrap_err().to_string(), - "Second inequality must be < because the price can never reach the price at the max tick" - ); - - //ratio of min tick - let result = get_tick_at_sqrt_ratio(MIN_SQRT_RATIO).unwrap(); - assert_eq!(result, MIN_TICK); - - //ratio of min tick + 1 - let result = get_tick_at_sqrt_ratio(U256::from_str("4295343490").unwrap()).unwrap(); - assert_eq!(result, MIN_TICK + 1); - } - - #[test] - fn test_roundtrip() { - for tick_index in [ - MIN_TICK + 1, // we can't use extremes because of rounding during roundtrip conversion - -1000, - -100, - -10, - -4, - -2, - 0, - 2, - 4, - 10, - 100, - 1000, - MAX_TICK - 1, - ] - .iter() - { - let sqrt_price = get_sqrt_ratio_at_tick(*tick_index).unwrap(); - let round_trip_tick_index = get_tick_at_sqrt_ratio(sqrt_price).unwrap(); - assert_eq!(round_trip_tick_index, *tick_index); - } - } - - #[test] - fn test_u256_to_u64f64_q64_96() { - // Test tick 0 (sqrt price = 1.0 * 2^96) - let tick0_sqrt_price = U256::from(1u128 << 96); - let fixed_price = u256_q64_96_to_u64f64(tick0_sqrt_price).unwrap(); - - // Should be 1.0 in U64F64 - assert_eq!(fixed_price, U64F64::from_num(1.0)); - - // Round trip back to U256 Q64.96 - let back_to_u256 = u64f64_to_u256_q64_96(fixed_price); - assert_eq!(back_to_u256, tick0_sqrt_price); - } - - #[test] - fn test_tick_index_to_sqrt_price() { - let tick_spacing = SqrtPrice::from_num(1.0001); - - // check tick bounds - assert_eq!( - TickIndex(MIN_TICK).try_to_sqrt_price(), - Err(TickMathError::TickOutOfBounds) - ); - - assert_eq!( - TickIndex(MAX_TICK).try_to_sqrt_price(), - Err(TickMathError::TickOutOfBounds), - ); - - assert!( - TickIndex::MAX.try_to_sqrt_price().unwrap().abs_diff( - TickIndex::new_unchecked(TickIndex::MAX.get() + 1).as_sqrt_price_bounded() - ) < SqrtPrice::from_num(1e-6) - ); - - assert!( - TickIndex::MIN.try_to_sqrt_price().unwrap().abs_diff( - TickIndex::new_unchecked(TickIndex::MIN.get() - 1).as_sqrt_price_bounded() - ) < SqrtPrice::from_num(1e-6) - ); - - // At tick index 0, the sqrt price should be 1.0 - let sqrt_price = TickIndex(0).try_to_sqrt_price().unwrap(); - assert_eq!(sqrt_price, SqrtPrice::from_num(1.0)); - - let sqrt_price = TickIndex(2).try_to_sqrt_price().unwrap(); - assert!(sqrt_price.abs_diff(tick_spacing) < SqrtPrice::from_num(1e-10)); - - let sqrt_price = TickIndex(4).try_to_sqrt_price().unwrap(); - // Calculate the expected value: (1 + TICK_SPACING/1e9 + 1.0)^2 - let expected = tick_spacing * tick_spacing; - assert!(sqrt_price.abs_diff(expected) < SqrtPrice::from_num(1e-10)); - - // Test with tick index 10 - let sqrt_price = TickIndex(10).try_to_sqrt_price().unwrap(); - // Calculate the expected value: (1 + TICK_SPACING/1e9 + 1.0)^5 - let expected = tick_spacing.checked_pow(5).unwrap(); - assert!( - sqrt_price.abs_diff(expected) < SqrtPrice::from_num(1e-10), - "diff: {}", - sqrt_price.abs_diff(expected), - ); - } - - #[test] - fn test_sqrt_price_to_tick_index() { - let tick_spacing = SqrtPrice::from_num(1.0001); - let tick_index = TickIndex::try_from_sqrt_price(SqrtPrice::from_num(1.0)).unwrap(); - assert_eq!(tick_index, TickIndex::new_unchecked(0)); - - // Test with sqrt price equal to tick_spacing_tao (should be tick index 2) - let epsilon = SqrtPrice::from_num(0.0000000000000001); - assert!( - TickIndex::new_unchecked(2) - .as_sqrt_price_bounded() - .abs_diff(tick_spacing) - < epsilon - ); - - // Test with sqrt price equal to tick_spacing_tao^2 (should be tick index 4) - let sqrt_price = tick_spacing * tick_spacing; - assert!( - TickIndex::new_unchecked(4) - .as_sqrt_price_bounded() - .abs_diff(sqrt_price) - < epsilon - ); - - // Test with sqrt price equal to tick_spacing_tao^5 (should be tick index 10) - let sqrt_price = tick_spacing.checked_pow(5).unwrap(); - assert!( - TickIndex::new_unchecked(10) - .as_sqrt_price_bounded() - .abs_diff(sqrt_price) - < epsilon - ); - } - - #[test] - fn test_roundtrip_tick_index_sqrt_price() { - for i32_value in [ - TickIndex::MIN.get(), - -1000, - -100, - -10, - -4, - -2, - 0, - 2, - 4, - 10, - 100, - 1000, - TickIndex::MAX.get(), - ] - .into_iter() - { - let tick_index = TickIndex::new_unchecked(i32_value); - let sqrt_price = tick_index.try_to_sqrt_price().unwrap(); - let round_trip_tick_index = TickIndex::try_from_sqrt_price(sqrt_price).unwrap(); - assert_eq!(round_trip_tick_index, tick_index); - } - } - - #[test] - fn test_from_offset_index() { - // Test various tick indices - for i32_value in [ - TickIndex::MIN.get(), - -1000, - -100, - -10, - 0, - 10, - 100, - 1000, - TickIndex::MAX.get(), - ] { - let original_tick = TickIndex::new_unchecked(i32_value); - - // Calculate the offset index (adding OFFSET) - let offset_index = (i32_value + TickIndex::OFFSET.get()) as u32; - - // Convert back from offset index to tick index - let roundtrip_tick = TickIndex::from_offset_index(offset_index).unwrap(); - - // Check that we get the same tick index back - assert_eq!(original_tick, roundtrip_tick); - } - - // Test out of bounds values - let too_large = (TickIndex::MAX.get() + TickIndex::OFFSET.get() + 1) as u32; - assert!(TickIndex::from_offset_index(too_large).is_err()); - } - - #[test] - fn test_tick_price_sanity_check() { - let min_price = TickIndex::MIN.try_to_sqrt_price().unwrap(); - let max_price = TickIndex::MAX.try_to_sqrt_price().unwrap(); - - assert!(min_price > 0.); - assert!(max_price > 0.); - assert!(max_price > min_price); - assert!(min_price < 0.000001); - assert!(max_price > 10.); - - // Roundtrip conversions - let min_price_sqrt = TickIndex::MIN.try_to_sqrt_price().unwrap(); - let min_tick = TickIndex::try_from_sqrt_price(min_price_sqrt).unwrap(); - assert_eq!(min_tick, TickIndex::MIN); - - let max_price_sqrt: SqrtPrice = TickIndex::MAX.try_to_sqrt_price().unwrap(); - let max_tick = TickIndex::try_from_sqrt_price(max_price_sqrt).unwrap(); - assert_eq!(max_tick, TickIndex::MAX); - } - - #[test] - fn test_to_sqrt_price_bounded() { - assert_eq!( - TickIndex::MAX.as_sqrt_price_bounded(), - TickIndex::MAX.try_to_sqrt_price().unwrap() - ); - - assert_eq!( - TickIndex::MIN.as_sqrt_price_bounded(), - TickIndex::MIN.try_to_sqrt_price().unwrap() - ); - } - - mod active_tick_index_manager { - - use super::*; - - #[test] - fn test_tick_search_basic() { - new_test_ext().execute_with(|| { - let netuid = NetUid::from(1); - - ActiveTickIndexManager::::insert(netuid, TickIndex::MIN); - - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, TickIndex::MIN) - .unwrap(), - TickIndex::MIN - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, TickIndex::MAX) - .unwrap(), - TickIndex::MIN - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower( - netuid, - TickIndex::MAX.saturating_div(2) - ) - .unwrap(), - TickIndex::MIN - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower( - netuid, - TickIndex::MAX.prev().unwrap() - ) - .unwrap(), - TickIndex::MIN - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower( - netuid, - TickIndex::MIN.next().unwrap() - ) - .unwrap(), - TickIndex::MIN - ); - - assert_eq!( - ActiveTickIndexManager::::find_closest_higher(netuid, TickIndex::MIN) - .unwrap(), - TickIndex::MIN - ); - assert!( - ActiveTickIndexManager::::find_closest_higher(netuid, TickIndex::MAX) - .is_none() - ); - assert!( - ActiveTickIndexManager::::find_closest_higher( - netuid, - TickIndex::MAX.saturating_div(2) - ) - .is_none() - ); - assert!( - ActiveTickIndexManager::::find_closest_higher( - netuid, - TickIndex::MAX.prev().unwrap() - ) - .is_none() - ); - assert!( - ActiveTickIndexManager::::find_closest_higher( - netuid, - TickIndex::MIN.next().unwrap() - ) - .is_none() - ); - - ActiveTickIndexManager::::insert(netuid, TickIndex::MAX); - - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, TickIndex::MIN) - .unwrap(), - TickIndex::MIN - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, TickIndex::MAX) - .unwrap(), - TickIndex::MAX - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower( - netuid, - TickIndex::MAX.saturating_div(2) - ) - .unwrap(), - TickIndex::MIN - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower( - netuid, - TickIndex::MAX.prev().unwrap() - ) - .unwrap(), - TickIndex::MIN - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower( - netuid, - TickIndex::MIN.next().unwrap() - ) - .unwrap(), - TickIndex::MIN - ); - }); - } - - #[test] - fn test_tick_search_sparse_queries() { - new_test_ext().execute_with(|| { - let active_index = TickIndex::MIN.saturating_add(10); - let netuid = NetUid::from(1); - - ActiveTickIndexManager::::insert(netuid, active_index); - - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, active_index) - .unwrap(), - active_index - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower( - netuid, - TickIndex::MIN.saturating_add(11) - ) - .unwrap(), - active_index - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower( - netuid, - TickIndex::MIN.saturating_add(12) - ) - .unwrap(), - active_index - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, TickIndex::MIN), - None - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower( - netuid, - TickIndex::MIN.saturating_add(9) - ), - None - ); - - assert_eq!( - ActiveTickIndexManager::::find_closest_higher(netuid, active_index) - .unwrap(), - active_index - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher( - netuid, - TickIndex::MIN.saturating_add(11) - ), - None - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher( - netuid, - TickIndex::MIN.saturating_add(12) - ), - None - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher(netuid, TickIndex::MIN) - .unwrap(), - active_index - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher( - netuid, - TickIndex::MIN.saturating_add(9) - ) - .unwrap(), - active_index - ); - }); - } - - #[test] - fn test_tick_search_many_lows() { - new_test_ext().execute_with(|| { - let netuid = NetUid::from(1); - - (0..1000).for_each(|i| { - ActiveTickIndexManager::::insert( - netuid, - TickIndex::MIN.saturating_add(i), - ); - }); - - for i in 0..1000 { - let test_index = TickIndex::MIN.saturating_add(i); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, test_index) - .unwrap(), - test_index - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher(netuid, test_index) - .unwrap(), - test_index - ); - } - }); - } - - #[test] - fn test_tick_search_many_sparse() { - new_test_ext().execute_with(|| { - let netuid = NetUid::from(1); - let count = 1000; - - for i in 0..=count { - ActiveTickIndexManager::::insert( - netuid, - TickIndex::new_unchecked(i * 10), - ); - } - - for i in 1..count { - let tick = TickIndex::new_unchecked(i * 10); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, tick).unwrap(), - tick - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher(netuid, tick).unwrap(), - tick - ); - for j in 1..=9 { - let before_tick = TickIndex::new_unchecked(i * 10 - j); - let after_tick = TickIndex::new_unchecked(i * 10 + j); - let prev_tick = TickIndex::new_unchecked((i - 1) * 10); - let next_tick = TickIndex::new_unchecked((i + 1) * 10); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, before_tick) - .unwrap(), - prev_tick - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, after_tick) - .unwrap(), - tick - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher( - netuid, - before_tick - ) - .unwrap(), - tick - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher(netuid, after_tick) - .unwrap(), - next_tick - ); - } - } - }); - } - - #[test] - fn test_tick_search_many_lows_sparse_reversed() { - new_test_ext().execute_with(|| { - let netuid = NetUid::from(1); - let count = 1000; - - for i in (0..=count).rev() { - ActiveTickIndexManager::::insert( - netuid, - TickIndex::new_unchecked(i * 10), - ); - } - - for i in 1..count { - let tick = TickIndex::new_unchecked(i * 10); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, tick).unwrap(), - tick - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher(netuid, tick).unwrap(), - tick - ); - for j in 1..=9 { - let before_tick = TickIndex::new_unchecked(i * 10 - j); - let after_tick = TickIndex::new_unchecked(i * 10 + j); - let prev_tick = TickIndex::new_unchecked((i - 1) * 10); - let next_tick = TickIndex::new_unchecked((i + 1) * 10); - - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, before_tick) - .unwrap(), - prev_tick - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, after_tick) - .unwrap(), - tick - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher( - netuid, - before_tick - ) - .unwrap(), - tick - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher(netuid, after_tick) - .unwrap(), - next_tick - ); - } - } - }); - } - - #[test] - fn test_tick_search_repeated_insertions() { - new_test_ext().execute_with(|| { - let netuid = NetUid::from(1); - let count = 1000; - - for _ in 0..10 { - for i in 0..=count { - let tick = TickIndex::new_unchecked(i * 10); - ActiveTickIndexManager::::insert(netuid, tick); - } - - for i in 1..count { - let tick = TickIndex::new_unchecked(i * 10); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, tick) - .unwrap(), - tick - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher(netuid, tick) - .unwrap(), - tick - ); - for j in 1..=9 { - let before_tick = TickIndex::new_unchecked(i * 10 - j); - let after_tick = TickIndex::new_unchecked(i * 10 + j); - let prev_tick = TickIndex::new_unchecked((i - 1) * 10); - let next_tick = TickIndex::new_unchecked((i + 1) * 10); - - assert_eq!( - ActiveTickIndexManager::::find_closest_lower( - netuid, - before_tick - ) - .unwrap(), - prev_tick - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower( - netuid, after_tick - ) - .unwrap(), - tick - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher( - netuid, - before_tick - ) - .unwrap(), - tick - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher( - netuid, after_tick - ) - .unwrap(), - next_tick - ); - } - } - } - }); - } - - #[test] - fn test_tick_search_full_range() { - new_test_ext().execute_with(|| { - let netuid = NetUid::from(1); - let step = 1019; - // Get the full valid tick range by subtracting MIN from MAX - let count = (TickIndex::MAX.get() - TickIndex::MIN.get()) / step; - - for i in 0..=count { - let index = TickIndex::MIN.saturating_add(i * step); - ActiveTickIndexManager::::insert(netuid, index); - } - for i in 1..count { - let index = TickIndex::MIN.saturating_add(i * step); - - let prev_index = TickIndex::new_unchecked(index.get() - step); - let next_minus_one = TickIndex::new_unchecked(index.get() + step - 1); - - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, prev_index) - .unwrap(), - prev_index - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, index).unwrap(), - index - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, next_minus_one) - .unwrap(), - index - ); - - let mid_next = TickIndex::new_unchecked(index.get() + step / 2); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, mid_next) - .unwrap(), - index - ); - - assert_eq!( - ActiveTickIndexManager::::find_closest_higher(netuid, index).unwrap(), - index - ); - - let next_index = TickIndex::new_unchecked(index.get() + step); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher(netuid, next_index) - .unwrap(), - next_index - ); - - let mid_next = TickIndex::new_unchecked(index.get() + step / 2); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher(netuid, mid_next) - .unwrap(), - next_index - ); - - let next_minus_1 = TickIndex::new_unchecked(index.get() + step - 1); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher(netuid, next_minus_1) - .unwrap(), - next_index - ); - for j in 1..=9 { - let before_index = TickIndex::new_unchecked(index.get() - j); - let after_index = TickIndex::new_unchecked(index.get() + j); - - assert_eq!( - ActiveTickIndexManager::::find_closest_lower( - netuid, - before_index - ) - .unwrap(), - prev_index - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, after_index) - .unwrap(), - index - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher( - netuid, - before_index - ) - .unwrap(), - index - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher( - netuid, - after_index - ) - .unwrap(), - next_index - ); - } - } - }); - } - - #[test] - fn test_tick_remove_basic() { - new_test_ext().execute_with(|| { - let netuid = NetUid::from(1); - - ActiveTickIndexManager::::insert(netuid, TickIndex::MIN); - ActiveTickIndexManager::::insert(netuid, TickIndex::MAX); - ActiveTickIndexManager::::remove(netuid, TickIndex::MAX); - - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, TickIndex::MIN) - .unwrap(), - TickIndex::MIN - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, TickIndex::MAX) - .unwrap(), - TickIndex::MIN - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower( - netuid, - TickIndex::MAX.saturating_div(2) - ) - .unwrap(), - TickIndex::MIN - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower( - netuid, - TickIndex::MAX.prev().unwrap() - ) - .unwrap(), - TickIndex::MIN - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower( - netuid, - TickIndex::MIN.next().unwrap() - ) - .unwrap(), - TickIndex::MIN - ); - - assert_eq!( - ActiveTickIndexManager::::find_closest_higher(netuid, TickIndex::MIN) - .unwrap(), - TickIndex::MIN - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher(netuid, TickIndex::MAX), - None - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher( - netuid, - TickIndex::MAX.saturating_div(2) - ), - None - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher( - netuid, - TickIndex::MAX.prev().unwrap() - ), - None - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher( - netuid, - TickIndex::MIN.next().unwrap() - ), - None - ); - }); - } - - #[test] - fn test_tick_remove_full_range() { - new_test_ext().execute_with(|| { - let netuid = NetUid::from(1); - let step = 1019; - // Get the full valid tick range by subtracting MIN from MAX - let count = (TickIndex::MAX.get() - TickIndex::MIN.get()) / step; - let remove_frequency = 5; // Remove every 5th tick - - // Insert ticks - for i in 0..=count { - let index = TickIndex::MIN.saturating_add(i * step); - ActiveTickIndexManager::::insert(netuid, index); - } - - // Remove some ticks - for i in 1..count { - if i % remove_frequency == 0 { - let index = TickIndex::MIN.saturating_add(i * step); - ActiveTickIndexManager::::remove(netuid, index); - } - } - - // Verify - for i in 1..count { - let index = TickIndex::MIN.saturating_add(i * step); - - if i % remove_frequency == 0 { - let lower = - ActiveTickIndexManager::::find_closest_lower(netuid, index); - let higher = - ActiveTickIndexManager::::find_closest_higher(netuid, index); - assert!(lower != Some(index)); - assert!(higher != Some(index)); - } else { - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, index) - .unwrap(), - index - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher(netuid, index) - .unwrap(), - index - ); - } - } - }); - } - } -} diff --git a/pallets/transaction-fee/src/lib.rs b/pallets/transaction-fee/src/lib.rs index fc2a16a409..013903ea8e 100644 --- a/pallets/transaction-fee/src/lib.rs +++ b/pallets/transaction-fee/src/lib.rs @@ -30,7 +30,7 @@ use subtensor_swap_interface::SwapHandler; use core::marker::PhantomData; use smallvec::smallvec; use sp_std::vec::Vec; -use substrate_fixed::types::U96F32; +use substrate_fixed::types::U64F64; use subtensor_runtime_common::{Balance, Currency, NetUid}; // Tests @@ -145,7 +145,7 @@ where // This is not ideal because it may not pay all fees, but UX is the priority // and this approach still provides spam protection. alpha_vec.iter().any(|(hotkey, netuid)| { - let alpha_balance = U96F32::saturating_from_num( + let alpha_balance = U64F64::saturating_from_num( pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( hotkey, coldkey, *netuid, ), @@ -168,13 +168,13 @@ where alpha_vec.iter().for_each(|(hotkey, netuid)| { // Divide tao_amount evenly among all alpha entries - let alpha_balance = U96F32::saturating_from_num( + let alpha_balance = U64F64::saturating_from_num( pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( hotkey, coldkey, *netuid, ), ); let alpha_price = pallet_subtensor_swap::Pallet::::current_alpha_price(*netuid); - let alpha_fee = U96F32::saturating_from_num(tao_per_entry) + let alpha_fee = U64F64::saturating_from_num(tao_per_entry) .checked_div(alpha_price) .unwrap_or(alpha_balance) .min(alpha_balance) diff --git a/pallets/transaction-fee/src/tests/mod.rs b/pallets/transaction-fee/src/tests/mod.rs index b6697e87f0..4c79c368cb 100644 --- a/pallets/transaction-fee/src/tests/mod.rs +++ b/pallets/transaction-fee/src/tests/mod.rs @@ -2,12 +2,11 @@ use crate::TransactionSource; use frame_support::assert_ok; use frame_support::dispatch::GetDispatchInfo; -use pallet_subtensor_swap::AlphaSqrtPrice; use sp_runtime::{ traits::{DispatchTransaction, TransactionExtension, TxBaseImplication}, transaction_validity::{InvalidTransaction, TransactionValidityError}, }; -use substrate_fixed::types::U64F64; +// use substrate_fixed::types::U64F64; use subtensor_runtime_common::AlphaCurrency; use mock::*; @@ -387,78 +386,80 @@ fn test_remove_stake_not_enough_balance_for_fees() { #[test] #[ignore] fn test_remove_stake_edge_alpha() { - new_test_ext().execute_with(|| { - let stake_amount = TAO; - let sn = setup_subnets(1, 1); - setup_stake( - sn.subnets[0].netuid, - &sn.coldkey, - &sn.hotkeys[0], - stake_amount, - ); - - // Simulate stake removal to get how much TAO should we get for unstaked Alpha - let current_stake = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &sn.hotkeys[0], - &sn.coldkey, - sn.subnets[0].netuid, - ); - - // Forse-set signer balance to ED - let current_balance = Balances::free_balance(sn.coldkey); - let _ = SubtensorModule::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 = AlphaCurrency::from(1_000_000); - SubtensorModule::decrease_stake_for_hotkey_and_coldkey_on_subnet( - &sn.hotkeys[0], - &sn.coldkey, - sn.subnets[0].netuid, - current_stake - new_current_stake, - ); - - // Remove stake - let call = RuntimeCall::SubtensorModule(pallet_subtensor::Call::remove_stake { - hotkey: sn.hotkeys[0], - netuid: sn.subnets[0].netuid, - amount_unstaked: new_current_stake, - }); - - // Dispatch the extrinsic with ChargeTransactionPayment extension - let info = call.get_dispatch_info(); - let ext = pallet_transaction_payment::ChargeTransactionPayment::::from(0); - let result = ext.validate( - RuntimeOrigin::signed(sn.coldkey).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - - // Ok - Validation passed - assert_ok!(result); - - // Lower Alpha price to 0.0001 so that there is not enough alpha to cover tx fees - AlphaSqrtPrice::::insert(sn.subnets[0].netuid, U64F64::from_num(0.01)); - let result_low_alpha_price = ext.validate( - RuntimeOrigin::signed(sn.coldkey).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - assert_eq!( - result_low_alpha_price.unwrap_err(), - TransactionValidityError::Invalid(InvalidTransaction::Payment) - ); - }); + todo!(); + + // new_test_ext().execute_with(|| { + // let stake_amount = TAO; + // let sn = setup_subnets(1, 1); + // setup_stake( + // sn.subnets[0].netuid, + // &sn.coldkey, + // &sn.hotkeys[0], + // stake_amount, + // ); + + // // Simulate stake removal to get how much TAO should we get for unstaked Alpha + // let current_stake = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + // &sn.hotkeys[0], + // &sn.coldkey, + // sn.subnets[0].netuid, + // ); + + // // Forse-set signer balance to ED + // let current_balance = Balances::free_balance(sn.coldkey); + // let _ = SubtensorModule::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 = AlphaCurrency::from(1_000_000); + // SubtensorModule::decrease_stake_for_hotkey_and_coldkey_on_subnet( + // &sn.hotkeys[0], + // &sn.coldkey, + // sn.subnets[0].netuid, + // current_stake - new_current_stake, + // ); + + // // Remove stake + // let call = RuntimeCall::SubtensorModule(pallet_subtensor::Call::remove_stake { + // hotkey: sn.hotkeys[0], + // netuid: sn.subnets[0].netuid, + // amount_unstaked: new_current_stake, + // }); + + // // Dispatch the extrinsic with ChargeTransactionPayment extension + // let info = call.get_dispatch_info(); + // let ext = pallet_transaction_payment::ChargeTransactionPayment::::from(0); + // let result = ext.validate( + // RuntimeOrigin::signed(sn.coldkey).into(), + // &call.clone(), + // &info, + // 10, + // (), + // &TxBaseImplication(()), + // TransactionSource::External, + // ); + + // // Ok - Validation passed + // assert_ok!(result); + + // // Lower Alpha price to 0.0001 so that there is not enough alpha to cover tx fees + // AlphaSqrtPrice::::insert(sn.subnets[0].netuid, U64F64::from_num(0.01)); + // let result_low_alpha_price = ext.validate( + // RuntimeOrigin::signed(sn.coldkey).into(), + // &call.clone(), + // &info, + // 10, + // (), + // &TxBaseImplication(()), + // TransactionSource::External, + // ); + // assert_eq!( + // result_low_alpha_price.unwrap_err(), + // TransactionValidityError::Invalid(InvalidTransaction::Payment) + // ); + // }); } // Validation passes, but transaction fails => TAO fees are paid diff --git a/precompiles/src/alpha.rs b/precompiles/src/alpha.rs index 8dcea0e829..98737f1269 100644 --- a/precompiles/src/alpha.rs +++ b/precompiles/src/alpha.rs @@ -5,7 +5,7 @@ use pallet_evm::{BalanceConverter, PrecompileHandle, SubstrateBalance}; use precompile_utils::EvmResult; use sp_core::U256; use sp_std::vec::Vec; -use substrate_fixed::types::U96F32; +use substrate_fixed::types::{U64F64, U96F32}; use subtensor_runtime_common::{Currency, NetUid}; use subtensor_swap_interface::{Order, SwapHandler}; @@ -37,7 +37,7 @@ where fn get_alpha_price(_handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { 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 = current_alpha_price.saturating_mul(U64F64::from_num(1_000_000_000)); let price: SubstrateBalance = price.saturating_to_num::().into(); let price_eth = ::BalanceConverter::into_evm_balance(price) .map(|amount| amount.into_u256()) @@ -194,18 +194,18 @@ where .filter(|(netuid, _)| *netuid != NetUid::ROOT) .collect::>(); - let mut sum_alpha_price: U96F32 = U96F32::from_num(0); + let mut sum_alpha_price: U64F64 = U64F64::from_num(0); for (netuid, _) in netuids { let price = as SwapHandler>::current_alpha_price( netuid.into(), ); - if price < U96F32::from_num(1) { + if price < U64F64::from_num(1) { sum_alpha_price = sum_alpha_price.saturating_add(price); } } - let price = sum_alpha_price.saturating_mul(U96F32::from_num(1_000_000_000)); + let price = sum_alpha_price.saturating_mul(U64F64::from_num(1_000_000_000)); let price: SubstrateBalance = price.saturating_to_num::().into(); let price_eth = ::BalanceConverter::into_evm_balance(price) .map(|amount| amount.into_u256()) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index c738e34c9b..fce0123e98 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -2491,10 +2491,10 @@ impl_runtime_apis! { impl pallet_subtensor_swap_runtime_api::SwapRuntimeApi for Runtime { fn current_alpha_price(netuid: NetUid) -> u64 { - use substrate_fixed::types::U96F32; + use substrate_fixed::types::U64F64; pallet_subtensor_swap::Pallet::::current_price(netuid.into()) - .saturating_mul(U96F32::from_num(1_000_000_000)) + .saturating_mul(U64F64::from_num(1_000_000_000)) .saturating_to_num() } From 79d697b116004a96986bb7f8b4b18c58194e0411 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Tue, 16 Dec 2025 13:53:26 -0500 Subject: [PATCH 058/240] Fix tests --- pallets/admin-utils/src/lib.rs | 4 ++-- pallets/admin-utils/src/tests/mock.rs | 2 +- pallets/subtensor/src/subnets/mechanism.rs | 2 +- pallets/subtensor/src/tests/mechanism.rs | 1 + pallets/subtensor/src/tests/mock.rs | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index fca6ba76c1..8f8605feec 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -2229,7 +2229,7 @@ pub mod pallet { } /// Sets the global maximum number of mechanisms in a subnet - #[pallet::call_index(84)] + #[pallet::call_index(85)] #[pallet::weight(Weight::from_parts(15_000_000, 0) .saturating_add(::DbWeight::get().reads(1_u64)) .saturating_add(::DbWeight::get().writes(1_u64)))] @@ -2241,7 +2241,7 @@ pub mod pallet { pallet_subtensor::Pallet::::do_set_max_mechanism_count(max_mechanism_count)?; Ok(()) } - + /// Sets the minimum number of non-immortal & non-immune UIDs that must remain in a subnet #[pallet::call_index(84)] #[pallet::weight(( diff --git a/pallets/admin-utils/src/tests/mock.rs b/pallets/admin-utils/src/tests/mock.rs index 0140808baa..7faa69d041 100644 --- a/pallets/admin-utils/src/tests/mock.rs +++ b/pallets/admin-utils/src/tests/mock.rs @@ -91,7 +91,7 @@ parameter_types! { pub const SelfOwnership: u64 = 2; pub const InitialImmunityPeriod: u16 = 2; pub const InitialMinAllowedUids: u16 = 2; - pub const InitialMaxAllowedUids: u16 = 16; + pub const InitialMaxAllowedUids: u16 = 256; pub const InitialBondsMovingAverage: u64 = 900_000; pub const InitialBondsPenalty: u16 = u16::MAX; pub const InitialBondsResetOn: bool = false; diff --git a/pallets/subtensor/src/subnets/mechanism.rs b/pallets/subtensor/src/subnets/mechanism.rs index 9055e7ef88..a59334a76f 100644 --- a/pallets/subtensor/src/subnets/mechanism.rs +++ b/pallets/subtensor/src/subnets/mechanism.rs @@ -102,7 +102,7 @@ impl Pallet { let max_uids_over_all_mechanisms = max_uids.saturating_mul(u8::from(mechanism_count) as u16); ensure!( - max_uids_over_all_mechanisms <= T::DefaultMaxAllowedUids::get(), + max_uids_over_all_mechanisms <= DefaultMaxAllowedUids::::get(), Error::::TooManyUIDsPerMechanism ); Ok(()) diff --git a/pallets/subtensor/src/tests/mechanism.rs b/pallets/subtensor/src/tests/mechanism.rs index 9e6450e09c..7f0ead8918 100644 --- a/pallets/subtensor/src/tests/mechanism.rs +++ b/pallets/subtensor/src/tests/mechanism.rs @@ -210,6 +210,7 @@ fn do_set_mechanism_count_ok_at_effective_cap() { new_test_ext(1).execute_with(|| { let netuid = NetUid::from(4u16); NetworksAdded::::insert(NetUid::from(4u16), true); // base subnet exists + MaxAllowedUids::::insert(netuid, 128u16); // Effective bound is min(runtime cap, compile-time cap) let runtime_cap = MaxMechanismCount::::get(); // e.g., MechId::from(8) diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index 090fcf8f75..ba47c7d1b9 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -166,7 +166,7 @@ parameter_types! { pub const SelfOwnership: u64 = 2; pub const InitialImmunityPeriod: u16 = 2; pub const InitialMinAllowedUids: u16 = 2; - pub const InitialMaxAllowedUids: u16 = 4; + pub const InitialMaxAllowedUids: u16 = 256; pub const InitialBondsMovingAverage: u64 = 900_000; pub const InitialBondsPenalty:u16 = u16::MAX; pub const InitialBondsResetOn: bool = false; From ac1b1c88e50fd2fb7a3591945dfe75d9f9949f15 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Tue, 16 Dec 2025 20:52:14 +0100 Subject: [PATCH 059/240] add prod_or_fast --- runtime/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 168b9076f2..83e9e9693d 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -1046,8 +1046,8 @@ parameter_types! { 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: BlockNumber = 5 * 24 * 60 * 60 / 12; // 5 days - pub const InitialColdkeySwapReannouncementDelay: BlockNumber = 24 * 60 * 60 / 12; // 1 day + pub const InitialColdkeySwapAnnouncementDelay: BlockNumber = prod_or_fast!(* 24 * 60 * 60 / 12, 50); // 5 days + pub const InitialColdkeySwapReannouncementDelay: BlockNumber = prod_or_fast!(24 * 60 * 60 / 12, 10); // 1 day pub const InitialDissolveNetworkScheduleDuration: BlockNumber = 5 * 24 * 60 * 60 / 12; // 5 days pub const SubtensorInitialTaoWeight: u64 = 971_718_665_099_567_868; // 0.05267697438728329% tao weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks From c9aedf647e7d1605be557e90e0bf8e4043bdb9e9 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Wed, 17 Dec 2025 09:51:35 -0500 Subject: [PATCH 060/240] Use Sam's bigmath crate for exponentiation --- Cargo.lock | 230 ++++++++++- Cargo.toml | 1 + pallets/swap/Cargo.toml | 2 + pallets/swap/src/pallet/impls.rs | 10 +- pallets/swap/src/pallet/reserve_weights.rs | 458 ++++++++++++++++++++- 5 files changed, 671 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eeffdabd0f..771c399a9b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -71,6 +71,17 @@ dependencies = [ "subtle 2.6.1", ] +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom 0.2.16", + "once_cell", + "version_check", +] + [[package]] name = "ahash" version = "0.8.12" @@ -497,7 +508,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43d68f2d516162846c1238e755a7c4d131b892b70cc70c471a8e3ca3ed818fce" dependencies = [ - "ahash", + "ahash 0.8.12", "ark-ff 0.5.0", "ark-poly 0.5.0", "ark-serialize 0.5.0", @@ -732,7 +743,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "579305839da207f02b89cd1679e50e67b4331e2f9294a57693e5051b7703fe27" dependencies = [ - "ahash", + "ahash 0.8.12", "ark-ff 0.5.0", "ark-serialize 0.5.0", "ark-std 0.5.0", @@ -1669,6 +1680,29 @@ dependencies = [ "piper", ] +[[package]] +name = "borsh" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" +dependencies = [ + "borsh-derive", + "cfg_aliases 0.2.1", +] + +[[package]] +name = "borsh-derive" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0686c856aa6aac0c4498f936d7d6a02df690f614c03e4d906d1018062b5c5e2c" +dependencies = [ + "once_cell", + "proc-macro-crate 3.4.0", + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "bounded-collections" version = "0.1.9" @@ -1944,6 +1978,28 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" +[[package]] +name = "bytecheck" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "bytemuck" version = "1.24.0" @@ -2464,6 +2520,28 @@ dependencies = [ "libc", ] +[[package]] +name = "crabtime" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81605e11aed454fb3838bc408f091d17f2f6d31613fb897f561a45bc8fb98378" +dependencies = [ + "crabtime-internal", +] + +[[package]] +name = "crabtime-internal" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e49bfb43e6dfb9026c045fc1fd5cde79f4927ea6d1d89a2d346c55879e85900c" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version 0.4.1", + "syn 2.0.106", + "toml 0.8.23", +] + [[package]] name = "cranelift-bforest" version = "0.95.1" @@ -5691,6 +5769,9 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.8", +] [[package]] name = "hashbrown" @@ -5698,7 +5779,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash", + "ahash 0.8.12", ] [[package]] @@ -5707,7 +5788,7 @@ version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ - "ahash", + "ahash 0.8.12", "allocator-api2", "serde", ] @@ -10837,6 +10918,8 @@ dependencies = [ "log", "pallet-subtensor-swap-runtime-api", "parity-scale-codec", + "rand 0.8.5", + "safe-bigmath", "safe-math", "scale-info", "serde", @@ -13472,6 +13555,26 @@ dependencies = [ "cc", ] +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "quanta" version = "0.12.6" @@ -13580,6 +13683,29 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "quoth" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3fcb67d48b8ef70eda1c84514f4ba26e4904c40c36940cec7aa2c45cd8d114b" +dependencies = [ + "quoth-macros", + "regex", + "rust_decimal", + "safe-string", +] + +[[package]] +name = "quoth-macros" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9e5bab2cb57954711cf4165382479a56c3b9d2c94f513ab2a634aea00baed4a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "r-efi" version = "5.3.0" @@ -13858,6 +13984,15 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" +[[package]] +name = "rend" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] + [[package]] name = "resolv-conf" version = "0.7.5" @@ -13912,6 +14047,35 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "rkyv" +version = "0.7.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b" +dependencies = [ + "bitvec", + "bytecheck", + "bytes", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "rlp" version = "0.5.2" @@ -14147,6 +14311,22 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" +[[package]] +name = "rust_decimal" +version = "1.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35affe401787a9bd846712274d97654355d21b2a2c092a3139aabe31e9022282" +dependencies = [ + "arrayvec 0.7.6", + "borsh", + "bytes", + "num-traits", + "rand 0.8.5", + "rkyv", + "serde", + "serde_json", +] + [[package]] name = "rustc-demangle" version = "0.1.26" @@ -14402,6 +14582,18 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +[[package]] +name = "safe-bigmath" +version = "0.1.0" +source = "git+https://github.com/sam0x17/safe-math?rev=6d81eb0#6d81eb059a7dde19b712988a399a616d8f225e88" +dependencies = [ + "crabtime", + "num-bigint", + "num-integer", + "num-traits", + "quoth", +] + [[package]] name = "safe-math" version = "0.1.0" @@ -14421,6 +14613,12 @@ dependencies = [ "rustc_version 0.2.3", ] +[[package]] +name = "safe-string" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "828a62cafa42d2d1b5ad4592c47946c156972e1e8d6e929e566256d841177b59" + [[package]] name = "safe_arch" version = "0.7.4" @@ -14842,7 +15040,7 @@ name = "sc-consensus-grandpa" version = "0.36.0" source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=81fa2c54e94f824eba7dabe9dffd063481cb2d80#81fa2c54e94f824eba7dabe9dffd063481cb2d80" dependencies = [ - "ahash", + "ahash 0.8.12", "array-bytes 6.2.3", "async-trait", "dyn-clone", @@ -15145,7 +15343,7 @@ name = "sc-network-gossip" version = "0.51.0" source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=81fa2c54e94f824eba7dabe9dffd063481cb2d80#81fa2c54e94f824eba7dabe9dffd063481cb2d80" dependencies = [ - "ahash", + "ahash 0.8.12", "futures", "futures-timer", "log", @@ -15858,7 +16056,7 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "356285bbf17bea63d9e52e96bd18f039672ac92b55b8cb997d6162a2a37d1649" dependencies = [ - "ahash", + "ahash 0.8.12", "cfg-if", "hashbrown 0.13.2", ] @@ -15922,6 +16120,12 @@ dependencies = [ "sha2 0.10.9", ] +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + [[package]] name = "sec1" version = "0.7.3" @@ -16353,6 +16557,12 @@ dependencies = [ "wide", ] +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "simple-dns" version = "0.9.3" @@ -17433,7 +17643,7 @@ name = "sp-trie" version = "40.0.0" source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=81fa2c54e94f824eba7dabe9dffd063481cb2d80#81fa2c54e94f824eba7dabe9dffd063481cb2d80" dependencies = [ - "ahash", + "ahash 0.8.12", "foldhash 0.1.5", "hash-db", "hashbrown 0.15.5", @@ -18081,7 +18291,7 @@ dependencies = [ name = "subtensor-macros" version = "0.1.0" dependencies = [ - "ahash", + "ahash 0.8.12", "proc-macro2", "quote", "syn 2.0.106", @@ -19674,7 +19884,7 @@ version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c128c039340ffd50d4195c3f8ce31aac357f06804cfc494c8b9508d4b30dca4" dependencies = [ - "ahash", + "ahash 0.8.12", "hashbrown 0.14.5", "string-interner", ] diff --git a/Cargo.toml b/Cargo.toml index 1d65a3cd5e..384a572151 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,6 +61,7 @@ pallet-subtensor-swap-runtime-api = { path = "pallets/swap/runtime-api", default pallet-subtensor-swap-rpc = { path = "pallets/swap/rpc", default-features = false } procedural-fork = { path = "support/procedural-fork", default-features = false } safe-math = { path = "primitives/safe-math", default-features = false } +safe-bigmath = { package = "safe-bigmath", git = "https://github.com/sam0x17/safe-math", rev = "6d81eb0", default-features = false } share-pool = { path = "primitives/share-pool", default-features = false } subtensor-macros = { path = "support/macros", default-features = false } subtensor-custom-rpc = { default-features = false, path = "pallets/subtensor/rpc" } diff --git a/pallets/swap/Cargo.toml b/pallets/swap/Cargo.toml index 7de8e49c1d..19fa17fd3d 100644 --- a/pallets/swap/Cargo.toml +++ b/pallets/swap/Cargo.toml @@ -12,6 +12,7 @@ frame-support.workspace = true frame-system.workspace = true log.workspace = true safe-math.workspace = true +safe-bigmath.workspace = true scale-info = { workspace = true, features = ["derive"] } serde = { workspace = true, optional = true } sp-arithmetic.workspace = true @@ -28,6 +29,7 @@ subtensor-swap-interface.workspace = true [dev-dependencies] sp-tracing.workspace = true +rand = "0.8" [lints] workspace = true diff --git a/pallets/swap/src/pallet/impls.rs b/pallets/swap/src/pallet/impls.rs index d025ed9571..6ea373acbf 100644 --- a/pallets/swap/src/pallet/impls.rs +++ b/pallets/swap/src/pallet/impls.rs @@ -43,16 +43,10 @@ impl Pallet { 1 => { let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); if !alpha_reserve.is_zero() { - let alpha_reserve = U64F64::saturating_from_num(alpha_reserve); let tao_reserve = - U64F64::saturating_from_num(T::TaoReserve::reserve(netuid.into())); + T::TaoReserve::reserve(netuid.into()); let reserve_weight = SwapReserveWeight::::get(netuid); - let base_weight = - U64F64::saturating_from_num(reserve_weight.get_base_weight()); - let quote_weight = - U64F64::saturating_from_num(reserve_weight.get_quote_weight()); - let w1_div_w2 = base_weight.safe_div(quote_weight); - w1_div_w2.saturating_mul(tao_reserve.safe_div(alpha_reserve)) + reserve_weight.calculate_price(tao_reserve.into(), alpha_reserve.into()) } else { U64F64::saturating_from_num(0) } diff --git a/pallets/swap/src/pallet/reserve_weights.rs b/pallets/swap/src/pallet/reserve_weights.rs index 3b7fb89a6c..b0c54fd70c 100644 --- a/pallets/swap/src/pallet/reserve_weights.rs +++ b/pallets/swap/src/pallet/reserve_weights.rs @@ -1,16 +1,22 @@ use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::pallet_prelude::*; -use safe_math::SafeDiv; +use safe_math::*; +use safe_bigmath::*; +use sp_arithmetic::Perquintill; +use sp_runtime::Saturating; +use substrate_fixed::types::U64F64; use subtensor_macros::freeze_struct; -#[freeze_struct("6382cc997c2e2049")] +#[freeze_struct("a83c5690c57a33b")] #[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct ReserveWeight { - quote: u64, + quote: Perquintill } // Lower imit of weights is 0.01 -const MIN_WEIGHT: u64 = 184467440737095516; +pub const ACCURACY: u64 = 1_000_000_000_000_000_000_u64; +pub const MIN_WEIGHT: Perquintill = Perquintill::from_parts(ACCURACY / 100); +pub const ONE: Perquintill = Perquintill::from_parts(ACCURACY); #[derive(Debug)] pub enum ReserveWeightError { @@ -20,13 +26,13 @@ pub enum ReserveWeightError { impl Default for ReserveWeight { fn default() -> Self { Self { - quote: u64::MAX.safe_div(2_u64) + quote: Perquintill::from_rational(1u128, 2u128) } } } impl ReserveWeight { - pub fn new(quote: u64) -> Result { + pub fn new(quote: Perquintill) -> Result { if Self::check_constraints(quote) { Ok(ReserveWeight { quote }) } else { @@ -34,24 +40,452 @@ impl ReserveWeight { } } - fn check_constraints(quote: u64) -> bool { - let base = u64::MAX.saturating_sub(quote); + fn check_constraints(quote: Perquintill) -> bool { + let base = ONE.saturating_sub(quote); (base >= MIN_WEIGHT) && (quote >= MIN_WEIGHT) } - pub fn get_quote_weight(&self) -> u64 { + pub fn get_quote_weight(&self) -> Perquintill { self.quote } - pub fn get_base_weight(&self) -> u64 { - u64::MAX.saturating_sub(self.quote) + pub fn get_base_weight(&self) -> Perquintill { + ONE.saturating_sub(self.quote) } - pub fn set_quote_weight(&self, new_value: u64) -> Result<(), ReserveWeightError> { + pub fn set_quote_weight(&self, new_value: Perquintill) -> Result<(), ReserveWeightError> { if Self::check_constraints(new_value) { Ok(()) } else { Err(ReserveWeightError::InvalidValue) } } + + fn exp_scaled(&self, x: u64, dx: u64, base_quote: bool) -> U64F64 { + let den = x.saturating_add(dx); + if den == 0 { + return U64F64::saturating_from_num(0); + } + let w1: u128 = self.get_base_weight().deconstruct() as u128; + let w2: u128 = self.get_quote_weight().deconstruct() as u128; + + let precision = 128; + let x_safe = SafeInt::from(x); + let delta_safe = SafeInt::from(dx); + let w1_safe = SafeInt::from(w1); + let w2_safe = SafeInt::from(w2); + let perquintill_scale = SafeInt::from(ACCURACY as u128); + let denominator = x_safe.clone() + delta_safe; + let maybe_result_safe_int = if base_quote { + + // println!("SafeInt::pow_ratio_scaled input: "); + // println!(" x_safe = {:?}", x_safe); + // println!(" denominator = {:?}", denominator); + // println!(" w1_safe = {:?}", w1_safe); + // println!(" w2_safe = {:?}", w2_safe); + // println!(" precision = {:?}", precision); + // println!(" perquintill_scale = {:?}", perquintill_scale); + + SafeInt::pow_ratio_scaled( + &x_safe, + &denominator, + &w1_safe, + &w2_safe, + precision, + &perquintill_scale, + ) + } else { + SafeInt::pow_ratio_scaled( + &x_safe, + &denominator, + &w2_safe, + &w1_safe, + precision, + &perquintill_scale, + ) + }; + + // println!("SafeInt::pow_ratio_scaled output: {:?}", maybe_result_safe_int); + + if let Some(result_safe_int) = maybe_result_safe_int { + if let Some(result_u64) = result_safe_int.to_u64() { + return U64F64::saturating_from_num(result_u64) + .safe_div(U64F64::saturating_from_num(ACCURACY)); + } + } + return U64F64::saturating_from_num(0); + } + + /// Calculates exponent of (x / (x + ∆x)) ^ (w_base/w_quote) + pub fn exp_base_quote(&self, x: u64, dx: u64) -> U64F64 { + self.exp_scaled(x, dx, true) + } + + /// Calculates exponent of (y / (y + ∆y)) ^ (w_quote/w_base) + pub fn exp_quote_base(&self, y: u64, dy: u64) -> U64F64 { + self.exp_scaled(y, dy, false) + } + + /// Calculates price as (w1/w2) * (y/x) + pub fn calculate_price(&self, x: u64, y: u64) -> U64F64 { + let w2_fixed = U64F64::saturating_from_num(self.get_base_weight().deconstruct()); + let w1_fixed = U64F64::saturating_from_num(self.get_quote_weight().deconstruct()); + let x_fixed = U64F64::saturating_from_num(x); + let y_fixed = U64F64::saturating_from_num(y); + w2_fixed.safe_div(w1_fixed).saturating_mul(y_fixed.safe_div(x_fixed)) + } } + +// cargo test --package pallet-subtensor-swap --lib -- pallet::reserve_weights::tests --nocapture +#[cfg(test)] +mod tests { + use approx::assert_abs_diff_eq; + use crate::pallet::ReserveWeight; + use crate::pallet::reserve_weights::*; + use sp_arithmetic::Perquintill; + + fn perquintill_to_f64(p: Perquintill) -> f64 { + let parts = p.deconstruct() as f64; + parts / ACCURACY as f64 + } + + #[test] + fn test_perquintill_power() { + const PRECISION: u32 = 256; + const PERQUINTILL: u128 = ACCURACY as u128; + + let x = SafeInt::from(21_000_000_000_000_000u64); + let delta = SafeInt::from(7_000_000_000_000_000u64); + let w1 = SafeInt::from(600_000_000_000_000_000u128); + let w2 = SafeInt::from(400_000_000_000_000_000u128); + let denominator = &x + δ + assert_eq!(w1.clone() + w2.clone(), SafeInt::from(PERQUINTILL)); + + let perquintill_result = SafeInt::pow_ratio_scaled( + &x, + &denominator, + &w1, + &w2, + PRECISION, + &SafeInt::from(PERQUINTILL), + ) + .expect("perquintill integer result"); + + assert_eq!(perquintill_result, SafeInt::from(649_519_052_838_328_985u128)); + let readable = safe_bigmath::SafeDec::<18>::from_raw(perquintill_result); + assert_eq!(format!("{}", readable), "0.649519052838328985"); + } + + /// Validate realistic values that can be calculated with f32 precision + #[test] + fn test_exp_bae_quote_happy_path() { + + // Outer test cases: w_quote + [ + Perquintill::from_rational(500_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(500_000_000_001_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(500_000_000_100_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(500_000_001_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(500_000_010_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(500_000_100_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(500_001_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(500_010_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(500_100_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(501_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(510_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(100_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(100_000_000_001_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(200_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(300_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(400_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(600_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(700_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(800_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(899_999_999_999_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(900_000_000_000_u128, 1_000_000_000_000_u128), + ] + .into_iter() + .for_each(|w_quote| { + // Inner test cases: y, x, ∆x + [ + ( + 1_000_000_000_000_u64, + 100_000_000_000_000_u64, + 100_000_000_u64, + ), + ( + 1_000_000_000_000_u64, + 100_000_000_000_000_u64, + 100_000_000_u64, + ), + ( + 100_000_000_000_u64, + 100_000_000_000_000_u64, + 100_000_000_u64, + ), + ( + 100_000_000_000_u64, + 100_000_000_000_000_u64, + 1_000_000_u64, + ), + ( + 100_000_000_000_u64, + 100_000_000_000_000_u64, + 1_000_000_000_000_u64, + ), + ( + 1_000_000_000_u64, + 100_000_000_000_000_u64, + 1_000_000_000_000_u64, + ), + ( + 1_000_000_u64, + 100_000_000_000_000_u64, + 1_000_000_000_000_u64, + ), + ( + 1_000_u64, + 100_000_000_000_000_u64, + 1_000_000_000_000_u64, + ), + ( + 1_000_u64, + 100_000_000_000_000_u64, + 1_000_000_000_u64, + ), + ( + 1_000_u64, + 100_000_000_000_000_u64, + 1_000_000_u64, + ), + ( + 1_000_u64, + 100_000_000_000_000_u64, + 1_000_u64, + ), + ( + 1_000_u64, + 100_000_000_000_000_u64, + 100_000_000_000_000_u64, + ), + ( + 10_u64, + 100_000_000_000_000_u64, + 100_000_000_000_000_u64, + ), + ] + .into_iter() + .for_each(|(y, x, dx)| { + let rw = ReserveWeight::new(w_quote).unwrap(); + let e = rw.exp_base_quote(x, dx); + let one = U64F64::from_num(1); + let y_fixed = U64F64::from_num(y); + let dy = y_fixed * (one - e); + + let w1 = perquintill_to_f64(rw.get_base_weight()); + let w2 = perquintill_to_f64(rw.get_quote_weight()); + let e_expected = (x as f64 / (x as f64 + dx as f64)).powf(w1/w2); + let dy_expected = y as f64 * (1. - e_expected); + + let mut eps = dy_expected / 100000.; + if eps > 0.1 { + eps = 0.1; + } + assert_abs_diff_eq!( + dy.to_num::(), + dy_expected, + epsilon = eps, + ); + }) + }); + } + + /// This test exercises practical application edge cases of exp_base_quote + /// The practical formula where this function is used: + /// ∆y = y * (exp_base_quote(x, ∆x) - 1) + /// + /// The test validates that two different sets of parameters produce (sensibly) + /// different results + /// + #[test] + fn test_exp_bae_quote_dy_precision() { + + // Test cases: y, x1, ∆x1, w_quote1, x2, ∆x2, w_quote2 + // Realized dy1 should be greater than dy2 + [ + ( + 1_000_000_000_u64, + 21_000_000_000_000_000_u64, + 21_000_000_000_u64, + Perquintill::from_rational(1_000_000_000_000_u128, 2_000_000_000_000_u128), + 21_000_000_000_000_000_u64, + 21_000_000_000_u64, + Perquintill::from_rational(1_000_000_000_001_u128, 2_000_000_000_000_u128), + ), + ( + 1_000_000_000_u64, + 21_000_000_000_000_000_u64, + 21_000_000_000_u64, + Perquintill::from_rational(1_000_000_000_000_u128, 2_000_000_000_001_u128), + 21_000_000_000_000_000_u64, + 21_000_000_000_u64, + Perquintill::from_rational(1_000_000_000_000_u128, 2_000_000_000_000_u128), + ), + ( + 1_000_000_000_u64, + 21_000_000_000_000_000_u64, + 2_u64, + Perquintill::from_rational(1_000_000_000_000_u128, 2_000_000_000_000_u128), + 21_000_000_000_000_000_u64, + 1_u64, + Perquintill::from_rational(1_000_000_000_000_u128, 2_000_000_000_000_u128), + ), + ( + 1_000_000_000_u64, + 21_000_000_000_000_000_u64, + 1_u64, + Perquintill::from_rational(1_000_000_000_000_u128, 2_000_000_000_000_u128), + 21_000_000_000_000_000_u64, + 1_u64, + Perquintill::from_rational(1_010_000_000_000_u128, 2_000_000_000_000_u128), + ), + ( + 1_000_000_000_u64, + 21_000_000_000_000_000_u64, + 1_u64, + Perquintill::from_rational(1_000_000_000_000_u128, 2_010_000_000_000_u128), + 21_000_000_000_000_000_u64, + 1_u64, + Perquintill::from_rational(1_000_000_000_000_u128, 2_000_000_000_000_u128), + ), + ] + .into_iter() + .for_each(|(y, x1, dx1, w_quote1, x2, dx2, w_quote2)| { + let rw1 = ReserveWeight::new(w_quote1).unwrap(); + let rw2 = ReserveWeight::new(w_quote2).unwrap(); + + println!("rw1 = {:?}", rw1); + println!("rw2 = {:?}", rw2); + + let exp1 = rw1.exp_base_quote(x1, dx1); + let exp2 = rw2.exp_base_quote(x2, dx2); + + println!("exp1 = {:?}", exp1); + println!("exp2 = {:?}", exp2); + + let one = U64F64::from_num(1); + let y_fixed = U64F64::from_num(y); + let dy1 = y_fixed * (one - exp1); + let dy2 = y_fixed * (one - exp2); + + println!("dy1 = {:?}", dy1); + println!("dy2 = {:?}", dy2); + + assert!(dy1 > dy2); + + let zero = U64F64::from_num(0); + assert!(dy1 != zero); + assert!(dy2 != zero); + }) + } + + + + /// Test the broad range of w_quote values, usually should be ignored + #[ignore] + #[test] + fn test_exp_quote_broad_range() { + + let y = 1_000_000_000_000_u64; + let x = 100_000_000_000_000_u64; + let dx = 10_000_000_u64; + + let mut prev = U64F64::from_num(1_000_000_000); + let mut last_progress = 0.; + let start = 100_000_000_000_u128; + let stop = 900_000_000_000_u128; + for num in (start..=stop).step_by(1000 as usize) { + let w_quote = Perquintill::from_rational(num, 1_000_000_000_000_u128); + let rw = ReserveWeight::new(w_quote).unwrap(); + let e = rw.exp_base_quote(x, dx); + + let one = U64F64::from_num(1); + // println!("e = {:?}", e); + // println!("1 - e = {:?}", one - e); + + let dy = U64F64::from_num(y) * (one - e); + // println!("dy = {:?}", dy); + + let progress = (num as f64 - start as f64) / (stop as f64 - start as f64); + + if progress - last_progress >= 0.0001 { + println!("progress = {:?}%", progress*100.); + println!("dy = {:?}", dy); + last_progress = progress; + } + + assert!(dy != U64F64::from_num(0)); + assert!(dy <= prev); + prev = dy; + } + + } + + #[test] + fn test_exp_quote_fuzzy() { + use rand::{Rng, SeedableRng}; + use rand::rngs::StdRng; + + let mut rng = StdRng::seed_from_u64(42); + + let iterations = 1_000_000_000; + for i in 0..iterations { + + let x: u64 = rng.gen_range(1_000..21_000_000_000_000_000); + let y: u64 = rng.gen_range(1_000..21_000_000_000_000_000); + let dx: u64 = rng.gen_range(1_000..=x); + let w_numerator: u64 = rng.gen_range(ACCURACY/10..=ACCURACY/10*9); + let w_quote = Perquintill::from_rational(w_numerator, ACCURACY); + + let rw = ReserveWeight::new(w_quote).unwrap(); + let e = rw.exp_base_quote(x, dx); + + let one = U64F64::from_num(1); + // println!("e = {:?}", e); + // println!("1 - e = {:?}", one - e); + + let dy = U64F64::from_num(y) * (one - e); + // println!("dy = {:?}", dy); + + if i % 1000 == 0 { + let progress = i as f64 / iterations as f64; + println!("progress = {:?}%", progress*100.); + println!("dy = {:?}", dy); + } + + // Calculate expected in f64 and approx-assert + let w1 = perquintill_to_f64(rw.get_base_weight()); + let w2 = perquintill_to_f64(rw.get_quote_weight()); + let e_expected = (x as f64 / (x as f64 + dx as f64)).powf(w1/w2); + let dy_expected = y as f64 * (1. - e_expected); + + let actual = dy.to_num::(); + let mut eps = dy_expected / 1_000_000_000.; + if eps > 100.0 { + eps = 100.0; + } + + assert!( + (actual - dy_expected).abs() <= eps, + "dy mismatch:\n actual: {}\n expected: {}\n eps: {}\nParameters:\n x: {}\n y: {}\n dx: {}\n w_numerator: {}\n", + actual, + dy_expected, + eps, + x, y, dx, + w_numerator, + ); + } + + } + +} \ No newline at end of file From 1bc30924d08d96be3cc59346ea529f0248a0fc84 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Wed, 17 Dec 2025 10:17:11 -0500 Subject: [PATCH 061/240] Adjust fuzzy epsilon --- pallets/swap/src/pallet/reserve_weights.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pallets/swap/src/pallet/reserve_weights.rs b/pallets/swap/src/pallet/reserve_weights.rs index b0c54fd70c..a32058e895 100644 --- a/pallets/swap/src/pallet/reserve_weights.rs +++ b/pallets/swap/src/pallet/reserve_weights.rs @@ -474,6 +474,9 @@ mod tests { if eps > 100.0 { eps = 100.0; } + if eps < 1.0 { + eps = 1.0; + } assert!( (actual - dy_expected).abs() <= eps, From 81eab8e84f4a358a94dbd04ea099aef8a582f04a Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Wed, 17 Dec 2025 12:08:12 -0500 Subject: [PATCH 062/240] Use rayon for fuzzy testing --- Cargo.lock | 1 + .../subtensor/src/coinbase/block_emission.rs | 4 +- .../subtensor/src/coinbase/run_coinbase.rs | 3 +- pallets/subtensor/src/staking/remove_stake.rs | 4 +- pallets/subtensor/src/staking/stake_utils.rs | 8 +- pallets/subtensor/src/tests/claim_root.rs | 30 ++-- pallets/subtensor/src/tests/move_stake.rs | 5 +- pallets/subtensor/src/tests/networks.rs | 32 ++-- pallets/subtensor/src/tests/staking.rs | 9 +- pallets/swap/Cargo.toml | 1 + pallets/swap/src/benchmarking.rs | 11 +- pallets/swap/src/mock.rs | 10 +- pallets/swap/src/pallet/impls.rs | 27 ++- pallets/swap/src/pallet/mod.rs | 10 +- pallets/swap/src/pallet/reserve_weights.rs | 158 +++++++----------- pallets/swap/src/pallet/swap_step.rs | 58 ++----- pallets/swap/src/pallet/tests.rs | 64 ++----- 17 files changed, 177 insertions(+), 258 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 771c399a9b..b0a4e2c02f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10919,6 +10919,7 @@ dependencies = [ "pallet-subtensor-swap-runtime-api", "parity-scale-codec", "rand 0.8.5", + "rayon", "safe-bigmath", "safe-math", "scale-info", diff --git a/pallets/subtensor/src/coinbase/block_emission.rs b/pallets/subtensor/src/coinbase/block_emission.rs index 6cdd8abf20..c499fe3117 100644 --- a/pallets/subtensor/src/coinbase/block_emission.rs +++ b/pallets/subtensor/src/coinbase/block_emission.rs @@ -64,9 +64,7 @@ impl Pallet { // Avoid rounding errors. let zero = U64F64::saturating_from_num(0); let one = U64F64::saturating_from_num(1); - if tao_in_emission < one - || alpha_in_emission < one - { + if tao_in_emission < one || alpha_in_emission < one { alpha_in_emission = zero; tao_in_emission = zero; } diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 0862c220bf..5576cf7b04 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -135,7 +135,8 @@ impl Pallet { log::debug!("alpha_emission_i: {alpha_emission_i:?}"); // Get subnet price. - let price_i: U96F32 = U96F32::saturating_from_num(T::SwapInterface::current_alpha_price(netuid_i.into())); + let price_i: U96F32 = + U96F32::saturating_from_num(T::SwapInterface::current_alpha_price(netuid_i.into())); log::debug!("price_i: {price_i:?}"); let mut tao_in_i: U96F32 = tao_emission_i; diff --git a/pallets/subtensor/src/staking/remove_stake.rs b/pallets/subtensor/src/staking/remove_stake.rs index 7a7c1a2d79..01427545b4 100644 --- a/pallets/subtensor/src/staking/remove_stake.rs +++ b/pallets/subtensor/src/staking/remove_stake.rs @@ -463,7 +463,9 @@ impl Pallet { .saturating_to_num::(); owner_emission_tao = if owner_alpha_u64 > 0 { - let cur_price: U96F32 = U96F32::saturating_from_num(T::SwapInterface::current_alpha_price(netuid.into())); + let cur_price: U96F32 = U96F32::saturating_from_num( + T::SwapInterface::current_alpha_price(netuid.into()), + ); let val_u64 = U96F32::from_num(owner_alpha_u64) .saturating_mul(cur_price) .floor() diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index 50f8badfd4..e7f4813843 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -63,10 +63,10 @@ impl Pallet { // Because alpha = b / (b + h), where b and h > 0, alpha < 1, so 1 - alpha > 0. // We can use unsigned type here: U96F32 let one_minus_alpha: U96F32 = U96F32::saturating_from_num(1.0).saturating_sub(alpha); - let current_price: U96F32 = alpha.saturating_mul( - U96F32::saturating_from_num(T::SwapInterface::current_alpha_price(netuid.into()) - .min(U64F64::saturating_from_num(1.0))), - ); + let current_price: U96F32 = alpha.saturating_mul(U96F32::saturating_from_num( + T::SwapInterface::current_alpha_price(netuid.into()) + .min(U64F64::saturating_from_num(1.0)), + )); let current_moving: U96F32 = one_minus_alpha.saturating_mul(Self::get_moving_alpha_price(netuid)); // Convert batch to signed I96F32 to avoid migration of SubnetMovingPrice for now`` diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index 659ddf27cc..ca4b6f0da4 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -5,16 +5,25 @@ use crate::tests::mock::{ RuntimeOrigin, SubtensorModule, Test, add_dynamic_network, new_test_ext, run_to_block, }; use crate::{ - DefaultMinRootClaimAmount, Error, MAX_NUM_ROOT_CLAIMS, MAX_ROOT_CLAIM_THRESHOLD, NetworksAdded, - NumRootClaim, NumStakingColdkeys, - //PendingRootAlphaDivs, - RootClaimable, RootClaimableThreshold, - StakingColdkeys, StakingColdkeysByIndex, SubnetAlphaIn, SubnetMechanism, + DefaultMinRootClaimAmount, + Error, + MAX_NUM_ROOT_CLAIMS, + MAX_ROOT_CLAIM_THRESHOLD, + NetworksAdded, + NumRootClaim, + NumStakingColdkeys, + //PendingRootAlphaDivs, + RootClaimable, + RootClaimableThreshold, + StakingColdkeys, + StakingColdkeysByIndex, + SubnetAlphaIn, + SubnetMechanism, // SubnetMovingPrice, - SubnetTAO, - // SubnetTaoFlow, - SubtokenEnabled, - //Tempo, + SubnetTAO, + // SubnetTaoFlow, + SubtokenEnabled, + //Tempo, pallet, }; use crate::{RootClaimType, RootClaimTypeEnum, RootClaimed}; @@ -26,7 +35,8 @@ 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, +use substrate_fixed::types::{ + I96F32, //U64F64, U96F32 }; use subtensor_runtime_common::{AlphaCurrency, Currency, NetUid, TaoCurrency}; diff --git a/pallets/subtensor/src/tests/move_stake.rs b/pallets/subtensor/src/tests/move_stake.rs index 419ecedbc3..77012e6817 100644 --- a/pallets/subtensor/src/tests/move_stake.rs +++ b/pallets/subtensor/src/tests/move_stake.rs @@ -619,8 +619,9 @@ fn test_do_move_event_emission() { // Move stake and capture events System::reset_events(); - let current_price = - U96F32::from_num(::SwapInterface::current_alpha_price(netuid.into())); + let current_price = U96F32::from_num( + ::SwapInterface::current_alpha_price(netuid.into()), + ); let tao_equivalent = (current_price * U96F32::from_num(alpha)).to_num::(); // no fee conversion assert_ok!(SubtensorModule::do_move_stake( RuntimeOrigin::signed(coldkey), diff --git a/pallets/subtensor/src/tests/networks.rs b/pallets/subtensor/src/tests/networks.rs index d02afb522a..d75cf2c625 100644 --- a/pallets/subtensor/src/tests/networks.rs +++ b/pallets/subtensor/src/tests/networks.rs @@ -7,13 +7,14 @@ use frame_support::{assert_err, assert_ok}; use frame_system::Config; use sp_core::U256; use sp_std::collections::{ - //btree_map::BTreeMap, -vec_deque::VecDeque}; + //btree_map::BTreeMap, + vec_deque::VecDeque, +}; use substrate_fixed::types::{I96F32, U64F64, U96F32}; use subtensor_runtime_common::{MechId, NetUidStorageIndex, TaoCurrency}; use subtensor_swap_interface::{ - //Order, - SwapHandler + //Order, + SwapHandler, }; #[test] @@ -252,8 +253,9 @@ fn dissolve_owner_cut_refund_logic() { // Use the current alpha price to estimate the TAO equivalent. let owner_emission_tao = { - let price: U96F32 = - U96F32::from_num(::SwapInterface::current_alpha_price(net.into())); + let price: U96F32 = U96F32::from_num( + ::SwapInterface::current_alpha_price(net.into()), + ); U96F32::from_num(owner_alpha_u64) .saturating_mul(price) .floor() @@ -903,8 +905,9 @@ fn destroy_alpha_out_many_stakers_complex_distribution() { let owner_emission_tao: u64 = { // Fallback matches the pallet's fallback - let price: U96F32 = - U96F32::from_num(::SwapInterface::current_alpha_price(netuid.into())); + let price: U96F32 = U96F32::from_num( + ::SwapInterface::current_alpha_price(netuid.into()), + ); U96F32::from_num(owner_alpha_u64) .saturating_mul(price) .floor() @@ -983,8 +986,9 @@ fn destroy_alpha_out_refund_gating_by_registration_block() { .saturating_to_num::(); let owner_emission_tao_u64 = { - let price: U96F32 = - U96F32::from_num(::SwapInterface::current_alpha_price(netuid.into())); + let price: U96F32 = U96F32::from_num( + ::SwapInterface::current_alpha_price(netuid.into()), + ); U96F32::from_num(owner_alpha_u64) .saturating_mul(price) .floor() @@ -2157,10 +2161,10 @@ fn massive_dissolve_refund_and_reregistration_flow_is_lossless_and_cleans_state( // // Compute the exact min stake per the pallet rule: DefaultMinStake + fee(DefaultMinStake). // let min_stake = DefaultMinStake::::get(); - // let order = GetAlphaForTao::::with_amount(min_stake); + // let order = GetAlphaForTao::::with_amount(min_stake); // let fee_for_min = pallet_subtensor_swap::Pallet::::sim_swap( // net_new, - // order, + // order, // ) // .map(|r| r.fee_paid) // .unwrap_or_else(|_e| { @@ -2177,10 +2181,10 @@ fn massive_dissolve_refund_and_reregistration_flow_is_lossless_and_cleans_state( // let a_prev: u64 = Alpha::::get((hot1, cold, net_new)).saturating_to_num(); // // Expected α for this exact τ, using the same sim path as the pallet. - // let order = GetAlphaForTao::::with_amount(min_amount_required); + // let order = GetAlphaForTao::::with_amount(min_amount_required); // let expected_alpha_out = pallet_subtensor_swap::Pallet::::sim_swap( // net_new, - // order, + // order, // ) // .map(|r| r.amount_paid_out) // .expect("sim_swap must succeed for fresh net and min amount"); diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index 6fa7b4e056..7cdfedab1a 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -716,7 +716,9 @@ fn test_remove_stake_total_balance_no_change() { // Add subnet TAO for the equivalent amount added at price let amount_tao = U96F32::from_num(amount) - * U96F32::from_num(::SwapInterface::current_alpha_price(netuid.into())); + * U96F32::from_num( + ::SwapInterface::current_alpha_price(netuid.into()), + ); SubnetTAO::::mutate(netuid, |v| { *v += amount_tao.saturating_to_num::().into() }); @@ -2177,8 +2179,9 @@ fn test_get_total_delegated_stake_after_unstaking() { netuid, unstake_amount_alpha.into() )); - let current_price = - U96F32::from_num(::SwapInterface::current_alpha_price(netuid.into())); + let current_price = U96F32::from_num( + ::SwapInterface::current_alpha_price(netuid.into()), + ); // Calculate the expected delegated stake let unstake_amount = diff --git a/pallets/swap/Cargo.toml b/pallets/swap/Cargo.toml index 19fa17fd3d..89534fa81c 100644 --- a/pallets/swap/Cargo.toml +++ b/pallets/swap/Cargo.toml @@ -30,6 +30,7 @@ subtensor-swap-interface.workspace = true [dev-dependencies] sp-tracing.workspace = true rand = "0.8" +rayon = "1.10" [lints] workspace = true diff --git a/pallets/swap/src/benchmarking.rs b/pallets/swap/src/benchmarking.rs index a7fcfedb4e..1ff029c281 100644 --- a/pallets/swap/src/benchmarking.rs +++ b/pallets/swap/src/benchmarking.rs @@ -8,9 +8,7 @@ use frame_system::RawOrigin; use subtensor_runtime_common::NetUid; use crate::{ - pallet::{ - Call, Config, Pallet, PositionsV2, - }, + pallet::{Call, Config, Pallet, PositionsV2}, position::{Position, PositionId}, }; @@ -41,12 +39,7 @@ mod benchmarks { let hotkey: T::AccountId = account("hotkey", 0, 0); #[extrinsic_call] - add_liquidity( - RawOrigin::Signed(caller), - hotkey, - netuid, - 1000, - ); + add_liquidity(RawOrigin::Signed(caller), hotkey, netuid, 1000); } #[benchmark] diff --git a/pallets/swap/src/mock.rs b/pallets/swap/src/mock.rs index e5703c9d79..931e1d0279 100644 --- a/pallets/swap/src/mock.rs +++ b/pallets/swap/src/mock.rs @@ -16,9 +16,13 @@ use sp_runtime::{ }; // use substrate_fixed::types::U64F64; use subtensor_runtime_common::{ - AlphaCurrency, BalanceOps, - // Currency, - CurrencyReserve, NetUid, SubnetInfo, TaoCurrency, + AlphaCurrency, + BalanceOps, + // Currency, + CurrencyReserve, + NetUid, + SubnetInfo, + TaoCurrency, }; use subtensor_swap_interface::Order; diff --git a/pallets/swap/src/pallet/impls.rs b/pallets/swap/src/pallet/impls.rs index 6ea373acbf..a3236b593e 100644 --- a/pallets/swap/src/pallet/impls.rs +++ b/pallets/swap/src/pallet/impls.rs @@ -5,9 +5,13 @@ use sp_arithmetic::helpers_128bit; use sp_runtime::{DispatchResult, traits::AccountIdConversion}; use substrate_fixed::types::U64F64; use subtensor_runtime_common::{ - AlphaCurrency, - // BalanceOps, - Currency, CurrencyReserve, NetUid, SubnetInfo, TaoCurrency, + AlphaCurrency, + // BalanceOps, + Currency, + CurrencyReserve, + NetUid, + SubnetInfo, + TaoCurrency, }; use subtensor_swap_interface::{ DefaultPriceLimit, Order as OrderT, SwapEngine, SwapHandler, SwapResult, @@ -15,9 +19,7 @@ use subtensor_swap_interface::{ use super::pallet::*; use super::swap_step::{BasicSwapStep, SwapStep}; -use crate::{ - position::PositionId, -}; +use crate::position::PositionId; #[derive(Debug, PartialEq)] pub struct UpdateLiquidityResult { @@ -43,8 +45,7 @@ impl Pallet { 1 => { let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); if !alpha_reserve.is_zero() { - let tao_reserve = - T::TaoReserve::reserve(netuid.into()); + let tao_reserve = T::TaoReserve::reserve(netuid.into()); let reserve_weight = SwapReserveWeight::::get(netuid); reserve_weight.calculate_price(tao_reserve.into(), alpha_reserve.into()) } else { @@ -62,7 +63,7 @@ impl Pallet { } // Initialize the pal-swap: - // Reserves are re-purposed, nothing to set, just query values for creation + // Reserves are re-purposed, nothing to set, just query values for creation // of protocol position let tao_reserve = T::TaoReserve::reserve(netuid.into()); let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); @@ -316,14 +317,12 @@ impl Pallet { // } pub(crate) fn min_price_inner() -> C { - u64::from(1_000_u64) - .into() + u64::from(1_000_u64).into() } pub(crate) fn max_price_inner() -> C { - u64::from(1_000_000_000_000_000_u64) - .into() - } + u64::from(1_000_000_000_000_000_u64).into() + } /// Returns the protocol account ID /// diff --git a/pallets/swap/src/pallet/mod.rs b/pallets/swap/src/pallet/mod.rs index 66f82ed4af..7e20c4bc95 100644 --- a/pallets/swap/src/pallet/mod.rs +++ b/pallets/swap/src/pallet/mod.rs @@ -9,8 +9,8 @@ use subtensor_runtime_common::{ }; use crate::{ - position::{Position, PositionId}, pallet::reserve_weights::ReserveWeight, + position::{Position, PositionId}, weights::WeightInfo, }; @@ -394,12 +394,8 @@ mod pallet { Error::::SubtokenDisabled ); - let (position_id, tao, alpha) = Self::do_add_liquidity( - netuid.into(), - &coldkey, - &hotkey, - liquidity, - )?; + let (position_id, tao, alpha) = + Self::do_add_liquidity(netuid.into(), &coldkey, &hotkey, liquidity)?; let alpha = AlphaCurrency::from(alpha); let tao = TaoCurrency::from(tao); diff --git a/pallets/swap/src/pallet/reserve_weights.rs b/pallets/swap/src/pallet/reserve_weights.rs index a32058e895..017808fc54 100644 --- a/pallets/swap/src/pallet/reserve_weights.rs +++ b/pallets/swap/src/pallet/reserve_weights.rs @@ -1,16 +1,16 @@ use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::pallet_prelude::*; -use safe_math::*; use safe_bigmath::*; +use safe_math::*; use sp_arithmetic::Perquintill; use sp_runtime::Saturating; use substrate_fixed::types::U64F64; use subtensor_macros::freeze_struct; -#[freeze_struct("a83c5690c57a33b")] +#[freeze_struct("8c6bbe52ef752203")] #[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct ReserveWeight { - quote: Perquintill + quote: Perquintill, } // Lower imit of weights is 0.01 @@ -26,7 +26,7 @@ pub enum ReserveWeightError { impl Default for ReserveWeight { fn default() -> Self { Self { - quote: Perquintill::from_rational(1u128, 2u128) + quote: Perquintill::from_rational(1u128, 2u128), } } } @@ -77,7 +77,6 @@ impl ReserveWeight { let perquintill_scale = SafeInt::from(ACCURACY as u128); let denominator = x_safe.clone() + delta_safe; let maybe_result_safe_int = if base_quote { - // println!("SafeInt::pow_ratio_scaled input: "); // println!(" x_safe = {:?}", x_safe); // println!(" denominator = {:?}", denominator); @@ -132,16 +131,18 @@ impl ReserveWeight { let w1_fixed = U64F64::saturating_from_num(self.get_quote_weight().deconstruct()); let x_fixed = U64F64::saturating_from_num(x); let y_fixed = U64F64::saturating_from_num(y); - w2_fixed.safe_div(w1_fixed).saturating_mul(y_fixed.safe_div(x_fixed)) + w2_fixed + .safe_div(w1_fixed) + .saturating_mul(y_fixed.safe_div(x_fixed)) } } -// cargo test --package pallet-subtensor-swap --lib -- pallet::reserve_weights::tests --nocapture +// cargo test --package pallet-subtensor-swap --lib -- pallet::reserve_weights::tests --nocapture #[cfg(test)] mod tests { - use approx::assert_abs_diff_eq; use crate::pallet::ReserveWeight; use crate::pallet::reserve_weights::*; + use approx::assert_abs_diff_eq; use sp_arithmetic::Perquintill; fn perquintill_to_f64(p: Perquintill) -> f64 { @@ -171,7 +172,10 @@ mod tests { ) .expect("perquintill integer result"); - assert_eq!(perquintill_result, SafeInt::from(649_519_052_838_328_985u128)); + assert_eq!( + perquintill_result, + SafeInt::from(649_519_052_838_328_985u128) + ); let readable = safe_bigmath::SafeDec::<18>::from_raw(perquintill_result); assert_eq!(format!("{}", readable), "0.649519052838328985"); } @@ -179,7 +183,6 @@ mod tests { /// Validate realistic values that can be calculated with f32 precision #[test] fn test_exp_bae_quote_happy_path() { - // Outer test cases: w_quote [ Perquintill::from_rational(500_000_000_000_u128, 1_000_000_000_000_u128), @@ -223,11 +226,7 @@ mod tests { 100_000_000_000_000_u64, 100_000_000_u64, ), - ( - 100_000_000_000_u64, - 100_000_000_000_000_u64, - 1_000_000_u64, - ), + (100_000_000_000_u64, 100_000_000_000_000_u64, 1_000_000_u64), ( 100_000_000_000_u64, 100_000_000_000_000_u64, @@ -243,36 +242,12 @@ mod tests { 100_000_000_000_000_u64, 1_000_000_000_000_u64, ), - ( - 1_000_u64, - 100_000_000_000_000_u64, - 1_000_000_000_000_u64, - ), - ( - 1_000_u64, - 100_000_000_000_000_u64, - 1_000_000_000_u64, - ), - ( - 1_000_u64, - 100_000_000_000_000_u64, - 1_000_000_u64, - ), - ( - 1_000_u64, - 100_000_000_000_000_u64, - 1_000_u64, - ), - ( - 1_000_u64, - 100_000_000_000_000_u64, - 100_000_000_000_000_u64, - ), - ( - 10_u64, - 100_000_000_000_000_u64, - 100_000_000_000_000_u64, - ), + (1_000_u64, 100_000_000_000_000_u64, 1_000_000_000_000_u64), + (1_000_u64, 100_000_000_000_000_u64, 1_000_000_000_u64), + (1_000_u64, 100_000_000_000_000_u64, 1_000_000_u64), + (1_000_u64, 100_000_000_000_000_u64, 1_000_u64), + (1_000_u64, 100_000_000_000_000_u64, 100_000_000_000_000_u64), + (10_u64, 100_000_000_000_000_u64, 100_000_000_000_000_u64), ] .into_iter() .for_each(|(y, x, dx)| { @@ -284,32 +259,27 @@ mod tests { let w1 = perquintill_to_f64(rw.get_base_weight()); let w2 = perquintill_to_f64(rw.get_quote_weight()); - let e_expected = (x as f64 / (x as f64 + dx as f64)).powf(w1/w2); + let e_expected = (x as f64 / (x as f64 + dx as f64)).powf(w1 / w2); let dy_expected = y as f64 * (1. - e_expected); let mut eps = dy_expected / 100000.; if eps > 0.1 { eps = 0.1; } - assert_abs_diff_eq!( - dy.to_num::(), - dy_expected, - epsilon = eps, - ); + assert_abs_diff_eq!(dy.to_num::(), dy_expected, epsilon = eps,); }) }); } /// This test exercises practical application edge cases of exp_base_quote - /// The practical formula where this function is used: + /// The practical formula where this function is used: /// ∆y = y * (exp_base_quote(x, ∆x) - 1) - /// - /// The test validates that two different sets of parameters produce (sensibly) + /// + /// The test validates that two different sets of parameters produce (sensibly) /// different results - /// + /// #[test] fn test_exp_bae_quote_dy_precision() { - // Test cases: y, x1, ∆x1, w_quote1, x2, ∆x2, w_quote2 // Realized dy1 should be greater than dy2 [ @@ -389,13 +359,10 @@ mod tests { }) } - - /// Test the broad range of w_quote values, usually should be ignored #[ignore] #[test] fn test_exp_quote_broad_range() { - let y = 1_000_000_000_000_u64; let x = 100_000_000_000_000_u64; let dx = 10_000_000_u64; @@ -419,7 +386,7 @@ mod tests { let progress = (num as f64 - start as f64) / (stop as f64 - start as f64); if progress - last_progress >= 0.0001 { - println!("progress = {:?}%", progress*100.); + println!("progress = {:?}%", progress * 100.); println!("dy = {:?}", dy); last_progress = progress; } @@ -428,51 +395,48 @@ mod tests { assert!(dy <= prev); prev = dy; } - } #[test] fn test_exp_quote_fuzzy() { - use rand::{Rng, SeedableRng}; use rand::rngs::StdRng; - - let mut rng = StdRng::seed_from_u64(42); - - let iterations = 1_000_000_000; - for i in 0..iterations { - - let x: u64 = rng.gen_range(1_000..21_000_000_000_000_000); - let y: u64 = rng.gen_range(1_000..21_000_000_000_000_000); - let dx: u64 = rng.gen_range(1_000..=x); - let w_numerator: u64 = rng.gen_range(ACCURACY/10..=ACCURACY/10*9); + use rand::{Rng, SeedableRng}; + use rayon::prelude::*; + use std::sync::Arc; + use std::sync::atomic::{AtomicUsize, Ordering}; + + const ITERATIONS: usize = 1_000_000_000; + let counter = Arc::new(AtomicUsize::new(0)); + + (0..ITERATIONS) + .into_par_iter() + .for_each(|i| { + // Each iteration gets its own deterministic RNG. + // Seed depends on i, so runs are reproducible. + let mut rng = StdRng::seed_from_u64(42 + i as u64); + + let x: u64 = rng.gen_range(1_000..21_000_000_000_000_000); // Alpha reserve + let y: u64 = rng.gen_range(1_000..21_000_000_000_000_000); // TAO reserve (allow huge prices) + let dx: u64 = rng.gen_range(1_000..=21_000_000_000_000_000); // Alhpa sold (allow huge values) + let w_numerator: u64 = rng.gen_range(ACCURACY / 10..=ACCURACY / 10 * 9); let w_quote = Perquintill::from_rational(w_numerator, ACCURACY); let rw = ReserveWeight::new(w_quote).unwrap(); let e = rw.exp_base_quote(x, dx); let one = U64F64::from_num(1); - // println!("e = {:?}", e); - // println!("1 - e = {:?}", one - e); - let dy = U64F64::from_num(y) * (one - e); - // println!("dy = {:?}", dy); - - if i % 1000 == 0 { - let progress = i as f64 / iterations as f64; - println!("progress = {:?}%", progress*100.); - println!("dy = {:?}", dy); - } // Calculate expected in f64 and approx-assert let w1 = perquintill_to_f64(rw.get_base_weight()); let w2 = perquintill_to_f64(rw.get_quote_weight()); - let e_expected = (x as f64 / (x as f64 + dx as f64)).powf(w1/w2); + let e_expected = (x as f64 / (x as f64 + dx as f64)).powf(w1 / w2); let dy_expected = y as f64 * (1. - e_expected); let actual = dy.to_num::(); - let mut eps = dy_expected / 1_000_000_000.; - if eps > 100.0 { - eps = 100.0; + let mut eps = dy_expected / 1_000_000.; + if eps > 1000.0 { + eps = 1000.0; } if eps < 1.0 { eps = 1.0; @@ -480,15 +444,19 @@ mod tests { assert!( (actual - dy_expected).abs() <= eps, - "dy mismatch:\n actual: {}\n expected: {}\n eps: {}\nParameters:\n x: {}\n y: {}\n dx: {}\n w_numerator: {}\n", - actual, - dy_expected, - eps, - x, y, dx, - w_numerator, + "dy mismatch:\n actual: {}\n expected: {}\n eps: {}\nParameters:\n x: {}\n y: {}\n dx: {}\n w_numerator: {}\n", + actual, dy_expected, eps, x, y, dx, w_numerator, ); - } - } + // Also assert that we didn't sell more than y + assert!(dy <= y, "dy = {},\ny = {}", dy, y,); -} \ No newline at end of file + // Print progress + let done = counter.fetch_add(1, Ordering::Relaxed) + 1; + if done % 1_000_000 == 0 { + let progress = done as f64 / ITERATIONS as f64 * 100.0; + println!("progress = {progress:.4}%"); + } + }); + } +} diff --git a/pallets/swap/src/pallet/swap_step.rs b/pallets/swap/src/pallet/swap_step.rs index c7abebbb29..83918b1dd3 100644 --- a/pallets/swap/src/pallet/swap_step.rs +++ b/pallets/swap/src/pallet/swap_step.rs @@ -49,8 +49,7 @@ where let requested_delta_in = amount_remaining.saturating_sub(fee); // Target and current prices - let target_price = - Self::price_target(requested_delta_in); + let target_price = Self::price_target(requested_delta_in); let current_price = Pallet::::current_price(netuid); Self { @@ -79,33 +78,20 @@ where // Calculate the stopping price: The price at which we either reach the limit price, // or exchange the full amount. - if Self::price_is_closer(&self.target_price, &self.limit_price) - { + if Self::price_is_closer(&self.target_price, &self.limit_price) { // Case 1. target_quantity is the lowest, execute in full self.final_price = self.target_price; self.delta_in = self.requested_delta_in; } else { // Case 2. lim_quantity is the lowest self.final_price = self.limit_price; - self.delta_in = Self::delta_in( - self.current_price, - self.limit_price, - ); + self.delta_in = Self::delta_in(self.current_price, self.limit_price); recalculate_fee = true; } - log::trace!( - "\tCurrent Price : {}", - self.current_price - ); - log::trace!( - "\tTarget Price : {}", - self.target_price - ); - log::trace!( - "\tLimit Price : {}", - self.limit_price - ); + log::trace!("\tCurrent Price : {}", self.current_price); + log::trace!("\tTarget Price : {}", self.target_price); + log::trace!("\tLimit Price : {}", self.limit_price); log::trace!("\tDelta In : {}", self.delta_in); // Because on step creation we calculate fee off the total amount, we might need to @@ -128,10 +114,7 @@ where /// Process a single step of a swap fn process_swap(&self) -> Result, Error> { // Hold the fees - Self::add_fees( - self.netuid, - self.fee, - ); + Self::add_fees(self.netuid, self.fee); // Convert amounts, actual swap happens here let delta_out = Self::convert_deltas(self.netuid, self.delta_in); @@ -148,16 +131,11 @@ where impl SwapStep for BasicSwapStep { - fn delta_in( - _price_curr: U64F64, - _price_target: U64F64, - ) -> TaoCurrency { + fn delta_in(_price_curr: U64F64, _price_target: U64F64) -> TaoCurrency { todo!(); } - fn price_target( - _delta_in: TaoCurrency, - ) -> U64F64 { + fn price_target(_delta_in: TaoCurrency) -> U64F64 { todo!(); } @@ -177,16 +155,11 @@ impl SwapStep impl SwapStep for BasicSwapStep { - fn delta_in( - _price_curr: U64F64, - _price_target: U64F64, - ) -> AlphaCurrency { + fn delta_in(_price_curr: U64F64, _price_target: U64F64) -> AlphaCurrency { todo!(); } - fn price_target( - _delta_in: AlphaCurrency, - ) -> U64F64 { + fn price_target(_delta_in: AlphaCurrency) -> U64F64 { todo!(); } @@ -210,15 +183,10 @@ where PaidOut: Currency, { /// Get the input amount needed to reach the target price - fn delta_in( - price_curr: U64F64, - price_target: U64F64, - ) -> PaidIn; + fn delta_in(price_curr: U64F64, price_target: U64F64) -> PaidIn; /// Get the target price based on the input amount - fn price_target( - delta_in: PaidIn, - ) -> U64F64; + fn price_target(delta_in: PaidIn) -> U64F64; /// Returns True if price1 is closer to the current price than price2 /// in terms of order direction. diff --git a/pallets/swap/src/pallet/tests.rs b/pallets/swap/src/pallet/tests.rs index 7d94b6083e..1b4876da88 100644 --- a/pallets/swap/src/pallet/tests.rs +++ b/pallets/swap/src/pallet/tests.rs @@ -8,7 +8,8 @@ use approx::assert_abs_diff_eq; use frame_support::{ //assert_err, - assert_noop, assert_ok + assert_noop, + assert_ok, }; // use sp_arithmetic::helpers_128bit; use sp_runtime::DispatchError; @@ -17,8 +18,8 @@ use subtensor_runtime_common::NetUid; use subtensor_swap_interface::Order as OrderT; use super::*; -use crate::pallet::swap_step::*; use crate::mock::*; +use crate::pallet::swap_step::*; #[allow(dead_code)] fn get_min_price() -> U64F64 { @@ -862,13 +863,13 @@ fn test_swap_single_position() { // macro_rules! perform_test { // ($order_t:ident, - // $price_low_offset:expr, - // $price_high_offset:expr, - // $position_liquidity:expr, - // $liquidity_fraction:expr, - // $limit_price:expr, - // $price_should_grow:expr - // ) => { + // $price_low_offset:expr, + // $price_high_offset:expr, + // $position_liquidity:expr, + // $liquidity_fraction:expr, + // $limit_price:expr, + // $price_should_grow:expr + // ) => { // new_test_ext().execute_with(|| { // let price_low_offset = $price_low_offset; // let price_high_offset = $price_high_offset; @@ -1250,8 +1251,7 @@ fn test_swap_precision_edge_case() { let limit_price: U64F64 = get_min_price(); // Swap - let swap_result = - Pallet::::do_swap(netuid, order, limit_price, false, true).unwrap(); + let swap_result = Pallet::::do_swap(netuid, order, limit_price, false, true).unwrap(); assert!(swap_result.amount_paid_out > TaoCurrency::ZERO); }); @@ -1268,45 +1268,15 @@ fn test_convert_deltas() { (1500, 1000, 0.5, 0.5, 1, 0, 2), (1500, 1000, 0.5, 0.5, 10000, 4444, 22500), (1500, 1000, 0.5, 0.5, 1000000, 444444, 2250000), - ( - 1500, 1000, 0.5, 0.5, - u64::MAX, - 2000000000000, - 3000000000000, - ), - ( - 1, 1000000, 0.5, 0.5, - 1, - 18406523739291577836, - 465, - ), + (1500, 1000, 0.5, 0.5, u64::MAX, 2000000000000, 3000000000000), + (1, 1000000, 0.5, 0.5, 1, 18406523739291577836, 465), (1, 1000000, 0.5, 0.5, 10000, u64::MAX, 465), - ( - 1, 1000000, 0.5, 0.5, - 1000000, - u64::MAX, - 465, - ), - ( - 1, 1000000, 0.5, 0.5, - u64::MAX, - u64::MAX, - 464, - ), - ( - 1000000, 1, 0.5, 0.5, - 1, - 0, - 18406523745214495085, - ), + (1, 1000000, 0.5, 0.5, 1000000, u64::MAX, 465), + (1, 1000000, 0.5, 0.5, u64::MAX, u64::MAX, 464), + (1000000, 1, 0.5, 0.5, 1, 0, 18406523745214495085), (1000000, 1, 0.5, 0.5, 10000, 0, u64::MAX), (1000000, 1, 0.5, 0.5, 1000000, 0, u64::MAX), - ( - 1000000, 1, 0.5, 0.5, - u64::MAX, - 2000000000000, - u64::MAX, - ), + (1000000, 1, 0.5, 0.5, u64::MAX, 2000000000000, u64::MAX), ] { { assert_abs_diff_eq!( From 8bdafa7518660458ea14986e050cc9a135e21398 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Wed, 17 Dec 2025 12:22:51 -0500 Subject: [PATCH 063/240] Cleanup full vs limited range testing --- pallets/swap/src/pallet/reserve_weights.rs | 24 +++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/pallets/swap/src/pallet/reserve_weights.rs b/pallets/swap/src/pallet/reserve_weights.rs index 017808fc54..2af61e557f 100644 --- a/pallets/swap/src/pallet/reserve_weights.rs +++ b/pallets/swap/src/pallet/reserve_weights.rs @@ -414,10 +414,24 @@ mod tests { // Each iteration gets its own deterministic RNG. // Seed depends on i, so runs are reproducible. let mut rng = StdRng::seed_from_u64(42 + i as u64); - - let x: u64 = rng.gen_range(1_000..21_000_000_000_000_000); // Alpha reserve - let y: u64 = rng.gen_range(1_000..21_000_000_000_000_000); // TAO reserve (allow huge prices) - let dx: u64 = rng.gen_range(1_000..=21_000_000_000_000_000); // Alhpa sold (allow huge values) + let max_supply: u64 = 21_000_000_000_000_000; + let full_range = true; + + let x: u64 = rng.gen_range(1_000..=max_supply); // Alpha reserve + let y: u64 = if full_range { + // TAO reserve (allow huge prices) + rng.gen_range(1_000..=max_supply) + } else { + // TAO reserve (limit prices with 0-1000) + rng.gen_range(1_000..x.saturating_mul(1000).min(max_supply)) + }; + let dx: u64 = if full_range { + // Alhpa sold (allow huge values) + rng.gen_range(1_000..=21_000_000_000_000_000) + } else { + // Alhpa sold (do not sell more than 100% of what's in alpha reserve) + rng.gen_range(1_000..=x) + }; let w_numerator: u64 = rng.gen_range(ACCURACY / 10..=ACCURACY / 10 * 9); let w_quote = Perquintill::from_rational(w_numerator, ACCURACY); @@ -448,7 +462,7 @@ mod tests { actual, dy_expected, eps, x, y, dx, w_numerator, ); - // Also assert that we didn't sell more than y + // Assert that we aren't giving out more than reserve y assert!(dy <= y, "dy = {},\ny = {}", dy, y,); // Print progress From 9dba44e10b32d98febb89902b4dcf73747e3d326 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Wed, 17 Dec 2025 18:35:07 -0800 Subject: [PATCH 064/240] fix typo --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 83e9e9693d..11f61b0a49 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -1046,7 +1046,7 @@ parameter_types! { 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: BlockNumber = prod_or_fast!(* 24 * 60 * 60 / 12, 50); // 5 days + pub const InitialColdkeySwapAnnouncementDelay: BlockNumber = prod_or_fast!(5 * 24 * 60 * 60 / 12, 50); // 5 days pub const InitialColdkeySwapReannouncementDelay: BlockNumber = prod_or_fast!(24 * 60 * 60 / 12, 10); // 1 day pub const InitialDissolveNetworkScheduleDuration: BlockNumber = 5 * 24 * 60 * 60 / 12; // 5 days pub const SubtensorInitialTaoWeight: u64 = 971_718_665_099_567_868; // 0.05267697438728329% tao weight. From b91ca213198d81a6a894b1e5fb6f232b9dfaff17 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Thu, 18 Dec 2025 14:41:48 -0500 Subject: [PATCH 065/240] Update bigmath, debug current_price, implement adjust_protocol_liquidity + tests --- Cargo.lock | 4 +- Cargo.toml | 2 +- pallets/swap/src/mock.rs | 38 ++ pallets/swap/src/pallet/impls.rs | 62 ++- pallets/swap/src/pallet/mod.rs | 3 + pallets/swap/src/pallet/reserve_weights.rs | 151 ++++---- pallets/swap/src/pallet/tests.rs | 424 ++++++++++++++------- 7 files changed, 446 insertions(+), 238 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b0a4e2c02f..5a0ad55884 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14585,8 +14585,8 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "safe-bigmath" -version = "0.1.0" -source = "git+https://github.com/sam0x17/safe-math?rev=6d81eb0#6d81eb059a7dde19b712988a399a616d8f225e88" +version = "0.2.0" +source = "git+https://github.com/sam0x17/safe-math?rev=4fb5b1c#4fb5b1c1e4231251f3b7bb397acc49c1665cee8d" dependencies = [ "crabtime", "num-bigint", diff --git a/Cargo.toml b/Cargo.toml index 384a572151..9c08cdf650 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,7 +61,7 @@ pallet-subtensor-swap-runtime-api = { path = "pallets/swap/runtime-api", default pallet-subtensor-swap-rpc = { path = "pallets/swap/rpc", default-features = false } procedural-fork = { path = "support/procedural-fork", default-features = false } safe-math = { path = "primitives/safe-math", default-features = false } -safe-bigmath = { package = "safe-bigmath", git = "https://github.com/sam0x17/safe-math", rev = "6d81eb0", default-features = false } +safe-bigmath = { package = "safe-bigmath", git = "https://github.com/sam0x17/safe-math", rev = "4fb5b1c", default-features = false } share-pool = { path = "primitives/share-pool", default-features = false } subtensor-macros = { path = "support/macros", default-features = false } subtensor-custom-rpc = { default-features = false, path = "pallets/subtensor/rpc" } diff --git a/pallets/swap/src/mock.rs b/pallets/swap/src/mock.rs index 931e1d0279..fed68195f8 100644 --- a/pallets/swap/src/mock.rs +++ b/pallets/swap/src/mock.rs @@ -14,6 +14,7 @@ use sp_runtime::{ BuildStorage, Vec, traits::{BlakeTwo256, IdentityLookup}, }; +use std::{cell::RefCell, collections::HashMap}; // use substrate_fixed::types::U64F64; use subtensor_runtime_common::{ AlphaCurrency, @@ -93,11 +94,34 @@ parameter_types! { pub const MinimumReserves: NonZeroU64 = NonZeroU64::new(1).unwrap(); } +thread_local! { + // maps netuid -> mocked tao reserve + static MOCK_TAO_RESERVES: RefCell> = + RefCell::new(HashMap::new()); + // maps netuid -> mocked alpha reserve + static MOCK_ALPHA_RESERVES: RefCell> = + RefCell::new(HashMap::new()); +} + #[derive(Clone)] pub struct TaoReserve; +impl TaoReserve { + pub fn set_mock_reserve(netuid: NetUid, value: TaoCurrency) { + MOCK_TAO_RESERVES.with(|m| { + m.borrow_mut().insert(netuid, value); + }); + } +} + impl CurrencyReserve for TaoReserve { fn reserve(netuid: NetUid) -> TaoCurrency { + // If test has set an override, use it + if let Some(val) = MOCK_TAO_RESERVES.with(|m| m.borrow().get(&netuid).cloned()) { + return val; + } + + // Otherwise, fall back to our defaults match netuid.into() { 123u16 => 10_000, WRAPPING_FEES_NETUID => 100_000_000_000, @@ -113,8 +137,22 @@ impl CurrencyReserve for TaoReserve { #[derive(Clone)] pub struct AlphaReserve; +impl AlphaReserve { + pub fn set_mock_reserve(netuid: NetUid, value: AlphaCurrency) { + MOCK_ALPHA_RESERVES.with(|m| { + m.borrow_mut().insert(netuid, value); + }); + } +} + impl CurrencyReserve for AlphaReserve { fn reserve(netuid: NetUid) -> AlphaCurrency { + // If test has set an override, use it + if let Some(val) = MOCK_ALPHA_RESERVES.with(|m| m.borrow().get(&netuid).cloned()) { + return val; + } + + // Otherwise, fall back to our defaults match netuid.into() { 123u16 => 10_000.into(), WRAPPING_FEES_NETUID => 400_000_000_000.into(), diff --git a/pallets/swap/src/pallet/impls.rs b/pallets/swap/src/pallet/impls.rs index a3236b593e..05707241a8 100644 --- a/pallets/swap/src/pallet/impls.rs +++ b/pallets/swap/src/pallet/impls.rs @@ -1,7 +1,10 @@ use frame_support::storage::{TransactionOutcome, transactional}; use frame_support::{ensure, pallet_prelude::DispatchError, traits::Get}; use safe_math::*; -use sp_arithmetic::helpers_128bit; +use sp_arithmetic::{ + //helpers_128bit, + Perquintill +}; use sp_runtime::{DispatchResult, traits::AccountIdConversion}; use substrate_fixed::types::U64F64; use subtensor_runtime_common::{ @@ -19,7 +22,11 @@ use subtensor_swap_interface::{ use super::pallet::*; use super::swap_step::{BasicSwapStep, SwapStep}; -use crate::position::PositionId; +use crate::{ + pallet::ReserveWeight, + pallet::reserve_weights::ReserveWeightError, + position::PositionId +}; #[derive(Debug, PartialEq)] pub struct UpdateLiquidityResult { @@ -47,7 +54,7 @@ impl Pallet { if !alpha_reserve.is_zero() { let tao_reserve = T::TaoReserve::reserve(netuid.into()); let reserve_weight = SwapReserveWeight::::get(netuid); - reserve_weight.calculate_price(tao_reserve.into(), alpha_reserve.into()) + reserve_weight.calculate_price(alpha_reserve.into(), tao_reserve.into()) } else { U64F64::saturating_from_num(0) } @@ -62,19 +69,26 @@ impl Pallet { return Ok(()); } + // Insert 0.5 into SwapReserveWeight + let reserve_weight = ReserveWeight::new(Perquintill::from_rational(1_u64, 2_u64)).map_err(|err| match err { + ReserveWeightError::InvalidValue => Error::::ReservesOutOfBalance, + })?; + SwapReserveWeight::::insert(netuid, reserve_weight); + + // TODO: Review when/if we have user liquidity // Initialize the pal-swap: // Reserves are re-purposed, nothing to set, just query values for creation // of protocol position - let tao_reserve = T::TaoReserve::reserve(netuid.into()); - let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); + // let tao_reserve = T::TaoReserve::reserve(netuid.into()); + // let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); // Set initial (protocol owned) liquidity and positions // Protocol liquidity makes one position // We are using the sp_arithmetic sqrt here, which works for u128 - let _liquidity = helpers_128bit::sqrt( - (tao_reserve.to_u64() as u128).saturating_mul(alpha_reserve.to_u64() as u128), - ) as u64; - let _protocol_account_id = Self::protocol_account_id(); + // let liquidity = helpers_128bit::sqrt( + // (tao_reserve.to_u64() as u128).saturating_mul(alpha_reserve.to_u64() as u128), + // ) as u64; + // let protocol_account_id = Self::protocol_account_id(); // let (position, _, _) = Self::add_liquidity_not_insert( // netuid, @@ -86,18 +100,34 @@ impl Pallet { // Positions::::insert(&(netuid, protocol_account_id, position.id), position); - // Ok(()) - - todo!() + Ok(()) } /// Adjusts protocol liquidity with new values of TAO and Alpha reserve pub(super) fn adjust_protocol_liquidity( - _netuid: NetUid, - _tao_delta: TaoCurrency, - _alpha_delta: AlphaCurrency, + netuid: NetUid, + tao_delta: TaoCurrency, + alpha_delta: AlphaCurrency, ) { - todo!(); + // Get reserves and price + let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); + let tao_reserve = T::TaoReserve::reserve(netuid.into()); + let reserve_weight = SwapReserveWeight::::get(netuid); + let price = reserve_weight.calculate_price(alpha_reserve.into(), tao_reserve.into()); + + // Calculate new to-be reserves (do not update here) + let new_tao_reserve = U64F64::saturating_from_num(u64::from(tao_reserve.saturating_add(tao_delta))); + let new_alpha_reserve = U64F64::saturating_from_num(u64::from(alpha_reserve.saturating_add(alpha_delta))); + + // Calculate new reserve weights + // w2_new = (y + ∆y) / ( p•(x + ∆x) + (y + ∆y) ) + let denominator = price.saturating_mul(new_alpha_reserve).saturating_add(new_tao_reserve).saturating_to_num::(); + let maybe_new_reserve_weight = ReserveWeight::new(Perquintill::from_rational(new_tao_reserve.saturating_to_num::(), denominator)).map_err(|err| match err { + ReserveWeightError::InvalidValue => Error::::ReservesOutOfBalance, + }); + if let Ok(new_reserve_weight) = maybe_new_reserve_weight { + SwapReserveWeight::::insert(netuid, new_reserve_weight); + } } /// Executes a token swap on the specified subnet. diff --git a/pallets/swap/src/pallet/mod.rs b/pallets/swap/src/pallet/mod.rs index 7e20c4bc95..1a988e60dc 100644 --- a/pallets/swap/src/pallet/mod.rs +++ b/pallets/swap/src/pallet/mod.rs @@ -292,6 +292,9 @@ mod pallet { /// The subnet does not have subtoken enabled SubtokenDisabled, + + /// Swap reserves are too imbalanced + ReservesOutOfBalance, } #[pallet::call] diff --git a/pallets/swap/src/pallet/reserve_weights.rs b/pallets/swap/src/pallet/reserve_weights.rs index 2af61e557f..c9fe9090f7 100644 --- a/pallets/swap/src/pallet/reserve_weights.rs +++ b/pallets/swap/src/pallet/reserve_weights.rs @@ -69,7 +69,7 @@ impl ReserveWeight { let w1: u128 = self.get_base_weight().deconstruct() as u128; let w2: u128 = self.get_quote_weight().deconstruct() as u128; - let precision = 128; + let precision = 256; let x_safe = SafeInt::from(x); let delta_safe = SafeInt::from(dx); let w1_safe = SafeInt::from(w1); @@ -77,14 +77,6 @@ impl ReserveWeight { let perquintill_scale = SafeInt::from(ACCURACY as u128); let denominator = x_safe.clone() + delta_safe; let maybe_result_safe_int = if base_quote { - // println!("SafeInt::pow_ratio_scaled input: "); - // println!(" x_safe = {:?}", x_safe); - // println!(" denominator = {:?}", denominator); - // println!(" w1_safe = {:?}", w1_safe); - // println!(" w2_safe = {:?}", w2_safe); - // println!(" precision = {:?}", precision); - // println!(" perquintill_scale = {:?}", perquintill_scale); - SafeInt::pow_ratio_scaled( &x_safe, &denominator, @@ -104,8 +96,6 @@ impl ReserveWeight { ) }; - // println!("SafeInt::pow_ratio_scaled output: {:?}", maybe_result_safe_int); - if let Some(result_safe_int) = maybe_result_safe_int { if let Some(result_u64) = result_safe_int.to_u64() { return U64F64::saturating_from_num(result_u64) @@ -127,12 +117,12 @@ impl ReserveWeight { /// Calculates price as (w1/w2) * (y/x) pub fn calculate_price(&self, x: u64, y: u64) -> U64F64 { - let w2_fixed = U64F64::saturating_from_num(self.get_base_weight().deconstruct()); - let w1_fixed = U64F64::saturating_from_num(self.get_quote_weight().deconstruct()); + let w2_fixed = U64F64::saturating_from_num(self.get_quote_weight().deconstruct()); + let w1_fixed = U64F64::saturating_from_num(self.get_base_weight().deconstruct()); let x_fixed = U64F64::saturating_from_num(x); let y_fixed = U64F64::saturating_from_num(y); - w2_fixed - .safe_div(w1_fixed) + w1_fixed + .safe_div(w2_fixed) .saturating_mul(y_fixed.safe_div(x_fixed)) } } @@ -152,7 +142,7 @@ mod tests { #[test] fn test_perquintill_power() { - const PRECISION: u32 = 256; + const PRECISION: u32 = 4096; const PERQUINTILL: u128 = ACCURACY as u128; let x = SafeInt::from(21_000_000_000_000_000u64); @@ -180,79 +170,81 @@ mod tests { assert_eq!(format!("{}", readable), "0.649519052838328985"); } - /// Validate realistic values that can be calculated with f32 precision + /// Validate realistic values that can be calculated with f64 precision #[test] fn test_exp_bae_quote_happy_path() { // Outer test cases: w_quote [ Perquintill::from_rational(500_000_000_000_u128, 1_000_000_000_000_u128), Perquintill::from_rational(500_000_000_001_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(500_000_000_100_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(500_000_001_000_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(500_000_010_000_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(500_000_100_000_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(500_001_000_000_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(500_010_000_000_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(500_100_000_000_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(501_000_000_000_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(510_000_000_000_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(100_000_000_000_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(100_000_000_001_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(200_000_000_000_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(300_000_000_000_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(400_000_000_000_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(600_000_000_000_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(700_000_000_000_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(800_000_000_000_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(899_999_999_999_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(900_000_000_000_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(500_000_000_100_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(500_000_001_000_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(500_000_010_000_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(500_000_100_000_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(500_001_000_000_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(500_010_000_000_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(500_100_000_000_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(501_000_000_000_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(510_000_000_000_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(100_000_000_000_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(100_000_000_001_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(200_000_000_000_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(300_000_000_000_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(400_000_000_000_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(600_000_000_000_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(700_000_000_000_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(800_000_000_000_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(899_999_999_999_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(900_000_000_000_u128, 1_000_000_000_000_u128), ] .into_iter() .for_each(|w_quote| { // Inner test cases: y, x, ∆x [ - ( - 1_000_000_000_000_u64, - 100_000_000_000_000_u64, - 100_000_000_u64, - ), - ( - 1_000_000_000_000_u64, - 100_000_000_000_000_u64, - 100_000_000_u64, - ), - ( - 100_000_000_000_u64, - 100_000_000_000_000_u64, - 100_000_000_u64, - ), - (100_000_000_000_u64, 100_000_000_000_000_u64, 1_000_000_u64), - ( - 100_000_000_000_u64, - 100_000_000_000_000_u64, - 1_000_000_000_000_u64, - ), - ( - 1_000_000_000_u64, - 100_000_000_000_000_u64, - 1_000_000_000_000_u64, - ), - ( - 1_000_000_u64, - 100_000_000_000_000_u64, - 1_000_000_000_000_u64, - ), - (1_000_u64, 100_000_000_000_000_u64, 1_000_000_000_000_u64), - (1_000_u64, 100_000_000_000_000_u64, 1_000_000_000_u64), - (1_000_u64, 100_000_000_000_000_u64, 1_000_000_u64), - (1_000_u64, 100_000_000_000_000_u64, 1_000_u64), - (1_000_u64, 100_000_000_000_000_u64, 100_000_000_000_000_u64), - (10_u64, 100_000_000_000_000_u64, 100_000_000_000_000_u64), + // ( + // 1_000_000_000_000_u64, + // 100_000_000_000_000_u64, + // 100_000_000_u64, + // ), + // ( + // 1_000_000_000_000_u64, + // 100_000_000_000_000_u64, + // 100_000_000_u64, + // ), + // ( + // 100_000_000_000_u64, + // 100_000_000_000_000_u64, + // 100_000_000_u64, + // ), + // (100_000_000_000_u64, 100_000_000_000_000_u64, 1_000_000_u64), + // ( + // 100_000_000_000_u64, + // 100_000_000_000_000_u64, + // 1_000_000_000_000_u64, + // ), + // ( + // 1_000_000_000_u64, + // 100_000_000_000_000_u64, + // 1_000_000_000_000_u64, + // ), + // ( + // 1_000_000_u64, + // 100_000_000_000_000_u64, + // 1_000_000_000_000_u64, + // ), + // (1_000_u64, 100_000_000_000_000_u64, 1_000_000_000_000_u64), + // (1_000_u64, 100_000_000_000_000_u64, 1_000_000_000_u64), + // (1_000_u64, 100_000_000_000_000_u64, 1_000_000_u64), + // (1_000_u64, 100_000_000_000_000_u64, 1_000_u64), + // (1_000_u64, 100_000_000_000_000_u64, 100_000_000_000_000_u64), + // (10_u64, 100_000_000_000_000_u64, 100_000_000_000_000_u64), + (1_000_000_000_u64, 4_000_000_000_u64, 1_000_000_000_000_u64), ] .into_iter() .for_each(|(y, x, dx)| { let rw = ReserveWeight::new(w_quote).unwrap(); let e = rw.exp_base_quote(x, dx); + println!("e = {:?}", e); let one = U64F64::from_num(1); let y_fixed = U64F64::from_num(y); let dy = y_fixed * (one - e); @@ -260,6 +252,7 @@ mod tests { let w1 = perquintill_to_f64(rw.get_base_weight()); let w2 = perquintill_to_f64(rw.get_quote_weight()); let e_expected = (x as f64 / (x as f64 + dx as f64)).powf(w1 / w2); + println!("e_expected = {:?}", e_expected); let dy_expected = y as f64 * (1. - e_expected); let mut eps = dy_expected / 100000.; @@ -334,23 +327,14 @@ mod tests { let rw1 = ReserveWeight::new(w_quote1).unwrap(); let rw2 = ReserveWeight::new(w_quote2).unwrap(); - println!("rw1 = {:?}", rw1); - println!("rw2 = {:?}", rw2); - let exp1 = rw1.exp_base_quote(x1, dx1); let exp2 = rw2.exp_base_quote(x2, dx2); - println!("exp1 = {:?}", exp1); - println!("exp2 = {:?}", exp2); - let one = U64F64::from_num(1); let y_fixed = U64F64::from_num(y); let dy1 = y_fixed * (one - exp1); let dy2 = y_fixed * (one - exp2); - println!("dy1 = {:?}", dy1); - println!("dy2 = {:?}", dy2); - assert!(dy1 > dy2); let zero = U64F64::from_num(0); @@ -397,6 +381,7 @@ mod tests { } } + // #[ignore] #[test] fn test_exp_quote_fuzzy() { use rand::rngs::StdRng; @@ -467,7 +452,7 @@ mod tests { // Print progress let done = counter.fetch_add(1, Ordering::Relaxed) + 1; - if done % 1_000_000 == 0 { + if done % 100_000_000 == 0 { let progress = done as f64 / ITERATIONS as f64 * 100.0; println!("progress = {progress:.4}%"); } diff --git a/pallets/swap/src/pallet/tests.rs b/pallets/swap/src/pallet/tests.rs index 1b4876da88..eb7408e2ee 100644 --- a/pallets/swap/src/pallet/tests.rs +++ b/pallets/swap/src/pallet/tests.rs @@ -11,7 +11,10 @@ use frame_support::{ assert_noop, assert_ok, }; -// use sp_arithmetic::helpers_128bit; +use sp_arithmetic::{ + //helpers_128bit, + Perquintill +}; use sp_runtime::DispatchError; use substrate_fixed::types::U64F64; use subtensor_runtime_common::NetUid; @@ -21,6 +24,9 @@ use super::*; use crate::mock::*; use crate::pallet::swap_step::*; +// Run all tests: +// cargo test --package pallet-subtensor-swap --lib -- pallet::tests --nocapture + #[allow(dead_code)] fn get_min_price() -> U64F64 { U64F64::saturating_from_num(Pallet::::min_price_inner::()) @@ -67,6 +73,192 @@ mod dispatchables { }); } + fn perquintill_to_f64(p: Perquintill) -> f64 { + let parts = p.deconstruct() as f64; + parts / 1_000_000_000_000_000_000_f64 + } + + /// cargo test --package pallet-subtensor-swap --lib -- pallet::tests::dispatchables::test_adjust_protocol_liquidity_happy --exact --nocapture + #[test] + fn test_adjust_protocol_liquidity_happy() { + // test case: tao_delta, alpha_delta + [ + (0_u64, 0_u64), + (0_u64, 1_u64), + (1_u64, 0_u64), + (1_u64, 1_u64), + (0_u64, 10_u64), + (10_u64, 0_u64), + (10_u64, 10_u64), + (0_u64, 100_u64), + (100_u64, 0_u64), + (100_u64, 100_u64), + (0_u64, 1_000_u64), + (1_000_u64, 0_u64), + (1_000_u64, 1_000_u64), + (1_000_000_u64, 0_u64), + (0_u64, 1_000_000_u64), + (1_000_000_u64, 1_000_000_u64), + (1_000_000_000_u64, 0_u64), + (0_u64, 1_000_000_000_u64), + (1_000_000_000_u64, 1_000_000_000_u64), + (1_000_000_000_000_u64, 0_u64), + (0_u64, 1_000_000_000_000_u64), + (1_000_000_000_000_u64, 1_000_000_000_000_u64), + (1_u64, 2_u64), + (2_u64, 1_u64), + (10_u64, 20_u64), + (20_u64, 10_u64), + (100_u64, 200_u64), + (200_u64, 100_u64), + (1_000_u64, 2_000_u64), + (2_000_u64, 1_000_u64), + (1_000_000_u64, 2_000_000_u64), + (2_000_000_u64, 1_000_000_u64), + (1_000_000_000_u64, 2_000_000_000_u64), + (2_000_000_000_u64, 1_000_000_000_u64), + (1_000_000_000_000_u64, 2_000_000_000_000_u64), + (2_000_000_000_000_u64, 1_000_000_000_000_u64), + (1_234_567_u64, 2_432_765_u64), + (1_234_567_u64, 2_432_765_890_u64), + ] + .into_iter() + .for_each(|(tao_delta, alpha_delta)| { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1); + + let tao_delta = TaoCurrency::from(tao_delta); + let alpha_delta = AlphaCurrency::from(alpha_delta); + + // Initialize reserves and price + let tao = TaoCurrency::from(1_000_000_000_000_u64); + let alpha = AlphaCurrency::from(4_000_000_000_000_u64); + TaoReserve::set_mock_reserve(netuid, tao); + AlphaReserve::set_mock_reserve(netuid, alpha); + let price_before = Swap::current_price(netuid); + + // Adjust reserves + Swap::adjust_protocol_liquidity(netuid, tao_delta, alpha_delta); + TaoReserve::set_mock_reserve(netuid, tao + tao_delta); + AlphaReserve::set_mock_reserve(netuid, alpha + alpha_delta); + + // Check that price didn't change + let price_after = Swap::current_price(netuid); + assert_abs_diff_eq!( + price_before.to_num::(), + price_after.to_num::(), + epsilon = price_before.to_num::() / 1_000_000_000_000. + ); + + // Check that reserve weight was properly updated + let new_tao = u64::from(tao + tao_delta) as f64; + let new_alpha = u64::from(alpha + alpha_delta) as f64; + let expected_quote_weight = new_tao / (new_alpha * price_before.to_num::() + new_tao); + let expected_quote_weight_delta = expected_quote_weight - 0.5; + let res_weights = SwapReserveWeight::::get(netuid); + let actual_quote_weight_delta = perquintill_to_f64(res_weights.get_quote_weight()) - 0.5; + let eps = expected_quote_weight / 1_000_000_000_000.; + assert_abs_diff_eq!( + expected_quote_weight_delta, + actual_quote_weight_delta, + epsilon = eps + ); + }); + }); + } + + /// Should work ok when initial alpha is zero + /// cargo test --package pallet-subtensor-swap --lib -- pallet::tests::dispatchables::test_adjust_protocol_liquidity_zero_alpha --exact --nocapture + #[test] + fn test_adjust_protocol_liquidity_zero_alpha() { + // test case: tao_delta, alpha_delta + [ + (0_u64, 0_u64), + (0_u64, 1_u64), + (1_u64, 0_u64), + (1_u64, 1_u64), + (0_u64, 10_u64), + (10_u64, 0_u64), + (10_u64, 10_u64), + (0_u64, 100_u64), + (100_u64, 0_u64), + (100_u64, 100_u64), + (0_u64, 1_000_u64), + (1_000_u64, 0_u64), + (1_000_u64, 1_000_u64), + (1_000_000_u64, 0_u64), + (0_u64, 1_000_000_u64), + (1_000_000_u64, 1_000_000_u64), + (1_000_000_000_u64, 0_u64), + (0_u64, 1_000_000_000_u64), + (1_000_000_000_u64, 1_000_000_000_u64), + (1_000_000_000_000_u64, 0_u64), + (0_u64, 1_000_000_000_000_u64), + (1_000_000_000_000_u64, 1_000_000_000_000_u64), + (1_u64, 2_u64), + (2_u64, 1_u64), + (10_u64, 20_u64), + (20_u64, 10_u64), + (100_u64, 200_u64), + (200_u64, 100_u64), + (1_000_u64, 2_000_u64), + (2_000_u64, 1_000_u64), + (1_000_000_u64, 2_000_000_u64), + (2_000_000_u64, 1_000_000_u64), + (1_000_000_000_u64, 2_000_000_000_u64), + (2_000_000_000_u64, 1_000_000_000_u64), + (1_000_000_000_000_u64, 2_000_000_000_000_u64), + (2_000_000_000_000_u64, 1_000_000_000_000_u64), + (1_234_567_u64, 2_432_765_u64), + (1_234_567_u64, 2_432_765_890_u64), + ] + .into_iter() + .for_each(|(tao_delta, alpha_delta)| { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1); + + let tao_delta = TaoCurrency::from(tao_delta); + let alpha_delta = AlphaCurrency::from(alpha_delta); + + // Initialize reserves and price + // broken state: Zero price because of zero alpha reserve + let tao = TaoCurrency::from(1_000_000_000_000_u64); + let alpha = AlphaCurrency::from(0_u64); + TaoReserve::set_mock_reserve(netuid, tao); + AlphaReserve::set_mock_reserve(netuid, alpha); + let price_before = Swap::current_price(netuid); + assert_eq!(price_before, U64F64::from_num(0)); + + // Adjust reserves + Swap::adjust_protocol_liquidity(netuid, tao_delta, alpha_delta); + TaoReserve::set_mock_reserve(netuid, tao + tao_delta); + AlphaReserve::set_mock_reserve(netuid, alpha + alpha_delta); + + // Check that price didn't change + let price_after = Swap::current_price(netuid); + assert_abs_diff_eq!( + price_before.to_num::(), + price_after.to_num::(), + epsilon = price_before.to_num::() / 1_000_000_000_000. + ); + + // Check that reserve weight was properly updated + let new_tao = u64::from(tao + tao_delta) as f64; + let new_alpha = u64::from(alpha + alpha_delta) as f64; + let expected_quote_weight = new_tao / (new_alpha * price_before.to_num::() + new_tao); + let expected_quote_weight_delta = expected_quote_weight - 0.5; + let res_weights = SwapReserveWeight::::get(netuid); + let actual_quote_weight_delta = perquintill_to_f64(res_weights.get_quote_weight()) - 0.5; + let eps = expected_quote_weight / 1_000_000_000_000.; + assert_abs_diff_eq!( + expected_quote_weight_delta, + actual_quote_weight_delta, + epsilon = eps + ); + }); + }); + } + // #[test] // fn test_toggle_user_liquidity() { // new_test_ext().execute_with(|| { @@ -126,6 +318,13 @@ fn test_swap_initialization() { epsilon = 0.000000001 ); + // Verify that swap reserve weight is initialized + let reserve_weight = SwapReserveWeight::::get(netuid); + assert_eq!( + reserve_weight.get_quote_weight(), + Perquintill::from_rational(1_u64, 2_u64), + ); + // // Calculate expected liquidity // let expected_liquidity = // helpers_128bit::sqrt((tao.to_u64() as u128).saturating_mul(alpha.to_u64() as u128)) @@ -699,144 +898,97 @@ fn test_modify_position_basic() { // cargo test --package pallet-subtensor-swap --lib -- pallet::impls::tests::test_swap_basic --exact --show-output #[test] fn test_swap_basic() { - todo!(); - - // new_test_ext().execute_with(|| { - // fn perform_test( - // netuid: NetUid, - // order: Order, - // limit_price: f64, - // output_amount: u64, - // price_should_grow: bool, - // ) where - // Order: OrderT, - // Order::PaidIn: GlobalFeeInfo, - // BasicSwapStep: - // SwapStep, - // { - // // Consumed liquidity ticks - // let tick_low = TickIndex::MIN; - // let tick_high = TickIndex::MAX; - // let liquidity = order.amount().to_u64(); - - // // Setup swap - // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - - // // Get tick infos before the swap - // let tick_low_info_before = Ticks::::get(netuid, tick_low).unwrap_or_default(); - // let tick_high_info_before = Ticks::::get(netuid, tick_high).unwrap_or_default(); - // let liquidity_before = CurrentLiquidity::::get(netuid); - - // // Get current price - // let current_price = Pallet::::current_price(netuid); - - // // Swap - // let sqrt_limit_price = SqrtPrice::from_num((limit_price).sqrt()); - // let swap_result = - // Pallet::::do_swap(netuid, order.clone(), sqrt_limit_price, false, false) - // .unwrap(); - // assert_abs_diff_eq!( - // swap_result.amount_paid_out.to_u64(), - // output_amount, - // epsilon = output_amount / 100 - // ); - - // assert_abs_diff_eq!( - // swap_result.paid_in_reserve_delta() as u64, - // liquidity, - // epsilon = liquidity / 10 - // ); - // assert_abs_diff_eq!( - // swap_result.paid_out_reserve_delta() as i64, - // -(output_amount as i64), - // epsilon = output_amount as i64 / 10 - // ); - - // // Check that low and high ticks' fees were updated properly, and liquidity values were not updated - // let tick_low_info = Ticks::::get(netuid, tick_low).unwrap(); - // let tick_high_info = Ticks::::get(netuid, tick_high).unwrap(); - // let expected_liquidity_net_low = tick_low_info_before.liquidity_net; - // let expected_liquidity_gross_low = tick_low_info_before.liquidity_gross; - // let expected_liquidity_net_high = tick_high_info_before.liquidity_net; - // let expected_liquidity_gross_high = tick_high_info_before.liquidity_gross; - // assert_eq!(tick_low_info.liquidity_net, expected_liquidity_net_low,); - // assert_eq!(tick_low_info.liquidity_gross, expected_liquidity_gross_low,); - // assert_eq!(tick_high_info.liquidity_net, expected_liquidity_net_high,); - // assert_eq!( - // tick_high_info.liquidity_gross, - // expected_liquidity_gross_high, - // ); - - // // Expected fee amount - // let fee_rate = FeeRate::::get(netuid) as f64 / u16::MAX as f64; - // let expected_fee = (liquidity as f64 * fee_rate) as u64; - - // // Global fees should be updated - // let actual_global_fee = (order.amount().global_fee(netuid).to_num::() - // * (liquidity_before as f64)) as u64; - - // assert!((swap_result.fee_paid.to_u64() as i64 - expected_fee as i64).abs() <= 1); - // assert!((actual_global_fee as i64 - expected_fee as i64).abs() <= 1); - - // // Tick fees should be updated - - // // Liquidity position should not be updated - // let protocol_id = Pallet::::protocol_account_id(); - // let positions = - // Positions::::iter_prefix_values((netuid, protocol_id)).collect::>(); - // let position = positions.first().unwrap(); - - // assert_eq!( - // position.liquidity, - // helpers_128bit::sqrt( - // TaoReserve::reserve(netuid.into()).to_u64() as u128 - // * AlphaReserve::reserve(netuid.into()).to_u64() as u128 - // ) as u64 - // ); - // assert_eq!(position.tick_low, tick_low); - // assert_eq!(position.tick_high, tick_high); - // assert_eq!(position.fees_alpha, 0); - // assert_eq!(position.fees_tao, 0); - - // // Current liquidity is not updated - // assert_eq!(CurrentLiquidity::::get(netuid), liquidity_before); + new_test_ext().execute_with(|| { + fn perform_test( + netuid: NetUid, + order: Order, + limit_price: f64, + output_amount: u64, + price_should_grow: bool, + ) where + Order: OrderT, + BasicSwapStep: + SwapStep, + { + let swap_amount = order.amount().to_u64(); + + // Setup swap + assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + + // Get current price + let current_price_before = Pallet::::current_price(netuid); + + // Swap + let limit_price_fixed = U64F64::from_num(limit_price); + let swap_result = + Pallet::::do_swap(netuid, order.clone(), limit_price_fixed, false, false) + .unwrap(); + assert_abs_diff_eq!( + swap_result.amount_paid_out.to_u64(), + output_amount, + epsilon = output_amount / 100 + ); - // // Assert that price movement is in correct direction - // let sqrt_current_price_after = AlphaSqrtPrice::::get(netuid); - // let current_price_after = Pallet::::current_price(netuid); - // assert_eq!(current_price_after >= current_price, price_should_grow); + assert_abs_diff_eq!( + swap_result.paid_in_reserve_delta() as u64, + swap_amount, + epsilon = swap_amount / 10 + ); + assert_abs_diff_eq!( + swap_result.paid_out_reserve_delta() as i64, + -(output_amount as i64), + epsilon = output_amount as i64 / 10 + ); - // // Assert that current tick is updated - // let current_tick = CurrentTick::::get(netuid); - // let expected_current_tick = - // TickIndex::from_sqrt_price_bounded(sqrt_current_price_after); - // assert_eq!(current_tick, expected_current_tick); - // } + // Expected fee amount + let fee_rate = FeeRate::::get(netuid) as f64 / u16::MAX as f64; + let expected_fee = (swap_amount as f64 * fee_rate) as u64; + + // Liquidity position should not be updated + // let protocol_id = Pallet::::protocol_account_id(); + // let positions = + // PositionsV2::::iter_prefix_values((netuid, protocol_id)).collect::>(); + // let position = positions.first().unwrap(); + + // assert_eq!( + // position.liquidity, + // helpers_128bit::sqrt( + // TaoReserve::reserve(netuid.into()).to_u64() as u128 + // * AlphaReserve::reserve(netuid.into()).to_u64() as u128 + // ) as u64 + // ); + // assert_eq!(position.fees_alpha, 0); + // assert_eq!(position.fees_tao, 0); + + // Assert that price movement is in correct direction + let current_price_after = Pallet::::current_price(netuid); + assert_eq!(current_price_after >= current_price_before, price_should_grow); + } - // // Current price is 0.25 - // // Test case is (order_type, liquidity, limit_price, output_amount) - // perform_test( - // 1.into(), - // GetAlphaForTao::with_amount(1_000), - // 1000.0, - // 3990, - // true, - // ); - // perform_test( - // 2.into(), - // GetTaoForAlpha::with_amount(1_000), - // 0.0001, - // 250, - // false, - // ); - // perform_test( - // 3.into(), - // GetAlphaForTao::with_amount(500_000_000), - // 1000.0, - // 2_000_000_000, - // true, - // ); - // }); + // Current price is 0.25 + // Test case is (order_type, liquidity, limit_price, output_amount) + perform_test( + 1.into(), + GetAlphaForTao::with_amount(1_000), + 1000.0, + 3990, + true, + ); + perform_test( + 2.into(), + GetTaoForAlpha::with_amount(1_000), + 0.0001, + 250, + false, + ); + perform_test( + 3.into(), + GetAlphaForTao::with_amount(500_000_000), + 1000.0, + 2_000_000_000, + true, + ); + }); } // In this test the swap starts and ends within one (large liquidity) position From f3daa7930d179408b57ce9f24c5d68e07ef7a0ff Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Thu, 18 Dec 2025 17:37:13 -0500 Subject: [PATCH 066/240] Implement delta_in and tests --- pallets/swap/src/pallet/impls.rs | 45 ++-- pallets/swap/src/pallet/reserve_weights.rs | 292 ++++++++++++++++----- pallets/swap/src/pallet/swap_step.rs | 52 +++- pallets/swap/src/pallet/tests.rs | 57 ++-- 4 files changed, 333 insertions(+), 113 deletions(-) diff --git a/pallets/swap/src/pallet/impls.rs b/pallets/swap/src/pallet/impls.rs index 05707241a8..33a1bb46bc 100644 --- a/pallets/swap/src/pallet/impls.rs +++ b/pallets/swap/src/pallet/impls.rs @@ -3,7 +3,7 @@ use frame_support::{ensure, pallet_prelude::DispatchError, traits::Get}; use safe_math::*; use sp_arithmetic::{ //helpers_128bit, - Perquintill + Perquintill, }; use sp_runtime::{DispatchResult, traits::AccountIdConversion}; use substrate_fixed::types::U64F64; @@ -23,9 +23,7 @@ use subtensor_swap_interface::{ use super::pallet::*; use super::swap_step::{BasicSwapStep, SwapStep}; use crate::{ - pallet::ReserveWeight, - pallet::reserve_weights::ReserveWeightError, - position::PositionId + pallet::ReserveWeight, pallet::reserve_weights::ReserveWeightError, position::PositionId, }; #[derive(Debug, PartialEq)] @@ -70,9 +68,11 @@ impl Pallet { } // Insert 0.5 into SwapReserveWeight - let reserve_weight = ReserveWeight::new(Perquintill::from_rational(1_u64, 2_u64)).map_err(|err| match err { - ReserveWeightError::InvalidValue => Error::::ReservesOutOfBalance, - })?; + let reserve_weight = ReserveWeight::new(Perquintill::from_rational(1_u64, 2_u64)).map_err( + |err| match err { + ReserveWeightError::InvalidValue => Error::::ReservesOutOfBalance, + }, + )?; SwapReserveWeight::::insert(netuid, reserve_weight); // TODO: Review when/if we have user liquidity @@ -116,17 +116,32 @@ impl Pallet { let price = reserve_weight.calculate_price(alpha_reserve.into(), tao_reserve.into()); // Calculate new to-be reserves (do not update here) - let new_tao_reserve = U64F64::saturating_from_num(u64::from(tao_reserve.saturating_add(tao_delta))); - let new_alpha_reserve = U64F64::saturating_from_num(u64::from(alpha_reserve.saturating_add(alpha_delta))); + let new_tao_reserve = + U64F64::saturating_from_num(u64::from(tao_reserve.saturating_add(tao_delta))); + let new_alpha_reserve = + U64F64::saturating_from_num(u64::from(alpha_reserve.saturating_add(alpha_delta))); // Calculate new reserve weights // w2_new = (y + ∆y) / ( p•(x + ∆x) + (y + ∆y) ) - let denominator = price.saturating_mul(new_alpha_reserve).saturating_add(new_tao_reserve).saturating_to_num::(); - let maybe_new_reserve_weight = ReserveWeight::new(Perquintill::from_rational(new_tao_reserve.saturating_to_num::(), denominator)).map_err(|err| match err { - ReserveWeightError::InvalidValue => Error::::ReservesOutOfBalance, - }); - if let Ok(new_reserve_weight) = maybe_new_reserve_weight { - SwapReserveWeight::::insert(netuid, new_reserve_weight); + let denominator = price + .saturating_mul(new_alpha_reserve) + .saturating_add(new_tao_reserve) + .saturating_to_num::(); + if denominator != 0 { + // Both TAO and Alpha are non-zero, normal case + let maybe_new_reserve_weight = ReserveWeight::new(Perquintill::from_rational( + new_tao_reserve.saturating_to_num::(), + denominator, + )) + .map_err(|err| match err { + ReserveWeightError::InvalidValue => Error::::ReservesOutOfBalance, + }); + if let Ok(new_reserve_weight) = maybe_new_reserve_weight { + SwapReserveWeight::::insert(netuid, new_reserve_weight); + } + } else { + // Either TAO or Alpha reserve were and/or remain zero => Initialize weights to 0.5 + SwapReserveWeight::::insert(netuid, DefaultReserveWeight::get()); } } diff --git a/pallets/swap/src/pallet/reserve_weights.rs b/pallets/swap/src/pallet/reserve_weights.rs index c9fe9090f7..5ea0668f54 100644 --- a/pallets/swap/src/pallet/reserve_weights.rs +++ b/pallets/swap/src/pallet/reserve_weights.rs @@ -125,6 +125,78 @@ impl ReserveWeight { .safe_div(w2_fixed) .saturating_mul(y_fixed.safe_div(x_fixed)) } + + /// Calculates quote delta needed to reach the price up when byuing + pub fn calculate_quote_delta_in( + &self, + current_price: U64F64, + target_price: U64F64, + reserve: u64, + ) -> u64 { + let base_numerator: u128 = target_price.to_bits(); + let base_denominator: u128 = current_price.to_bits(); + let w1_fixed: u128 = self.get_base_weight().deconstruct() as u128; + let scale: u128 = 10u128.pow(18); + + let maybe_exp_result = SafeInt::pow_ratio_scaled( + &SafeInt::from(base_numerator), + &SafeInt::from(base_denominator), + // &SafeInt::from(3u128), + // &SafeInt::from(4u128), + &SafeInt::from(w1_fixed), + &SafeInt::from(ACCURACY), + 160, + &SafeInt::from(scale), + ); + + if let Some(exp_result_safe_int) = maybe_exp_result { + if let Some(exp_result_u64) = exp_result_safe_int.to_u64() { + let reserve_fixed = U64F64::saturating_from_num(reserve); + let exp_result_fixed = U64F64::saturating_from_num(exp_result_u64); + let one = U64F64::saturating_from_num(1); + let scale_fixed = U64F64::saturating_from_num(scale); + return reserve_fixed + .saturating_mul(exp_result_fixed.safe_div(scale_fixed).saturating_sub(one)) + .saturating_to_num::(); + } + } + return 0u64; + } + + /// Calculates base delta needed to reach the price down when selling + pub fn calculate_base_delta_in( + &self, + current_price: U64F64, + target_price: U64F64, + reserve: u64, + ) -> u64 { + let base_numerator: u128 = current_price.to_bits(); + let base_denominator: u128 = target_price.to_bits(); + let w2_fixed: u128 = self.get_quote_weight().deconstruct() as u128; + let scale: u128 = 10u128.pow(18); + + let maybe_exp_result = SafeInt::pow_ratio_scaled( + &SafeInt::from(base_numerator), + &SafeInt::from(base_denominator), + &SafeInt::from(w2_fixed), + &SafeInt::from(ACCURACY), + 160, + &SafeInt::from(scale), + ); + + if let Some(exp_result_safe_int) = maybe_exp_result { + if let Some(exp_result_u64) = exp_result_safe_int.to_u64() { + let reserve_fixed = U64F64::saturating_from_num(reserve); + let exp_result_fixed = U64F64::saturating_from_num(exp_result_u64); + let one = U64F64::saturating_from_num(1); + let scale_fixed = U64F64::saturating_from_num(scale); + return reserve_fixed + .saturating_mul(exp_result_fixed.safe_div(scale_fixed).saturating_sub(one)) + .saturating_to_num::(); + } + } + return 0u64; + } } // cargo test --package pallet-subtensor-swap --lib -- pallet::reserve_weights::tests --nocapture @@ -172,79 +244,78 @@ mod tests { /// Validate realistic values that can be calculated with f64 precision #[test] - fn test_exp_bae_quote_happy_path() { + fn test_exp_base_quote_happy_path() { // Outer test cases: w_quote [ Perquintill::from_rational(500_000_000_000_u128, 1_000_000_000_000_u128), Perquintill::from_rational(500_000_000_001_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(500_000_000_100_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(500_000_001_000_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(500_000_010_000_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(500_000_100_000_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(500_001_000_000_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(500_010_000_000_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(500_100_000_000_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(501_000_000_000_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(510_000_000_000_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(100_000_000_000_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(100_000_000_001_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(200_000_000_000_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(300_000_000_000_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(400_000_000_000_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(600_000_000_000_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(700_000_000_000_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(800_000_000_000_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(899_999_999_999_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(900_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(500_000_000_100_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(500_000_001_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(500_000_010_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(500_000_100_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(500_001_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(500_010_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(500_100_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(501_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(510_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(100_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(100_000_000_001_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(200_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(300_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(400_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(600_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(700_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(800_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(899_999_999_999_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(900_000_000_000_u128, 1_000_000_000_000_u128), ] .into_iter() .for_each(|w_quote| { // Inner test cases: y, x, ∆x [ - // ( - // 1_000_000_000_000_u64, - // 100_000_000_000_000_u64, - // 100_000_000_u64, - // ), - // ( - // 1_000_000_000_000_u64, - // 100_000_000_000_000_u64, - // 100_000_000_u64, - // ), - // ( - // 100_000_000_000_u64, - // 100_000_000_000_000_u64, - // 100_000_000_u64, - // ), - // (100_000_000_000_u64, 100_000_000_000_000_u64, 1_000_000_u64), - // ( - // 100_000_000_000_u64, - // 100_000_000_000_000_u64, - // 1_000_000_000_000_u64, - // ), - // ( - // 1_000_000_000_u64, - // 100_000_000_000_000_u64, - // 1_000_000_000_000_u64, - // ), - // ( - // 1_000_000_u64, - // 100_000_000_000_000_u64, - // 1_000_000_000_000_u64, - // ), - // (1_000_u64, 100_000_000_000_000_u64, 1_000_000_000_000_u64), - // (1_000_u64, 100_000_000_000_000_u64, 1_000_000_000_u64), - // (1_000_u64, 100_000_000_000_000_u64, 1_000_000_u64), - // (1_000_u64, 100_000_000_000_000_u64, 1_000_u64), - // (1_000_u64, 100_000_000_000_000_u64, 100_000_000_000_000_u64), - // (10_u64, 100_000_000_000_000_u64, 100_000_000_000_000_u64), + ( + 1_000_000_000_000_u64, + 100_000_000_000_000_u64, + 100_000_000_u64, + ), + ( + 1_000_000_000_000_u64, + 100_000_000_000_000_u64, + 100_000_000_u64, + ), + ( + 100_000_000_000_u64, + 100_000_000_000_000_u64, + 100_000_000_u64, + ), + (100_000_000_000_u64, 100_000_000_000_000_u64, 1_000_000_u64), + ( + 100_000_000_000_u64, + 100_000_000_000_000_u64, + 1_000_000_000_000_u64, + ), + ( + 1_000_000_000_u64, + 100_000_000_000_000_u64, + 1_000_000_000_000_u64, + ), + ( + 1_000_000_u64, + 100_000_000_000_000_u64, + 1_000_000_000_000_u64, + ), + (1_000_u64, 100_000_000_000_000_u64, 1_000_000_000_000_u64), + (1_000_u64, 100_000_000_000_000_u64, 1_000_000_000_u64), + (1_000_u64, 100_000_000_000_000_u64, 1_000_000_u64), + (1_000_u64, 100_000_000_000_000_u64, 1_000_u64), + (1_000_u64, 100_000_000_000_000_u64, 100_000_000_000_000_u64), + (10_u64, 100_000_000_000_000_u64, 100_000_000_000_000_u64), (1_000_000_000_u64, 4_000_000_000_u64, 1_000_000_000_000_u64), ] .into_iter() .for_each(|(y, x, dx)| { let rw = ReserveWeight::new(w_quote).unwrap(); let e = rw.exp_base_quote(x, dx); - println!("e = {:?}", e); let one = U64F64::from_num(1); let y_fixed = U64F64::from_num(y); let dy = y_fixed * (one - e); @@ -252,7 +323,6 @@ mod tests { let w1 = perquintill_to_f64(rw.get_base_weight()); let w2 = perquintill_to_f64(rw.get_quote_weight()); let e_expected = (x as f64 / (x as f64 + dx as f64)).powf(w1 / w2); - println!("e_expected = {:?}", e_expected); let dy_expected = y as f64 * (1. - e_expected); let mut eps = dy_expected / 100000.; @@ -272,7 +342,7 @@ mod tests { /// different results /// #[test] - fn test_exp_bae_quote_dy_precision() { + fn test_exp_base_quote_dy_precision() { // Test cases: y, x1, ∆x1, w_quote1, x2, ∆x2, w_quote2 // Realized dy1 should be greater than dy2 [ @@ -381,7 +451,7 @@ mod tests { } } - // #[ignore] + #[ignore] #[test] fn test_exp_quote_fuzzy() { use rand::rngs::StdRng; @@ -458,4 +528,104 @@ mod tests { } }); } + + #[test] + fn test_calculate_quote_delta_in() { + let num = 250_000_000_000_u128; // w1 = 0.75 + let w_quote = Perquintill::from_rational(num, 1_000_000_000_000_u128); + let rw = ReserveWeight::new(w_quote).unwrap(); + + let current_price: U64F64 = U64F64::from_num(0.1); + let target_price: U64F64 = U64F64::from_num(0.2); + let tao_reserve: u64 = 1_000_000_000; + + let dy = rw.calculate_quote_delta_in(current_price, target_price, tao_reserve); + + // ∆y = y•[(p'/p)^w1 - 1] + let dy_expected = tao_reserve as f64 + * ((target_price.to_num::() / current_price.to_num::()).powf(0.75) - 1.0); + + assert_eq!(dy, dy_expected as u64,); + } + + #[test] + fn test_calculate_base_delta_in() { + let num = 250_000_000_000_u128; // w2 = 0.25 + let w_quote = Perquintill::from_rational(num, 1_000_000_000_000_u128); + let rw = ReserveWeight::new(w_quote).unwrap(); + + let current_price: U64F64 = U64F64::from_num(0.2); + let target_price: U64F64 = U64F64::from_num(0.1); + let alpha_reserve: u64 = 1_000_000_000; + + let dx = rw.calculate_base_delta_in(current_price, target_price, alpha_reserve); + + // ∆x = x•[(p/p')^w2 - 1] + let dx_expected = alpha_reserve as f64 + * ((current_price.to_num::() / target_price.to_num::()).powf(0.25) - 1.0); + + assert_eq!(dx, dx_expected as u64,); + } + + #[test] + fn test_calculate_quote_delta_in_impossible() { + let num = 250_000_000_000_u128; // w1 = 0.75 + let w_quote = Perquintill::from_rational(num, 1_000_000_000_000_u128); + let rw = ReserveWeight::new(w_quote).unwrap(); + + // Impossible price (lower) + let current_price: U64F64 = U64F64::from_num(0.1); + let target_price: U64F64 = U64F64::from_num(0.05); + let tao_reserve: u64 = 1_000_000_000; + + let dy = rw.calculate_quote_delta_in(current_price, target_price, tao_reserve); + let dy_expected = 0u64; + + assert_eq!(dy, dy_expected as u64,); + } + + #[test] + fn test_calculate_base_delta_in_impossible() { + let num = 250_000_000_000_u128; // w2 = 0.25 + let w_quote = Perquintill::from_rational(num, 1_000_000_000_000_u128); + let rw = ReserveWeight::new(w_quote).unwrap(); + + // Impossible price (higher) + let current_price: U64F64 = U64F64::from_num(0.1); + let target_price: U64F64 = U64F64::from_num(0.2); + let alpha_reserve: u64 = 1_000_000_000; + + let dx = rw.calculate_base_delta_in(current_price, target_price, alpha_reserve); + let dx_expected = 0u64; + + assert_eq!(dx, dx_expected as u64,); + } + + #[test] + fn test_calculate_delta_in_reverse_swap() { + let num = 500_000_000_000_u128; + let w_quote = Perquintill::from_rational(num, 1_000_000_000_000_u128); + let rw = ReserveWeight::new(w_quote).unwrap(); + + let current_price: U64F64 = U64F64::from_num(0.1); + let target_price: U64F64 = U64F64::from_num(0.2); + let tao_reserve: u64 = 1_000_000_000; + + // Here is the simple case of w1 = w2 = 0.5, so alpha = tao / price + let alpha_reserve: u64 = (tao_reserve as f64 / current_price.to_num::()) as u64; + + let dy = rw.calculate_quote_delta_in(current_price, target_price, tao_reserve); + let dx = alpha_reserve as f64 + * (1.0 + - (tao_reserve as f64 / (tao_reserve as f64 + dy as f64)) + .powf(num as f64 / (1_000_000_000_000 - num) as f64)); + + // Verify that buying with dy will in fact bring the price to target_price + let actual_price = rw.calculate_price(alpha_reserve - dx as u64, tao_reserve + dy); + assert_abs_diff_eq!( + actual_price.to_num::(), + target_price.to_num::(), + epsilon = target_price.to_num::() / 1_000_000_000. + ); + } } diff --git a/pallets/swap/src/pallet/swap_step.rs b/pallets/swap/src/pallet/swap_step.rs index 83918b1dd3..e4a0fbf01c 100644 --- a/pallets/swap/src/pallet/swap_step.rs +++ b/pallets/swap/src/pallet/swap_step.rs @@ -2,7 +2,7 @@ use core::marker::PhantomData; use safe_math::*; use substrate_fixed::types::U64F64; -use subtensor_runtime_common::{AlphaCurrency, Currency, NetUid, TaoCurrency}; +use subtensor_runtime_common::{AlphaCurrency, Currency, CurrencyReserve, NetUid, TaoCurrency}; use super::pallet::*; @@ -85,7 +85,7 @@ where } else { // Case 2. lim_quantity is the lowest self.final_price = self.limit_price; - self.delta_in = Self::delta_in(self.current_price, self.limit_price); + self.delta_in = Self::delta_in(self.netuid, self.current_price, self.limit_price); recalculate_fee = true; } @@ -131,8 +131,14 @@ where impl SwapStep for BasicSwapStep { - fn delta_in(_price_curr: U64F64, _price_target: U64F64) -> TaoCurrency { - todo!(); + fn delta_in(netuid: NetUid, price_curr: U64F64, price_target: U64F64) -> TaoCurrency { + let tao_reserve = T::TaoReserve::reserve(netuid.into()); + let reserve_weight = SwapReserveWeight::::get(netuid); + TaoCurrency::from(reserve_weight.calculate_quote_delta_in( + price_curr, + price_target, + tao_reserve.into(), + )) } fn price_target(_delta_in: TaoCurrency) -> U64F64 { @@ -147,16 +153,31 @@ impl SwapStep todo!(); } - fn convert_deltas(_netuid: NetUid, _delta_in: TaoCurrency) -> AlphaCurrency { - todo!(); + fn convert_deltas(netuid: NetUid, delta_in: TaoCurrency) -> AlphaCurrency { + let tao_reserve = T::TaoReserve::reserve(netuid.into()); + let reserve_weight = SwapReserveWeight::::get(netuid); + let e = reserve_weight.exp_quote_base(tao_reserve.into(), delta_in.into()); + let one = U64F64::from_num(1); + let tao_reserve_fixed = U64F64::from_num(tao_reserve); + AlphaCurrency::from( + tao_reserve_fixed + .saturating_mul(one.saturating_sub(e)) + .saturating_to_num::(), + ) } } impl SwapStep for BasicSwapStep { - fn delta_in(_price_curr: U64F64, _price_target: U64F64) -> AlphaCurrency { - todo!(); + fn delta_in(netuid: NetUid, price_curr: U64F64, price_target: U64F64) -> AlphaCurrency { + let alpha_reserve = T::TaoReserve::reserve(netuid); + let reserve_weight = SwapReserveWeight::::get(netuid); + AlphaCurrency::from(reserve_weight.calculate_base_delta_in( + price_curr, + price_target, + alpha_reserve.into(), + )) } fn price_target(_delta_in: AlphaCurrency) -> U64F64 { @@ -171,8 +192,17 @@ impl SwapStep todo!(); } - fn convert_deltas(_netuid: NetUid, _delta_in: AlphaCurrency) -> TaoCurrency { - todo!(); + fn convert_deltas(netuid: NetUid, delta_in: AlphaCurrency) -> TaoCurrency { + let reserve_weight = SwapReserveWeight::::get(netuid); + let alpha_reserve = T::TaoReserve::reserve(netuid); + let e = reserve_weight.exp_base_quote(alpha_reserve.into(), delta_in.into()); + let one = U64F64::from_num(1); + let alpha_reserve_fixed = U64F64::from_num(u64::from(alpha_reserve)); + TaoCurrency::from( + alpha_reserve_fixed + .saturating_mul(one.saturating_sub(e)) + .saturating_to_num::(), + ) } } @@ -183,7 +213,7 @@ where PaidOut: Currency, { /// Get the input amount needed to reach the target price - fn delta_in(price_curr: U64F64, price_target: U64F64) -> PaidIn; + fn delta_in(netuid: NetUid, price_curr: U64F64, price_target: U64F64) -> PaidIn; /// Get the target price based on the input amount fn price_target(delta_in: PaidIn) -> U64F64; diff --git a/pallets/swap/src/pallet/tests.rs b/pallets/swap/src/pallet/tests.rs index eb7408e2ee..19b1a9d839 100644 --- a/pallets/swap/src/pallet/tests.rs +++ b/pallets/swap/src/pallet/tests.rs @@ -13,7 +13,7 @@ use frame_support::{ }; use sp_arithmetic::{ //helpers_128bit, - Perquintill + Perquintill, }; use sp_runtime::DispatchError; use substrate_fixed::types::U64F64; @@ -153,10 +153,12 @@ mod dispatchables { // Check that reserve weight was properly updated let new_tao = u64::from(tao + tao_delta) as f64; let new_alpha = u64::from(alpha + alpha_delta) as f64; - let expected_quote_weight = new_tao / (new_alpha * price_before.to_num::() + new_tao); + let expected_quote_weight = + new_tao / (new_alpha * price_before.to_num::() + new_tao); let expected_quote_weight_delta = expected_quote_weight - 0.5; let res_weights = SwapReserveWeight::::get(netuid); - let actual_quote_weight_delta = perquintill_to_f64(res_weights.get_quote_weight()) - 0.5; + let actual_quote_weight_delta = + perquintill_to_f64(res_weights.get_quote_weight()) - 0.5; let eps = expected_quote_weight / 1_000_000_000_000.; assert_abs_diff_eq!( expected_quote_weight_delta, @@ -228,37 +230,37 @@ mod dispatchables { AlphaReserve::set_mock_reserve(netuid, alpha); let price_before = Swap::current_price(netuid); assert_eq!(price_before, U64F64::from_num(0)); - + let new_tao = u64::from(tao + tao_delta) as f64; + let new_alpha = u64::from(alpha + alpha_delta) as f64; + // Adjust reserves Swap::adjust_protocol_liquidity(netuid, tao_delta, alpha_delta); TaoReserve::set_mock_reserve(netuid, tao + tao_delta); AlphaReserve::set_mock_reserve(netuid, alpha + alpha_delta); + let res_weights = SwapReserveWeight::::get(netuid); + let actual_quote_weight = perquintill_to_f64(res_weights.get_quote_weight()); + // Check that price didn't change let price_after = Swap::current_price(netuid); - assert_abs_diff_eq!( - price_before.to_num::(), - price_after.to_num::(), - epsilon = price_before.to_num::() / 1_000_000_000_000. - ); - - // Check that reserve weight was properly updated - let new_tao = u64::from(tao + tao_delta) as f64; - let new_alpha = u64::from(alpha + alpha_delta) as f64; - let expected_quote_weight = new_tao / (new_alpha * price_before.to_num::() + new_tao); - let expected_quote_weight_delta = expected_quote_weight - 0.5; - let res_weights = SwapReserveWeight::::get(netuid); - let actual_quote_weight_delta = perquintill_to_f64(res_weights.get_quote_weight()) - 0.5; - let eps = expected_quote_weight / 1_000_000_000_000.; - assert_abs_diff_eq!( - expected_quote_weight_delta, - actual_quote_weight_delta, - epsilon = eps - ); + if new_alpha == 0. { + // If the pool state is still broken (∆x = 0), no change + assert_eq!(actual_quote_weight, 0.5); + assert_eq!(price_after, U64F64::from_num(0)); + } else { + // Price got fixed + let expected_price = new_tao / new_alpha; + assert_abs_diff_eq!( + expected_price, + price_after.to_num::(), + epsilon = price_before.to_num::() / 1_000_000_000_000. + ); + assert_eq!(actual_quote_weight, 0.5); + } }); }); } - + // #[test] // fn test_toggle_user_liquidity() { // new_test_ext().execute_with(|| { @@ -895,7 +897,7 @@ fn test_modify_position_basic() { // }); } -// cargo test --package pallet-subtensor-swap --lib -- pallet::impls::tests::test_swap_basic --exact --show-output +// cargo test --package pallet-subtensor-swap --lib -- pallet::tests::test_swap_basic --exact --nocapture #[test] fn test_swap_basic() { new_test_ext().execute_with(|| { @@ -962,7 +964,10 @@ fn test_swap_basic() { // Assert that price movement is in correct direction let current_price_after = Pallet::::current_price(netuid); - assert_eq!(current_price_after >= current_price_before, price_should_grow); + assert_eq!( + current_price_after >= current_price_before, + price_should_grow + ); } // Current price is 0.25 From e3d458551100d7c11e8932077d06ed8f939b0527 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Fri, 19 Dec 2025 11:11:45 +0100 Subject: [PATCH 067/240] update comment --- pallets/subtensor/src/macros/dispatches.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index e24c497acb..ba8f6819d8 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2354,8 +2354,8 @@ mod dispatches { /// This is required before the coldkey swap can be performed /// after the delay period. /// - /// It can be reannounced after a delay of `ColdkeySwapReannouncementDelay` between the - /// original announcement and the reannouncement. + /// It can be reannounced after a delay of `ColdkeySwapReannouncementDelay` following + /// the first valid execution block of the original announcement. /// /// The dispatch origin of this call must be the original coldkey that made the announcement. /// From be0e7dd330d6f0c1fb496867fa6adac65fde43da Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Fri, 19 Dec 2025 15:45:25 +0100 Subject: [PATCH 068/240] bump spec version --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index c4b3cc10e7..1e36415812 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -241,7 +241,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: 365, + spec_version: 366, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 0fe9842a2c55b86f3b8503076facf6974f0b5e34 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Fri, 19 Dec 2025 13:39:56 -0500 Subject: [PATCH 069/240] Update bigmath, add testcases --- Cargo.lock | 2 +- Cargo.toml | 2 +- pallets/swap/src/pallet/reserve_weights.rs | 8 +- pallets/swap/src/pallet/tests.rs | 106 +++++++++++++++++++++ 4 files changed, 115 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5a0ad55884..b7d0c4be4f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14586,7 +14586,7 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "safe-bigmath" version = "0.2.0" -source = "git+https://github.com/sam0x17/safe-math?rev=4fb5b1c#4fb5b1c1e4231251f3b7bb397acc49c1665cee8d" +source = "git+https://github.com/sam0x17/safe-math?rev=fd8e816#fd8e8167ff95761d9b6407e2feec47032c2311ca" dependencies = [ "crabtime", "num-bigint", diff --git a/Cargo.toml b/Cargo.toml index 9c08cdf650..4b93e54e23 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,7 +61,7 @@ pallet-subtensor-swap-runtime-api = { path = "pallets/swap/runtime-api", default pallet-subtensor-swap-rpc = { path = "pallets/swap/rpc", default-features = false } procedural-fork = { path = "support/procedural-fork", default-features = false } safe-math = { path = "primitives/safe-math", default-features = false } -safe-bigmath = { package = "safe-bigmath", git = "https://github.com/sam0x17/safe-math", rev = "4fb5b1c", default-features = false } +safe-bigmath = { package = "safe-bigmath", git = "https://github.com/sam0x17/safe-math", rev = "fd8e816", default-features = false } share-pool = { path = "primitives/share-pool", default-features = false } subtensor-macros = { path = "support/macros", default-features = false } subtensor-custom-rpc = { default-features = false, path = "pallets/subtensor/rpc" } diff --git a/pallets/swap/src/pallet/reserve_weights.rs b/pallets/swap/src/pallet/reserve_weights.rs index 5ea0668f54..935692b8d6 100644 --- a/pallets/swap/src/pallet/reserve_weights.rs +++ b/pallets/swap/src/pallet/reserve_weights.rs @@ -268,6 +268,7 @@ mod tests { Perquintill::from_rational(800_000_000_000_u128, 1_000_000_000_000_u128), Perquintill::from_rational(899_999_999_999_u128, 1_000_000_000_000_u128), Perquintill::from_rational(900_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(102_337_248_363_782_924_u128, 1_000_000_000_000_000_000_u128), ] .into_iter() .for_each(|w_quote| { @@ -310,7 +311,12 @@ mod tests { (1_000_u64, 100_000_000_000_000_u64, 1_000_u64), (1_000_u64, 100_000_000_000_000_u64, 100_000_000_000_000_u64), (10_u64, 100_000_000_000_000_u64, 100_000_000_000_000_u64), + + // Extreme values of ∆x for small x (1_000_000_000_u64, 4_000_000_000_u64, 1_000_000_000_000_u64), + (1_000_000_000_000_u64, 1_000_u64, 1_000_000_000_000_u64), + (5_628_038_062_729_553_u64, 400_775_553_u64, 14_446_633_907_665_582_u64), + (5_600_000_000_000_000_u64, 400_000_000_u64, 14_000_000_000_000_000_u64), ] .into_iter() .for_each(|(y, x, dx)| { @@ -329,7 +335,7 @@ mod tests { if eps > 0.1 { eps = 0.1; } - assert_abs_diff_eq!(dy.to_num::(), dy_expected, epsilon = eps,); + assert_abs_diff_eq!(dy.to_num::(), dy_expected, epsilon = eps); }) }); } diff --git a/pallets/swap/src/pallet/tests.rs b/pallets/swap/src/pallet/tests.rs index 19b1a9d839..d6dc712b3e 100644 --- a/pallets/swap/src/pallet/tests.rs +++ b/pallets/swap/src/pallet/tests.rs @@ -169,6 +169,110 @@ mod dispatchables { }); } + /// This test case verifies that small gradual injections (like emissions in every block) + /// in the worst case + /// - Do not cause price to change + /// - Result in the same weight change as one large injection + /// + /// cargo test --package pallet-subtensor-swap --lib -- pallet::tests::dispatchables::test_adjust_protocol_liquidity_small_deltas --exact --nocapture + #[test] + fn test_adjust_protocol_liquidity_small_deltas() { + // The number of times (blocks) over which gradual injections will be made + // const ITERATIONS: u64 = 1_000_000; + const ITERATIONS: u64 = 10; + const PRICE_PRECISION: f64 = 0.000_000_001; + const WEIGHT_PRECISION: f64 = 0.000_000_000_000_000_001; + + let initial_tao_reserve = TaoCurrency::from(1_000_000_000_000_000_u64); + let initial_alpha_reserve = AlphaCurrency::from(10_000_000_000_000_000_u64); + + // test case: tao_delta, alpha_delta + [ + (0_u64, 0_u64), + // (0_u64, 1_u64), + // (1_u64, 0_u64), + // (1_u64, 1_u64), + // (0_u64, 10_u64), + // (10_u64, 0_u64), + // (10_u64, 10_u64), + // (0_u64, 100_u64), + // (100_u64, 0_u64), + // (100_u64, 100_u64), + // (0_u64, 987_u64), + // (987_u64, 0_u64), + // (876_u64, 987_u64), + // (0_u64, 1_000_u64), + // (1_000_u64, 0_u64), + // (1_000_u64, 1_000_u64), + // (0_u64, 1_234_u64), + // (1_234_u64, 0_u64), + // (1_234_u64, 4_321_u64), + ] + .into_iter() + .for_each(|(tao_delta, alpha_delta)| { + new_test_ext().execute_with(|| { + let netuid1 = NetUid::from(1); + + let tao_delta = TaoCurrency::from(tao_delta); + let alpha_delta = AlphaCurrency::from(alpha_delta); + + // Initialize realistically large reserves + let mut tao = initial_tao_reserve; + let mut alpha = initial_alpha_reserve; + TaoReserve::set_mock_reserve(netuid1, tao); + AlphaReserve::set_mock_reserve(netuid1, alpha); + let price_before = Swap::current_price(netuid1); + + // Adjust reserves gradually + for _ in 0..ITERATIONS { + Swap::adjust_protocol_liquidity(netuid1, tao_delta, alpha_delta); + tao += tao_delta; + alpha += alpha_delta; + TaoReserve::set_mock_reserve(netuid1, tao); + AlphaReserve::set_mock_reserve(netuid1, alpha); + } + + // Check that price didn't change + let price_after = Swap::current_price(netuid1); + assert_abs_diff_eq!( + price_before.to_num::(), + price_after.to_num::(), + epsilon = PRICE_PRECISION + ); + + ///////////////////////// + + // Now do one-time big injection with another netuid and compare weights + + let netuid2 = NetUid::from(2); + + // Initialize same large reserves + TaoReserve::set_mock_reserve(netuid2, initial_tao_reserve); + AlphaReserve::set_mock_reserve(netuid2, initial_alpha_reserve); + + // Adjust reserves by one large amount at once + let tao_delta_once = TaoCurrency::from(ITERATIONS * u64::from(tao_delta)); + let alpha_delta_once = AlphaCurrency::from(ITERATIONS * u64::from(alpha_delta)); + Swap::adjust_protocol_liquidity(netuid2, tao_delta_once, alpha_delta_once); + TaoReserve::set_mock_reserve(netuid2, initial_tao_reserve + tao_delta_once); + AlphaReserve::set_mock_reserve(netuid2, initial_alpha_reserve + alpha_delta_once); + + // Compare reserve weights for netuid 1 and 2 + let res_weights1 = SwapReserveWeight::::get(netuid1); + let res_weights2 = SwapReserveWeight::::get(netuid2); + let actual_quote_weight1 = + perquintill_to_f64(res_weights1.get_quote_weight()); + let actual_quote_weight2 = + perquintill_to_f64(res_weights2.get_quote_weight()); + assert_abs_diff_eq!( + actual_quote_weight1, + actual_quote_weight2, + epsilon = WEIGHT_PRECISION + ); + }); + }); + } + /// Should work ok when initial alpha is zero /// cargo test --package pallet-subtensor-swap --lib -- pallet::tests::dispatchables::test_adjust_protocol_liquidity_zero_alpha --exact --nocapture #[test] @@ -257,6 +361,8 @@ mod dispatchables { ); assert_eq!(actual_quote_weight, 0.5); } + + }); }); } From 9292044ff324279e9d7f4cd04dba9a40092e8721 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 22 Dec 2025 15:37:48 -0500 Subject: [PATCH 070/240] Basic swap works --- pallets/swap/src/pallet/impls.rs | 52 +++---- pallets/swap/src/pallet/mod.rs | 10 +- pallets/swap/src/pallet/reserve_weights.rs | 75 ++++++++- pallets/swap/src/pallet/swap_step.rs | 48 ++++-- pallets/swap/src/pallet/tests.rs | 168 ++++++++++++++------- 5 files changed, 244 insertions(+), 109 deletions(-) diff --git a/pallets/swap/src/pallet/impls.rs b/pallets/swap/src/pallet/impls.rs index f54a3b136b..c0819ca756 100644 --- a/pallets/swap/src/pallet/impls.rs +++ b/pallets/swap/src/pallet/impls.rs @@ -109,39 +109,31 @@ impl Pallet { tao_delta: TaoCurrency, alpha_delta: AlphaCurrency, ) { - // Get reserves and price + // Get reserves let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); let tao_reserve = T::TaoReserve::reserve(netuid.into()); - let reserve_weight = SwapReserveWeight::::get(netuid); - let price = reserve_weight.calculate_price(alpha_reserve.into(), tao_reserve.into()); - - // Calculate new to-be reserves (do not update here) - let new_tao_reserve = - U64F64::saturating_from_num(u64::from(tao_reserve.saturating_add(tao_delta))); - let new_alpha_reserve = - U64F64::saturating_from_num(u64::from(alpha_reserve.saturating_add(alpha_delta))); - - // Calculate new reserve weights - // w2_new = (y + ∆y) / ( p•(x + ∆x) + (y + ∆y) ) - let denominator = price - .saturating_mul(new_alpha_reserve) - .saturating_add(new_tao_reserve) - .saturating_to_num::(); - if denominator != 0 { - // Both TAO and Alpha are non-zero, normal case - let maybe_new_reserve_weight = ReserveWeight::new(Perquintill::from_rational( - new_tao_reserve.saturating_to_num::(), - denominator, - )) - .map_err(|err| match err { - ReserveWeightError::InvalidValue => Error::::ReservesOutOfBalance, - }); - if let Ok(new_reserve_weight) = maybe_new_reserve_weight { - SwapReserveWeight::::insert(netuid, new_reserve_weight); - } + let mut reserve_weight = SwapReserveWeight::::get(netuid); + + // Update weights and log errors if they go out of range + if reserve_weight + .update_weights_for_added_liquidity( + u64::from(tao_reserve), + u64::from(alpha_reserve), + u64::from(tao_delta), + u64::from(alpha_delta), + ) + .is_err() + { + log::error!( + "Reserves are out of range for emission: netuid = {}, tao = {}, alpha = {}, tao_delta = {}, alpha_delta = {}", + netuid, + tao_reserve, + alpha_reserve, + tao_delta, + alpha_delta + ); } else { - // Either TAO or Alpha reserve were and/or remain zero => Initialize weights to 0.5 - SwapReserveWeight::::insert(netuid, DefaultReserveWeight::get()); + SwapReserveWeight::::insert(netuid, reserve_weight); } } diff --git a/pallets/swap/src/pallet/mod.rs b/pallets/swap/src/pallet/mod.rs index 1a988e60dc..7c6b484f79 100644 --- a/pallets/swap/src/pallet/mod.rs +++ b/pallets/swap/src/pallet/mod.rs @@ -97,7 +97,7 @@ mod pallet { // /// Storage to determine whether swap V3 was initialized for a specific subnet. // #[pallet::storage] - // pub type PalSwapInitialized = StorageMap<_, Twox64Concat, NetUid, bool, ValueQuery>; + // pub type SwapV3Initialized = StorageMap<_, Twox64Concat, NetUid, bool, ValueQuery>; // /// Storage for the square root price of Alpha token for each subnet. // #[pallet::storage] @@ -174,6 +174,14 @@ mod pallet { #[pallet::storage] pub type PalSwapInitialized = StorageMap<_, Twox64Concat, NetUid, bool, ValueQuery>; + /// Total fees in TAO per subnet due to be paid to users / protocol + #[pallet::storage] + pub type FeesTao = StorageMap<_, Twox64Concat, NetUid, TaoCurrency, ValueQuery>; + + /// Total fees in Alpha per subnet due to be paid to users / protocol + #[pallet::storage] + pub type FeesAlpha = StorageMap<_, Twox64Concat, NetUid, AlphaCurrency, ValueQuery>; + #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { diff --git a/pallets/swap/src/pallet/reserve_weights.rs b/pallets/swap/src/pallet/reserve_weights.rs index 935692b8d6..f42f5252da 100644 --- a/pallets/swap/src/pallet/reserve_weights.rs +++ b/pallets/swap/src/pallet/reserve_weights.rs @@ -3,6 +3,7 @@ use frame_support::pallet_prelude::*; use safe_bigmath::*; use safe_math::*; use sp_arithmetic::Perquintill; +use sp_core::U256; use sp_runtime::Saturating; use substrate_fixed::types::U64F64; use subtensor_macros::freeze_struct; @@ -68,14 +69,14 @@ impl ReserveWeight { } let w1: u128 = self.get_base_weight().deconstruct() as u128; let w2: u128 = self.get_quote_weight().deconstruct() as u128; + let x_plus_dx = x.saturating_add(dx); let precision = 256; let x_safe = SafeInt::from(x); - let delta_safe = SafeInt::from(dx); let w1_safe = SafeInt::from(w1); let w2_safe = SafeInt::from(w2); let perquintill_scale = SafeInt::from(ACCURACY as u128); - let denominator = x_safe.clone() + delta_safe; + let denominator = SafeInt::from(x_plus_dx); let maybe_result_safe_int = if base_quote { SafeInt::pow_ratio_scaled( &x_safe, @@ -126,6 +127,58 @@ impl ReserveWeight { .saturating_mul(y_fixed.safe_div(x_fixed)) } + /// Multiply a u128 value by a Perquintill with u128 result rounded to the + /// nearest integer + fn mul_perquintill_round(p: Perquintill, value: u128) -> u128 { + let parts = p.deconstruct() as u128; + let acc = ACCURACY as u128; + + let num = U256::from(value) * U256::from(parts); + let den = U256::from(acc); + + // add 0.5 ulp before integer division → round-to-nearest + let res = (num + den / U256::from(2u8)) / den; + res.min(U256::from(u128::MAX)).as_u128() + } + + pub fn update_weights_for_added_liquidity( + &mut self, + tao_reserve: u64, + alpha_reserve: u64, + tao_delta: u64, + alpha_delta: u64, + ) -> Result<(), ReserveWeightError> { + // Calculate new to-be reserves (do not update here) + let tao_reserve_u128 = u64::from(tao_reserve) as u128; + let alpha_reserve_u128 = u64::from(alpha_reserve) as u128; + let tao_delta_u128 = u64::from(tao_delta) as u128; + let alpha_delta_u128 = u64::from(alpha_delta) as u128; + let new_tao_reserve_u128 = tao_reserve_u128.saturating_add(tao_delta_u128); + let new_alpha_reserve_u128 = alpha_reserve_u128.saturating_add(alpha_delta_u128); + + // Calculate new weights + let quantity_1: u128 = Self::mul_perquintill_round( + self.get_base_weight(), + tao_reserve_u128.saturating_mul(new_alpha_reserve_u128), + ); + let quantity_2: u128 = Self::mul_perquintill_round( + self.get_quote_weight(), + alpha_reserve_u128.saturating_mul(new_tao_reserve_u128), + ); + let q_sum = quantity_1.saturating_add(quantity_2); + + // Calculate new reserve weights + let new_reserve_weight = if q_sum != 0 { + // Both TAO and Alpha are non-zero, normal case + Perquintill::from_rational(quantity_2, q_sum) + } else { + // Either TAO or Alpha reserve were and/or remain zero => Initialize weights to 0.5 + Perquintill::from_rational(1u128, 2u128) + }; + + self.set_quote_weight(new_reserve_weight) + } + /// Calculates quote delta needed to reach the price up when byuing pub fn calculate_quote_delta_in( &self, @@ -268,7 +321,10 @@ mod tests { Perquintill::from_rational(800_000_000_000_u128, 1_000_000_000_000_u128), Perquintill::from_rational(899_999_999_999_u128, 1_000_000_000_000_u128), Perquintill::from_rational(900_000_000_000_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(102_337_248_363_782_924_u128, 1_000_000_000_000_000_000_u128), + Perquintill::from_rational( + 102_337_248_363_782_924_u128, + 1_000_000_000_000_000_000_u128, + ), ] .into_iter() .for_each(|w_quote| { @@ -311,12 +367,19 @@ mod tests { (1_000_u64, 100_000_000_000_000_u64, 1_000_u64), (1_000_u64, 100_000_000_000_000_u64, 100_000_000_000_000_u64), (10_u64, 100_000_000_000_000_u64, 100_000_000_000_000_u64), - // Extreme values of ∆x for small x (1_000_000_000_u64, 4_000_000_000_u64, 1_000_000_000_000_u64), (1_000_000_000_000_u64, 1_000_u64, 1_000_000_000_000_u64), - (5_628_038_062_729_553_u64, 400_775_553_u64, 14_446_633_907_665_582_u64), - (5_600_000_000_000_000_u64, 400_000_000_u64, 14_000_000_000_000_000_u64), + ( + 5_628_038_062_729_553_u64, + 400_775_553_u64, + 14_446_633_907_665_582_u64, + ), + ( + 5_600_000_000_000_000_u64, + 400_000_000_u64, + 14_000_000_000_000_000_u64, + ), ] .into_iter() .for_each(|(y, x, dx)| { diff --git a/pallets/swap/src/pallet/swap_step.rs b/pallets/swap/src/pallet/swap_step.rs index e4a0fbf01c..a9eede62b7 100644 --- a/pallets/swap/src/pallet/swap_step.rs +++ b/pallets/swap/src/pallet/swap_step.rs @@ -49,7 +49,7 @@ where let requested_delta_in = amount_remaining.saturating_sub(fee); // Target and current prices - let target_price = Self::price_target(requested_delta_in); + let target_price = Self::price_target(netuid, requested_delta_in); let current_price = Pallet::::current_price(netuid); Self { @@ -141,26 +141,35 @@ impl SwapStep )) } - fn price_target(_delta_in: TaoCurrency) -> U64F64 { - todo!(); + fn price_target(netuid: NetUid, delta_in: TaoCurrency) -> U64F64 { + let tao_reserve = T::TaoReserve::reserve(netuid.into()); + let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); + let reserve_weight = SwapReserveWeight::::get(netuid); + let dy = delta_in; + let dx = Self::convert_deltas(netuid, dy); + reserve_weight.calculate_price( + u64::from(alpha_reserve.saturating_sub(dx)), + u64::from(tao_reserve.saturating_add(dy)), + ) } fn price_is_closer(price1: &U64F64, price2: &U64F64) -> bool { price1 <= price2 } - fn add_fees(_netuid: NetUid, _fee: TaoCurrency) { - todo!(); + fn add_fees(netuid: NetUid, fee: TaoCurrency) { + FeesTao::::mutate(netuid, |total| *total = total.saturating_add(fee)) } fn convert_deltas(netuid: NetUid, delta_in: TaoCurrency) -> AlphaCurrency { + let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); let tao_reserve = T::TaoReserve::reserve(netuid.into()); let reserve_weight = SwapReserveWeight::::get(netuid); let e = reserve_weight.exp_quote_base(tao_reserve.into(), delta_in.into()); let one = U64F64::from_num(1); - let tao_reserve_fixed = U64F64::from_num(tao_reserve); + let alpha_reserve_fixed = U64F64::from_num(alpha_reserve); AlphaCurrency::from( - tao_reserve_fixed + alpha_reserve_fixed .saturating_mul(one.saturating_sub(e)) .saturating_to_num::(), ) @@ -180,26 +189,35 @@ impl SwapStep )) } - fn price_target(_delta_in: AlphaCurrency) -> U64F64 { - todo!(); + fn price_target(netuid: NetUid, delta_in: AlphaCurrency) -> U64F64 { + let tao_reserve = T::TaoReserve::reserve(netuid.into()); + let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); + let reserve_weight = SwapReserveWeight::::get(netuid); + let dx = delta_in; + let dy = Self::convert_deltas(netuid, dx); + reserve_weight.calculate_price( + u64::from(alpha_reserve.saturating_add(dx)), + u64::from(tao_reserve.saturating_sub(dy)), + ) } fn price_is_closer(price1: &U64F64, price2: &U64F64) -> bool { price1 >= price2 } - fn add_fees(_netuid: NetUid, _fee: AlphaCurrency) { - todo!(); + fn add_fees(netuid: NetUid, fee: AlphaCurrency) { + FeesAlpha::::mutate(netuid, |total| *total = total.saturating_add(fee)) } fn convert_deltas(netuid: NetUid, delta_in: AlphaCurrency) -> TaoCurrency { + let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); + let tao_reserve = T::TaoReserve::reserve(netuid.into()); let reserve_weight = SwapReserveWeight::::get(netuid); - let alpha_reserve = T::TaoReserve::reserve(netuid); let e = reserve_weight.exp_base_quote(alpha_reserve.into(), delta_in.into()); let one = U64F64::from_num(1); - let alpha_reserve_fixed = U64F64::from_num(u64::from(alpha_reserve)); + let tao_reserve_fixed = U64F64::from_num(u64::from(tao_reserve)); TaoCurrency::from( - alpha_reserve_fixed + tao_reserve_fixed .saturating_mul(one.saturating_sub(e)) .saturating_to_num::(), ) @@ -216,7 +234,7 @@ where fn delta_in(netuid: NetUid, price_curr: U64F64, price_target: U64F64) -> PaidIn; /// Get the target price based on the input amount - fn price_target(delta_in: PaidIn) -> U64F64; + fn price_target(netuid: NetUid, delta_in: PaidIn) -> U64F64; /// Returns True if price1 is closer to the current price than price2 /// in terms of order direction. diff --git a/pallets/swap/src/pallet/tests.rs b/pallets/swap/src/pallet/tests.rs index d6dc712b3e..fa441cb1f1 100644 --- a/pallets/swap/src/pallet/tests.rs +++ b/pallets/swap/src/pallet/tests.rs @@ -173,43 +173,50 @@ mod dispatchables { /// in the worst case /// - Do not cause price to change /// - Result in the same weight change as one large injection - /// - /// cargo test --package pallet-subtensor-swap --lib -- pallet::tests::dispatchables::test_adjust_protocol_liquidity_small_deltas --exact --nocapture + /// + /// This is a long test that only tests validity of weights math. Run again if changing + /// ReserveWeight::update_weights_for_added_liquidity + /// + /// cargo test --package pallet-subtensor-swap --lib -- pallet::tests::dispatchables::test_adjust_protocol_liquidity_deltas --exact --nocapture + #[ignore] #[test] - fn test_adjust_protocol_liquidity_small_deltas() { + fn test_adjust_protocol_liquidity_deltas() { // The number of times (blocks) over which gradual injections will be made - // const ITERATIONS: u64 = 1_000_000; - const ITERATIONS: u64 = 10; - const PRICE_PRECISION: f64 = 0.000_000_001; + // One year price drift due to precision is under 1e-6 + const ITERATIONS: u64 = 2_700_000; + const PRICE_PRECISION: f64 = 0.000_001; + const PREC_LARGE_DELTA: f64 = 0.001; const WEIGHT_PRECISION: f64 = 0.000_000_000_000_000_001; let initial_tao_reserve = TaoCurrency::from(1_000_000_000_000_000_u64); let initial_alpha_reserve = AlphaCurrency::from(10_000_000_000_000_000_u64); - // test case: tao_delta, alpha_delta + // test case: tao_delta, alpha_delta, price_precision [ - (0_u64, 0_u64), - // (0_u64, 1_u64), - // (1_u64, 0_u64), - // (1_u64, 1_u64), - // (0_u64, 10_u64), - // (10_u64, 0_u64), - // (10_u64, 10_u64), - // (0_u64, 100_u64), - // (100_u64, 0_u64), - // (100_u64, 100_u64), - // (0_u64, 987_u64), - // (987_u64, 0_u64), - // (876_u64, 987_u64), - // (0_u64, 1_000_u64), - // (1_000_u64, 0_u64), - // (1_000_u64, 1_000_u64), - // (0_u64, 1_234_u64), - // (1_234_u64, 0_u64), - // (1_234_u64, 4_321_u64), + (0_u64, 0_u64, PRICE_PRECISION), + (0_u64, 1_u64, PRICE_PRECISION), + (1_u64, 0_u64, PRICE_PRECISION), + (1_u64, 1_u64, PRICE_PRECISION), + (0_u64, 10_u64, PRICE_PRECISION), + (10_u64, 0_u64, PRICE_PRECISION), + (10_u64, 10_u64, PRICE_PRECISION), + (0_u64, 100_u64, PRICE_PRECISION), + (100_u64, 0_u64, PRICE_PRECISION), + (100_u64, 100_u64, PRICE_PRECISION), + (0_u64, 987_u64, PRICE_PRECISION), + (987_u64, 0_u64, PRICE_PRECISION), + (876_u64, 987_u64, PRICE_PRECISION), + (0_u64, 1_000_u64, PRICE_PRECISION), + (1_000_u64, 0_u64, PRICE_PRECISION), + (1_000_u64, 1_000_u64, PRICE_PRECISION), + (0_u64, 1_234_u64, PRICE_PRECISION), + (1_234_u64, 0_u64, PRICE_PRECISION), + (1_234_u64, 4_321_u64, PRICE_PRECISION), + (1_234_000_u64, 4_321_000_u64, PREC_LARGE_DELTA), + (1_234_u64, 4_321_000_u64, PREC_LARGE_DELTA), ] .into_iter() - .for_each(|(tao_delta, alpha_delta)| { + .for_each(|(tao_delta, alpha_delta, price_precision)| { new_test_ext().execute_with(|| { let netuid1 = NetUid::from(1); @@ -237,7 +244,7 @@ mod dispatchables { assert_abs_diff_eq!( price_before.to_num::(), price_after.to_num::(), - epsilon = PRICE_PRECISION + epsilon = price_precision ); ///////////////////////// @@ -260,10 +267,8 @@ mod dispatchables { // Compare reserve weights for netuid 1 and 2 let res_weights1 = SwapReserveWeight::::get(netuid1); let res_weights2 = SwapReserveWeight::::get(netuid2); - let actual_quote_weight1 = - perquintill_to_f64(res_weights1.get_quote_weight()); - let actual_quote_weight2 = - perquintill_to_f64(res_weights2.get_quote_weight()); + let actual_quote_weight1 = perquintill_to_f64(res_weights1.get_quote_weight()); + let actual_quote_weight2 = perquintill_to_f64(res_weights2.get_quote_weight()); assert_abs_diff_eq!( actual_quote_weight1, actual_quote_weight2, @@ -361,8 +366,6 @@ mod dispatchables { ); assert_eq!(actual_quote_weight, 0.5); } - - }); }); } @@ -1011,7 +1014,6 @@ fn test_swap_basic() { netuid: NetUid, order: Order, limit_price: f64, - output_amount: u64, price_should_grow: bool, ) where Order: OrderT, @@ -1023,36 +1025,91 @@ fn test_swap_basic() { // Setup swap assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + // Price is 0.25 + let initial_tao_reserve = TaoCurrency::from(1_000_000_000_u64); + let initial_alpha_reserve = AlphaCurrency::from(4_000_000_000_u64); + TaoReserve::set_mock_reserve(netuid, initial_tao_reserve); + AlphaReserve::set_mock_reserve(netuid, initial_alpha_reserve); + // Get current price let current_price_before = Pallet::::current_price(netuid); + // Get reserves + let tao_reserve = TaoReserve::reserve(netuid.into()).to_u64(); + let alpha_reserve = AlphaReserve::reserve(netuid.into()).to_u64(); + + // Expected fee amount + let fee_rate = FeeRate::::get(netuid) as f64 / u16::MAX as f64; + let expected_fee = (swap_amount as f64 * fee_rate) as u64; + + // Calculate expected output amount using f64 math + // This is a simple case when w1 = w2 = 0.5, so there's no + // exponentiation needed + let x = alpha_reserve as f64; + let y = tao_reserve as f64; + let expected_output_amount = if price_should_grow { + x * (1.0 - y / (y + (swap_amount - expected_fee) as f64)) + } else { + y * (1.0 - x / (x + (swap_amount - expected_fee) as f64)) + }; + // Swap let limit_price_fixed = U64F64::from_num(limit_price); let swap_result = Pallet::::do_swap(netuid, order.clone(), limit_price_fixed, false, false) .unwrap(); assert_abs_diff_eq!( - swap_result.amount_paid_out.to_u64(), - output_amount, - epsilon = output_amount / 100 + swap_result.amount_paid_out.to_u64() as u64, + expected_output_amount as u64, + epsilon = 1 ); assert_abs_diff_eq!( swap_result.paid_in_reserve_delta() as u64, - swap_amount, - epsilon = swap_amount / 10 + (swap_amount - expected_fee) as u64, + epsilon = 1 ); assert_abs_diff_eq!( swap_result.paid_out_reserve_delta() as i64, - -(output_amount as i64), - epsilon = output_amount as i64 / 10 + -(expected_output_amount as i64), + epsilon = 1 ); - // Expected fee amount - let fee_rate = FeeRate::::get(netuid) as f64 / u16::MAX as f64; - let expected_fee = (swap_amount as f64 * fee_rate) as u64; + // Update reserves (because it happens outside of do_swap in stake_utils) + if price_should_grow { + TaoReserve::set_mock_reserve( + netuid, + TaoCurrency::from( + (u64::from(initial_tao_reserve) as i128 + + swap_result.paid_in_reserve_delta()) as u64, + ), + ); + AlphaReserve::set_mock_reserve( + netuid, + AlphaCurrency::from( + (u64::from(initial_alpha_reserve) as i128 + swap_result.paid_out_reserve_delta()) + as u64, + ), + ); + } else { + TaoReserve::set_mock_reserve( + netuid, + TaoCurrency::from( + (u64::from(initial_tao_reserve) as i128 + + swap_result.paid_out_reserve_delta()) as u64, + ), + ); + AlphaReserve::set_mock_reserve( + netuid, + AlphaCurrency::from( + (u64::from(initial_alpha_reserve) as i128 + swap_result.paid_in_reserve_delta()) + as u64, + ), + ); + } // Liquidity position should not be updated + // TODO: Revise when user liquidity is in place // let protocol_id = Pallet::::protocol_account_id(); // let positions = // PositionsV2::::iter_prefix_values((netuid, protocol_id)).collect::>(); @@ -1078,25 +1135,22 @@ fn test_swap_basic() { // Current price is 0.25 // Test case is (order_type, liquidity, limit_price, output_amount) + perform_test(1.into(), GetAlphaForTao::with_amount(1_000), 1000.0, true); + perform_test(1.into(), GetAlphaForTao::with_amount(2_000), 1000.0, true); + perform_test(1.into(), GetAlphaForTao::with_amount(123_456), 1000.0, true); + perform_test(2.into(), GetTaoForAlpha::with_amount(1_000), 0.0001, false); + perform_test(2.into(), GetTaoForAlpha::with_amount(2_000), 0.0001, false); + perform_test(2.into(), GetTaoForAlpha::with_amount(123_456), 0.0001, false); perform_test( - 1.into(), - GetAlphaForTao::with_amount(1_000), + 3.into(), + GetAlphaForTao::with_amount(1_000_000_000), 1000.0, - 3990, true, ); - perform_test( - 2.into(), - GetTaoForAlpha::with_amount(1_000), - 0.0001, - 250, - false, - ); perform_test( 3.into(), - GetAlphaForTao::with_amount(500_000_000), + GetAlphaForTao::with_amount(10_000_000_000), 1000.0, - 2_000_000_000, true, ); }); From 30be6ea9477b6be91ecd0d4381e941c57eb80f6a Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 22 Dec 2025 18:23:17 -0500 Subject: [PATCH 071/240] Swap tests in progress --- pallets/swap/src/pallet/impls.rs | 56 +- pallets/swap/src/pallet/reserve_weights.rs | 172 +- pallets/swap/src/pallet/swap_step.rs | 24 +- pallets/swap/src/pallet/tests.rs | 2344 +++++++++----------- 4 files changed, 1155 insertions(+), 1441 deletions(-) diff --git a/pallets/swap/src/pallet/impls.rs b/pallets/swap/src/pallet/impls.rs index c0819ca756..0b36aee691 100644 --- a/pallets/swap/src/pallet/impls.rs +++ b/pallets/swap/src/pallet/impls.rs @@ -215,6 +215,9 @@ impl Pallet { Self::maybe_initialize_palswap(netuid)?; + println!("Self::current_price(netuid) = {:?}", Self::current_price(netuid)); + println!("limit_price = {:?}", limit_price); + // Because user specifies the limit price, check that it is in fact beoynd the current one ensure!( order.is_beyond_price_limit(Self::current_price(netuid), limit_price), @@ -371,6 +374,9 @@ impl Pallet { /// Dissolve all LPs and clean state. pub fn do_dissolve_all_liquidity_providers(_netuid: NetUid) -> DispatchResult { + // TODO: Revise when user liquidity is available + Ok(()) + // if PalSwapInitialized::::get(netuid) { // // 1) Snapshot only *non‑protocol* positions: (owner, position_id). // struct CloseItem { @@ -485,17 +491,19 @@ impl Pallet { // ); // Ok(()) - - todo!(); } + /// TODO: Revise when user liquidity is available /// Clear **protocol-owned** liquidity and wipe all swap state for `netuid`. - pub fn do_clear_protocol_liquidity(_netuid: NetUid) -> DispatchResult { + pub fn do_clear_protocol_liquidity(netuid: NetUid) -> DispatchResult { // let protocol_account = Self::protocol_account_id(); - // // 1) Force-close only protocol positions, burning proceeds. - // let mut burned_tao = TaoCurrency::ZERO; - // let mut burned_alpha = AlphaCurrency::ZERO; + // 1) Force-close only protocol positions, burning proceeds. + let burned_tao = T::TaoReserve::reserve(netuid.into()); + let burned_alpha = T::AlphaReserve::reserve(netuid.into()); + + T::TaoReserve::decrease_provided(netuid.into(), burned_tao); + T::AlphaReserve::decrease_provided(netuid.into(), burned_alpha); // // Collect protocol position IDs first to avoid mutating while iterating. // let protocol_pos_ids: sp_std::vec::Vec = Positions::::iter_prefix((netuid,)) @@ -535,34 +543,20 @@ impl Pallet { // } // } - // // 2) Clear active tick index entries, then all swap state (idempotent even if empty/non‑V3). - // let active_ticks: sp_std::vec::Vec = - // Ticks::::iter_prefix(netuid).map(|(ti, _)| ti).collect(); - // for ti in active_ticks { - // ActiveTickIndexManager::::remove(netuid, ti); - // } - - // let _ = Positions::::clear_prefix((netuid,), u32::MAX, None); - // let _ = Ticks::::clear_prefix(netuid, u32::MAX, None); + let _ = PositionsV2::::clear_prefix((netuid,), u32::MAX, None); - // FeeGlobalTao::::remove(netuid); - // FeeGlobalAlpha::::remove(netuid); - // CurrentLiquidity::::remove(netuid); - // CurrentTick::::remove(netuid); - // AlphaSqrtPrice::::remove(netuid); - // PalSwapInitialized::::remove(netuid); + FeesTao::::remove(netuid); + FeesAlpha::::remove(netuid); + PalSwapInitialized::::remove(netuid); + FeeRate::::remove(netuid); + EnabledUserLiquidity::::remove(netuid); + SwapReserveWeight::::remove(netuid); - // let _ = TickIndexBitmapWords::::clear_prefix((netuid,), u32::MAX, None); - // FeeRate::::remove(netuid); - // EnabledUserLiquidity::::remove(netuid); - - // log::debug!( - // "clear_protocol_liquidity: netuid={netuid:?}, protocol_burned: τ={burned_tao:?}, α={burned_alpha:?}; state cleared" - // ); - - // Ok(()) + log::debug!( + "clear_protocol_liquidity: netuid={netuid:?}, protocol_burned: τ={burned_tao:?}, α={burned_alpha:?}; state cleared" + ); - todo!(); + Ok(()) } } diff --git a/pallets/swap/src/pallet/reserve_weights.rs b/pallets/swap/src/pallet/reserve_weights.rs index f42f5252da..72994e8796 100644 --- a/pallets/swap/src/pallet/reserve_weights.rs +++ b/pallets/swap/src/pallet/reserve_weights.rs @@ -71,13 +71,23 @@ impl ReserveWeight { let w2: u128 = self.get_quote_weight().deconstruct() as u128; let x_plus_dx = x.saturating_add(dx); - let precision = 256; + let precision = 1024; let x_safe = SafeInt::from(x); let w1_safe = SafeInt::from(w1); let w2_safe = SafeInt::from(w2); let perquintill_scale = SafeInt::from(ACCURACY as u128); let denominator = SafeInt::from(x_plus_dx); let maybe_result_safe_int = if base_quote { + + println!("x = {:?}", x); + println!("dx = {:?}", dx); + println!("x_safe = {:?}", x_safe); + println!("denominator = {:?}", denominator); + println!("w1_safe = {:?}", w1_safe); + println!("w2_safe = {:?}", w2_safe); + println!("precision = {:?}", precision); + println!("perquintill_scale = {:?}", perquintill_scale); + SafeInt::pow_ratio_scaled( &x_safe, &denominator, @@ -300,86 +310,90 @@ mod tests { fn test_exp_base_quote_happy_path() { // Outer test cases: w_quote [ - Perquintill::from_rational(500_000_000_000_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(500_000_000_001_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(500_000_000_100_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(500_000_001_000_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(500_000_010_000_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(500_000_100_000_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(500_001_000_000_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(500_010_000_000_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(500_100_000_000_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(501_000_000_000_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(510_000_000_000_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(500_000_000_000_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(500_000_000_001_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(499_999_999_999_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(500_000_000_100_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(500_000_001_000_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(500_000_010_000_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(500_000_100_000_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(500_001_000_000_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(500_010_000_000_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(500_100_000_000_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(501_000_000_000_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(510_000_000_000_u128, 1_000_000_000_000_u128), Perquintill::from_rational(100_000_000_000_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(100_000_000_001_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(200_000_000_000_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(300_000_000_000_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(400_000_000_000_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(600_000_000_000_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(700_000_000_000_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(800_000_000_000_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(899_999_999_999_u128, 1_000_000_000_000_u128), - Perquintill::from_rational(900_000_000_000_u128, 1_000_000_000_000_u128), - Perquintill::from_rational( - 102_337_248_363_782_924_u128, - 1_000_000_000_000_000_000_u128, - ), + // Perquintill::from_rational(100_000_000_001_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(200_000_000_000_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(300_000_000_000_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(400_000_000_000_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(600_000_000_000_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(700_000_000_000_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(800_000_000_000_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(899_999_999_999_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational(900_000_000_000_u128, 1_000_000_000_000_u128), + // Perquintill::from_rational( + // 102_337_248_363_782_924_u128, + // 1_000_000_000_000_000_000_u128, + // ), ] .into_iter() .for_each(|w_quote| { // Inner test cases: y, x, ∆x [ - ( - 1_000_000_000_000_u64, - 100_000_000_000_000_u64, - 100_000_000_u64, - ), - ( - 1_000_000_000_000_u64, - 100_000_000_000_000_u64, - 100_000_000_u64, - ), - ( - 100_000_000_000_u64, - 100_000_000_000_000_u64, - 100_000_000_u64, - ), - (100_000_000_000_u64, 100_000_000_000_000_u64, 1_000_000_u64), - ( - 100_000_000_000_u64, - 100_000_000_000_000_u64, - 1_000_000_000_000_u64, - ), - ( - 1_000_000_000_u64, - 100_000_000_000_000_u64, - 1_000_000_000_000_u64, - ), - ( - 1_000_000_u64, - 100_000_000_000_000_u64, - 1_000_000_000_000_u64, - ), - (1_000_u64, 100_000_000_000_000_u64, 1_000_000_000_000_u64), - (1_000_u64, 100_000_000_000_000_u64, 1_000_000_000_u64), - (1_000_u64, 100_000_000_000_000_u64, 1_000_000_u64), - (1_000_u64, 100_000_000_000_000_u64, 1_000_u64), - (1_000_u64, 100_000_000_000_000_u64, 100_000_000_000_000_u64), - (10_u64, 100_000_000_000_000_u64, 100_000_000_000_000_u64), - // Extreme values of ∆x for small x - (1_000_000_000_u64, 4_000_000_000_u64, 1_000_000_000_000_u64), - (1_000_000_000_000_u64, 1_000_u64, 1_000_000_000_000_u64), - ( - 5_628_038_062_729_553_u64, - 400_775_553_u64, - 14_446_633_907_665_582_u64, - ), - ( - 5_600_000_000_000_000_u64, - 400_000_000_u64, - 14_000_000_000_000_000_u64, - ), + // (1_000_u64, 1_000_u64, 0_u64), + // (1_000_u64, 1_000_u64, 1_u64), + (1_500_u64, 1_000_u64, 1_u64), + // ( + // 1_000_000_000_000_u64, + // 100_000_000_000_000_u64, + // 100_000_000_u64, + // ), + // ( + // 1_000_000_000_000_u64, + // 100_000_000_000_000_u64, + // 100_000_000_u64, + // ), + // ( + // 100_000_000_000_u64, + // 100_000_000_000_000_u64, + // 100_000_000_u64, + // ), + // (100_000_000_000_u64, 100_000_000_000_000_u64, 1_000_000_u64), + // ( + // 100_000_000_000_u64, + // 100_000_000_000_000_u64, + // 1_000_000_000_000_u64, + // ), + // ( + // 1_000_000_000_u64, + // 100_000_000_000_000_u64, + // 1_000_000_000_000_u64, + // ), + // ( + // 1_000_000_u64, + // 100_000_000_000_000_u64, + // 1_000_000_000_000_u64, + // ), + // (1_000_u64, 100_000_000_000_000_u64, 1_000_000_000_000_u64), + // (1_000_u64, 100_000_000_000_000_u64, 1_000_000_000_u64), + // (1_000_u64, 100_000_000_000_000_u64, 1_000_000_u64), + // (1_000_u64, 100_000_000_000_000_u64, 1_000_u64), + // (1_000_u64, 100_000_000_000_000_u64, 100_000_000_000_000_u64), + // (10_u64, 100_000_000_000_000_u64, 100_000_000_000_000_u64), + // // Extreme values of ∆x for small x + // (1_000_000_000_u64, 4_000_000_000_u64, 1_000_000_000_000_u64), + // (1_000_000_000_000_u64, 1_000_u64, 1_000_000_000_000_u64), + // ( + // 5_628_038_062_729_553_u64, + // 400_775_553_u64, + // 14_446_633_907_665_582_u64, + // ), + // ( + // 5_600_000_000_000_000_u64, + // 400_000_000_u64, + // 14_000_000_000_000_000_u64, + // ), ] .into_iter() .for_each(|(y, x, dx)| { @@ -387,16 +401,20 @@ mod tests { let e = rw.exp_base_quote(x, dx); let one = U64F64::from_num(1); let y_fixed = U64F64::from_num(y); + println!("debug 1: e = {:?}", e); let dy = y_fixed * (one - e); + println!("debug 2: dy = {:?}", dy); let w1 = perquintill_to_f64(rw.get_base_weight()); let w2 = perquintill_to_f64(rw.get_quote_weight()); let e_expected = (x as f64 / (x as f64 + dx as f64)).powf(w1 / w2); let dy_expected = y as f64 * (1. - e_expected); + + println!("debug 3: dy_expected = {:?}", dy_expected); let mut eps = dy_expected / 100000.; - if eps > 0.1 { - eps = 0.1; + if eps > 1.0 { + eps = 1.0; } assert_abs_diff_eq!(dy.to_num::(), dy_expected, epsilon = eps); }) diff --git a/pallets/swap/src/pallet/swap_step.rs b/pallets/swap/src/pallet/swap_step.rs index a9eede62b7..bfcdd5a4f9 100644 --- a/pallets/swap/src/pallet/swap_step.rs +++ b/pallets/swap/src/pallet/swap_step.rs @@ -1,5 +1,6 @@ use core::marker::PhantomData; +use frame_support::ensure; use safe_math::*; use substrate_fixed::types::U64F64; use subtensor_runtime_common::{AlphaCurrency, Currency, CurrencyReserve, NetUid, TaoCurrency}; @@ -52,6 +53,9 @@ where let target_price = Self::price_target(netuid, requested_delta_in); let current_price = Pallet::::current_price(netuid); + println!("requested_delta_in = {}", requested_delta_in); + println!("target_price = {}", target_price); + Self { netuid, drop_fees, @@ -109,16 +113,24 @@ where .saturating_to_num::() .into(); } + + + println!("=========== debug 1 (end of determine_action)"); } /// Process a single step of a swap fn process_swap(&self) -> Result, Error> { - // Hold the fees - Self::add_fees(self.netuid, self.fee); - // Convert amounts, actual swap happens here let delta_out = Self::convert_deltas(self.netuid, self.delta_in); log::trace!("\tDelta Out : {delta_out}"); + println!("delta_out = {}", delta_out); + ensure!( + delta_out > 0.into(), + Error::::ReservesTooLow + ); + + // Hold the fees + Self::add_fees(self.netuid, self.fee); Ok(SwapStepResult { fee_paid: self.fee, @@ -214,6 +226,12 @@ impl SwapStep let tao_reserve = T::TaoReserve::reserve(netuid.into()); let reserve_weight = SwapReserveWeight::::get(netuid); let e = reserve_weight.exp_base_quote(alpha_reserve.into(), delta_in.into()); + + println!("alpha_reserve = {}", alpha_reserve); + println!("tao_reserve = {}", tao_reserve); + println!("reserve_weight = {:?}", reserve_weight); + println!("e = {}", e); + let one = U64F64::from_num(1); let tao_reserve_fixed = U64F64::from_num(u64::from(tao_reserve)); TaoCurrency::from( diff --git a/pallets/swap/src/pallet/tests.rs b/pallets/swap/src/pallet/tests.rs index fa441cb1f1..ecdb810b24 100644 --- a/pallets/swap/src/pallet/tests.rs +++ b/pallets/swap/src/pallet/tests.rs @@ -29,12 +29,12 @@ use crate::pallet::swap_step::*; #[allow(dead_code)] fn get_min_price() -> U64F64 { - U64F64::saturating_from_num(Pallet::::min_price_inner::()) + U64F64::from_num(Pallet::::min_price_inner::()) / U64F64::from_num(1_000_000_000) } #[allow(dead_code)] fn get_max_price() -> U64F64 { - U64F64::saturating_from_num(Pallet::::max_price_inner::()) + U64F64::from_num(Pallet::::max_price_inner::()) / U64F64::from_num(1_000_000_000) } mod dispatchables { @@ -1156,421 +1156,33 @@ fn test_swap_basic() { }); } -// In this test the swap starts and ends within one (large liquidity) position -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor-swap --lib -- pallet::tests::test_swap_single_position --exact --show-output -#[test] -fn test_swap_single_position() { - todo!(); - - // let min_price = tick_to_price(TickIndex::MIN); - // let max_price = tick_to_price(TickIndex::MAX); - // let max_tick = price_to_tick(max_price); - // let netuid = NetUid::from(1); - // assert_eq!(max_tick, TickIndex::MAX); - - // let mut current_price_low = 0_f64; - // let mut current_price_high = 0_f64; - // let mut current_price = 0_f64; - // new_test_ext().execute_with(|| { - // let (low, high) = get_ticked_prices_around_current_price(); - // current_price_low = low; - // current_price_high = high; - // current_price = Pallet::::current_price(netuid).to_num::(); - // }); - - // macro_rules! perform_test { - // ($order_t:ident, - // $price_low_offset:expr, - // $price_high_offset:expr, - // $position_liquidity:expr, - // $liquidity_fraction:expr, - // $limit_price:expr, - // $price_should_grow:expr - // ) => { - // new_test_ext().execute_with(|| { - // let price_low_offset = $price_low_offset; - // let price_high_offset = $price_high_offset; - // let position_liquidity = $position_liquidity; - // let order_liquidity_fraction = $liquidity_fraction; - // let limit_price = $limit_price; - // let price_should_grow = $price_should_grow; - - // ////////////////////////////////////////////// - // // Initialize pool and add the user position - // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - // let tao_reserve = TaoReserve::reserve(netuid.into()).to_u64(); - // let alpha_reserve = AlphaReserve::reserve(netuid.into()).to_u64(); - // let protocol_liquidity = (tao_reserve as f64 * alpha_reserve as f64).sqrt(); - - // // Add liquidity - // let current_price = Pallet::::current_price(netuid).to_num::(); - // let sqrt_current_price = AlphaSqrtPrice::::get(netuid).to_num::(); - - // let price_low = price_low_offset + current_price; - // let price_high = price_high_offset + current_price; - // let tick_low = price_to_tick(price_low); - // let tick_high = price_to_tick(price_high); - // let (_position_id, _tao, _alpha) = Pallet::::do_add_liquidity( - // netuid, - // &OK_COLDKEY_ACCOUNT_ID, - // &OK_HOTKEY_ACCOUNT_ID, - // tick_low, - // tick_high, - // position_liquidity, - // ) - // .unwrap(); - - // // Liquidity position at correct ticks - // assert_eq!( - // Pallet::::count_positions(netuid, &OK_COLDKEY_ACCOUNT_ID), - // 1 - // ); - - // // Get tick infos before the swap - // let tick_low_info_before = Ticks::::get(netuid, tick_low).unwrap_or_default(); - // let tick_high_info_before = - // Ticks::::get(netuid, tick_high).unwrap_or_default(); - // let liquidity_before = CurrentLiquidity::::get(netuid); - // assert_abs_diff_eq!( - // liquidity_before as f64, - // protocol_liquidity + position_liquidity as f64, - // epsilon = liquidity_before as f64 / 1000. - // ); - - // ////////////////////////////////////////////// - // // Swap - - // // Calculate the expected output amount for the cornercase of one step - // let order_liquidity = order_liquidity_fraction * position_liquidity as f64; - - // let output_amount = >::approx_expected_swap_output( - // sqrt_current_price, - // liquidity_before as f64, - // order_liquidity, - // ); - - // // Do the swap - // let sqrt_limit_price = SqrtPrice::from_num((limit_price).sqrt()); - // let order = $order_t::with_amount(order_liquidity as u64); - // let swap_result = - // Pallet::::do_swap(netuid, order, sqrt_limit_price, false, false).unwrap(); - // assert_abs_diff_eq!( - // swap_result.amount_paid_out.to_u64() as f64, - // output_amount, - // epsilon = output_amount / 10. - // ); - - // if order_liquidity_fraction <= 0.001 { - // assert_abs_diff_eq!( - // swap_result.paid_in_reserve_delta() as i64, - // order_liquidity as i64, - // epsilon = order_liquidity as i64 / 10 - // ); - // assert_abs_diff_eq!( - // swap_result.paid_out_reserve_delta() as i64, - // -(output_amount as i64), - // epsilon = output_amount as i64 / 10 - // ); - // } - - // // Assert that price movement is in correct direction - // let current_price_after = Pallet::::current_price(netuid); - // assert_eq!(price_should_grow, current_price_after > current_price); - - // // Assert that for small amounts price stays within the user position - // if (order_liquidity_fraction <= 0.001) - // && (price_low_offset > 0.0001) - // && (price_high_offset > 0.0001) - // { - // assert!(current_price_after <= price_high); - // assert!(current_price_after >= price_low); - // } - - // // Check that low and high ticks' fees were updated properly - // let tick_low_info = Ticks::::get(netuid, tick_low).unwrap(); - // let tick_high_info = Ticks::::get(netuid, tick_high).unwrap(); - // let expected_liquidity_net_low = tick_low_info_before.liquidity_net; - // let expected_liquidity_gross_low = tick_low_info_before.liquidity_gross; - // let expected_liquidity_net_high = tick_high_info_before.liquidity_net; - // let expected_liquidity_gross_high = tick_high_info_before.liquidity_gross; - // assert_eq!(tick_low_info.liquidity_net, expected_liquidity_net_low,); - // assert_eq!(tick_low_info.liquidity_gross, expected_liquidity_gross_low,); - // assert_eq!(tick_high_info.liquidity_net, expected_liquidity_net_high,); - // assert_eq!( - // tick_high_info.liquidity_gross, - // expected_liquidity_gross_high, - // ); - - // // Expected fee amount - // let fee_rate = FeeRate::::get(netuid) as f64 / u16::MAX as f64; - // let expected_fee = (order_liquidity - order_liquidity / (1.0 + fee_rate)) as u64; - - // // // Global fees should be updated - // let actual_global_fee = ($order_t::with_amount(0) - // .amount() - // .global_fee(netuid) - // .to_num::() - // * (liquidity_before as f64)) as u64; - - // assert_abs_diff_eq!( - // swap_result.fee_paid.to_u64(), - // expected_fee, - // epsilon = expected_fee / 10 - // ); - // assert_abs_diff_eq!(actual_global_fee, expected_fee, epsilon = expected_fee / 10); - - // // Tick fees should be updated - - // // Liquidity position should not be updated - // let positions = - // Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) - // .collect::>(); - // let position = positions.first().unwrap(); - - // assert_eq!(position.liquidity, position_liquidity,); - // assert_eq!(position.tick_low, tick_low); - // assert_eq!(position.tick_high, tick_high); - // assert_eq!(position.fees_alpha, 0); - // assert_eq!(position.fees_tao, 0); - // }); - // }; - // } - - // // Current price is 0.25 - // // The test case is based on the current price and position prices are defined as a price - // // offset from the current price - // // Outer part of test case is Position: (price_low_offset, price_high_offset, liquidity) - // [ - // // Very localized position at the current price - // (-0.1, 0.1, 500_000_000_000_u64), - // // Repeat the protocol liquidity at maximum range - // ( - // min_price - current_price, - // max_price - current_price, - // 2_000_000_000_u64, - // ), - // // Repeat the protocol liquidity at current to max range - // ( - // current_price_high - current_price, - // max_price - current_price, - // 2_000_000_000_u64, - // ), - // // Repeat the protocol liquidity at min to current range - // ( - // min_price - current_price, - // current_price_low - current_price, - // 2_000_000_000_u64, - // ), - // // Half to double price - // (-0.125, 0.25, 2_000_000_000_u64), - // // A few other price ranges and liquidity volumes - // (-0.1, 0.1, 2_000_000_000_u64), - // (-0.1, 0.1, 10_000_000_000_u64), - // (-0.1, 0.1, 100_000_000_000_u64), - // (-0.01, 0.01, 100_000_000_000_u64), - // (-0.001, 0.001, 100_000_000_000_u64), - // ] - // .into_iter() - // .for_each( - // |(price_low_offset, price_high_offset, position_liquidity)| { - // // Inner part of test case is Order: (order_type, order_liquidity, limit_price) - // // order_liquidity is represented as a fraction of position_liquidity - // for liquidity_fraction in [0.0001, 0.001, 0.01, 0.1, 0.2, 0.5] { - // perform_test!( - // GetAlphaForTao, - // price_low_offset, - // price_high_offset, - // position_liquidity, - // liquidity_fraction, - // 1000.0_f64, - // true - // ); - // perform_test!( - // GetTaoForAlpha, - // price_low_offset, - // price_high_offset, - // position_liquidity, - // liquidity_fraction, - // 0.0001_f64, - // false - // ); - // } - // }, - // ); -} - -// This test is a sanity check for swap and multiple positions -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor-swap --lib -- pallet::impls::tests::test_swap_multiple_positions --exact --show-output --nocapture -#[test] -fn test_swap_multiple_positions() { - todo!(); - - // new_test_ext().execute_with(|| { - // let min_price = tick_to_price(TickIndex::MIN); - // let max_price = tick_to_price(TickIndex::MAX); - // let max_tick = price_to_tick(max_price); - // let netuid = NetUid::from(1); - // assert_eq!(max_tick, TickIndex::MAX); - - // ////////////////////////////////////////////// - // // Initialize pool and add the user position - // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - - // // Add liquidity - // let current_price = Pallet::::current_price(netuid).to_num::(); - - // // Current price is 0.25 - // // All positions below are placed at once - // [ - // // Very localized position at the current price - // (-0.1, 0.1, 500_000_000_000_u64), - // // Repeat the protocol liquidity at maximum range - // ( - // min_price - current_price, - // max_price - current_price, - // 2_000_000_000_u64, - // ), - // // Repeat the protocol liquidity at current to max range - // (0.0, max_price - current_price, 2_000_000_000_u64), - // // Repeat the protocol liquidity at min to current range - // (min_price - current_price, 0.0, 2_000_000_000_u64), - // // Half to double price - // (-0.125, 0.25, 2_000_000_000_u64), - // // A few other price ranges and liquidity volumes - // (-0.1, 0.1, 2_000_000_000_u64), - // (-0.1, 0.1, 10_000_000_000_u64), - // (-0.1, 0.1, 100_000_000_000_u64), - // (-0.01, 0.01, 100_000_000_000_u64), - // (-0.001, 0.001, 100_000_000_000_u64), - // // A few (overlapping) positions up the range - // (0.01, 0.02, 100_000_000_000_u64), - // (0.02, 0.03, 100_000_000_000_u64), - // (0.03, 0.04, 100_000_000_000_u64), - // (0.03, 0.05, 100_000_000_000_u64), - // // A few (overlapping) positions down the range - // (-0.02, -0.01, 100_000_000_000_u64), - // (-0.03, -0.02, 100_000_000_000_u64), - // (-0.04, -0.03, 100_000_000_000_u64), - // (-0.05, -0.03, 100_000_000_000_u64), - // ] - // .into_iter() - // .for_each( - // |(price_low_offset, price_high_offset, position_liquidity)| { - // let price_low = price_low_offset + current_price; - // let price_high = price_high_offset + current_price; - // let tick_low = price_to_tick(price_low); - // let tick_high = price_to_tick(price_high); - // let (_position_id, _tao, _alpha) = Pallet::::do_add_liquidity( - // netuid, - // &OK_COLDKEY_ACCOUNT_ID, - // &OK_HOTKEY_ACCOUNT_ID, - // tick_low, - // tick_high, - // position_liquidity, - // ) - // .unwrap(); - // }, - // ); - - // macro_rules! perform_test { - // ($order_t:ident, $order_liquidity:expr, $limit_price:expr, $should_price_grow:expr) => { - // ////////////////////////////////////////////// - // // Swap - // let order_liquidity = $order_liquidity; - // let limit_price = $limit_price; - // let should_price_grow = $should_price_grow; - - // let sqrt_current_price = AlphaSqrtPrice::::get(netuid); - // let current_price = (sqrt_current_price * sqrt_current_price).to_num::(); - // let liquidity_before = CurrentLiquidity::::get(netuid); - // let output_amount = >::approx_expected_swap_output( - // sqrt_current_price.to_num(), - // liquidity_before as f64, - // order_liquidity as f64, - // ); - - // // Do the swap - // let sqrt_limit_price = SqrtPrice::from_num((limit_price).sqrt()); - // let order = $order_t::with_amount(order_liquidity); - // let swap_result = - // Pallet::::do_swap(netuid, order, sqrt_limit_price, false, false).unwrap(); - // assert_abs_diff_eq!( - // swap_result.amount_paid_out.to_u64() as f64, - // output_amount, - // epsilon = output_amount / 10. - // ); - - // let tao_reserve = TaoReserve::reserve(netuid.into()).to_u64(); - // let alpha_reserve = AlphaReserve::reserve(netuid.into()).to_u64(); - // let output_amount = output_amount as u64; - - // assert!(output_amount > 0); - - // if alpha_reserve > order_liquidity && tao_reserve > order_liquidity { - // assert_abs_diff_eq!( - // swap_result.paid_in_reserve_delta() as i64, - // order_liquidity as i64, - // epsilon = order_liquidity as i64 / 100 - // ); - // assert_abs_diff_eq!( - // swap_result.paid_out_reserve_delta() as i64, - // -(output_amount as i64), - // epsilon = output_amount as i64 / 100 - // ); - // } - - // // Assert that price movement is in correct direction - // let sqrt_current_price_after = AlphaSqrtPrice::::get(netuid); - // let current_price_after = - // (sqrt_current_price_after * sqrt_current_price_after).to_num::(); - // assert_eq!(should_price_grow, current_price_after > current_price); - // }; - // } - - // // All these orders are executed without swap reset - // for order_liquidity in [ - // (100_000_u64), - // (1_000_000), - // (10_000_000), - // (100_000_000), - // (200_000_000), - // (500_000_000), - // (1_000_000_000), - // (10_000_000_000), - // ] { - // perform_test!(GetAlphaForTao, order_liquidity, 1000.0_f64, true); - // perform_test!(GetTaoForAlpha, order_liquidity, 0.0001_f64, false); - // } - - // // Current price shouldn't be much different from the original - // let sqrt_current_price_after = AlphaSqrtPrice::::get(netuid); - // let current_price_after = - // (sqrt_current_price_after * sqrt_current_price_after).to_num::(); - // assert_abs_diff_eq!( - // current_price, - // current_price_after, - // epsilon = current_price / 10. - // ) - // }); -} - // cargo test --package pallet-subtensor-swap --lib -- pallet::impls::tests::test_swap_precision_edge_case --exact --show-output #[test] fn test_swap_precision_edge_case() { - new_test_ext().execute_with(|| { - let netuid = NetUid::from(123); // 123 is netuid with low edge case liquidity - let order = GetTaoForAlpha::with_amount(1_000_000_000_000_000_000); + // Test case: tao_reserve, alpha_reserve, swap_amount + [ + (1_000_u64, 1_000_u64, 999_500_u64), + (1_000_000_u64, 1_000_000_u64, 999_500_000_u64) + ] + .into_iter() + .for_each(|(tao_reserve, alpha_reserve, swap_amount)| { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1); + let order = GetTaoForAlpha::with_amount(swap_amount); - // Setup swap - assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + // Very low reserves + TaoReserve::set_mock_reserve(netuid, TaoCurrency::from(tao_reserve)); + AlphaReserve::set_mock_reserve(netuid, AlphaCurrency::from(alpha_reserve)); - // Minimum possible limit price - let limit_price: U64F64 = get_min_price(); + // Minimum possible limit price + let limit_price: U64F64 = get_min_price(); + println!("limit_price = {:?}", limit_price); - // Swap - let swap_result = Pallet::::do_swap(netuid, order, limit_price, false, true).unwrap(); + // Swap + let swap_result = Pallet::::do_swap(netuid, order, limit_price, false, true).unwrap(); - assert!(swap_result.amount_paid_out > TaoCurrency::ZERO); + assert!(swap_result.amount_paid_out > TaoCurrency::ZERO); + }); }); } @@ -1581,38 +1193,79 @@ fn test_convert_deltas() { assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); // TODO: Add more test cases with different weights and edge cases for reserves - for (_tao, _alpha, _w1, _w2, delta_in, expected_buy, expected_sell) in [ - (1500, 1000, 0.5, 0.5, 1, 0, 2), - (1500, 1000, 0.5, 0.5, 10000, 4444, 22500), - (1500, 1000, 0.5, 0.5, 1000000, 444444, 2250000), - (1500, 1000, 0.5, 0.5, u64::MAX, 2000000000000, 3000000000000), - (1, 1000000, 0.5, 0.5, 1, 18406523739291577836, 465), - (1, 1000000, 0.5, 0.5, 10000, u64::MAX, 465), - (1, 1000000, 0.5, 0.5, 1000000, u64::MAX, 465), - (1, 1000000, 0.5, 0.5, u64::MAX, u64::MAX, 464), - (1000000, 1, 0.5, 0.5, 1, 0, 18406523745214495085), - (1000000, 1, 0.5, 0.5, 10000, 0, u64::MAX), - (1000000, 1, 0.5, 0.5, 1000000, 0, u64::MAX), - (1000000, 1, 0.5, 0.5, u64::MAX, 2000000000000, u64::MAX), + for (tao, alpha, w_quote, delta_in) in [ + (1500, 1000, 0.5, 1), + (1500, 1000, 0.5, 10000), + (1500, 1000, 0.5, 1000000), + (1500, 1000, 0.5, u64::MAX), + (1, 1000000, 0.5, 1), + (1, 1000000, 0.5, 10000), + (1, 1000000, 0.5, 1000000), + (1, 1000000, 0.5, u64::MAX), + (1000000, 1, 0.5, 1), + (1000000, 1, 0.5, 10000), + (1000000, 1, 0.5, 1000000), + (1000000, 1, 0.5, u64::MAX), + // Low quote weight + (1500, 1000, 0.1, 1), + (1500, 1000, 0.1, 10000), + (1500, 1000, 0.1, 1000000), + (1500, 1000, 0.1, u64::MAX), + (1, 1000000, 0.1, 1), + (1, 1000000, 0.1, 10000), + (1, 1000000, 0.1, 1000000), + (1, 1000000, 0.1, u64::MAX), + (1000000, 1, 0.1, 1), + (1000000, 1, 0.1, 10000), + (1000000, 1, 0.1, 1000000), + (1000000, 1, 0.1, u64::MAX), + // High quote weight + (1500, 1000, 0.9, 1), + (1500, 1000, 0.9, 10000), + (1500, 1000, 0.9, 1000000), + (1500, 1000, 0.9, u64::MAX), + (1, 1000000, 0.9, 1), + (1, 1000000, 0.9, 10000), + (1, 1000000, 0.9, 1000000), + (1, 1000000, 0.9, u64::MAX), + (1000000, 1, 0.9, 1), + (1000000, 1, 0.9, 10000), + (1000000, 1, 0.9, 1000000), + (1000000, 1, 0.9, u64::MAX), ] { - { - assert_abs_diff_eq!( - BasicSwapStep::::convert_deltas( - netuid, - delta_in.into() - ), - expected_sell.into(), - epsilon = 2.into() - ); - assert_abs_diff_eq!( - BasicSwapStep::::convert_deltas( - netuid, - delta_in.into() - ), - expected_buy.into(), - epsilon = 2.into() - ); - } + // Initialize reserves and weights + TaoReserve::set_mock_reserve(netuid, TaoCurrency::from(tao)); + AlphaReserve::set_mock_reserve(netuid, AlphaCurrency::from(alpha)); + let w_accuracy = 1_000_000_000_f64; + let w_quote_pt = Perquintill::from_rational((w_quote as f64 * w_accuracy) as u128, w_accuracy as u128); + let rw = ReserveWeight::new(w_quote_pt).unwrap(); + SwapReserveWeight::::insert(netuid, rw); + + // Calculate expected swap results (buy and sell) using f64 math + let y = tao as f64; + let x = alpha as f64; + let d = delta_in as f64; + let w1_div_w2 = (1. - w_quote) / w_quote; + let w2_div_w1 = w_quote / (1. - w_quote); + let expected_sell = y * (1. - (x / (x + d)).powf(w1_div_w2)); + let expected_buy = x * (1. - (y / (y + d)).powf(w2_div_w1)); + + assert_abs_diff_eq!( + u64::from(BasicSwapStep::::convert_deltas( + netuid, + delta_in.into() + )), + expected_sell as u64, + epsilon = 2u64 + ); + assert_abs_diff_eq!( + u64::from(BasicSwapStep::::convert_deltas( + netuid, + delta_in.into() + )), + expected_buy as u64, + epsilon = 2u64 + ); } }); } @@ -1691,68 +1344,67 @@ fn test_convert_deltas() { // }); // } -/// Test correctness of swap fees: -/// - Fees are distribued to (concentrated) liquidity providers -/// -#[test] -fn test_swap_fee_correctness() { - todo!(); - - // new_test_ext().execute_with(|| { - // let min_price = get_min_price(); - // let max_price = get_max_price(); - // let netuid = NetUid::from(1); +// TODO: revise when user liquidity is available +// Test correctness of swap fees: +// - Fees are distribued to (concentrated) liquidity providers +// +// #[test] +// fn test_swap_fee_correctness() { +// new_test_ext().execute_with(|| { +// let min_price = get_min_price(); +// let max_price = get_max_price(); +// let netuid = NetUid::from(1); - // // Provide very spread liquidity at the range from min to max that matches protocol liquidity - // let liquidity = 2_000_000_000_000_u64; // 1x of protocol liquidity +// // Provide very spread liquidity at the range from min to max that matches protocol liquidity +// let liquidity = 2_000_000_000_000_u64; // 1x of protocol liquidity - // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); +// assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - // // Add user liquidity - // let (position_id, _tao, _alpha) = Pallet::::do_add_liquidity( - // netuid, - // &OK_COLDKEY_ACCOUNT_ID, - // &OK_HOTKEY_ACCOUNT_ID, - // tick_low, - // tick_high, - // liquidity, - // ) - // .unwrap(); +// // Add user liquidity +// let (position_id, _tao, _alpha) = Pallet::::do_add_liquidity( +// netuid, +// &OK_COLDKEY_ACCOUNT_ID, +// &OK_HOTKEY_ACCOUNT_ID, +// tick_low, +// tick_high, +// liquidity, +// ) +// .unwrap(); - // // Swap buy and swap sell - // Pallet::::do_swap( - // netuid, - // GetAlphaForTao::with_amount(liquidity / 10), - // u64::MAX.into(), - // false, - // false, - // ) - // .unwrap(); - // Pallet::::do_swap( - // netuid, - // GetTaoForAlpha::with_amount(liquidity / 10), - // 0_u64.into(), - // false, - // false, - // ) - // .unwrap(); - - // // Get user position - // let mut position = - // Positions::::get((netuid, OK_COLDKEY_ACCOUNT_ID, position_id)).unwrap(); - // assert_eq!(position.liquidity, liquidity); - // assert_eq!(position.tick_low, tick_low); - // assert_eq!(position.tick_high, tick_high); - - // // Check that 50% of fees were credited to the position - // let fee_rate = FeeRate::::get(NetUid::from(netuid)) as f64 / u16::MAX as f64; - // let (actual_fee_tao, actual_fee_alpha) = position.collect_fees(); - // let expected_fee = (fee_rate * (liquidity / 10) as f64 * 0.5) as u64; - - // assert_abs_diff_eq!(actual_fee_tao, expected_fee, epsilon = 1,); - // assert_abs_diff_eq!(actual_fee_alpha, expected_fee, epsilon = 1,); - // }); -} +// // Swap buy and swap sell +// Pallet::::do_swap( +// netuid, +// GetAlphaForTao::with_amount(liquidity / 10), +// u64::MAX.into(), +// false, +// false, +// ) +// .unwrap(); +// Pallet::::do_swap( +// netuid, +// GetTaoForAlpha::with_amount(liquidity / 10), +// 0_u64.into(), +// false, +// false, +// ) +// .unwrap(); + +// // Get user position +// let mut position = +// Positions::::get((netuid, OK_COLDKEY_ACCOUNT_ID, position_id)).unwrap(); +// assert_eq!(position.liquidity, liquidity); +// assert_eq!(position.tick_low, tick_low); +// assert_eq!(position.tick_high, tick_high); + +// // Check that 50% of fees were credited to the position +// let fee_rate = FeeRate::::get(NetUid::from(netuid)) as f64 / u16::MAX as f64; +// let (actual_fee_tao, actual_fee_alpha) = position.collect_fees(); +// let expected_fee = (fee_rate * (liquidity / 10) as f64 * 0.5) as u64; + +// assert_abs_diff_eq!(actual_fee_tao, expected_fee, epsilon = 1,); +// assert_abs_diff_eq!(actual_fee_alpha, expected_fee, epsilon = 1,); +// }); +// } #[test] fn test_rollback_works() { @@ -1780,81 +1432,80 @@ fn test_rollback_works() { }) } -/// Test correctness of swap fees: -/// - New LP is not eligible to previously accrued fees -/// -/// cargo test --package pallet-subtensor-swap --lib -- pallet::tests::test_new_lp_doesnt_get_old_fees --exact --show-output -#[test] -fn test_new_lp_doesnt_get_old_fees() { - todo!(); +// TODO: Revise when user liquidity is available +// Test correctness of swap fees: +// - New LP is not eligible to previously accrued fees +// +// cargo test --package pallet-subtensor-swap --lib -- pallet::tests::test_new_lp_doesnt_get_old_fees --exact --show-output +// #[test] +// fn test_new_lp_doesnt_get_old_fees() { +// new_test_ext().execute_with(|| { +// let min_price = tick_to_price(TickIndex::MIN); +// let max_price = tick_to_price(TickIndex::MAX); +// let netuid = NetUid::from(1); - // new_test_ext().execute_with(|| { - // let min_price = tick_to_price(TickIndex::MIN); - // let max_price = tick_to_price(TickIndex::MAX); - // let netuid = NetUid::from(1); +// // Provide very spread liquidity at the range from min to max that matches protocol liquidity +// let liquidity = 2_000_000_000_000_u64; // 1x of protocol liquidity - // // Provide very spread liquidity at the range from min to max that matches protocol liquidity - // let liquidity = 2_000_000_000_000_u64; // 1x of protocol liquidity +// assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); +// // Calculate ticks +// let tick_low = price_to_tick(min_price); +// let tick_high = price_to_tick(max_price); - // // Calculate ticks - // let tick_low = price_to_tick(min_price); - // let tick_high = price_to_tick(max_price); +// // Add user liquidity +// Pallet::::do_add_liquidity( +// netuid, +// &OK_COLDKEY_ACCOUNT_ID, +// &OK_HOTKEY_ACCOUNT_ID, +// tick_low, +// tick_high, +// liquidity, +// ) +// .unwrap(); - // // Add user liquidity - // Pallet::::do_add_liquidity( - // netuid, - // &OK_COLDKEY_ACCOUNT_ID, - // &OK_HOTKEY_ACCOUNT_ID, - // tick_low, - // tick_high, - // liquidity, - // ) - // .unwrap(); +// // Swap buy and swap sell +// Pallet::::do_swap( +// netuid, +// GetAlphaForTao::with_amount(liquidity / 10), +// u64::MAX.into(), +// false, +// false, +// ) +// .unwrap(); +// Pallet::::do_swap( +// netuid, +// GetTaoForAlpha::with_amount(liquidity / 10), +// 0_u64.into(), +// false, +// false, +// ) +// .unwrap(); - // // Swap buy and swap sell - // Pallet::::do_swap( - // netuid, - // GetAlphaForTao::with_amount(liquidity / 10), - // u64::MAX.into(), - // false, - // false, - // ) - // .unwrap(); - // Pallet::::do_swap( - // netuid, - // GetTaoForAlpha::with_amount(liquidity / 10), - // 0_u64.into(), - // false, - // false, - // ) - // .unwrap(); - - // // Add liquidity from a different user to a new tick - // let (position_id_2, _tao, _alpha) = Pallet::::do_add_liquidity( - // netuid, - // &OK_COLDKEY_ACCOUNT_ID_2, - // &OK_HOTKEY_ACCOUNT_ID_2, - // tick_low.next().unwrap(), - // tick_high.prev().unwrap(), - // liquidity, - // ) - // .unwrap(); - - // // Get user position - // let mut position = - // Positions::::get((netuid, OK_COLDKEY_ACCOUNT_ID_2, position_id_2)).unwrap(); - // assert_eq!(position.liquidity, liquidity); - // assert_eq!(position.tick_low, tick_low.next().unwrap()); - // assert_eq!(position.tick_high, tick_high.prev().unwrap()); - - // // Check that collected fees are 0 - // let (actual_fee_tao, actual_fee_alpha) = position.collect_fees(); - // assert_abs_diff_eq!(actual_fee_tao, 0, epsilon = 1); - // assert_abs_diff_eq!(actual_fee_alpha, 0, epsilon = 1); - // }); -} +// // Add liquidity from a different user to a new tick +// let (position_id_2, _tao, _alpha) = Pallet::::do_add_liquidity( +// netuid, +// &OK_COLDKEY_ACCOUNT_ID_2, +// &OK_HOTKEY_ACCOUNT_ID_2, +// tick_low.next().unwrap(), +// tick_high.prev().unwrap(), +// liquidity, +// ) +// .unwrap(); + +// // Get user position +// let mut position = +// Positions::::get((netuid, OK_COLDKEY_ACCOUNT_ID_2, position_id_2)).unwrap(); +// assert_eq!(position.liquidity, liquidity); +// assert_eq!(position.tick_low, tick_low.next().unwrap()); +// assert_eq!(position.tick_high, tick_high.prev().unwrap()); + +// // Check that collected fees are 0 +// let (actual_fee_tao, actual_fee_alpha) = position.collect_fees(); +// assert_abs_diff_eq!(actual_fee_tao, 0, epsilon = 1); +// assert_abs_diff_eq!(actual_fee_alpha, 0, epsilon = 1); +// }); +// } #[allow(dead_code)] fn bbox(t: U64F64, a: U64F64, b: U64F64) -> U64F64 { @@ -1873,192 +1524,190 @@ fn print_current_price(netuid: NetUid) { log::trace!("Current price: {current_price:.6}"); } -/// RUST_LOG=pallet_subtensor_swap=trace cargo test --package pallet-subtensor-swap --lib -- pallet::tests::test_wrapping_fees --exact --show-output --nocapture -#[test] -fn test_wrapping_fees() { - todo!(); - - // new_test_ext().execute_with(|| { - // let netuid = NetUid::from(WRAPPING_FEES_NETUID); - // let position_1_low_price = 0.20; - // let position_1_high_price = 0.255; - // let position_2_low_price = 0.255; - // let position_2_high_price = 0.257; - // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - - // Pallet::::do_add_liquidity( - // netuid, - // &OK_COLDKEY_ACCOUNT_ID_RICH, - // &OK_COLDKEY_ACCOUNT_ID_RICH, - // price_to_tick(position_1_low_price), - // price_to_tick(position_1_high_price), - // 1_000_000_000_u64, - // ) - // .unwrap(); - - // print_current_price(netuid); - - // let order = GetTaoForAlpha::with_amount(800_000_000); - // let sqrt_limit_price = SqrtPrice::from_num(0.000001); - // Pallet::::do_swap(netuid, order, sqrt_limit_price, false, false).unwrap(); +// TODO: Revise when user liquidity is available +// RUST_LOG=pallet_subtensor_swap=trace cargo test --package pallet-subtensor-swap --lib -- pallet::tests::test_wrapping_fees --exact --show-output --nocapture +// #[test] +// fn test_wrapping_fees() { +// new_test_ext().execute_with(|| { +// let netuid = NetUid::from(WRAPPING_FEES_NETUID); +// let position_1_low_price = 0.20; +// let position_1_high_price = 0.255; +// let position_2_low_price = 0.255; +// let position_2_high_price = 0.257; +// assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - // let order = GetAlphaForTao::with_amount(1_850_000_000); - // let sqrt_limit_price = SqrtPrice::from_num(1_000_000.0); +// Pallet::::do_add_liquidity( +// netuid, +// &OK_COLDKEY_ACCOUNT_ID_RICH, +// &OK_COLDKEY_ACCOUNT_ID_RICH, +// price_to_tick(position_1_low_price), +// price_to_tick(position_1_high_price), +// 1_000_000_000_u64, +// ) +// .unwrap(); - // print_current_price(netuid); +// print_current_price(netuid); - // Pallet::::do_swap(netuid, order, sqrt_limit_price, false, false).unwrap(); +// let order = GetTaoForAlpha::with_amount(800_000_000); +// let sqrt_limit_price = SqrtPrice::from_num(0.000001); +// Pallet::::do_swap(netuid, order, sqrt_limit_price, false, false).unwrap(); - // print_current_price(netuid); +// let order = GetAlphaForTao::with_amount(1_850_000_000); +// let sqrt_limit_price = SqrtPrice::from_num(1_000_000.0); - // let add_liquidity_result = Pallet::::do_add_liquidity( - // netuid, - // &OK_COLDKEY_ACCOUNT_ID_RICH, - // &OK_COLDKEY_ACCOUNT_ID_RICH, - // price_to_tick(position_2_low_price), - // price_to_tick(position_2_high_price), - // 1_000_000_000_u64, - // ) - // .unwrap(); +// print_current_price(netuid); - // let order = GetTaoForAlpha::with_amount(1_800_000_000); - // let sqrt_limit_price = SqrtPrice::from_num(0.000001); +// Pallet::::do_swap(netuid, order, sqrt_limit_price, false, false).unwrap(); - // let initial_sqrt_price = AlphaSqrtPrice::::get(netuid); - // Pallet::::do_swap(netuid, order, sqrt_limit_price, false, false).unwrap(); - // let final_sqrt_price = AlphaSqrtPrice::::get(netuid); +// print_current_price(netuid); - // print_current_price(netuid); +// let add_liquidity_result = Pallet::::do_add_liquidity( +// netuid, +// &OK_COLDKEY_ACCOUNT_ID_RICH, +// &OK_COLDKEY_ACCOUNT_ID_RICH, +// price_to_tick(position_2_low_price), +// price_to_tick(position_2_high_price), +// 1_000_000_000_u64, +// ) +// .unwrap(); - // let mut position = - // Positions::::get((netuid, &OK_COLDKEY_ACCOUNT_ID_RICH, add_liquidity_result.0)) - // .unwrap(); +// let order = GetTaoForAlpha::with_amount(1_800_000_000); +// let sqrt_limit_price = SqrtPrice::from_num(0.000001); - // let initial_box_price = bbox( - // initial_sqrt_price, - // position.tick_low.try_to_sqrt_price().unwrap(), - // position.tick_high.try_to_sqrt_price().unwrap(), - // ); +// let initial_sqrt_price = AlphaSqrtPrice::::get(netuid); +// Pallet::::do_swap(netuid, order, sqrt_limit_price, false, false).unwrap(); +// let final_sqrt_price = AlphaSqrtPrice::::get(netuid); - // let final_box_price = bbox( - // final_sqrt_price, - // position.tick_low.try_to_sqrt_price().unwrap(), - // position.tick_high.try_to_sqrt_price().unwrap(), - // ); +// print_current_price(netuid); - // let fee_rate = FeeRate::::get(netuid) as f64 / u16::MAX as f64; +// let mut position = +// Positions::::get((netuid, &OK_COLDKEY_ACCOUNT_ID_RICH, add_liquidity_result.0)) +// .unwrap(); - // log::trace!("fee_rate: {fee_rate:.6}"); - // log::trace!("position.liquidity: {}", position.liquidity); - // log::trace!( - // "initial_box_price: {:.6}", - // initial_box_price.to_num::() - // ); - // log::trace!("final_box_price: {:.6}", final_box_price.to_num::()); +// let initial_box_price = bbox( +// initial_sqrt_price, +// position.tick_low.try_to_sqrt_price().unwrap(), +// position.tick_high.try_to_sqrt_price().unwrap(), +// ); - // let expected_fee_tao = ((fee_rate / (1.0 - fee_rate)) - // * (position.liquidity as f64) - // * (final_box_price.to_num::() - initial_box_price.to_num::())) - // as u64; +// let final_box_price = bbox( +// final_sqrt_price, +// position.tick_low.try_to_sqrt_price().unwrap(), +// position.tick_high.try_to_sqrt_price().unwrap(), +// ); - // let expected_fee_alpha = ((fee_rate / (1.0 - fee_rate)) - // * (position.liquidity as f64) - // * ((1.0 / final_box_price.to_num::()) - (1.0 / initial_box_price.to_num::()))) - // as u64; +// let fee_rate = FeeRate::::get(netuid) as f64 / u16::MAX as f64; - // log::trace!("Expected ALPHA fee: {:.6}", expected_fee_alpha as f64); +// log::trace!("fee_rate: {fee_rate:.6}"); +// log::trace!("position.liquidity: {}", position.liquidity); +// log::trace!( +// "initial_box_price: {:.6}", +// initial_box_price.to_num::() +// ); +// log::trace!("final_box_price: {:.6}", final_box_price.to_num::()); - // let (fee_tao, fee_alpha) = position.collect_fees(); +// let expected_fee_tao = ((fee_rate / (1.0 - fee_rate)) +// * (position.liquidity as f64) +// * (final_box_price.to_num::() - initial_box_price.to_num::())) +// as u64; - // log::trace!("Collected fees: TAO: {fee_tao}, ALPHA: {fee_alpha}"); +// let expected_fee_alpha = ((fee_rate / (1.0 - fee_rate)) +// * (position.liquidity as f64) +// * ((1.0 / final_box_price.to_num::()) - (1.0 / initial_box_price.to_num::()))) +// as u64; - // assert_abs_diff_eq!(fee_tao, expected_fee_tao, epsilon = 1); - // assert_abs_diff_eq!(fee_alpha, expected_fee_alpha, epsilon = 1); - // }); -} +// log::trace!("Expected ALPHA fee: {:.6}", expected_fee_alpha as f64); -/// Test that price moves less with provided liquidity -/// cargo test --package pallet-subtensor-swap --lib -- pallet::tests::test_less_price_movement --exact --show-output -#[test] -fn test_less_price_movement() { - todo!(); +// let (fee_tao, fee_alpha) = position.collect_fees(); - // let netuid = NetUid::from(1); - // let mut last_end_price = U64F64::from_num(0); - // let initial_stake_liquidity = 1_000_000_000; - // let swapped_liquidity = 1_000_000; - - // // Test case is (order_type, provided_liquidity) - // // Testing algorithm: - // // - Stake initial_stake_liquidity - // // - Provide liquidity if iteration provides lq - // // - Buy or sell - // // - Save end price if iteration doesn't provide lq - // macro_rules! perform_test { - // ($order_t:ident, $provided_liquidity:expr, $limit_price:expr, $should_price_shrink:expr) => { - // let provided_liquidity = $provided_liquidity; - // let should_price_shrink = $should_price_shrink; - // let limit_price = $limit_price; - // new_test_ext().execute_with(|| { - // // Setup swap - // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); +// log::trace!("Collected fees: TAO: {fee_tao}, ALPHA: {fee_alpha}"); - // // Buy Alpha - // assert_ok!(Pallet::::do_swap( - // netuid, - // GetAlphaForTao::with_amount(initial_stake_liquidity), - // SqrtPrice::from_num(10_000_000_000_u64), - // false, - // false - // )); - - // // Get current price - // let start_price = Pallet::::current_price(netuid); - - // // Add liquidity if this test iteration provides - // if provided_liquidity > 0 { - // let tick_low = price_to_tick(start_price.to_num::() * 0.5); - // let tick_high = price_to_tick(start_price.to_num::() * 1.5); - // assert_ok!(Pallet::::do_add_liquidity( - // netuid, - // &OK_COLDKEY_ACCOUNT_ID, - // &OK_HOTKEY_ACCOUNT_ID, - // tick_low, - // tick_high, - // provided_liquidity, - // )); - // } - - // // Swap - // let sqrt_limit_price = SqrtPrice::from_num(limit_price); - // assert_ok!(Pallet::::do_swap( - // netuid, - // $order_t::with_amount(swapped_liquidity), - // sqrt_limit_price, - // false, - // false - // )); - - // let end_price = Pallet::::current_price(netuid); - - // // Save end price if iteration doesn't provide or compare with previous end price if - // // it does - // if provided_liquidity > 0 { - // assert_eq!(should_price_shrink, end_price < last_end_price); - // } else { - // last_end_price = end_price; - // } - // }); - // }; - // } +// assert_abs_diff_eq!(fee_tao, expected_fee_tao, epsilon = 1); +// assert_abs_diff_eq!(fee_alpha, expected_fee_alpha, epsilon = 1); +// }); +// } - // for provided_liquidity in [0, 1_000_000_000_000_u64] { - // perform_test!(GetAlphaForTao, provided_liquidity, 1000.0_f64, true); - // } - // for provided_liquidity in [0, 1_000_000_000_000_u64] { - // perform_test!(GetTaoForAlpha, provided_liquidity, 0.001_f64, false); - // } -} +// TODO: Revise when user liquidity is available +// Test that price moves less with more liquidity +// cargo test --package pallet-subtensor-swap --lib -- pallet::tests::test_less_price_movement --exact --show-output +// #[test] +// fn test_less_price_movement() { +// let netuid = NetUid::from(1); +// let mut last_end_price = U64F64::from_num(0); +// let initial_stake_liquidity = 1_000_000_000; +// let swapped_liquidity = 1_000_000; + +// // Test case is (order_type, provided_liquidity) +// // Testing algorithm: +// // - Stake initial_stake_liquidity +// // - Provide liquidity if iteration provides lq +// // - Buy or sell +// // - Save end price if iteration doesn't provide lq +// macro_rules! perform_test { +// ($order_t:ident, $provided_liquidity:expr, $limit_price:expr, $should_price_shrink:expr) => { +// let provided_liquidity = $provided_liquidity; +// let should_price_shrink = $should_price_shrink; +// let limit_price = $limit_price; +// new_test_ext().execute_with(|| { +// // Setup swap +// assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + +// // Buy Alpha +// assert_ok!(Pallet::::do_swap( +// netuid, +// GetAlphaForTao::with_amount(initial_stake_liquidity), +// SqrtPrice::from_num(10_000_000_000_u64), +// false, +// false +// )); + +// // Get current price +// let start_price = Pallet::::current_price(netuid); + +// // Add liquidity if this test iteration provides +// if provided_liquidity > 0 { +// let tick_low = price_to_tick(start_price.to_num::() * 0.5); +// let tick_high = price_to_tick(start_price.to_num::() * 1.5); +// assert_ok!(Pallet::::do_add_liquidity( +// netuid, +// &OK_COLDKEY_ACCOUNT_ID, +// &OK_HOTKEY_ACCOUNT_ID, +// tick_low, +// tick_high, +// provided_liquidity, +// )); +// } + +// // Swap +// let sqrt_limit_price = SqrtPrice::from_num(limit_price); +// assert_ok!(Pallet::::do_swap( +// netuid, +// $order_t::with_amount(swapped_liquidity), +// sqrt_limit_price, +// false, +// false +// )); + +// let end_price = Pallet::::current_price(netuid); + +// // Save end price if iteration doesn't provide or compare with previous end price if +// // it does +// if provided_liquidity > 0 { +// assert_eq!(should_price_shrink, end_price < last_end_price); +// } else { +// last_end_price = end_price; +// } +// }); +// }; +// } + +// for provided_liquidity in [0, 1_000_000_000_000_u64] { +// perform_test!(GetAlphaForTao, provided_liquidity, 1000.0_f64, true); +// } +// for provided_liquidity in [0, 1_000_000_000_000_u64] { +// perform_test!(GetTaoForAlpha, provided_liquidity, 0.001_f64, false); +// } +// } #[test] fn test_swap_subtoken_disabled() { @@ -2091,111 +1740,111 @@ fn test_swap_subtoken_disabled() { }); } -#[test] -fn test_liquidate_v3_removes_positions_ticks_and_state() { - todo!(); +// TODO: Revise when user liquidity is available +// #[test] +// fn test_liquidate_v3_removes_positions_ticks_and_state() { +// new_test_ext().execute_with(|| { +// let netuid = NetUid::from(1); - // new_test_ext().execute_with(|| { - // let netuid = NetUid::from(1); +// // Initialize V3 (creates protocol position, ticks, price, liquidity) +// assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); +// assert!(PalSwapInitialized::::get(netuid)); - // // Initialize V3 (creates protocol position, ticks, price, liquidity) - // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - // assert!(PalSwapInitialized::::get(netuid)); +// // Enable user LP +// assert_ok!(Swap::toggle_user_liquidity( +// RuntimeOrigin::root(), +// netuid.into(), +// true +// )); - // // Enable user LP - // assert_ok!(Swap::toggle_user_liquidity( - // RuntimeOrigin::root(), - // netuid.into(), - // true - // )); - - // // Add a user position across the full range to ensure ticks/bitmap are populated. - // let min_price = get_min_price(); - // let max_price = get_max_price(); - // let liquidity = 2_000_000_000_u64; +// // Add a user position across the full range to ensure ticks/bitmap are populated. +// let min_price = get_min_price(); +// let max_price = get_max_price(); +// let liquidity = 2_000_000_000_u64; - // let (_pos_id, _tao, _alpha) = Pallet::::do_add_liquidity( - // netuid, - // &OK_COLDKEY_ACCOUNT_ID, - // &OK_HOTKEY_ACCOUNT_ID, - // liquidity, - // ) - // .expect("add liquidity"); +// let (_pos_id, _tao, _alpha) = Pallet::::do_add_liquidity( +// netuid, +// &OK_COLDKEY_ACCOUNT_ID, +// &OK_HOTKEY_ACCOUNT_ID, +// liquidity, +// ) +// .expect("add liquidity"); - // // Accrue some global fees so we can verify fee storage is cleared later. - // let sqrt_limit_price = SqrtPrice::from_num(1_000_000.0); - // assert_ok!(Pallet::::do_swap( - // netuid, - // GetAlphaForTao::with_amount(1_000_000), - // sqrt_limit_price, - // false, - // false - // )); +// // Accrue some global fees so we can verify fee storage is cleared later. +// let sqrt_limit_price = SqrtPrice::from_num(1_000_000.0); +// assert_ok!(Pallet::::do_swap( +// netuid, +// GetAlphaForTao::with_amount(1_000_000), +// sqrt_limit_price, +// false, +// false +// )); - // // Sanity: protocol & user positions exist, ticks exist, liquidity > 0 - // let protocol_id = Pallet::::protocol_account_id(); - // let prot_positions = - // Positions::::iter_prefix_values((netuid, protocol_id)).collect::>(); - // assert!(!prot_positions.is_empty()); +// // Sanity: protocol & user positions exist, ticks exist, liquidity > 0 +// let protocol_id = Pallet::::protocol_account_id(); +// let prot_positions = +// Positions::::iter_prefix_values((netuid, protocol_id)).collect::>(); +// assert!(!prot_positions.is_empty()); - // let user_positions = Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) - // .collect::>(); - // assert_eq!(user_positions.len(), 1); +// let user_positions = Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) +// .collect::>(); +// assert_eq!(user_positions.len(), 1); - // assert!(Ticks::::get(netuid, TickIndex::MIN).is_some()); - // assert!(Ticks::::get(netuid, TickIndex::MAX).is_some()); - // assert!(CurrentLiquidity::::get(netuid) > 0); +// assert!(Ticks::::get(netuid, TickIndex::MIN).is_some()); +// assert!(Ticks::::get(netuid, TickIndex::MAX).is_some()); +// assert!(CurrentLiquidity::::get(netuid) > 0); - // let had_bitmap_words = TickIndexBitmapWords::::iter_prefix((netuid,)) - // .next() - // .is_some(); - // assert!(had_bitmap_words); +// let had_bitmap_words = TickIndexBitmapWords::::iter_prefix((netuid,)) +// .next() +// .is_some(); +// assert!(had_bitmap_words); - // // ACT: users-only liquidation then protocol clear - // assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - // assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); +// // ACT: users-only liquidation then protocol clear +// assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); +// assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); - // // ASSERT: positions cleared (both user and protocol) - // assert_eq!( - // Pallet::::count_positions(netuid, &OK_COLDKEY_ACCOUNT_ID), - // 0 - // ); - // let prot_positions_after = - // Positions::::iter_prefix_values((netuid, protocol_id)).collect::>(); - // assert!(prot_positions_after.is_empty()); - // let user_positions_after = - // Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) - // .collect::>(); - // assert!(user_positions_after.is_empty()); +// // ASSERT: positions cleared (both user and protocol) +// assert_eq!( +// Pallet::::count_positions(netuid, &OK_COLDKEY_ACCOUNT_ID), +// 0 +// ); +// let prot_positions_after = +// Positions::::iter_prefix_values((netuid, protocol_id)).collect::>(); +// assert!(prot_positions_after.is_empty()); +// let user_positions_after = +// Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) +// .collect::>(); +// assert!(user_positions_after.is_empty()); - // // ASSERT: ticks cleared - // 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: ticks cleared +// 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: fee globals cleared - // assert!(!FeeGlobalTao::::contains_key(netuid)); - // assert!(!FeeGlobalAlpha::::contains_key(netuid)); +// // ASSERT: fee globals cleared +// assert!(!FeeGlobalTao::::contains_key(netuid)); +// assert!(!FeeGlobalAlpha::::contains_key(netuid)); - // // ASSERT: price/tick/liquidity flags cleared - // assert!(!AlphaSqrtPrice::::contains_key(netuid)); - // assert!(!CurrentTick::::contains_key(netuid)); - // assert!(!CurrentLiquidity::::contains_key(netuid)); - // assert!(!PalSwapInitialized::::contains_key(netuid)); +// // ASSERT: price/tick/liquidity flags cleared +// assert!(!AlphaSqrtPrice::::contains_key(netuid)); +// assert!(!CurrentTick::::contains_key(netuid)); +// assert!(!CurrentLiquidity::::contains_key(netuid)); +// assert!(!PalSwapInitialized::::contains_key(netuid)); - // // ASSERT: active tick bitmap cleared - // assert!( - // TickIndexBitmapWords::::iter_prefix((netuid,)) - // .next() - // .is_none() - // ); +// // ASSERT: active tick bitmap cleared +// assert!( +// TickIndexBitmapWords::::iter_prefix((netuid,)) +// .next() +// .is_none() +// ); - // // ASSERT: knobs removed on dereg - // assert!(!FeeRate::::contains_key(netuid)); - // assert!(!EnabledUserLiquidity::::contains_key(netuid)); - // }); -} +// // ASSERT: knobs removed on dereg +// assert!(!FeeRate::::contains_key(netuid)); +// assert!(!EnabledUserLiquidity::::contains_key(netuid)); +// }); +// } +// TODO: Revise when user liquidity is available // V3 path with user liquidity disabled at teardown: // must still remove positions and clear state (after protocol clear). // #[test] @@ -2268,565 +1917,600 @@ fn test_liquidate_v3_removes_positions_ticks_and_state() { // }); // } -/// Non‑V3 path: V3 not initialized (no positions); function must still clear any residual storages and succeed. -#[test] -fn test_liquidate_non_v3_uninitialized_ok_and_clears() { - todo!(); +// Non‑palswap path: PalSwap not initialized (no positions, no map values); function +// must still clear any residual storages and succeed. +// TODO: Revise when user liquidity is available +// #[test] +// fn test_liquidate_pal_uninitialized_ok_and_clears() { +// new_test_ext().execute_with(|| { +// let netuid = NetUid::from(202); - // new_test_ext().execute_with(|| { - // let netuid = NetUid::from(202); +// // Insert map values +// PalSwapInitialized::::insert(netuid, false); - // // Sanity: V3 is not initialized - // assert!(!PalSwapInitialized::::get(netuid)); - // assert!( - // Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) - // .next() - // .is_none() - // ); +// // Sanity: PalSwap is not initialized +// assert!(!PalSwapInitialized::::get(netuid)); +// assert!( +// PositionsV2::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) +// .next() +// .is_none() +// ); - // // ACT - // assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); +// // ACT +// assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - // // ASSERT: Defensive clears leave no residues and do not panic - // assert!( - // Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) - // .next() - // .is_none() - // ); - // assert!(Ticks::::iter_prefix(netuid).next().is_none()); - // assert!( - // TickIndexBitmapWords::::iter_prefix((netuid,)) - // .next() - // .is_none() - // ); +// // ASSERT: Defensive clears leave no residues and do not panic +// assert!( +// PositionsV2::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) +// .next() +// .is_none() +// ); - // // All single-key maps should not have the key after liquidation - // assert!(!FeeGlobalTao::::contains_key(netuid)); - // assert!(!FeeGlobalAlpha::::contains_key(netuid)); - // assert!(!CurrentLiquidity::::contains_key(netuid)); - // assert!(!CurrentTick::::contains_key(netuid)); - // assert!(!AlphaSqrtPrice::::contains_key(netuid)); - // assert!(!PalSwapInitialized::::contains_key(netuid)); - // assert!(!FeeRate::::contains_key(netuid)); - // assert!(!EnabledUserLiquidity::::contains_key(netuid)); - // }); -} +// // All single-key maps should not have the key after liquidation +// assert!(!FeeRate::::contains_key(netuid)); +// assert!(!EnabledUserLiquidity::::contains_key(netuid)); +// assert!(!FeesTao::::contains_key(netuid)); +// assert!(!FeesAlpha::::contains_key(netuid)); +// assert!(!PalSwapInitialized::::contains_key(netuid)); +// assert!(!SwapReserveWeight::::contains_key(netuid)); +// }); +// } +/// Simple palswap path: PalSwap is initialized, but no positions, only protocol; function +/// must still clear any residual storages and succeed. +/// TODO: Revise when user liquidity is available #[test] -fn test_liquidate_idempotent() { - todo!(); +fn test_liquidate_pal_simple_ok_and_clears() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(202); + + // Insert map values + FeeRate::::insert(netuid, 1_000); + EnabledUserLiquidity::::insert(netuid, false); + FeesTao::::insert(netuid, TaoCurrency::from(1_000)); + FeesAlpha::::insert(netuid, AlphaCurrency::from(1_000)); + PalSwapInitialized::::insert(netuid, true); + let w_quote_pt = Perquintill::from_rational(1u128, 2u128); + let rw = ReserveWeight::new(w_quote_pt).unwrap(); + SwapReserveWeight::::insert(netuid, rw); + + // Sanity: PalSwap is not initialized + assert!(PalSwapInitialized::::get(netuid)); + assert!( + PositionsV2::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) + .next() + .is_none() + ); - // // V3 flavor - // new_test_ext().execute_with(|| { - // let netuid = NetUid::from(7); - // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + // ACT + assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); - // // Add a small user position - // assert_ok!(Swap::toggle_user_liquidity( - // RuntimeOrigin::root(), - // netuid.into(), - // true - // )); - // let tick_low = price_to_tick(0.2); - // let tick_high = price_to_tick(0.3); - // assert_ok!(Pallet::::do_add_liquidity( - // netuid, - // &OK_COLDKEY_ACCOUNT_ID, - // &OK_HOTKEY_ACCOUNT_ID, - // tick_low, - // tick_high, - // 123_456_789 - // )); + // ASSERT: Defensive clears leave no residues and do not panic + assert!( + PositionsV2::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) + .next() + .is_none() + ); - // // Users-only liquidations are idempotent. - // assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - // assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); + // All single-key maps should not have the key after liquidation + assert!(!FeeRate::::contains_key(netuid)); + assert!(!EnabledUserLiquidity::::contains_key(netuid)); + assert!(!FeesTao::::contains_key(netuid)); + assert!(!FeesAlpha::::contains_key(netuid)); + assert!(!PalSwapInitialized::::contains_key(netuid)); + assert!(!SwapReserveWeight::::contains_key(netuid)); + }); +} - // // Now clear protocol liquidity/state—also idempotent. - // assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); - // assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); +// TODO: Revise when user liquidity is available +// #[test] +// fn test_liquidate_idempotent() { +// // V3 flavor +// new_test_ext().execute_with(|| { +// let netuid = NetUid::from(7); +// assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - // // State remains empty - // assert!( - // Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) - // .next() - // .is_none() - // ); - // assert!(Ticks::::iter_prefix(netuid).next().is_none()); - // assert!( - // TickIndexBitmapWords::::iter_prefix((netuid,)) - // .next() - // .is_none() - // ); - // assert!(!PalSwapInitialized::::contains_key(netuid)); - // }); +// // Add a small user position +// assert_ok!(Swap::toggle_user_liquidity( +// RuntimeOrigin::root(), +// netuid.into(), +// true +// )); +// let tick_low = price_to_tick(0.2); +// let tick_high = price_to_tick(0.3); +// assert_ok!(Pallet::::do_add_liquidity( +// netuid, +// &OK_COLDKEY_ACCOUNT_ID, +// &OK_HOTKEY_ACCOUNT_ID, +// tick_low, +// tick_high, +// 123_456_789 +// )); - // // Non‑V3 flavor - // new_test_ext().execute_with(|| { - // let netuid = NetUid::from(8); +// // Users-only liquidations are idempotent. +// assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); +// assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - // // Never initialize V3; both calls no-op and succeed. - // assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - // assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); +// // Now clear protocol liquidity/state—also idempotent. +// assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); +// assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); - // assert!( - // Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) - // .next() - // .is_none() - // ); - // assert!(Ticks::::iter_prefix(netuid).next().is_none()); - // assert!( - // TickIndexBitmapWords::::iter_prefix((netuid,)) - // .next() - // .is_none() - // ); - // assert!(!PalSwapInitialized::::contains_key(netuid)); - // }); -} +// // State remains empty +// assert!( +// Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) +// .next() +// .is_none() +// ); +// assert!(Ticks::::iter_prefix(netuid).next().is_none()); +// assert!( +// TickIndexBitmapWords::::iter_prefix((netuid,)) +// .next() +// .is_none() +// ); +// assert!(!PalSwapInitialized::::contains_key(netuid)); +// }); -#[test] -fn liquidate_v3_refunds_user_funds_and_clears_state() { - todo!(); +// // Non‑V3 flavor +// new_test_ext().execute_with(|| { +// let netuid = NetUid::from(8); - // new_test_ext().execute_with(|| { - // let netuid = NetUid::from(1); +// // Never initialize V3; both calls no-op and succeed. +// assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); +// assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - // // 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_palswap(netuid)); +// assert!( +// Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) +// .next() +// .is_none() +// ); +// assert!(Ticks::::iter_prefix(netuid).next().is_none()); +// assert!( +// TickIndexBitmapWords::::iter_prefix((netuid,)) +// .next() +// .is_none() +// ); +// assert!(!PalSwapInitialized::::contains_key(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"); - // let alpha_taken = ::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(), alpha_taken); - - // // 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)" - // ); +// TODO: Revise when user liquidity is available +// #[test] +// fn liquidate_v3_refunds_user_funds_and_clears_state() { +// new_test_ext().execute_with(|| { +// let netuid = NetUid::from(1); - // // Clear protocol liquidity and V3 state now. - // assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); +// // 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_palswap(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!(!PalSwapInitialized::::contains_key(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"); +// let alpha_taken = ::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(), alpha_taken); -#[test] -fn refund_alpha_single_provider_exact() { - todo!(); +// // Users‑only liquidation. +// assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - // new_test_ext().execute_with(|| { - // let netuid = NetUid::from(11); - // let cold = OK_COLDKEY_ACCOUNT_ID; - // let hot = OK_HOTKEY_ACCOUNT_ID; +// // 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"); - // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); +// // 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)" +// ); - // // --- Create an alpha‑only position (range entirely above current tick → TAO = 0, ALPHA > 0). - // let ct = CurrentTick::::get(netuid); - // let tick_low = ct.next().expect("current tick should not be MAX in tests"); - // let tick_high = TickIndex::MAX; - - // let liquidity = 1_000_000_u64; - // let (_pos_id, tao_needed, alpha_needed) = - // Pallet::::do_add_liquidity(netuid, &cold, &hot, tick_low, tick_high, liquidity) - // .expect("add alpha-only liquidity"); - // assert_eq!(tao_needed, 0, "alpha-only position must not require TAO"); - // assert!(alpha_needed > 0, "alpha-only position must require ALPHA"); - - // // --- Snapshot BEFORE we withdraw funds (baseline for conservation). - // 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; - - // // --- Mimic extrinsic bookkeeping: withdraw α and record provided reserve. - // let alpha_taken = ::BalanceOps::decrease_stake( - // &cold, - // &hot, - // netuid.into(), - // alpha_needed.into(), - // ) - // .expect("decrease ALPHA"); - // AlphaReserve::increase_provided(netuid.into(), alpha_taken); - - // // --- Act: users‑only dissolve. - // assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - - // // --- Assert: total α conserved to owner (may be staked to validator). - // 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 conserved to the account" - // ); +// // Clear protocol liquidity and V3 state now. +// assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); - // // 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!(!PalSwapInitialized::::contains_key(netuid)); +// }); +// } - // // --- State is cleared. - // assert!(Ticks::::iter_prefix(netuid).next().is_none()); - // assert_eq!(Pallet::::count_positions(netuid, &cold), 0); - // assert!(!PalSwapInitialized::::contains_key(netuid)); - // }); -} +// TODO: Revise when user liquidity is available +// #[test] +// fn refund_alpha_single_provider_exact() { +// new_test_ext().execute_with(|| { +// let netuid = NetUid::from(11); +// let cold = OK_COLDKEY_ACCOUNT_ID; +// let hot = OK_HOTKEY_ACCOUNT_ID; -#[test] -fn refund_alpha_multiple_providers_proportional_to_principal() { - todo!(); +// assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - // new_test_ext().execute_with(|| { - // let netuid = NetUid::from(12); - // let c1 = OK_COLDKEY_ACCOUNT_ID; - // let h1 = OK_HOTKEY_ACCOUNT_ID; - // let c2 = OK_COLDKEY_ACCOUNT_ID_2; - // let h2 = OK_HOTKEY_ACCOUNT_ID_2; +// // --- Create an alpha‑only position (range entirely above current tick → TAO = 0, ALPHA > 0). +// let ct = CurrentTick::::get(netuid); +// let tick_low = ct.next().expect("current tick should not be MAX in tests"); +// let tick_high = TickIndex::MAX; + +// let liquidity = 1_000_000_u64; +// let (_pos_id, tao_needed, alpha_needed) = +// Pallet::::do_add_liquidity(netuid, &cold, &hot, tick_low, tick_high, liquidity) +// .expect("add alpha-only liquidity"); +// assert_eq!(tao_needed, 0, "alpha-only position must not require TAO"); +// assert!(alpha_needed > 0, "alpha-only position must require ALPHA"); + +// // --- Snapshot BEFORE we withdraw funds (baseline for conservation). +// 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; + +// // --- Mimic extrinsic bookkeeping: withdraw α and record provided reserve. +// let alpha_taken = ::BalanceOps::decrease_stake( +// &cold, +// &hot, +// netuid.into(), +// alpha_needed.into(), +// ) +// .expect("decrease ALPHA"); +// AlphaReserve::increase_provided(netuid.into(), alpha_taken); - // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); +// // --- Act: users‑only dissolve. +// assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - // // Use the same "above current tick" trick for alpha‑only positions. - // let ct = CurrentTick::::get(netuid); - // let tick_low = ct.next().expect("current tick should not be MAX in tests"); - // let tick_high = TickIndex::MAX; - - // // Provider #1 (smaller α) - // let liq1 = 700_000_u64; - // let (_p1, t1, a1) = - // Pallet::::do_add_liquidity(netuid, &c1, &h1, tick_low, tick_high, liq1) - // .expect("add alpha-only liquidity #1"); - // assert_eq!(t1, 0); - // assert!(a1 > 0); - - // // Provider #2 (larger α) - // let liq2 = 2_100_000_u64; - // let (_p2, t2, a2) = - // Pallet::::do_add_liquidity(netuid, &c2, &h2, tick_low, tick_high, liq2) - // .expect("add alpha-only liquidity #2"); - // assert_eq!(t2, 0); - // assert!(a2 > 0); - - // // Baselines BEFORE withdrawing - // let a1_before_hot = ::BalanceOps::alpha_balance(netuid.into(), &c1, &h1); - // let a1_before_owner = ::BalanceOps::alpha_balance(netuid.into(), &c1, &c1); - // let a1_before = a1_before_hot + a1_before_owner; - - // let a2_before_hot = ::BalanceOps::alpha_balance(netuid.into(), &c2, &h2); - // let a2_before_owner = ::BalanceOps::alpha_balance(netuid.into(), &c2, &c2); - // let a2_before = a2_before_hot + a2_before_owner; - - // // Withdraw α and account reserves for each provider. - // let a1_taken = - // ::BalanceOps::decrease_stake(&c1, &h1, netuid.into(), a1.into()) - // .expect("decrease α #1"); - // AlphaReserve::increase_provided(netuid.into(), a1_taken); - - // let a2_taken = - // ::BalanceOps::decrease_stake(&c2, &h2, netuid.into(), a2.into()) - // .expect("decrease α #2"); - // AlphaReserve::increase_provided(netuid.into(), a2_taken); - - // // Act - // assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - - // // Each owner is restored to their exact baseline. - // let a1_after_hot = ::BalanceOps::alpha_balance(netuid.into(), &c1, &h1); - // let a1_after_owner = ::BalanceOps::alpha_balance(netuid.into(), &c1, &c1); - // let a1_after = a1_after_hot + a1_after_owner; - // assert_eq!( - // a1_after, a1_before, - // "owner #1 must receive their α principal back" - // ); +// // --- Assert: total α conserved to owner (may be staked to validator). +// 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 conserved to the account" +// ); - // let a2_after_hot = ::BalanceOps::alpha_balance(netuid.into(), &c2, &h2); - // let a2_after_owner = ::BalanceOps::alpha_balance(netuid.into(), &c2, &c2); - // let a2_after = a2_after_hot + a2_after_owner; - // assert_eq!( - // a2_after, a2_before, - // "owner #2 must receive their α principal back" - // ); - // }); -} +// // Clear protocol liquidity and V3 state now. +// assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); -#[test] -fn refund_alpha_same_cold_multiple_hotkeys_conserved_to_owner() { - todo!(); +// // --- State is cleared. +// assert!(Ticks::::iter_prefix(netuid).next().is_none()); +// assert_eq!(Pallet::::count_positions(netuid, &cold), 0); +// assert!(!PalSwapInitialized::::contains_key(netuid)); +// }); +// } - // new_test_ext().execute_with(|| { - // let netuid = NetUid::from(13); - // let cold = OK_COLDKEY_ACCOUNT_ID; - // let hot1 = OK_HOTKEY_ACCOUNT_ID; - // let hot2 = OK_HOTKEY_ACCOUNT_ID_2; +// TODO: Revise when user liquidity is available +// #[test] +// fn refund_alpha_multiple_providers_proportional_to_principal() { +// new_test_ext().execute_with(|| { +// let netuid = NetUid::from(12); +// let c1 = OK_COLDKEY_ACCOUNT_ID; +// let h1 = OK_HOTKEY_ACCOUNT_ID; +// let c2 = OK_COLDKEY_ACCOUNT_ID_2; +// let h2 = OK_HOTKEY_ACCOUNT_ID_2; - // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); +// assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - // // Two alpha‑only positions on different hotkeys of the same owner. - // let ct = CurrentTick::::get(netuid); - // let tick_low = ct.next().expect("current tick should not be MAX in tests"); - // let tick_high = TickIndex::MAX; - - // let (_p1, _t1, a1) = - // Pallet::::do_add_liquidity(netuid, &cold, &hot1, tick_low, tick_high, 900_000) - // .expect("add alpha-only pos (hot1)"); - // let (_p2, _t2, a2) = - // Pallet::::do_add_liquidity(netuid, &cold, &hot2, tick_low, tick_high, 1_500_000) - // .expect("add alpha-only pos (hot2)"); - // assert!(a1 > 0 && a2 > 0); - - // // Baseline BEFORE: sum over (cold,hot1) + (cold,hot2) + (cold,cold). - // let before_hot1 = ::BalanceOps::alpha_balance(netuid.into(), &cold, &hot1); - // let before_hot2 = ::BalanceOps::alpha_balance(netuid.into(), &cold, &hot2); - // let before_owner = ::BalanceOps::alpha_balance(netuid.into(), &cold, &cold); - // let before_total = before_hot1 + before_hot2 + before_owner; - - // // Withdraw α from both hotkeys; track provided‑reserve. - // let t1 = - // ::BalanceOps::decrease_stake(&cold, &hot1, netuid.into(), a1.into()) - // .expect("decr α #hot1"); - // AlphaReserve::increase_provided(netuid.into(), t1); - - // let t2 = - // ::BalanceOps::decrease_stake(&cold, &hot2, netuid.into(), a2.into()) - // .expect("decr α #hot2"); - // AlphaReserve::increase_provided(netuid.into(), t2); - - // // Act - // assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - - // // The total α "owned" by the coldkey is conserved (credit may land on (cold,cold)). - // let after_hot1 = ::BalanceOps::alpha_balance(netuid.into(), &cold, &hot1); - // let after_hot2 = ::BalanceOps::alpha_balance(netuid.into(), &cold, &hot2); - // let after_owner = ::BalanceOps::alpha_balance(netuid.into(), &cold, &cold); - // let after_total = after_hot1 + after_hot2 + after_owner; - - // assert_eq!( - // after_total, before_total, - // "owner’s α must be conserved across hot ledgers + (owner,owner)" - // ); - // }); -} +// // Use the same "above current tick" trick for alpha‑only positions. +// let ct = CurrentTick::::get(netuid); +// let tick_low = ct.next().expect("current tick should not be MAX in tests"); +// let tick_high = TickIndex::MAX; + +// // Provider #1 (smaller α) +// let liq1 = 700_000_u64; +// let (_p1, t1, a1) = +// Pallet::::do_add_liquidity(netuid, &c1, &h1, tick_low, tick_high, liq1) +// .expect("add alpha-only liquidity #1"); +// assert_eq!(t1, 0); +// assert!(a1 > 0); + +// // Provider #2 (larger α) +// let liq2 = 2_100_000_u64; +// let (_p2, t2, a2) = +// Pallet::::do_add_liquidity(netuid, &c2, &h2, tick_low, tick_high, liq2) +// .expect("add alpha-only liquidity #2"); +// assert_eq!(t2, 0); +// assert!(a2 > 0); + +// // Baselines BEFORE withdrawing +// let a1_before_hot = ::BalanceOps::alpha_balance(netuid.into(), &c1, &h1); +// let a1_before_owner = ::BalanceOps::alpha_balance(netuid.into(), &c1, &c1); +// let a1_before = a1_before_hot + a1_before_owner; + +// let a2_before_hot = ::BalanceOps::alpha_balance(netuid.into(), &c2, &h2); +// let a2_before_owner = ::BalanceOps::alpha_balance(netuid.into(), &c2, &c2); +// let a2_before = a2_before_hot + a2_before_owner; + +// // Withdraw α and account reserves for each provider. +// let a1_taken = +// ::BalanceOps::decrease_stake(&c1, &h1, netuid.into(), a1.into()) +// .expect("decrease α #1"); +// AlphaReserve::increase_provided(netuid.into(), a1_taken); + +// let a2_taken = +// ::BalanceOps::decrease_stake(&c2, &h2, netuid.into(), a2.into()) +// .expect("decrease α #2"); +// AlphaReserve::increase_provided(netuid.into(), a2_taken); + +// // Act +// assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); -#[test] -fn test_dissolve_v3_green_path_refund_tao_stake_alpha_and_clear_state() { - todo!(); +// // Each owner is restored to their exact baseline. +// let a1_after_hot = ::BalanceOps::alpha_balance(netuid.into(), &c1, &h1); +// let a1_after_owner = ::BalanceOps::alpha_balance(netuid.into(), &c1, &c1); +// let a1_after = a1_after_hot + a1_after_owner; +// assert_eq!( +// a1_after, a1_before, +// "owner #1 must receive their α principal back" +// ); - // new_test_ext().execute_with(|| { - // // --- Setup --- - // let netuid = NetUid::from(42); - // let cold = OK_COLDKEY_ACCOUNT_ID; - // let hot = OK_HOTKEY_ACCOUNT_ID; +// let a2_after_hot = ::BalanceOps::alpha_balance(netuid.into(), &c2, &h2); +// let a2_after_owner = ::BalanceOps::alpha_balance(netuid.into(), &c2, &c2); +// let a2_after = a2_after_hot + a2_after_owner; +// assert_eq!( +// a2_after, a2_before, +// "owner #2 must receive their α principal back" +// ); +// }); +// } - // assert_ok!(Swap::toggle_user_liquidity( - // RuntimeOrigin::root(), - // netuid.into(), - // true - // )); - // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - // assert!(PalSwapInitialized::::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"); - // let alpha_taken = ::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_taken); - - // // --- 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)" - // ); +// TODO: Revise when user liquidity is available +// #[test] +// fn refund_alpha_same_cold_multiple_hotkeys_conserved_to_owner() { +// new_test_ext().execute_with(|| { +// let netuid = NetUid::from(13); +// let cold = OK_COLDKEY_ACCOUNT_ID; +// let hot1 = OK_HOTKEY_ACCOUNT_ID; +// let hot2 = OK_HOTKEY_ACCOUNT_ID_2; - // 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" - // ); +// assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - // 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" - // ); +// // Two alpha‑only positions on different hotkeys of the same owner. +// let ct = CurrentTick::::get(netuid); +// let tick_low = ct.next().expect("current tick should not be MAX in tests"); +// let tick_high = TickIndex::MAX; + +// let (_p1, _t1, a1) = +// Pallet::::do_add_liquidity(netuid, &cold, &hot1, tick_low, tick_high, 900_000) +// .expect("add alpha-only pos (hot1)"); +// let (_p2, _t2, a2) = +// Pallet::::do_add_liquidity(netuid, &cold, &hot2, tick_low, tick_high, 1_500_000) +// .expect("add alpha-only pos (hot2)"); +// assert!(a1 > 0 && a2 > 0); + +// // Baseline BEFORE: sum over (cold,hot1) + (cold,hot2) + (cold,cold). +// let before_hot1 = ::BalanceOps::alpha_balance(netuid.into(), &cold, &hot1); +// let before_hot2 = ::BalanceOps::alpha_balance(netuid.into(), &cold, &hot2); +// let before_owner = ::BalanceOps::alpha_balance(netuid.into(), &cold, &cold); +// let before_total = before_hot1 + before_hot2 + before_owner; + +// // Withdraw α from both hotkeys; track provided‑reserve. +// let t1 = +// ::BalanceOps::decrease_stake(&cold, &hot1, netuid.into(), a1.into()) +// .expect("decr α #hot1"); +// AlphaReserve::increase_provided(netuid.into(), t1); + +// let t2 = +// ::BalanceOps::decrease_stake(&cold, &hot2, netuid.into(), a2.into()) +// .expect("decr α #hot2"); +// AlphaReserve::increase_provided(netuid.into(), t2); + +// // Act +// assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - // 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" - // ); - // } +// // The total α "owned" by the coldkey is conserved (credit may land on (cold,cold)). +// let after_hot1 = ::BalanceOps::alpha_balance(netuid.into(), &cold, &hot1); +// let after_hot2 = ::BalanceOps::alpha_balance(netuid.into(), &cold, &hot2); +// let after_owner = ::BalanceOps::alpha_balance(netuid.into(), &cold, &cold); +// let after_total = after_hot1 + after_hot2 + after_owner; - // // Now clear protocol liquidity & state and assert full reset. - // assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); +// assert_eq!( +// after_total, before_total, +// "owner’s α must be conserved across hot ledgers + (owner,owner)" +// ); +// }); +// } - // 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" - // ); +// TODO: Revise when user liquidity is available +// #[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!(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!(!PalSwapInitialized::::contains_key(netuid)); +// assert_ok!(Swap::toggle_user_liquidity( +// RuntimeOrigin::root(), +// netuid.into(), +// true +// )); +// assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); +// assert!(PalSwapInitialized::::get(netuid)); - // assert!(!FeeGlobalTao::::contains_key(netuid)); - // assert!(!FeeGlobalAlpha::::contains_key(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"); +// let alpha_taken = ::BalanceOps::decrease_stake( +// &cold, +// &hot, +// netuid.into(), +// alpha_needed.into(), +// ) +// .expect("decrease ALPHA"); - // assert!( - // TickIndexBitmapWords::::iter_prefix((netuid,)) - // .next() - // .is_none(), - // "active tick bitmap words must be cleared" - // ); +// TaoReserve::increase_provided(netuid.into(), tao_taken); +// AlphaReserve::increase_provided(netuid.into(), alpha_taken); - // assert!(!FeeRate::::contains_key(netuid)); - // assert!(!EnabledUserLiquidity::::contains_key(netuid)); - // }); -} +// // --- 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!(!PalSwapInitialized::::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() { From 9de9f7c67f54c0fe3f1c7bcbe1b314017a90cf3d Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Tue, 23 Dec 2025 10:46:45 +0100 Subject: [PATCH 072/240] allow for reannouncement in tx ext --- pallets/subtensor/src/macros/dispatches.rs | 1 + pallets/subtensor/src/tests/swap_coldkey.rs | 13 ++++++++++++- pallets/subtensor/src/transaction_extension.rs | 3 ++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index ba8f6819d8..a5fdeb4b9e 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2381,6 +2381,7 @@ mod dispatches { let new_when = when.saturating_add(reannouncement_delay); ensure!(now >= new_when, Error::::ColdkeySwapReannouncedTooEarly); } else { + // Only charge the swap cost on the first announcement let swap_cost = Self::get_key_swap_cost(); Self::charge_swap_cost(&who, swap_cost)?; } diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index e9b6cf4c0b..d59e469f0a 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -1272,12 +1272,15 @@ fn test_remove_coldkey_swap_announcement_with_bad_origin_fails() { } #[test] -fn test_subtensor_extension_rejects_any_call_that_is_not_swap_coldkey_announced() { +fn test_subtensor_extension_rejects_any_call_that_is_not_announce_or_swap() { new_test_ext(0).execute_with(|| { let netuid = NetUid::from(1); let who = U256::from(0); let new_coldkey = U256::from(1); + let another_coldkey = U256::from(3); let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); + let another_coldkey_hash = + ::Hashing::hash_of(&another_coldkey); let hotkey = U256::from(2); let stake = DefaultMinStake::::get().to_u64(); assert_ne!(hotkey, who); @@ -1380,6 +1383,14 @@ fn test_subtensor_extension_rejects_any_call_that_is_not_swap_coldkey_announced( ); } + // Reannounce coldkey swap should succeed + let call = RuntimeCall::SubtensorModule(SubtensorCall::announce_coldkey_swap { + new_coldkey_hash: another_coldkey_hash, + }); + let info = call.get_dispatch_info(); + let ext = SubtensorTransactionExtension::::new(); + assert_ok!(ext.dispatch_transaction(RuntimeOrigin::signed(who).into(), call, &info, 0, 0)); + // Swap coldkey announced should succeed let call = RuntimeCall::SubtensorModule(SubtensorCall::swap_coldkey_announced { new_coldkey }); diff --git a/pallets/subtensor/src/transaction_extension.rs b/pallets/subtensor/src/transaction_extension.rs index 32eb057f98..7647a9d6ad 100644 --- a/pallets/subtensor/src/transaction_extension.rs +++ b/pallets/subtensor/src/transaction_extension.rs @@ -119,7 +119,8 @@ where if ColdkeySwapAnnouncements::::contains_key(who) && !matches!( call.is_sub_type(), - Some(Call::swap_coldkey_announced { .. }) + Some(Call::announce_coldkey_swap { .. }) + | Some(Call::swap_coldkey_announced { .. }) ) { return Err(CustomTransactionError::ColdkeySwapAnnounced.into()); From 22019e3b93f437e9cb0ae3061d26b0dd02cd181b Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Tue, 23 Dec 2025 11:36:22 -0500 Subject: [PATCH 073/240] Fix test_clear_protocol_liquidity_green_path --- pallets/swap/src/pallet/impls.rs | 2 + pallets/swap/src/pallet/tests.rs | 126 +++++++++++++------------------ 2 files changed, 55 insertions(+), 73 deletions(-) diff --git a/pallets/swap/src/pallet/impls.rs b/pallets/swap/src/pallet/impls.rs index 0b36aee691..5cb9f75000 100644 --- a/pallets/swap/src/pallet/impls.rs +++ b/pallets/swap/src/pallet/impls.rs @@ -100,6 +100,8 @@ impl Pallet { // Positions::::insert(&(netuid, protocol_account_id, position.id), position); + PalSwapInitialized::::insert(netuid, true); + Ok(()) } diff --git a/pallets/swap/src/pallet/tests.rs b/pallets/swap/src/pallet/tests.rs index ecdb810b24..f12742c5ce 100644 --- a/pallets/swap/src/pallet/tests.rs +++ b/pallets/swap/src/pallet/tests.rs @@ -1917,9 +1917,9 @@ fn test_swap_subtoken_disabled() { // }); // } +// TODO: Revise when user liquidity is available // Non‑palswap path: PalSwap not initialized (no positions, no map values); function // must still clear any residual storages and succeed. -// TODO: Revise when user liquidity is available // #[test] // fn test_liquidate_pal_uninitialized_ok_and_clears() { // new_test_ext().execute_with(|| { @@ -2512,88 +2512,68 @@ fn test_liquidate_pal_simple_ok_and_clears() { // }); // } +// TODO: Revise when user liquidity is available #[test] fn test_clear_protocol_liquidity_green_path() { - todo!(); + new_test_ext().execute_with(|| { + // --- Arrange --- + let netuid = NetUid::from(1); - // new_test_ext().execute_with(|| { - // // --- Arrange --- - // let netuid = NetUid::from(55); + // Ensure the "user liquidity enabled" flag exists so we can verify it's removed later. + EnabledUserLiquidity::::insert(netuid, true); - // // Ensure the "user liquidity enabled" flag exists so we can verify it's removed later. - // assert_ok!(Pallet::::toggle_user_liquidity( - // RuntimeOrigin::root(), - // netuid, - // true - // )); + // Initialize swap state + assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + assert!( + PalSwapInitialized::::get(netuid), + "Swap must be initialized" + ); - // // Initialize V3 state; this should set price/tick flags and create a protocol position. - // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - // assert!( - // PalSwapInitialized::::get(netuid), - // "V3 must be initialized" - // ); + // Sanity: protocol positions exist before clearing. + // TODO: Revise when user liquidity is available + // let protocol_id = Pallet::::protocol_account_id(); + // let prot_positions_before = + // Positions::::iter_prefix_values((netuid, protocol_id)).collect::>(); + // assert!( + // !prot_positions_before.is_empty(), + // "protocol positions should exist after V3 init" + // ); + + // --- Act --- + // Green path: just clear protocol liquidity and wipe all V3 state. + assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); - // // Sanity: protocol positions exist before clearing. - // let protocol_id = Pallet::::protocol_account_id(); - // let prot_positions_before = - // Positions::::iter_prefix_values((netuid, protocol_id)).collect::>(); - // assert!( - // !prot_positions_before.is_empty(), - // "protocol positions should exist after V3 init" - // ); + // --- Assert: all protocol positions removed --- + // TODO: Revise when user liquidity is available + // let prot_positions_after = + // Positions::::iter_prefix_values((netuid, protocol_id)).collect::>(); + // assert!( + // prot_positions_after.is_empty(), + // "protocol positions must be removed by do_clear_protocol_liquidity" + // ); - // // --- Act --- - // // Green path: just clear protocol liquidity and wipe all V3 state. - // assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); + // --- Assert: Swap data wiped (idempotent even if some maps were empty) --- - // // --- Assert: all protocol positions removed --- - // let prot_positions_after = - // Positions::::iter_prefix_values((netuid, protocol_id)).collect::>(); - // assert!( - // prot_positions_after.is_empty(), - // "protocol positions must be removed by do_clear_protocol_liquidity" - // ); + // Fee globals + assert!(!FeesTao::::contains_key(netuid)); + assert!(!FeesAlpha::::contains_key(netuid)); - // // --- Assert: V3 data wiped (idempotent even if some maps were empty) --- - // // Ticks / active tick bitmap - // assert!(Ticks::::iter_prefix(netuid).next().is_none()); - // assert!( - // TickIndexBitmapWords::::iter_prefix((netuid,)) - // .next() - // .is_none(), - // "active tick bitmap words must be cleared" - // ); + // Flags + assert!(!PalSwapInitialized::::contains_key(netuid)); - // // Fee globals - // assert!(!FeeGlobalTao::::contains_key(netuid)); - // assert!(!FeeGlobalAlpha::::contains_key(netuid)); - - // // Price / tick / liquidity / flags - // assert!(!AlphaSqrtPrice::::contains_key(netuid)); - // assert!(!CurrentTick::::contains_key(netuid)); - // assert!(!CurrentLiquidity::::contains_key(netuid)); - // assert!(!PalSwapInitialized::::contains_key(netuid)); - - // // Knobs removed - // assert!(!FeeRate::::contains_key(netuid)); - // assert!(!EnabledUserLiquidity::::contains_key(netuid)); - - // // --- And it's idempotent --- - // assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); - // assert!( - // Positions::::iter_prefix_values((netuid, protocol_id)) - // .next() - // .is_none() - // ); - // assert!(Ticks::::iter_prefix(netuid).next().is_none()); - // assert!( - // TickIndexBitmapWords::::iter_prefix((netuid,)) - // .next() - // .is_none() - // ); - // assert!(!PalSwapInitialized::::contains_key(netuid)); - // }); + // Knobs removed + assert!(!FeeRate::::contains_key(netuid)); + assert!(!EnabledUserLiquidity::::contains_key(netuid)); + + // --- And it's idempotent --- + assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); + // assert!( + // PositionsV2::::iter_prefix_values((netuid, protocol_id)) + // .next() + // .is_none() + // ); + assert!(!PalSwapInitialized::::contains_key(netuid)); + }); } #[allow(dead_code)] From 9c9b6464cc762b387b28447f5b337149b7e5e5a6 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Tue, 23 Dec 2025 16:04:44 -0500 Subject: [PATCH 074/240] Fix test_claim_root_with_run_coinbase --- pallets/subtensor/src/tests/claim_root.rs | 133 +++++++++++---------- pallets/swap/Cargo.toml | 3 +- pallets/swap/src/pallet/impls.rs | 3 - pallets/swap/src/pallet/reserve_weights.rs | 1 + pallets/swap/src/pallet/swap_step.rs | 13 -- 5 files changed, 70 insertions(+), 83 deletions(-) diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index 55ea2593e0..ec7126e8d9 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -19,11 +19,11 @@ use crate::{ StakingColdkeysByIndex, SubnetAlphaIn, SubnetMechanism, - // SubnetMovingPrice, + SubnetMovingPrice, SubnetTAO, // SubnetTaoFlow, SubtokenEnabled, - //Tempo, + Tempo, pallet, }; use crate::{RootClaimType, RootClaimTypeEnum, RootClaimed}; @@ -35,10 +35,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, U96F32}; use subtensor_runtime_common::{AlphaCurrency, Currency, NetUid, TaoCurrency}; use subtensor_swap_interface::SwapHandler; @@ -777,83 +774,87 @@ fn test_claim_root_with_drain_emissions_and_swap_claim_type() { }); } +/// cargo test --package pallet-subtensor --lib -- tests::claim_root::test_claim_root_with_run_coinbase --exact --nocapture #[test] fn test_claim_root_with_run_coinbase() { - todo!(); - - // new_test_ext(1).execute_with(|| { - // let owner_coldkey = U256::from(1001); - // let hotkey = U256::from(1002); - // let coldkey = U256::from(1003); - // let netuid = add_dynamic_network(&hotkey, &owner_coldkey); + new_test_ext(1).execute_with(|| { + let owner_coldkey = U256::from(1001); + let hotkey = U256::from(1002); + let coldkey = U256::from(1003); + let netuid = add_dynamic_network(&hotkey, &owner_coldkey); - // Tempo::::insert(netuid, 1); - // SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 + Tempo::::insert(netuid, 1); + SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 - // let root_stake = 200_000_000u64; - // SubnetTAO::::insert(NetUid::ROOT, TaoCurrency::from(root_stake)); + let root_stake = 200_000_000u64; + SubnetTAO::::insert(NetUid::ROOT, TaoCurrency::from(root_stake)); - // SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - // &hotkey, - // &coldkey, - // NetUid::ROOT, - // root_stake.into(), - // ); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &coldkey, + NetUid::ROOT, + root_stake.into(), + ); - // let initial_total_hotkey_alpha = 10_000_000u64; - // SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - // &hotkey, - // &owner_coldkey, - // netuid, - // initial_total_hotkey_alpha.into(), - // ); + let initial_total_hotkey_alpha = 10_000_000u64; + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &owner_coldkey, + netuid, + initial_total_hotkey_alpha.into(), + ); - // // Set moving price > 1.0 and price > 1.0 - // // So we turn ON root sell - // SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); - // pallet_subtensor_swap::AlphaSqrtPrice::::insert( - // netuid, - // U64F64::saturating_from_num(10.0), - // ); + // Set moving price > 1.0 and price > 1.0 + // So we turn ON root sell + SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); + let tao = TaoCurrency::from(10_000_000_000_000_u64); + let alpha = AlphaCurrency::from(1_000_000_000_000_u64); + SubnetTAO::::insert(netuid, tao); + SubnetAlphaIn::::insert(netuid, alpha); + let current_price = + ::SwapInterface::current_alpha_price(netuid.into()) + .saturating_to_num::(); + assert_eq!(current_price, 10.0f64); + RootClaimableThreshold::::insert(netuid, I96F32::from_num(0)); - // // Make sure we are root selling, so we have root alpha divs. - // let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); - // assert!(root_sell_flag, "Root sell flag should be true"); + // Make sure we are root selling, so we have root alpha divs. + let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); + assert!(root_sell_flag, "Root sell flag should be true"); - // // Distribute pending root alpha + // Distribute pending root alpha - // let initial_stake: u64 = - // SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid) - // .into(); - // assert_eq!(initial_stake, 0u64); + let initial_stake: u64 = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid) + .into(); + assert_eq!(initial_stake, 0u64); - // let block_emissions = 1_000_000u64; - // SubtensorModule::run_coinbase(U96F32::from(block_emissions)); + let block_emissions = 1_000_000u64; + SubtensorModule::run_coinbase(U96F32::from(block_emissions)); - // // Claim root alpha + // Claim root alpha - // let initial_stake: u64 = - // SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid) - // .into(); - // assert_eq!(initial_stake, 0u64); + let initial_stake: u64 = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid) + .into(); + assert_eq!(initial_stake, 0u64); - // assert_ok!(SubtensorModule::set_root_claim_type( - // RuntimeOrigin::signed(coldkey), - // RootClaimTypeEnum::Keep - // ),); - // assert_eq!(RootClaimType::::get(coldkey), RootClaimTypeEnum::Keep); + assert_ok!(SubtensorModule::set_root_claim_type( + RuntimeOrigin::signed(coldkey), + RootClaimTypeEnum::Keep + ),); + assert_eq!(RootClaimType::::get(coldkey), RootClaimTypeEnum::Keep); - // assert_ok!(SubtensorModule::claim_root( - // RuntimeOrigin::signed(coldkey), - // BTreeSet::from([netuid]) - // )); + assert_ok!(SubtensorModule::claim_root( + RuntimeOrigin::signed(coldkey), + BTreeSet::from([netuid]) + )); - // let new_stake: u64 = - // SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid) - // .into(); + let new_stake: u64 = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid) + .into(); - // assert!(new_stake > 0); - // }); + assert!(new_stake > 0); + }); } #[test] diff --git a/pallets/swap/Cargo.toml b/pallets/swap/Cargo.toml index 89534fa81c..cb3fd6a1f2 100644 --- a/pallets/swap/Cargo.toml +++ b/pallets/swap/Cargo.toml @@ -29,7 +29,7 @@ subtensor-swap-interface.workspace = true [dev-dependencies] sp-tracing.workspace = true -rand = "0.8" +rand = { version = "0.8", default-features = false } rayon = "1.10" [lints] @@ -46,6 +46,7 @@ std = [ "log/std", "pallet-subtensor-swap-runtime-api/std", "safe-math/std", + "safe-bigmath/std", "scale-info/std", "serde/std", "sp-arithmetic/std", diff --git a/pallets/swap/src/pallet/impls.rs b/pallets/swap/src/pallet/impls.rs index 5cb9f75000..e8f1c1bc3e 100644 --- a/pallets/swap/src/pallet/impls.rs +++ b/pallets/swap/src/pallet/impls.rs @@ -217,9 +217,6 @@ impl Pallet { Self::maybe_initialize_palswap(netuid)?; - println!("Self::current_price(netuid) = {:?}", Self::current_price(netuid)); - println!("limit_price = {:?}", limit_price); - // Because user specifies the limit price, check that it is in fact beoynd the current one ensure!( order.is_beyond_price_limit(Self::current_price(netuid), limit_price), diff --git a/pallets/swap/src/pallet/reserve_weights.rs b/pallets/swap/src/pallet/reserve_weights.rs index 72994e8796..0e7ae7af95 100644 --- a/pallets/swap/src/pallet/reserve_weights.rs +++ b/pallets/swap/src/pallet/reserve_weights.rs @@ -264,6 +264,7 @@ impl ReserveWeight { // cargo test --package pallet-subtensor-swap --lib -- pallet::reserve_weights::tests --nocapture #[cfg(test)] +#[cfg(feature = "std")] mod tests { use crate::pallet::ReserveWeight; use crate::pallet::reserve_weights::*; diff --git a/pallets/swap/src/pallet/swap_step.rs b/pallets/swap/src/pallet/swap_step.rs index bfcdd5a4f9..ee345d57d3 100644 --- a/pallets/swap/src/pallet/swap_step.rs +++ b/pallets/swap/src/pallet/swap_step.rs @@ -53,9 +53,6 @@ where let target_price = Self::price_target(netuid, requested_delta_in); let current_price = Pallet::::current_price(netuid); - println!("requested_delta_in = {}", requested_delta_in); - println!("target_price = {}", target_price); - Self { netuid, drop_fees, @@ -113,9 +110,6 @@ where .saturating_to_num::() .into(); } - - - println!("=========== debug 1 (end of determine_action)"); } /// Process a single step of a swap @@ -123,7 +117,6 @@ where // Convert amounts, actual swap happens here let delta_out = Self::convert_deltas(self.netuid, self.delta_in); log::trace!("\tDelta Out : {delta_out}"); - println!("delta_out = {}", delta_out); ensure!( delta_out > 0.into(), Error::::ReservesTooLow @@ -226,12 +219,6 @@ impl SwapStep let tao_reserve = T::TaoReserve::reserve(netuid.into()); let reserve_weight = SwapReserveWeight::::get(netuid); let e = reserve_weight.exp_base_quote(alpha_reserve.into(), delta_in.into()); - - println!("alpha_reserve = {}", alpha_reserve); - println!("tao_reserve = {}", tao_reserve); - println!("reserve_weight = {:?}", reserve_weight); - println!("e = {}", e); - let one = U64F64::from_num(1); let tao_reserve_fixed = U64F64::from_num(u64::from(tao_reserve)); TaoCurrency::from( From a27f1b500dde65602acf49f4aec7afc680675134 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Tue, 23 Dec 2025 16:59:48 -0500 Subject: [PATCH 075/240] Add swap initialization to interface --- pallets/subtensor/src/tests/claim_root.rs | 420 ++++++++++++++-------- pallets/subtensor/src/tests/coinbase.rs | 56 +-- pallets/subtensor/src/tests/staking.rs | 24 +- pallets/swap-interface/src/lib.rs | 1 + pallets/swap/src/pallet/impls.rs | 3 + 5 files changed, 276 insertions(+), 228 deletions(-) diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index ec7126e8d9..74020c3a70 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -12,7 +12,7 @@ use crate::{ NetworksAdded, NumRootClaim, NumStakingColdkeys, - //PendingRootAlphaDivs, + PendingRootAlphaDivs, RootClaimable, RootClaimableThreshold, StakingColdkeys, @@ -21,7 +21,7 @@ use crate::{ SubnetMechanism, SubnetMovingPrice, SubnetTAO, - // SubnetTaoFlow, + SubnetTaoFlow, SubtokenEnabled, Tempo, pallet, @@ -35,7 +35,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, U96F32}; use subtensor_runtime_common::{AlphaCurrency, Currency, NetUid, TaoCurrency}; use subtensor_swap_interface::SwapHandler; @@ -900,71 +900,74 @@ fn test_claim_root_block_hash_indices() { #[test] fn test_claim_root_with_block_emissions() { - todo!(); - - // new_test_ext(0).execute_with(|| { - // let owner_coldkey = U256::from(1001); - // let hotkey = U256::from(1002); - // let coldkey = U256::from(1003); - // let netuid = add_dynamic_network(&hotkey, &owner_coldkey); - - // Tempo::::insert(netuid, 1); - // SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 - - // let root_stake = 200_000_000u64; - // SubnetTAO::::insert(NetUid::ROOT, TaoCurrency::from(root_stake)); - - // SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - // &hotkey, - // &coldkey, - // NetUid::ROOT, - // root_stake.into(), - // ); - // SubtensorModule::maybe_add_coldkey_index(&coldkey); - - // // Set moving price > 1.0 and price > 1.0 - // // So we turn ON root sell - // SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); - // pallet_subtensor_swap::AlphaSqrtPrice::::insert( - // netuid, - // U64F64::saturating_from_num(10.0), - // ); - - // // Make sure we are root selling, so we have root alpha divs. - // let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); - // assert!(root_sell_flag, "Root sell flag should be true"); - - // let initial_total_hotkey_alpha = 10_000_000u64; - // SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - // &hotkey, - // &owner_coldkey, - // netuid, - // initial_total_hotkey_alpha.into(), - // ); - - // assert_ok!(SubtensorModule::set_root_claim_type( - // RuntimeOrigin::signed(coldkey), - // RootClaimTypeEnum::Keep - // ),); - // assert_eq!(RootClaimType::::get(coldkey), RootClaimTypeEnum::Keep); - - // // Distribute pending root alpha - - // let initial_stake: u64 = - // SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid) - // .into(); - // assert_eq!(initial_stake, 0u64); - - // run_to_block(2); - - // // Check stake after block emissions - - // let new_stake: u64 = - // SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid) - // .into(); - - // assert!(new_stake > 0); - // }); + new_test_ext(0).execute_with(|| { + let owner_coldkey = U256::from(1001); + let hotkey = U256::from(1002); + let coldkey = U256::from(1003); + let netuid = add_dynamic_network(&hotkey, &owner_coldkey); + + Tempo::::insert(netuid, 1); + SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 + + let root_stake = 200_000_000u64; + SubnetTAO::::insert(NetUid::ROOT, TaoCurrency::from(root_stake)); + + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &coldkey, + NetUid::ROOT, + root_stake.into(), + ); + SubtensorModule::maybe_add_coldkey_index(&coldkey); + + // Set moving price > 1.0 and price > 1.0 + // So we turn ON root sell + SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); + let tao = TaoCurrency::from(10_000_000_000_000_u64); + let alpha = AlphaCurrency::from(1_000_000_000_000_u64); + SubnetTAO::::insert(netuid, tao); + SubnetAlphaIn::::insert(netuid, alpha); + let current_price = + ::SwapInterface::current_alpha_price(netuid.into()) + .saturating_to_num::(); + assert_eq!(current_price, 10.0f64); + RootClaimableThreshold::::insert(netuid, I96F32::from_num(0)); + + // Make sure we are root selling, so we have root alpha divs. + let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); + assert!(root_sell_flag, "Root sell flag should be true"); + + let initial_total_hotkey_alpha = 10_000_000u64; + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &owner_coldkey, + netuid, + initial_total_hotkey_alpha.into(), + ); + + assert_ok!(SubtensorModule::set_root_claim_type( + RuntimeOrigin::signed(coldkey), + RootClaimTypeEnum::Keep + ),); + assert_eq!(RootClaimType::::get(coldkey), RootClaimTypeEnum::Keep); + + // Distribute pending root alpha + + let initial_stake: u64 = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid) + .into(); + assert_eq!(initial_stake, 0u64); + + run_to_block(2); + + // Check stake after block emissions + + let new_stake: u64 = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid) + .into(); + + assert!(new_stake > 0); + }); } #[test] fn test_populate_staking_maps() { @@ -1014,100 +1017,201 @@ fn test_populate_staking_maps() { }); } + #[test] fn test_claim_root_coinbase_distribution() { - todo!(); - - // new_test_ext(1).execute_with(|| { - // let owner_coldkey = U256::from(1001); - // let hotkey = U256::from(1002); - // let coldkey = U256::from(1003); - // let netuid = add_dynamic_network(&hotkey, &owner_coldkey); - - // Tempo::::insert(netuid, 1); - // SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 - - // let root_stake = 200_000_000u64; - // let initial_tao = 200_000_000u64; - // SubnetTAO::::insert(NetUid::ROOT, TaoCurrency::from(initial_tao)); - - // SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - // &hotkey, - // &coldkey, - // NetUid::ROOT, - // root_stake.into(), - // ); - - // let initial_total_hotkey_alpha = 10_000_000u64; - // SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - // &hotkey, - // &owner_coldkey, - // netuid, - // initial_total_hotkey_alpha.into(), - // ); - - // let initial_alpha_issuance = SubtensorModule::get_alpha_issuance(netuid); - // let alpha_emissions: AlphaCurrency = 1_000_000_000u64.into(); - - // // Set moving price > 1.0 and price > 1.0 - // // So we turn ON root sell - // SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); - // pallet_subtensor_swap::AlphaSqrtPrice::::insert( - // netuid, - // U64F64::saturating_from_num(10.0), - // ); - - // // Make sure we are root selling, so we have root alpha divs. - // let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); - // assert!(root_sell_flag, "Root sell flag should be true"); - - // // Set TAOFlow > 0 - // SubnetTaoFlow::::insert(netuid, 2222_i64); - - // // Check total issuance (saved to pending alpha divs) - // run_to_block(2); - - // let alpha_issuance = SubtensorModule::get_alpha_issuance(netuid); - // // We went two blocks so we should have 2x the alpha emissions - // assert_eq!( - // initial_alpha_issuance + alpha_emissions.saturating_mul(2.into()), - // alpha_issuance - // ); - - // let root_prop = initial_tao as f64 / (u64::from(alpha_issuance) + initial_tao) as f64; - // let root_validators_share = 0.5f64; - - // let expected_pending_root_alpha_divs = - // u64::from(alpha_emissions) as f64 * root_prop * root_validators_share; - // assert_abs_diff_eq!( - // u64::from(PendingRootAlphaDivs::::get(netuid)) as f64, - // expected_pending_root_alpha_divs, - // epsilon = 100f64 - // ); - - // // Epoch pending alphas divs is distributed - - // run_to_block(3); - - // assert_eq!(u64::from(PendingRootAlphaDivs::::get(netuid)), 0u64); - - // let claimable = *RootClaimable::::get(hotkey) - // .get(&netuid) - // .expect("claimable must exist at this point"); - - // let validator_take_percent = 0.18f64; - // let calculated_rate = (expected_pending_root_alpha_divs * 2f64) - // * (1f64 - validator_take_percent) - // / (root_stake as f64); - - // assert_abs_diff_eq!( - // claimable.saturating_to_num::(), - // calculated_rate, - // epsilon = 0.001f64, - // ); - // }); + new_test_ext(1).execute_with(|| { + let owner_coldkey = U256::from(1001); + let hotkey = U256::from(1002); + let coldkey = U256::from(1003); + let netuid = add_dynamic_network(&hotkey, &owner_coldkey); + + Tempo::::insert(netuid, 1); + SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 + + let root_stake = 200_000_000u64; + let initial_tao = 200_000_000u64; + SubnetTAO::::insert(NetUid::ROOT, TaoCurrency::from(initial_tao)); + + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &coldkey, + NetUid::ROOT, + root_stake.into(), + ); + + let initial_total_hotkey_alpha = 10_000_000u64; + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &owner_coldkey, + netuid, + initial_total_hotkey_alpha.into(), + ); + + // Set moving price > 1.0 and price > 1.0 + // So we turn ON root sell + SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); + let tao = TaoCurrency::from(20_000_000_000_u64); + let alpha = AlphaCurrency::from(10_000_000_000_u64); + SubnetTAO::::insert(netuid, tao); + SubnetAlphaIn::::insert(netuid, alpha); + let current_price = + ::SwapInterface::current_alpha_price(netuid.into()) + .saturating_to_num::(); + // assert_eq!(current_price, 10.0f64); + RootClaimableThreshold::::insert(netuid, I96F32::from_num(0)); + + let initial_alpha_issuance = SubtensorModule::get_alpha_issuance(netuid); + let alpha_emissions: AlphaCurrency = 1_000_000_000u64.into(); + + // Make sure we are root selling, so we have root alpha divs. + let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); + assert!(root_sell_flag, "Root sell flag should be true"); + + // Set TAOFlow > 0 + SubnetTaoFlow::::insert(netuid, 2222_i64); + + // Check total issuance (saved to pending alpha divs) + run_to_block(2); + + let alpha_issuance = SubtensorModule::get_alpha_issuance(netuid); + // We went two blocks so we should have 2x the alpha emissions + assert_eq!( + initial_alpha_issuance + alpha_emissions.saturating_mul(2.into()), + alpha_issuance + ); + + let root_prop = initial_tao as f64 / (u64::from(alpha_issuance) + initial_tao) as f64; + let root_validators_share = 0.5f64; + + let expected_pending_root_alpha_divs = + u64::from(alpha_emissions) as f64 * root_prop * root_validators_share; + assert_abs_diff_eq!( + u64::from(PendingRootAlphaDivs::::get(netuid)) as f64, + expected_pending_root_alpha_divs, + epsilon = 100f64 + ); + + // Epoch pending alphas divs is distributed + + run_to_block(3); + + assert_eq!(u64::from(PendingRootAlphaDivs::::get(netuid)), 0u64); + + let claimable = *RootClaimable::::get(hotkey) + .get(&netuid) + .expect("claimable must exist at this point"); + + let validator_take_percent = 0.18f64; + let calculated_rate = (expected_pending_root_alpha_divs * 2f64) + * (1f64 - validator_take_percent) + / (root_stake as f64); + + assert_abs_diff_eq!( + claimable.saturating_to_num::(), + calculated_rate, + epsilon = 0.001f64, + ); + }); } +// #[test] +// fn test_claim_root_coinbase_distribution() { +// new_test_ext(1).execute_with(|| { +// let owner_coldkey = U256::from(1001); +// let hotkey = U256::from(1002); +// let coldkey = U256::from(1003); +// let netuid = add_dynamic_network(&hotkey, &owner_coldkey); + +// Tempo::::insert(netuid, 1); +// SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 + +// let root_stake = 200_000_000u64; +// let initial_tao = 200_000_000u64; +// SubnetTAO::::insert(NetUid::ROOT, TaoCurrency::from(initial_tao)); + +// SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( +// &hotkey, +// &coldkey, +// NetUid::ROOT, +// root_stake.into(), +// ); + +// let initial_total_hotkey_alpha = 10_000_000u64; +// SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( +// &hotkey, +// &owner_coldkey, +// netuid, +// initial_total_hotkey_alpha.into(), +// ); + +// let initial_alpha_issuance = SubtensorModule::get_alpha_issuance(netuid); +// let alpha_emissions: AlphaCurrency = 1_000_000_000u64.into(); + +// // Set moving price > 1.0 and price > 1.0 +// // So we turn ON root sell +// SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); +// let tao = TaoCurrency::from(10_000_000_000_000_u64); +// let alpha = AlphaCurrency::from(1_000_000_000_000_u64); +// SubnetTAO::::insert(netuid, tao); +// SubnetAlphaIn::::insert(netuid, alpha); +// let current_price = +// ::SwapInterface::current_alpha_price(netuid.into()) +// .saturating_to_num::(); +// assert_eq!(current_price, 10.0f64); +// RootClaimableThreshold::::insert(netuid, I96F32::from_num(0)); + +// // Make sure we are root selling, so we have root alpha divs. +// let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); +// assert!(root_sell_flag, "Root sell flag should be true"); + +// // Set TAOFlow > 0 +// SubnetTaoFlow::::insert(netuid, 2222_i64); + +// // Check total issuance (saved to pending alpha divs) +// run_to_block(2); + +// let alpha_issuance = SubtensorModule::get_alpha_issuance(netuid); +// // We went two blocks so we should have 2x the alpha emissions +// assert_eq!( +// initial_alpha_issuance + alpha_emissions.saturating_mul(2.into()), +// alpha_issuance +// ); + +// let root_prop = initial_tao as f64 / (u64::from(alpha_issuance) + initial_tao) as f64; +// let root_validators_share = 0.5f64; + +// let expected_pending_root_alpha_divs = +// u64::from(alpha_emissions) as f64 * root_prop * root_validators_share; +// assert_abs_diff_eq!( +// u64::from(PendingRootAlphaDivs::::get(netuid)) as f64, +// expected_pending_root_alpha_divs, +// epsilon = 100f64 +// ); + +// // Epoch pending alphas divs is distributed + +// run_to_block(3); + +// assert_eq!(u64::from(PendingRootAlphaDivs::::get(netuid)), 0u64); + +// let claimable = *RootClaimable::::get(hotkey) +// .get(&netuid) +// .expect("claimable must exist at this point"); + +// let validator_take_percent = 0.18f64; +// let calculated_rate = (expected_pending_root_alpha_divs * 2f64) +// * (1f64 - validator_take_percent) +// / (root_stake as f64); + +// assert_abs_diff_eq!( +// claimable.saturating_to_num::(), +// calculated_rate, +// epsilon = 0.001f64, +// ); +// }); +// } + #[test] fn test_sudo_set_num_root_claims() { new_test_ext(1).execute_with(|| { diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index e8d4e0c85f..3b2f710d79 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -222,20 +222,8 @@ fn test_coinbase_tao_issuance_different_prices() { mock::setup_reserves(netuid2, initial_tao.into(), initial_alpha2.into()); // Force the swap to initialize - SubtensorModule::swap_tao_for_alpha( - netuid1, - TaoCurrency::ZERO, - 1_000_000_000_000.into(), - false, - ) - .unwrap(); - SubtensorModule::swap_tao_for_alpha( - netuid2, - TaoCurrency::ZERO, - 1_000_000_000_000.into(), - false, - ) - .unwrap(); + ::SwapInterface::init_swap(netuid1); + ::SwapInterface::init_swap(netuid2); // Make subnets dynamic. SubnetMechanism::::insert(netuid1, 1); @@ -298,20 +286,8 @@ fn test_coinbase_tao_issuance_different_prices() { // mock::setup_reserves(netuid2, initial_tao.into(), initial_alpha2.into()); // // Force the swap to initialize -// SubtensorModule::swap_tao_for_alpha( -// netuid1, -// TaoCurrency::ZERO, -// 1_000_000_000_000.into(), -// false, -// ) -// .unwrap(); -// SubtensorModule::swap_tao_for_alpha( -// netuid2, -// TaoCurrency::ZERO, -// 1_000_000_000_000.into(), -// false, -// ) -// .unwrap(); +// ::SwapInterface::init_swap(netuid1); +// ::SwapInterface::init_swap(netuid2); // // Set subnet prices to reversed proportion to ensure they don't affect emissions. // SubnetMovingPrice::::insert(netuid1, I96F32::from_num(2)); @@ -616,20 +592,8 @@ fn test_coinbase_alpha_issuance_with_cap_trigger_and_block_emission() { SubnetTaoFlow::::insert(netuid2, 200_000_000_i64); // Force the swap to initialize - SubtensorModule::swap_tao_for_alpha( - netuid1, - TaoCurrency::ZERO, - 1_000_000_000_000.into(), - false, - ) - .unwrap(); - SubtensorModule::swap_tao_for_alpha( - netuid2, - TaoCurrency::ZERO, - 1_000_000_000_000.into(), - false, - ) - .unwrap(); + ::SwapInterface::init_swap(netuid1); + ::SwapInterface::init_swap(netuid2); // Get the prices before the run_coinbase let price_1_before = ::SwapInterface::current_alpha_price(netuid1); @@ -2743,13 +2707,7 @@ fn test_coinbase_v3_liquidity_update() { // let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); // // Force the swap to initialize - // SubtensorModule::swap_tao_for_alpha( - // netuid, - // TaoCurrency::ZERO, - // 1_000_000_000_000.into(), - // false, - // ) - // .unwrap(); + // ::SwapInterface::init_swap(netuid); // let protocol_account_id = pallet_subtensor_swap::Pallet::::protocol_account_id(); // let position = pallet_subtensor_swap::Positions::::get(( diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index 7cdfedab1a..3417656a3a 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -573,13 +573,7 @@ fn test_add_stake_partial_below_min_stake_fails() { mock::setup_reserves(netuid, (amount * 10).into(), (amount * 10).into()); // Force the swap to initialize - SubtensorModule::swap_tao_for_alpha( - netuid, - TaoCurrency::ZERO, - 1_000_000_000_000.into(), - false, - ) - .unwrap(); + ::SwapInterface::init_swap(netuid); // Get the current price (should be 1.0) let current_price = @@ -2883,13 +2877,7 @@ fn test_max_amount_add_dynamic() { SubnetAlphaIn::::insert(netuid, alpha_in); // Force the swap to initialize - SubtensorModule::swap_tao_for_alpha( - netuid, - TaoCurrency::ZERO, - 1_000_000_000_000.into(), - false, - ) - .unwrap(); + ::SwapInterface::init_swap(netuid); if !alpha_in.is_zero() { let expected_price = U96F32::from_num(tao_in) / U96F32::from_num(alpha_in); @@ -5373,13 +5361,7 @@ fn test_large_swap() { // pallet_subtensor_swap::EnabledUserLiquidity::::insert(NetUid::from(netuid), true); // // Force the swap to initialize - // SubtensorModule::swap_tao_for_alpha( - // netuid, - // TaoCurrency::ZERO, - // 1_000_000_000_000.into(), - // false, - // ) - // .unwrap(); + // ::SwapInterface::init_swap(netuid); // setup_positions(netuid.into()); diff --git a/pallets/swap-interface/src/lib.rs b/pallets/swap-interface/src/lib.rs index c99db6a475..4b2069b119 100644 --- a/pallets/swap-interface/src/lib.rs +++ b/pallets/swap-interface/src/lib.rs @@ -51,6 +51,7 @@ pub trait SwapHandler { fn dissolve_all_liquidity_providers(netuid: NetUid) -> DispatchResult; fn toggle_user_liquidity(netuid: NetUid, enabled: bool); fn clear_protocol_liquidity(netuid: NetUid) -> DispatchResult; + fn init_swap(netuid: NetUid); } pub trait DefaultPriceLimit diff --git a/pallets/swap/src/pallet/impls.rs b/pallets/swap/src/pallet/impls.rs index e8f1c1bc3e..b6a6bcba67 100644 --- a/pallets/swap/src/pallet/impls.rs +++ b/pallets/swap/src/pallet/impls.rs @@ -697,4 +697,7 @@ impl SwapHandler for Pallet { fn clear_protocol_liquidity(netuid: NetUid) -> DispatchResult { Self::do_clear_protocol_liquidity(netuid) } + fn init_swap(netuid: NetUid) { + Self::maybe_initialize_palswap(netuid).unwrap_or_default(); + } } From afa0d4556e7f415416d0fe21beb8118f88ab4ce7 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Wed, 24 Dec 2025 13:23:37 -0500 Subject: [PATCH 076/240] Fix test_coinbase_subnet_terms_with_alpha_in_gt_alpha_emission --- pallets/subtensor/src/tests/claim_root.rs | 2 +- pallets/subtensor/src/tests/coinbase.rs | 131 +++++++++++----------- 2 files changed, 67 insertions(+), 66 deletions(-) diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index 74020c3a70..41c85114d9 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -1058,7 +1058,7 @@ fn test_claim_root_coinbase_distribution() { let current_price = ::SwapInterface::current_alpha_price(netuid.into()) .saturating_to_num::(); - // assert_eq!(current_price, 10.0f64); + assert_eq!(current_price, 10.0f64); RootClaimableThreshold::::insert(netuid, I96F32::from_num(0)); let initial_alpha_issuance = SubtensorModule::get_alpha_issuance(netuid); diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 3b2f710d79..bb8bcc71f5 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -3349,80 +3349,81 @@ fn test_coinbase_subnets_with_no_reg_get_no_emission() { // Tests for the excess TAO condition #[test] fn test_coinbase_subnet_terms_with_alpha_in_gt_alpha_emission() { - todo!(); + new_test_ext(1).execute_with(|| { + let zero = U96F32::saturating_from_num(0); + let netuid0 = add_dynamic_network(&U256::from(1), &U256::from(2)); + mock::setup_reserves( + netuid0, + TaoCurrency::from(1_000_000_000_000_000), + AlphaCurrency::from(1_000_000_000_000_000), + ); + // Initialize swap v3 + Swap::maybe_initialize_palswap(netuid0); - // new_test_ext(1).execute_with(|| { - // let zero = U96F32::saturating_from_num(0); - // let netuid0 = add_dynamic_network(&U256::from(1), &U256::from(2)); - // mock::setup_reserves( - // netuid0, - // TaoCurrency::from(1_000_000_000_000_000), - // AlphaCurrency::from(1_000_000_000_000_000), - // ); - // // Initialize swap v3 - // Swap::maybe_initialize_palswap(netuid0); - - // // Set netuid0 to have price tao_emission / price > alpha_emission - // let alpha_emission = U96F32::saturating_from_num( - // SubtensorModule::get_block_emission_for_issuance( - // SubtensorModule::get_alpha_issuance(netuid0).into(), - // ) - // .unwrap_or(0), - // ); - // let price_to_set: U64F64 = U64F64::saturating_from_num(0.01); - // let price_to_set_fixed: U96F32 = U96F32::saturating_from_num(price_to_set); - // let sqrt_price_to_set: U64F64 = sqrt(price_to_set).unwrap(); + // Set netuid0 to have price tao_emission / price > alpha_emission + let alpha_emission = U96F32::saturating_from_num( + SubtensorModule::get_block_emission_for_issuance( + SubtensorModule::get_alpha_issuance(netuid0).into(), + ) + .unwrap_or(0), + ); + let price_to_set: U64F64 = U64F64::saturating_from_num(0.01); + let price_to_set_fixed: U96F32 = U96F32::saturating_from_num(price_to_set); - // let tao_emission: U96F32 = U96F32::saturating_from_num(alpha_emission) - // .saturating_mul(price_to_set_fixed) - // .saturating_add(U96F32::saturating_from_num(0.01)); + let tao_emission: U96F32 = U96F32::saturating_from_num(alpha_emission) + .saturating_mul(price_to_set_fixed) + .saturating_add(U96F32::saturating_from_num(0.01)); - // // Set the price - // pallet_subtensor_swap::AlphaSqrtPrice::::insert(netuid0, sqrt_price_to_set); - // // Check the price is set - // assert_abs_diff_eq!( - // pallet_subtensor_swap::Pallet::::current_alpha_price(netuid0).to_num::(), - // price_to_set.to_num::(), - // epsilon = 0.001 - // ); + // Set the price + let tao = TaoCurrency::from(1_000_000_000_u64); + let alpha = AlphaCurrency::from((U64F64::saturating_from_num(u64::from(tao)) / price_to_set).to_num::()); + SubnetTAO::::insert(netuid0, tao); + SubnetAlphaIn::::insert(netuid0, alpha); - // let subnet_emissions = BTreeMap::from([(netuid0, tao_emission)]); + // Check the price is set + assert_abs_diff_eq!( + pallet_subtensor_swap::Pallet::::current_alpha_price(netuid0).to_num::(), + price_to_set.to_num::(), + epsilon = 0.001 + ); - // let (tao_in, alpha_in, alpha_out, excess_tao) = - // SubtensorModule::get_subnet_terms(&subnet_emissions); + let subnet_emissions = BTreeMap::from([(netuid0, tao_emission)]); - // // Check our condition is met - // assert!(tao_emission / price_to_set_fixed > alpha_emission); + let (tao_in, alpha_in, alpha_out, excess_tao) = + SubtensorModule::get_subnet_terms(&subnet_emissions); - // // alpha_out should be the alpha_emission, always - // assert_abs_diff_eq!( - // alpha_out[&netuid0].to_num::(), - // alpha_emission.to_num::(), - // epsilon = 0.01 - // ); + // Check our condition is met + assert!(tao_emission / price_to_set_fixed > alpha_emission); - // // alpha_in should equal the alpha_emission - // assert_abs_diff_eq!( - // alpha_in[&netuid0].to_num::(), - // alpha_emission.to_num::(), - // epsilon = 0.01 - // ); - // // tao_in should be the alpha_in at the ratio of the price - // assert_abs_diff_eq!( - // tao_in[&netuid0].to_num::(), - // alpha_in[&netuid0] - // .saturating_mul(price_to_set_fixed) - // .to_num::(), - // epsilon = 0.01 - // ); + // alpha_out should be the alpha_emission, always + assert_abs_diff_eq!( + alpha_out[&netuid0].to_num::(), + alpha_emission.to_num::(), + epsilon = 0.01 + ); - // // excess_tao should be the difference between the tao_emission and the tao_in - // assert_abs_diff_eq!( - // excess_tao[&netuid0].to_num::(), - // tao_emission.to_num::() - tao_in[&netuid0].to_num::(), - // epsilon = 0.01 - // ); - // }); + // alpha_in should equal the alpha_emission + assert_abs_diff_eq!( + alpha_in[&netuid0].to_num::(), + alpha_emission.to_num::(), + epsilon = 0.01 + ); + // tao_in should be the alpha_in at the ratio of the price + assert_abs_diff_eq!( + tao_in[&netuid0].to_num::(), + alpha_in[&netuid0] + .saturating_mul(price_to_set_fixed) + .to_num::(), + epsilon = 0.01 + ); + + // excess_tao should be the difference between the tao_emission and the tao_in + assert_abs_diff_eq!( + excess_tao[&netuid0].to_num::(), + tao_emission.to_num::() - tao_in[&netuid0].to_num::(), + epsilon = 0.01 + ); + }); } #[test] From 243df1eee715d49a0f709e62d6d88a8d638cc312 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Wed, 24 Dec 2025 13:30:15 -0500 Subject: [PATCH 077/240] Fix test_mining_emission_distribution_with_no_root_sell --- pallets/subtensor/src/tests/coinbase.rs | 428 ++++++++++++------------ 1 file changed, 212 insertions(+), 216 deletions(-) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index bb8bcc71f5..edecb60b4a 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -2693,49 +2693,49 @@ fn test_run_coinbase_not_started_start_after() { }); } -/// Test that coinbase updates protocol position liquidity -/// cargo test --package pallet-subtensor --lib -- tests::coinbase::test_coinbase_v3_liquidity_update --exact --show-output -#[test] -fn test_coinbase_v3_liquidity_update() { - todo!(); - - // new_test_ext(1).execute_with(|| { - // let owner_hotkey = U256::from(1); - // let owner_coldkey = U256::from(2); - // // add network - // let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); +// TODO: Revise when user liquidity is available +// Test that coinbase updates protocol position liquidity +// cargo test --package pallet-subtensor --lib -- tests::coinbase::test_coinbase_v3_liquidity_update --exact --show-output +// #[test] +// fn test_coinbase_v3_liquidity_update() { +// new_test_ext(1).execute_with(|| { +// let owner_hotkey = U256::from(1); +// let owner_coldkey = U256::from(2); - // // Force the swap to initialize - // ::SwapInterface::init_swap(netuid); +// // add network +// let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); - // let protocol_account_id = pallet_subtensor_swap::Pallet::::protocol_account_id(); - // let position = pallet_subtensor_swap::Positions::::get(( - // netuid, - // protocol_account_id, - // PositionId::from(1), - // )) - // .unwrap(); - // let liquidity_before = position.liquidity; - - // // Enable emissions and run coinbase (which will increase position liquidity) - // let emission: u64 = 1_234_567; - // // Set the TAO flow to non-zero - // SubnetTaoFlow::::insert(netuid, 8348383_i64); - // FirstEmissionBlockNumber::::insert(netuid, 0); - // SubtensorModule::run_coinbase(U96F32::from_num(emission)); +// // Force the swap to initialize +// ::SwapInterface::init_swap(netuid); + +// let protocol_account_id = pallet_subtensor_swap::Pallet::::protocol_account_id(); +// let position = pallet_subtensor_swap::Positions::::get(( +// netuid, +// protocol_account_id, +// PositionId::from(1), +// )) +// .unwrap(); +// let liquidity_before = position.liquidity; + +// // Enable emissions and run coinbase (which will increase position liquidity) +// let emission: u64 = 1_234_567; +// // Set the TAO flow to non-zero +// SubnetTaoFlow::::insert(netuid, 8348383_i64); +// FirstEmissionBlockNumber::::insert(netuid, 0); +// SubtensorModule::run_coinbase(U96F32::from_num(emission)); - // let position_after = pallet_subtensor_swap::Positions::::get(( - // netuid, - // protocol_account_id, - // PositionId::from(1), - // )) - // .unwrap(); - // let liquidity_after = position_after.liquidity; +// let position_after = pallet_subtensor_swap::Positions::::get(( +// netuid, +// protocol_account_id, +// PositionId::from(1), +// )) +// .unwrap(); +// let liquidity_after = position_after.liquidity; - // assert!(liquidity_before < liquidity_after); - // }); -} +// assert!(liquidity_before < liquidity_after); +// }); +// } // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_drain_alpha_childkey_parentkey_with_burn --exact --show-output --nocapture #[test] @@ -2938,196 +2938,192 @@ fn test_zero_shares_zero_emission() { #[test] fn test_mining_emission_distribution_with_no_root_sell() { - todo!(); - - // new_test_ext(1).execute_with(|| { - // let validator_coldkey = U256::from(1); - // let validator_hotkey = U256::from(2); - // let validator_miner_coldkey = U256::from(3); - // let validator_miner_hotkey = U256::from(4); - // let miner_coldkey = U256::from(5); - // let miner_hotkey = U256::from(6); - // let netuid = NetUid::from(1); - // let subnet_tempo = 10; - // let stake: u64 = 100_000_000_000; - // let root_stake: u64 = 200_000_000_000; // 200 TAO - - // // Create root network - // SubtensorModule::set_tao_weight(0); // Start tao weight at 0 - // SubtokenEnabled::::insert(NetUid::ROOT, true); - // NetworksAdded::::insert(NetUid::ROOT, true); - - // // Add network, register hotkeys, and setup network parameters - // add_network(netuid, subnet_tempo, 0); - // SubnetMechanism::::insert(netuid, 1); // Set mechanism to 1 - - // // Setup large LPs to prevent slippage - // SubnetTAO::::insert(netuid, TaoCurrency::from(1_000_000_000_000_000)); - // SubnetAlphaIn::::insert(netuid, AlphaCurrency::from(1_000_000_000_000_000)); - - // register_ok_neuron(netuid, validator_hotkey, validator_coldkey, 0); - // register_ok_neuron(netuid, validator_miner_hotkey, validator_miner_coldkey, 1); - // register_ok_neuron(netuid, miner_hotkey, miner_coldkey, 2); - // SubtensorModule::add_balance_to_coldkey_account( - // &validator_coldkey, - // stake + ExistentialDeposit::get(), - // ); - // SubtensorModule::add_balance_to_coldkey_account( - // &validator_miner_coldkey, - // stake + ExistentialDeposit::get(), - // ); - // SubtensorModule::add_balance_to_coldkey_account( - // &miner_coldkey, - // stake + ExistentialDeposit::get(), - // ); - // SubtensorModule::set_weights_set_rate_limit(netuid, 0); - // step_block(subnet_tempo); - // SubnetOwnerCut::::set(u16::MAX / 10); - // // There are two validators and three neurons - // MaxAllowedUids::::set(netuid, 3); - // SubtensorModule::set_max_allowed_validators(netuid, 2); - - // // Setup stakes: - // // Stake from validator - // // Stake from valiminer - // assert_ok!(SubtensorModule::add_stake( - // RuntimeOrigin::signed(validator_coldkey), - // validator_hotkey, - // netuid, - // stake.into() - // )); - // assert_ok!(SubtensorModule::add_stake( - // RuntimeOrigin::signed(validator_miner_coldkey), - // validator_miner_hotkey, - // netuid, - // stake.into() - // )); - - // // Setup YUMA so that it creates emissions - // Weights::::insert(NetUidStorageIndex::from(netuid), 0, vec![(1, 0xFFFF)]); - // Weights::::insert(NetUidStorageIndex::from(netuid), 1, vec![(2, 0xFFFF)]); - // BlockAtRegistration::::set(netuid, 0, 1); - // BlockAtRegistration::::set(netuid, 1, 1); - // BlockAtRegistration::::set(netuid, 2, 1); - // LastUpdate::::set(NetUidStorageIndex::from(netuid), vec![2, 2, 2]); - // Kappa::::set(netuid, u16::MAX / 5); - // ActivityCutoff::::set(netuid, u16::MAX); // makes all stake active - // ValidatorPermit::::insert(netuid, vec![true, true, false]); - - // // Run run_coinbase until emissions are drained - // step_block(subnet_tempo); + new_test_ext(1).execute_with(|| { + let validator_coldkey = U256::from(1); + let validator_hotkey = U256::from(2); + let validator_miner_coldkey = U256::from(3); + let validator_miner_hotkey = U256::from(4); + let miner_coldkey = U256::from(5); + let miner_hotkey = U256::from(6); + let netuid = NetUid::from(1); + let subnet_tempo = 10; + let stake: u64 = 100_000_000_000; + let root_stake: u64 = 200_000_000_000; // 200 TAO + + // Create root network + SubtensorModule::set_tao_weight(0); // Start tao weight at 0 + SubtokenEnabled::::insert(NetUid::ROOT, true); + NetworksAdded::::insert(NetUid::ROOT, true); + + // Add network, register hotkeys, and setup network parameters + add_network(netuid, subnet_tempo, 0); + SubnetMechanism::::insert(netuid, 1); // Set mechanism to 1 + + // Setup large LPs to prevent slippage + SubnetTAO::::insert(netuid, TaoCurrency::from(1_000_000_000_000_000)); + SubnetAlphaIn::::insert(netuid, AlphaCurrency::from(1_000_000_000_000_000)); + + register_ok_neuron(netuid, validator_hotkey, validator_coldkey, 0); + register_ok_neuron(netuid, validator_miner_hotkey, validator_miner_coldkey, 1); + register_ok_neuron(netuid, miner_hotkey, miner_coldkey, 2); + SubtensorModule::add_balance_to_coldkey_account( + &validator_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::add_balance_to_coldkey_account( + &validator_miner_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::add_balance_to_coldkey_account( + &miner_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::set_weights_set_rate_limit(netuid, 0); + step_block(subnet_tempo); + SubnetOwnerCut::::set(u16::MAX / 10); + // There are two validators and three neurons + MaxAllowedUids::::set(netuid, 3); + SubtensorModule::set_max_allowed_validators(netuid, 2); + + // Setup stakes: + // Stake from validator + // Stake from valiminer + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_coldkey), + validator_hotkey, + netuid, + stake.into() + )); + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_miner_coldkey), + validator_miner_hotkey, + netuid, + stake.into() + )); - // // Add stake to validator so it has root stake - // SubtensorModule::add_balance_to_coldkey_account(&validator_coldkey, root_stake.into()); - // // init root - // assert_ok!(SubtensorModule::add_stake( - // RuntimeOrigin::signed(validator_coldkey), - // validator_hotkey, - // NetUid::ROOT, - // root_stake.into() - // )); - // // Set tao weight non zero - // SubtensorModule::set_tao_weight(u64::MAX / 10); + // Setup YUMA so that it creates emissions + Weights::::insert(NetUidStorageIndex::from(netuid), 0, vec![(1, 0xFFFF)]); + Weights::::insert(NetUidStorageIndex::from(netuid), 1, vec![(2, 0xFFFF)]); + BlockAtRegistration::::set(netuid, 0, 1); + BlockAtRegistration::::set(netuid, 1, 1); + BlockAtRegistration::::set(netuid, 2, 1); + LastUpdate::::set(NetUidStorageIndex::from(netuid), vec![2, 2, 2]); + Kappa::::set(netuid, u16::MAX / 5); + ActivityCutoff::::set(netuid, u16::MAX); // makes all stake active + ValidatorPermit::::insert(netuid, vec![true, true, false]); + + // Run run_coinbase until emissions are drained + step_block(subnet_tempo); + + // Add stake to validator so it has root stake + SubtensorModule::add_balance_to_coldkey_account(&validator_coldkey, root_stake.into()); + // init root + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_coldkey), + validator_hotkey, + NetUid::ROOT, + root_stake.into() + )); + // Set tao weight non zero + SubtensorModule::set_tao_weight(u64::MAX / 10); - // // Make root sell NOT happen - // // set price very low, e.g. a lot of alpha in - // //SubnetAlphaIn::::insert(netuid, AlphaCurrency::from(1_000_000_000_000_000)); - // pallet_subtensor_swap::AlphaSqrtPrice::::insert( - // netuid, - // U64F64::saturating_from_num(0.01), - // ); + // Make root sell NOT happen + // set price very low, e.g. a lot of alpha in + //SubnetAlphaIn::::insert(netuid, AlphaCurrency::from(1_000_000_000_000_000)); + let alpha = AlphaCurrency::from(1_000_000_000_000_000_000_u64); + SubnetAlphaIn::::insert(netuid, alpha); - // // Make sure we ARE NOT root selling, so we do not have root alpha divs. - // let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); - // assert!(!root_sell_flag, "Root sell flag should be false"); + // Make sure we ARE NOT root selling, so we do not have root alpha divs. + let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); + assert!(!root_sell_flag, "Root sell flag should be false"); - // // Run run_coinbase until emissions are drained - // step_block(subnet_tempo); + // Run run_coinbase until emissions are drained + step_block(subnet_tempo); - // let old_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); - // let per_block_emission = SubtensorModule::get_block_emission_for_issuance( - // SubtensorModule::get_alpha_issuance(netuid).into(), - // ) - // .unwrap_or(0); + let old_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); + let per_block_emission = SubtensorModule::get_block_emission_for_issuance( + SubtensorModule::get_alpha_issuance(netuid).into(), + ) + .unwrap_or(0); - // // step by one block - // step_block(1); - // // Verify that root alpha divs - // let new_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); - // // Check that we are indeed NOT root selling, i.e. that root alpha divs are NOT increasing - // assert_eq!( - // new_root_alpha_divs, old_root_alpha_divs, - // "Root alpha divs should not increase" - // ); - // // Check root divs are zero - // assert_eq!( - // new_root_alpha_divs, - // AlphaCurrency::ZERO, - // "Root alpha divs should be zero" - // ); - // let miner_stake_before_epoch = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - // &miner_hotkey, - // &miner_coldkey, - // netuid, - // ); - // // Run again but with some root stake - // step_block(subnet_tempo - 2); - // assert_abs_diff_eq!( - // PendingServerEmission::::get(netuid).to_u64(), - // U96F32::saturating_from_num(per_block_emission) - // .saturating_mul(U96F32::saturating_from_num(subnet_tempo as u64)) - // .saturating_mul(U96F32::saturating_from_num(0.5)) // miner cut - // .saturating_mul(U96F32::saturating_from_num(0.90)) - // .saturating_to_num::(), - // epsilon = 100_000_u64.into() - // ); - // step_block(1); - // assert!( - // BlocksSinceLastStep::::get(netuid) == 0, - // "Blocks since last step should be 0" - // ); + // step by one block + step_block(1); + // Verify that root alpha divs + let new_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); + // Check that we are indeed NOT root selling, i.e. that root alpha divs are NOT increasing + assert_eq!( + new_root_alpha_divs, old_root_alpha_divs, + "Root alpha divs should not increase" + ); + // Check root divs are zero + assert_eq!( + new_root_alpha_divs, + AlphaCurrency::ZERO, + "Root alpha divs should be zero" + ); + let miner_stake_before_epoch = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &miner_hotkey, + &miner_coldkey, + netuid, + ); + // Run again but with some root stake + step_block(subnet_tempo - 2); + assert_abs_diff_eq!( + PendingServerEmission::::get(netuid).to_u64(), + U96F32::saturating_from_num(per_block_emission) + .saturating_mul(U96F32::saturating_from_num(subnet_tempo as u64)) + .saturating_mul(U96F32::saturating_from_num(0.5)) // miner cut + .saturating_mul(U96F32::saturating_from_num(0.90)) + .saturating_to_num::(), + epsilon = 100_000_u64.into() + ); + step_block(1); + assert!( + BlocksSinceLastStep::::get(netuid) == 0, + "Blocks since last step should be 0" + ); - // let miner_uid = Uids::::get(netuid, miner_hotkey).unwrap_or(0); - // log::info!("Miner uid: {miner_uid:?}"); - // let miner_incentive: AlphaCurrency = { - // let miner_incentive = Incentive::::get(NetUidStorageIndex::from(netuid)) - // .get(miner_uid as usize) - // .copied(); + let miner_uid = Uids::::get(netuid, miner_hotkey).unwrap_or(0); + log::info!("Miner uid: {miner_uid:?}"); + let miner_incentive: AlphaCurrency = { + let miner_incentive = Incentive::::get(NetUidStorageIndex::from(netuid)) + .get(miner_uid as usize) + .copied(); - // assert!(miner_incentive.is_some()); + assert!(miner_incentive.is_some()); - // (miner_incentive.unwrap_or_default() as u64).into() - // }; - // log::info!("Miner incentive: {miner_incentive:?}"); + (miner_incentive.unwrap_or_default() as u64).into() + }; + log::info!("Miner incentive: {miner_incentive:?}"); - // // Miner emissions - // let miner_emission_1: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - // &miner_hotkey, - // &miner_coldkey, - // netuid, - // ) - // .to_u64() - // - miner_stake_before_epoch.to_u64(); + // Miner emissions + let miner_emission_1: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &miner_hotkey, + &miner_coldkey, + netuid, + ) + .to_u64() + - miner_stake_before_epoch.to_u64(); - // assert_abs_diff_eq!( - // Incentive::::get(NetUidStorageIndex::from(netuid)) - // .iter() - // .sum::(), - // u16::MAX, - // epsilon = 10 - // ); + assert_abs_diff_eq!( + Incentive::::get(NetUidStorageIndex::from(netuid)) + .iter() + .sum::(), + u16::MAX, + epsilon = 10 + ); - // assert_abs_diff_eq!( - // miner_emission_1, - // U96F32::saturating_from_num(miner_incentive) - // .saturating_div(u16::MAX.into()) - // .saturating_mul(U96F32::saturating_from_num(per_block_emission)) - // .saturating_mul(U96F32::saturating_from_num(subnet_tempo + 1)) - // .saturating_mul(U96F32::saturating_from_num(0.45)) // miner cut - // .saturating_to_num::(), - // epsilon = 1_000_000_u64 - // ); - // }); + assert_abs_diff_eq!( + miner_emission_1, + U96F32::saturating_from_num(miner_incentive) + .saturating_div(u16::MAX.into()) + .saturating_mul(U96F32::saturating_from_num(per_block_emission)) + .saturating_mul(U96F32::saturating_from_num(subnet_tempo + 1)) + .saturating_mul(U96F32::saturating_from_num(0.45)) // miner cut + .saturating_to_num::(), + epsilon = 1_000_000_u64 + ); + }); } // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_mining_emission_distribution_with_root_sell --exact --show-output --nocapture From d2f9e2b0fe18e2276087b3d8cfc5b67f587b044b Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Wed, 24 Dec 2025 13:33:02 -0500 Subject: [PATCH 078/240] Fix test_mining_emission_distribution_with_root_sell --- pallets/subtensor/src/tests/coinbase.rs | 298 ++++++++++++------------ 1 file changed, 147 insertions(+), 151 deletions(-) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index edecb60b4a..e540f1e576 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -3129,177 +3129,173 @@ fn test_mining_emission_distribution_with_no_root_sell() { // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_mining_emission_distribution_with_root_sell --exact --show-output --nocapture #[test] fn test_mining_emission_distribution_with_root_sell() { - todo!(); + new_test_ext(1).execute_with(|| { + let validator_coldkey = U256::from(1); + let validator_hotkey = U256::from(2); + let validator_miner_coldkey = U256::from(3); + let validator_miner_hotkey = U256::from(4); + let miner_coldkey = U256::from(5); + let miner_hotkey = U256::from(6); + let subnet_tempo = 10; + let stake: u64 = 100_000_000_000; + let root_stake: u64 = 200_000_000_000; // 200 TAO - // new_test_ext(1).execute_with(|| { - // let validator_coldkey = U256::from(1); - // let validator_hotkey = U256::from(2); - // let validator_miner_coldkey = U256::from(3); - // let validator_miner_hotkey = U256::from(4); - // let miner_coldkey = U256::from(5); - // let miner_hotkey = U256::from(6); - // let subnet_tempo = 10; - // let stake: u64 = 100_000_000_000; - // let root_stake: u64 = 200_000_000_000; // 200 TAO + // Create root network + SubtensorModule::set_tao_weight(0); // Start tao weight at 0 + SubtokenEnabled::::insert(NetUid::ROOT, true); + NetworksAdded::::insert(NetUid::ROOT, true); - // // Create root network - // SubtensorModule::set_tao_weight(0); // Start tao weight at 0 - // SubtokenEnabled::::insert(NetUid::ROOT, true); - // NetworksAdded::::insert(NetUid::ROOT, true); + // Add network, register hotkeys, and setup network parameters + let owner_hotkey = U256::from(10); + let owner_coldkey = U256::from(11); + let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); + Tempo::::insert(netuid, 1); + FirstEmissionBlockNumber::::insert(netuid, 0); - // // Add network, register hotkeys, and setup network parameters - // let owner_hotkey = U256::from(10); - // let owner_coldkey = U256::from(11); - // let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); - // Tempo::::insert(netuid, 1); - // FirstEmissionBlockNumber::::insert(netuid, 0); + // Setup large LPs to prevent slippage + SubnetTAO::::insert(netuid, TaoCurrency::from(1_000_000_000_000_000)); + SubnetAlphaIn::::insert(netuid, AlphaCurrency::from(1_000_000_000_000_000)); - // // Setup large LPs to prevent slippage - // SubnetTAO::::insert(netuid, TaoCurrency::from(1_000_000_000_000_000)); - // SubnetAlphaIn::::insert(netuid, AlphaCurrency::from(1_000_000_000_000_000)); + register_ok_neuron(netuid, validator_hotkey, validator_coldkey, 0); + register_ok_neuron(netuid, validator_miner_hotkey, validator_miner_coldkey, 1); + register_ok_neuron(netuid, miner_hotkey, miner_coldkey, 2); + SubtensorModule::add_balance_to_coldkey_account( + &validator_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::add_balance_to_coldkey_account( + &validator_miner_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::add_balance_to_coldkey_account( + &miner_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::set_weights_set_rate_limit(netuid, 0); + step_block(subnet_tempo); + SubnetOwnerCut::::set(u16::MAX / 10); + // There are two validators and three neurons + MaxAllowedUids::::set(netuid, 3); + SubtensorModule::set_max_allowed_validators(netuid, 2); - // register_ok_neuron(netuid, validator_hotkey, validator_coldkey, 0); - // register_ok_neuron(netuid, validator_miner_hotkey, validator_miner_coldkey, 1); - // register_ok_neuron(netuid, miner_hotkey, miner_coldkey, 2); - // SubtensorModule::add_balance_to_coldkey_account( - // &validator_coldkey, - // stake + ExistentialDeposit::get(), - // ); - // SubtensorModule::add_balance_to_coldkey_account( - // &validator_miner_coldkey, - // stake + ExistentialDeposit::get(), - // ); - // SubtensorModule::add_balance_to_coldkey_account( - // &miner_coldkey, - // stake + ExistentialDeposit::get(), - // ); - // SubtensorModule::set_weights_set_rate_limit(netuid, 0); - // step_block(subnet_tempo); - // SubnetOwnerCut::::set(u16::MAX / 10); - // // There are two validators and three neurons - // MaxAllowedUids::::set(netuid, 3); - // SubtensorModule::set_max_allowed_validators(netuid, 2); + // Setup stakes: + // Stake from validator + // Stake from valiminer + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_coldkey), + validator_hotkey, + netuid, + stake.into() + )); + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_miner_coldkey), + validator_miner_hotkey, + netuid, + stake.into() + )); - // // Setup stakes: - // // Stake from validator - // // Stake from valiminer - // assert_ok!(SubtensorModule::add_stake( - // RuntimeOrigin::signed(validator_coldkey), - // validator_hotkey, - // netuid, - // stake.into() - // )); - // assert_ok!(SubtensorModule::add_stake( - // RuntimeOrigin::signed(validator_miner_coldkey), - // validator_miner_hotkey, - // netuid, - // stake.into() - // )); + // Setup YUMA so that it creates emissions + Weights::::insert(NetUidStorageIndex::from(netuid), 0, vec![(1, 0xFFFF)]); + Weights::::insert(NetUidStorageIndex::from(netuid), 1, vec![(2, 0xFFFF)]); + BlockAtRegistration::::set(netuid, 0, 1); + BlockAtRegistration::::set(netuid, 1, 1); + BlockAtRegistration::::set(netuid, 2, 1); + LastUpdate::::set(NetUidStorageIndex::from(netuid), vec![2, 2, 2]); + Kappa::::set(netuid, u16::MAX / 5); + ActivityCutoff::::set(netuid, u16::MAX); // makes all stake active + ValidatorPermit::::insert(netuid, vec![true, true, false]); - // // Setup YUMA so that it creates emissions - // Weights::::insert(NetUidStorageIndex::from(netuid), 0, vec![(1, 0xFFFF)]); - // Weights::::insert(NetUidStorageIndex::from(netuid), 1, vec![(2, 0xFFFF)]); - // BlockAtRegistration::::set(netuid, 0, 1); - // BlockAtRegistration::::set(netuid, 1, 1); - // BlockAtRegistration::::set(netuid, 2, 1); - // LastUpdate::::set(NetUidStorageIndex::from(netuid), vec![2, 2, 2]); - // Kappa::::set(netuid, u16::MAX / 5); - // ActivityCutoff::::set(netuid, u16::MAX); // makes all stake active - // ValidatorPermit::::insert(netuid, vec![true, true, false]); - - // // Run run_coinbase until emissions are drained - // step_block(subnet_tempo); + // Run run_coinbase until emissions are drained + step_block(subnet_tempo); - // // Add stake to validator so it has root stake - // SubtensorModule::add_balance_to_coldkey_account(&validator_coldkey, root_stake.into()); - // // init root - // assert_ok!(SubtensorModule::add_stake( - // RuntimeOrigin::signed(validator_coldkey), - // validator_hotkey, - // NetUid::ROOT, - // root_stake.into() - // )); - // // Set tao weight non zero - // SubtensorModule::set_tao_weight(u64::MAX / 10); + // Add stake to validator so it has root stake + SubtensorModule::add_balance_to_coldkey_account(&validator_coldkey, root_stake.into()); + // init root + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_coldkey), + validator_hotkey, + NetUid::ROOT, + root_stake.into() + )); + // Set tao weight non zero + SubtensorModule::set_tao_weight(u64::MAX / 10); - // // Make root sell happen - // // Set moving price > 1.0 - // // Set price > 1.0 - // pallet_subtensor_swap::AlphaSqrtPrice::::insert( - // netuid, - // U64F64::saturating_from_num(10.0), - // ); + // Make root sell happen + // Set moving price > 1.0 + // Set price > 1.0 + let alpha = AlphaCurrency::from(100_000_000_000_000); + SubnetAlphaIn::::insert(netuid, alpha); - // SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); + SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); - // // Make sure we are root selling, so we have root alpha divs. - // let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); - // assert!(root_sell_flag, "Root sell flag should be true"); + // Make sure we are root selling, so we have root alpha divs. + let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); + assert!(root_sell_flag, "Root sell flag should be true"); - // // Run run_coinbase until emissions are drained - // step_block(subnet_tempo); + // Run run_coinbase until emissions are drained + step_block(subnet_tempo); - // let old_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); - // let miner_stake_before_epoch = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - // &miner_hotkey, - // &miner_coldkey, - // netuid, - // ); + let old_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); + let miner_stake_before_epoch = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &miner_hotkey, + &miner_coldkey, + netuid, + ); - // // step by one block - // step_block(1); - // // Verify root alpha divs - // let new_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); - // // Check that we ARE root selling, i.e. that root alpha divs are changing - // assert_ne!( - // new_root_alpha_divs, old_root_alpha_divs, - // "Root alpha divs should be changing" - // ); - // assert!( - // new_root_alpha_divs > AlphaCurrency::ZERO, - // "Root alpha divs should be greater than 0" - // ); + // step by one block + step_block(1); + // Verify root alpha divs + let new_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); + // Check that we ARE root selling, i.e. that root alpha divs are changing + assert_ne!( + new_root_alpha_divs, old_root_alpha_divs, + "Root alpha divs should be changing" + ); + assert!( + new_root_alpha_divs > AlphaCurrency::ZERO, + "Root alpha divs should be greater than 0" + ); - // // Run again but with some root stake - // step_block(subnet_tempo - 1); + // Run again but with some root stake + step_block(subnet_tempo - 1); - // let miner_uid = Uids::::get(netuid, miner_hotkey).unwrap_or(0); - // let miner_incentive: AlphaCurrency = { - // let miner_incentive = Incentive::::get(NetUidStorageIndex::from(netuid)) - // .get(miner_uid as usize) - // .copied(); + let miner_uid = Uids::::get(netuid, miner_hotkey).unwrap_or(0); + let miner_incentive: AlphaCurrency = { + let miner_incentive = Incentive::::get(NetUidStorageIndex::from(netuid)) + .get(miner_uid as usize) + .copied(); - // assert!(miner_incentive.is_some()); + assert!(miner_incentive.is_some()); - // (miner_incentive.unwrap_or_default() as u64).into() - // }; - // log::info!("Miner incentive: {miner_incentive:?}"); + (miner_incentive.unwrap_or_default() as u64).into() + }; + log::info!("Miner incentive: {miner_incentive:?}"); - // let per_block_emission = SubtensorModule::get_block_emission_for_issuance( - // SubtensorModule::get_alpha_issuance(netuid).into(), - // ) - // .unwrap_or(0); + let per_block_emission = SubtensorModule::get_block_emission_for_issuance( + SubtensorModule::get_alpha_issuance(netuid).into(), + ) + .unwrap_or(0); - // // Miner emissions - // let miner_emission_1: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - // &miner_hotkey, - // &miner_coldkey, - // netuid, - // ) - // .to_u64() - // - miner_stake_before_epoch.to_u64(); - - // assert_abs_diff_eq!( - // miner_emission_1, - // U96F32::saturating_from_num(miner_incentive) - // .saturating_div(u16::MAX.into()) - // .saturating_mul(U96F32::saturating_from_num(per_block_emission)) - // .saturating_mul(U96F32::saturating_from_num(subnet_tempo + 1)) - // .saturating_mul(U96F32::saturating_from_num(0.45)) // miner cut - // .saturating_to_num::(), - // epsilon = 1_000_000_u64 - // ); - // }); + // Miner emissions + let miner_emission_1: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &miner_hotkey, + &miner_coldkey, + netuid, + ) + .to_u64() + - miner_stake_before_epoch.to_u64(); + + assert_abs_diff_eq!( + miner_emission_1, + U96F32::saturating_from_num(miner_incentive) + .saturating_div(u16::MAX.into()) + .saturating_mul(U96F32::saturating_from_num(per_block_emission)) + .saturating_mul(U96F32::saturating_from_num(subnet_tempo + 1)) + .saturating_mul(U96F32::saturating_from_num(0.45)) // miner cut + .saturating_to_num::(), + epsilon = 1_000_000_u64 + ); + }); } #[test] From 96402fd21628abe5ba269bae42e9dffc095b81c1 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Wed, 24 Dec 2025 13:35:59 -0500 Subject: [PATCH 079/240] Fix test_pending_emission_start_call_not_done --- pallets/subtensor/src/tests/coinbase.rs | 158 ++++++++++++------------ 1 file changed, 78 insertions(+), 80 deletions(-) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index e540f1e576..294552c760 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -3799,86 +3799,84 @@ fn test_coinbase_emit_to_subnets_with_root_sell() { // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_pending_emission_start_call_not_done --exact --show-output --nocapture #[test] fn test_pending_emission_start_call_not_done() { - todo!(); - - // new_test_ext(1).execute_with(|| { - // let validator_coldkey = U256::from(1); - // let validator_hotkey = U256::from(2); - // let subnet_tempo = 10; - // let stake: u64 = 100_000_000_000; - // let root_stake: u64 = 200_000_000_000; // 200 TAO - - // // Create root network - // NetworksAdded::::insert(NetUid::ROOT, true); - // // enabled root - // SubtokenEnabled::::insert(NetUid::ROOT, true); - - // // Add network, register hotkeys, and setup network parameters - // let owner_hotkey = U256::from(10); - // let owner_coldkey = U256::from(11); - // let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); - // // Remove FirstEmissionBlockNumber - // FirstEmissionBlockNumber::::remove(netuid); - // Tempo::::insert(netuid, subnet_tempo); - - // register_ok_neuron(netuid, validator_hotkey, validator_coldkey, 0); - // SubtensorModule::add_balance_to_coldkey_account( - // &validator_coldkey, - // stake + ExistentialDeposit::get(), - // ); - // SubtensorModule::set_weights_set_rate_limit(netuid, 0); - // step_block(subnet_tempo); - // SubnetOwnerCut::::set(u16::MAX / 10); - // // There are two validators and three neurons - // MaxAllowedUids::::set(netuid, 3); - // SubtensorModule::set_max_allowed_validators(netuid, 2); - - // // Add stake to validator so it has root stake - // SubtensorModule::add_balance_to_coldkey_account(&validator_coldkey, root_stake.into()); - // // init root - // assert_ok!(SubtensorModule::add_stake( - // RuntimeOrigin::signed(validator_coldkey), - // validator_hotkey, - // NetUid::ROOT, - // root_stake.into() - // )); - // // Set tao weight non zero - // SubtensorModule::set_tao_weight(u64::MAX / 10); - - // // Make root sell happen - // // Set moving price > 1.0 - // // Set price > 1.0 - // pallet_subtensor_swap::AlphaSqrtPrice::::insert( - // netuid, - // U64F64::saturating_from_num(10.0), - // ); - - // SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); - - // // Make sure we are root selling, so we have root alpha divs. - // let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); - // assert!(root_sell_flag, "Root sell flag should be true"); - - // // !!! Check that the subnet FirstEmissionBlockNumber is None -- no entry - // assert!(FirstEmissionBlockNumber::::get(netuid).is_none()); - - // // Run run_coinbase until emissions are accumulated - // step_block(subnet_tempo - 2); - - // // Verify that all pending emissions are zero - // assert_eq!( - // PendingServerEmission::::get(netuid), - // AlphaCurrency::ZERO - // ); - // assert_eq!( - // PendingValidatorEmission::::get(netuid), - // AlphaCurrency::ZERO - // ); - // assert_eq!( - // PendingRootAlphaDivs::::get(netuid), - // AlphaCurrency::ZERO - // ); - // }); + new_test_ext(1).execute_with(|| { + let validator_coldkey = U256::from(1); + let validator_hotkey = U256::from(2); + let subnet_tempo = 10; + let stake: u64 = 100_000_000_000; + let root_stake: u64 = 200_000_000_000; // 200 TAO + + // Create root network + NetworksAdded::::insert(NetUid::ROOT, true); + // enabled root + SubtokenEnabled::::insert(NetUid::ROOT, true); + + // Add network, register hotkeys, and setup network parameters + let owner_hotkey = U256::from(10); + let owner_coldkey = U256::from(11); + let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); + // Remove FirstEmissionBlockNumber + FirstEmissionBlockNumber::::remove(netuid); + Tempo::::insert(netuid, subnet_tempo); + + register_ok_neuron(netuid, validator_hotkey, validator_coldkey, 0); + SubtensorModule::add_balance_to_coldkey_account( + &validator_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::set_weights_set_rate_limit(netuid, 0); + step_block(subnet_tempo); + SubnetOwnerCut::::set(u16::MAX / 10); + // There are two validators and three neurons + MaxAllowedUids::::set(netuid, 3); + SubtensorModule::set_max_allowed_validators(netuid, 2); + + // Add stake to validator so it has root stake + SubtensorModule::add_balance_to_coldkey_account(&validator_coldkey, root_stake.into()); + // init root + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_coldkey), + validator_hotkey, + NetUid::ROOT, + root_stake.into() + )); + // Set tao weight non zero + SubtensorModule::set_tao_weight(u64::MAX / 10); + + // Make root sell happen + // Set moving price > 1.0 + // Set price > 1.0 + let tao = TaoCurrency::from(10_000_000_000_u64); + let alpha = AlphaCurrency::from(1_000_000_000_u64); + SubnetTAO::::insert(netuid, tao); + SubnetAlphaIn::::insert(netuid, alpha); + + SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); + + // Make sure we are root selling, so we have root alpha divs. + let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); + assert!(root_sell_flag, "Root sell flag should be true"); + + // !!! Check that the subnet FirstEmissionBlockNumber is None -- no entry + assert!(FirstEmissionBlockNumber::::get(netuid).is_none()); + + // Run run_coinbase until emissions are accumulated + step_block(subnet_tempo - 2); + + // Verify that all pending emissions are zero + assert_eq!( + PendingServerEmission::::get(netuid), + AlphaCurrency::ZERO + ); + assert_eq!( + PendingValidatorEmission::::get(netuid), + AlphaCurrency::ZERO + ); + assert_eq!( + PendingRootAlphaDivs::::get(netuid), + AlphaCurrency::ZERO + ); + }); } #[test] From 6c55c7bdc914c2229414df894d4f55d0afe85e65 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Wed, 24 Dec 2025 13:55:09 -0500 Subject: [PATCH 080/240] Remove non-zero delta-out requirement for zero delta-in. Fix test_migrate_reset_unactive_sn --- pallets/subtensor/src/tests/claim_root.rs | 2 +- pallets/subtensor/src/tests/migration.rs | 278 +++++++++++----------- pallets/swap/src/pallet/swap_step.rs | 16 +- 3 files changed, 145 insertions(+), 151 deletions(-) diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index 41c85114d9..852f8b4abd 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -1058,7 +1058,7 @@ fn test_claim_root_coinbase_distribution() { let current_price = ::SwapInterface::current_alpha_price(netuid.into()) .saturating_to_num::(); - assert_eq!(current_price, 10.0f64); + assert_eq!(current_price, 2.0f64); RootClaimableThreshold::::insert(netuid, I96F32::from_num(0)); let initial_alpha_issuance = SubtensorModule::get_alpha_issuance(netuid); diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index dc36e75014..aa35751aa0 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -2657,149 +2657,141 @@ fn test_migrate_reset_unactive_sn_get_unactive_netuids() { #[test] fn test_migrate_reset_unactive_sn() { - todo!(); - - // new_test_ext(1).execute_with(|| { - // let (active_netuids, inactive_netuids) = do_setup_unactive_sn(); - - // let initial_tao = Pallet::::get_network_min_lock(); - // let initial_alpha: AlphaCurrency = initial_tao.to_u64().into(); - - // // Run the migration - // let w = crate::migrations::migrate_reset_unactive_sn::migrate_reset_unactive_sn::(); - // assert!(!w.is_zero(), "weight must be non-zero"); - - // // Verify the results - // for netuid in &inactive_netuids { - // let actual_tao_lock_amount = SubnetLocked::::get(*netuid); - // let actual_tao_lock_amount_less_pool_tao = if (actual_tao_lock_amount < initial_tao) { - // TaoCurrency::ZERO - // } else { - // actual_tao_lock_amount - initial_tao - // }; - // assert_eq!( - // PendingServerEmission::::get(netuid), - // AlphaCurrency::ZERO - // ); - // assert_eq!( - // PendingValidatorEmission::::get(netuid), - // AlphaCurrency::ZERO - // ); - // assert_eq!( - // PendingRootAlphaDivs::::get(netuid), - // AlphaCurrency::ZERO - // ); - // assert_eq!( - // // not modified - // RAORecycledForRegistration::::get(netuid), - // actual_tao_lock_amount_less_pool_tao - // ); - // assert!(pallet_subtensor_swap::AlphaSqrtPrice::::contains_key( - // *netuid - // )); - // assert_eq!(PendingOwnerCut::::get(netuid), AlphaCurrency::ZERO); - // assert_ne!(SubnetTAO::::get(netuid), initial_tao); - // assert_ne!(SubnetAlphaIn::::get(netuid), initial_alpha); - // assert_ne!(SubnetAlphaOut::::get(netuid), AlphaCurrency::ZERO); - // assert_eq!(SubnetTaoInEmission::::get(netuid), TaoCurrency::ZERO); - // assert_eq!( - // SubnetAlphaInEmission::::get(netuid), - // AlphaCurrency::ZERO - // ); - // assert_eq!( - // SubnetAlphaOutEmission::::get(netuid), - // AlphaCurrency::ZERO - // ); - // assert_ne!(SubnetVolume::::get(netuid), 0u128); - // for hotkey in 0..10 { - // let hk = U256::from(hotkey); - // assert_ne!( - // TotalHotkeyAlpha::::get(hk, netuid), - // AlphaCurrency::ZERO - // ); - // assert_ne!( - // TotalHotkeyShares::::get(hk, netuid), - // U64F64::from_num(0.0) - // ); - // assert_ne!( - // TotalHotkeyAlphaLastEpoch::::get(hk, netuid), - // AlphaCurrency::ZERO - // ); - // assert_ne!(RootClaimable::::get(hk).get(netuid), None); - // for coldkey in 0..10 { - // let ck = U256::from(coldkey); - // assert_ne!(Alpha::::get((hk, ck, netuid)), U64F64::from_num(0.0)); - // assert_ne!(RootClaimed::::get((netuid, hk, ck)), 0u128); - // } - // } - - // // Don't touch SubnetLocked - // assert_ne!(SubnetLocked::::get(netuid), TaoCurrency::ZERO); - // } - - // // !!! Make sure the active subnets were not reset - // for netuid in &active_netuids { - // let actual_tao_lock_amount = SubnetLocked::::get(*netuid); - // let actual_tao_lock_amount_less_pool_tao = actual_tao_lock_amount - initial_tao; - // assert_ne!( - // PendingServerEmission::::get(netuid), - // AlphaCurrency::ZERO - // ); - // assert_ne!( - // PendingValidatorEmission::::get(netuid), - // AlphaCurrency::ZERO - // ); - // assert_ne!( - // PendingRootAlphaDivs::::get(netuid), - // AlphaCurrency::ZERO - // ); - // assert_eq!( - // // not modified - // RAORecycledForRegistration::::get(netuid), - // actual_tao_lock_amount_less_pool_tao - // ); - // assert_ne!(SubnetTaoInEmission::::get(netuid), TaoCurrency::ZERO); - // assert_ne!( - // SubnetAlphaInEmission::::get(netuid), - // AlphaCurrency::ZERO - // ); - // assert_ne!( - // SubnetAlphaOutEmission::::get(netuid), - // AlphaCurrency::ZERO - // ); - // assert!(pallet_subtensor_swap::AlphaSqrtPrice::::contains_key( - // *netuid - // )); - // assert_ne!(PendingOwnerCut::::get(netuid), AlphaCurrency::ZERO); - // assert_ne!(SubnetTAO::::get(netuid), initial_tao); - // assert_ne!(SubnetAlphaIn::::get(netuid), initial_alpha); - // assert_ne!(SubnetAlphaOut::::get(netuid), AlphaCurrency::ZERO); - // assert_ne!(SubnetVolume::::get(netuid), 0u128); - // for hotkey in 0..10 { - // let hk = U256::from(hotkey); - // assert_ne!( - // TotalHotkeyAlpha::::get(hk, netuid), - // AlphaCurrency::ZERO - // ); - // assert_ne!( - // TotalHotkeyShares::::get(hk, netuid), - // U64F64::from_num(0.0) - // ); - // assert_ne!( - // TotalHotkeyAlphaLastEpoch::::get(hk, netuid), - // AlphaCurrency::ZERO - // ); - // assert!(RootClaimable::::get(hk).contains_key(netuid)); - // for coldkey in 0..10 { - // let ck = U256::from(coldkey); - // assert_ne!(Alpha::::get((hk, ck, netuid)), U64F64::from_num(0.0)); - // assert_ne!(RootClaimed::::get((netuid, hk, ck)), 0u128); - // } - // } - // // Don't touch SubnetLocked - // assert_ne!(SubnetLocked::::get(netuid), TaoCurrency::ZERO); - // } - // }); + new_test_ext(1).execute_with(|| { + let (active_netuids, inactive_netuids) = do_setup_unactive_sn(); + + let initial_tao = Pallet::::get_network_min_lock(); + let initial_alpha: AlphaCurrency = initial_tao.to_u64().into(); + + // Run the migration + let w = crate::migrations::migrate_reset_unactive_sn::migrate_reset_unactive_sn::(); + assert!(!w.is_zero(), "weight must be non-zero"); + + // Verify the results + for netuid in &inactive_netuids { + let actual_tao_lock_amount = SubnetLocked::::get(*netuid); + let actual_tao_lock_amount_less_pool_tao = if (actual_tao_lock_amount < initial_tao) { + TaoCurrency::ZERO + } else { + actual_tao_lock_amount - initial_tao + }; + assert_eq!( + PendingServerEmission::::get(netuid), + AlphaCurrency::ZERO + ); + assert_eq!( + PendingValidatorEmission::::get(netuid), + AlphaCurrency::ZERO + ); + assert_eq!( + PendingRootAlphaDivs::::get(netuid), + AlphaCurrency::ZERO + ); + assert_eq!( + // not modified + RAORecycledForRegistration::::get(netuid), + actual_tao_lock_amount_less_pool_tao + ); + assert_eq!(PendingOwnerCut::::get(netuid), AlphaCurrency::ZERO); + assert_ne!(SubnetTAO::::get(netuid), initial_tao); + assert_ne!(SubnetAlphaIn::::get(netuid), initial_alpha); + assert_ne!(SubnetAlphaOut::::get(netuid), AlphaCurrency::ZERO); + assert_eq!(SubnetTaoInEmission::::get(netuid), TaoCurrency::ZERO); + assert_eq!( + SubnetAlphaInEmission::::get(netuid), + AlphaCurrency::ZERO + ); + assert_eq!( + SubnetAlphaOutEmission::::get(netuid), + AlphaCurrency::ZERO + ); + assert_ne!(SubnetVolume::::get(netuid), 0u128); + for hotkey in 0..10 { + let hk = U256::from(hotkey); + assert_ne!( + TotalHotkeyAlpha::::get(hk, netuid), + AlphaCurrency::ZERO + ); + assert_ne!( + TotalHotkeyShares::::get(hk, netuid), + U64F64::from_num(0.0) + ); + assert_ne!( + TotalHotkeyAlphaLastEpoch::::get(hk, netuid), + AlphaCurrency::ZERO + ); + assert_ne!(RootClaimable::::get(hk).get(netuid), None); + for coldkey in 0..10 { + let ck = U256::from(coldkey); + assert_ne!(Alpha::::get((hk, ck, netuid)), U64F64::from_num(0.0)); + assert_ne!(RootClaimed::::get((netuid, hk, ck)), 0u128); + } + } + + // Don't touch SubnetLocked + assert_ne!(SubnetLocked::::get(netuid), TaoCurrency::ZERO); + } + + // !!! Make sure the active subnets were not reset + for netuid in &active_netuids { + let actual_tao_lock_amount = SubnetLocked::::get(*netuid); + let actual_tao_lock_amount_less_pool_tao = actual_tao_lock_amount - initial_tao; + assert_ne!( + PendingServerEmission::::get(netuid), + AlphaCurrency::ZERO + ); + assert_ne!( + PendingValidatorEmission::::get(netuid), + AlphaCurrency::ZERO + ); + assert_ne!( + PendingRootAlphaDivs::::get(netuid), + AlphaCurrency::ZERO + ); + assert_eq!( + // not modified + RAORecycledForRegistration::::get(netuid), + actual_tao_lock_amount_less_pool_tao + ); + assert_ne!(SubnetTaoInEmission::::get(netuid), TaoCurrency::ZERO); + assert_ne!( + SubnetAlphaInEmission::::get(netuid), + AlphaCurrency::ZERO + ); + assert_ne!( + SubnetAlphaOutEmission::::get(netuid), + AlphaCurrency::ZERO + ); + assert_ne!(PendingOwnerCut::::get(netuid), AlphaCurrency::ZERO); + assert_ne!(SubnetTAO::::get(netuid), initial_tao); + assert_ne!(SubnetAlphaIn::::get(netuid), initial_alpha); + assert_ne!(SubnetAlphaOut::::get(netuid), AlphaCurrency::ZERO); + assert_ne!(SubnetVolume::::get(netuid), 0u128); + for hotkey in 0..10 { + let hk = U256::from(hotkey); + assert_ne!( + TotalHotkeyAlpha::::get(hk, netuid), + AlphaCurrency::ZERO + ); + assert_ne!( + TotalHotkeyShares::::get(hk, netuid), + U64F64::from_num(0.0) + ); + assert_ne!( + TotalHotkeyAlphaLastEpoch::::get(hk, netuid), + AlphaCurrency::ZERO + ); + assert!(RootClaimable::::get(hk).contains_key(netuid)); + for coldkey in 0..10 { + let ck = U256::from(coldkey); + assert_ne!(Alpha::::get((hk, ck, netuid)), U64F64::from_num(0.0)); + assert_ne!(RootClaimed::::get((netuid, hk, ck)), 0u128); + } + } + // Don't touch SubnetLocked + assert_ne!(SubnetLocked::::get(netuid), TaoCurrency::ZERO); + } + }); } #[test] diff --git a/pallets/swap/src/pallet/swap_step.rs b/pallets/swap/src/pallet/swap_step.rs index ee345d57d3..f4758b15f0 100644 --- a/pallets/swap/src/pallet/swap_step.rs +++ b/pallets/swap/src/pallet/swap_step.rs @@ -117,13 +117,15 @@ where // Convert amounts, actual swap happens here let delta_out = Self::convert_deltas(self.netuid, self.delta_in); log::trace!("\tDelta Out : {delta_out}"); - ensure!( - delta_out > 0.into(), - Error::::ReservesTooLow - ); - - // Hold the fees - Self::add_fees(self.netuid, self.fee); + if self.delta_in > 0.into() { + ensure!( + delta_out > 0.into(), + Error::::ReservesTooLow + ); + + // Hold the fees + Self::add_fees(self.netuid, self.fee); + } Ok(SwapStepResult { fee_paid: self.fee, From b8d5ffe4601f4fed683e4e4484807273ec0cdd1d Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Fri, 26 Dec 2025 13:30:06 -0500 Subject: [PATCH 081/240] Fix test_add_stake_insufficient_liquidity_one_side_ok --- pallets/subtensor/src/tests/networks.rs | 895 ++++++++++++------------ pallets/subtensor/src/tests/staking.rs | 2 +- 2 files changed, 448 insertions(+), 449 deletions(-) diff --git a/pallets/subtensor/src/tests/networks.rs b/pallets/subtensor/src/tests/networks.rs index c582352412..303b5a8b26 100644 --- a/pallets/subtensor/src/tests/networks.rs +++ b/pallets/subtensor/src/tests/networks.rs @@ -1783,454 +1783,453 @@ fn test_tempo_greater_than_weight_set_rate_limit() { }) } -#[allow(clippy::indexing_slicing)] -#[test] -fn massive_dissolve_refund_and_reregistration_flow_is_lossless_and_cleans_state() { - todo!(); - - // new_test_ext(0).execute_with(|| { - // // ──────────────────────────────────────────────────────────────────── - // // 0) Constants and helpers (distinct hotkeys & coldkeys) - // // ──────────────────────────────────────────────────────────────────── - // const NUM_NETS: usize = 4; - - // // Six LP coldkeys - // let cold_lps: [U256; 6] = [ - // U256::from(3001), - // U256::from(3002), - // U256::from(3003), - // U256::from(3004), - // U256::from(3005), - // U256::from(3006), - // ]; - - // // For each coldkey, define two DISTINCT hotkeys it owns. - // let mut cold_to_hots: BTreeMap = BTreeMap::new(); - // for &c in cold_lps.iter() { - // let h1 = U256::from(c.low_u64().saturating_add(100_000)); - // let h2 = U256::from(c.low_u64().saturating_add(200_000)); - // cold_to_hots.insert(c, [h1, h2]); - // } - - // // Distinct τ pot sizes per net. - // let pots: [u64; NUM_NETS] = [12_345, 23_456, 34_567, 45_678]; - - // let lp_sets_per_net: [&[U256]; NUM_NETS] = [ - // &cold_lps[0..4], // net0: A,B,C,D - // &cold_lps[2..6], // net1: C,D,E,F - // &cold_lps[0..6], // net2: A..F - // &cold_lps[1..5], // net3: B,C,D,E - // ]; - - // // Multiple bands/sizes → many positions per cold across nets, using mixed hotkeys. - // let bands: [i32; 3] = [5, 13, 30]; - // let liqs: [u64; 3] = [400_000, 700_000, 1_100_000]; - - // // Helper: add a V3 position via a (hot, cold) pair. - // let add_pos = |net: NetUid, hot: U256, cold: U256, band: i32, liq: u64| { - // let ct = pallet_subtensor_swap::CurrentTick::::get(net); - // let lo = ct.saturating_sub(band); - // let hi = ct.saturating_add(band); - // assert_ok!(pallet_subtensor_swap::Pallet::::add_liquidity( - // RuntimeOrigin::signed(cold), - // hot, - // net, - // lo, - // hi, - // liq - // )); - // }; - - // // ──────────────────────────────────────────────────────────────────── - // // 1) Create many subnets, enable V3, fix price at tick=0 (sqrt≈1) - // // ──────────────────────────────────────────────────────────────────── - // let mut nets: Vec = Vec::new(); - // for i in 0..NUM_NETS { - // let owner_hot = U256::from(10_000 + (i as u64)); - // let owner_cold = U256::from(20_000 + (i as u64)); - // let net = add_dynamic_network(&owner_hot, &owner_cold); - // SubtensorModule::set_max_registrations_per_block(net, 1_000u16); - // SubtensorModule::set_target_registrations_per_interval(net, 1_000u16); - // Emission::::insert(net, Vec::::new()); - // SubtensorModule::set_subnet_locked_balance(net, TaoCurrency::from(0)); - - // assert_ok!( - // pallet_subtensor_swap::Pallet::::toggle_user_liquidity( - // RuntimeOrigin::root(), - // net, - // true - // ) - // ); - - // // Price/tick pinned so LP math stays stable (sqrt(1)). - // let ct0 = pallet_subtensor_swap::tick::TickIndex::new_unchecked(0); - // let sqrt1 = ct0.try_to_sqrt_price().expect("sqrt(1) price"); - // pallet_subtensor_swap::CurrentTick::::set(net, ct0); - // pallet_subtensor_swap::AlphaSqrtPrice::::set(net, sqrt1); - - // nets.push(net); - // } - - // // Map net → index for quick lookups. - // let mut net_index: BTreeMap = BTreeMap::new(); - // for (i, &n) in nets.iter().enumerate() { - // net_index.insert(n, i); - // } - - // // ──────────────────────────────────────────────────────────────────── - // // 2) Pre-create a handful of small (hot, cold) pairs so accounts exist - // // ──────────────────────────────────────────────────────────────────── - // for id in 0u64..10 { - // let cold_acc = U256::from(1_000_000 + id); - // let hot_acc = U256::from(2_000_000 + id); - // for &net in nets.iter() { - // register_ok_neuron(net, hot_acc, cold_acc, 100_000 + id); - // } - // } - - // // ──────────────────────────────────────────────────────────────────── - // // 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); - // } - - // // τ balances before LP adds (after staking): - // let mut tao_before: BTreeMap = BTreeMap::new(); - - // // Ordered α snapshot per net at **pair granularity** (pre‑LP): - // let mut alpha_pairs_per_net: BTreeMap> = BTreeMap::new(); - - // // Register both hotkeys for each participating cold on each net and stake τ→α. - // for (ni, &net) in nets.iter().enumerate() { - // let participants = lp_sets_per_net[ni]; - // for &cold in participants.iter() { - // let [hot1, hot2] = cold_to_hots[&cold]; - - // // Ensure (hot, cold) neurons exist on this net. - // register_ok_neuron( - // net, - // hot1, - // cold, - // (ni as u64) * 10_000 + (hot1.low_u64() % 10_000), - // ); - // register_ok_neuron( - // net, - // hot2, - // cold, - // (ni as u64) * 10_000 + (hot2.low_u64() % 10_000) + 1, - // ); - - // // Stake τ (split across the two hotkeys). - // let base: u64 = - // 5_000_000 + ((ni as u64) * 1_000_000) + ((cold.low_u64() % 10) * 250_000); - // let stake1: u64 = base.saturating_mul(3) / 5; // 60% - // let stake2: u64 = base.saturating_sub(stake1); // 40% - - // assert_ok!(SubtensorModule::do_add_stake( - // RuntimeOrigin::signed(cold), - // hot1, - // net, - // stake1.into() - // )); - // assert_ok!(SubtensorModule::do_add_stake( - // RuntimeOrigin::signed(cold), - // hot2, - // net, - // stake2.into() - // )); - // } - // } - - // // Record τ balances now (post‑stake, pre‑LP). - // for &cold in cold_lps.iter() { - // tao_before.insert(cold, SubtensorModule::get_coldkey_balance(&cold)); - // } - // // Capture **pair‑level** α snapshot per net (pre‑LP). - // for ((hot, cold, net), amt) in Alpha::::iter() { - // if let Some(&ni) = net_index.get(&net) - // && lp_sets_per_net[ni].contains(&cold) { - // let a: u128 = amt.saturating_to_num(); - // if a > 0 { - // alpha_pairs_per_net - // .entry(net) - // .or_default() - // .push(((hot, cold), a)); - // } - // } - // } - - // // ──────────────────────────────────────────────────────────────────── - // // 4) Add many V3 positions per cold across nets, alternating hotkeys - // // ──────────────────────────────────────────────────────────────────── - // for (ni, &net) in nets.iter().enumerate() { - // let participants = lp_sets_per_net[ni]; - // for (pi, &cold) in participants.iter().enumerate() { - // let [hot1, hot2] = cold_to_hots[&cold]; - // let hots = [hot1, hot2]; - // for k in 0..3 { - // let band = bands[(pi + k) % bands.len()]; - // let liq = liqs[(ni + k) % liqs.len()]; - // let hot = hots[k % hots.len()]; - // add_pos(net, hot, cold, band, liq); - // } - // } - // } - - // // Snapshot τ balances AFTER LP adds (to measure actual principal debit). - // let mut tao_after_adds: BTreeMap = BTreeMap::new(); - // for &cold in cold_lps.iter() { - // tao_after_adds.insert(cold, SubtensorModule::get_coldkey_balance(&cold)); - // } - - // // ──────────────────────────────────────────────────────────────────── - // // 5) Compute Hamilton-apportionment BASE shares per cold and total leftover - // // from the **pair-level** pre‑LP α snapshot; also count pairs per cold. - // // ──────────────────────────────────────────────────────────────────── - // let mut base_share_cold: BTreeMap = - // cold_lps.iter().copied().map(|c| (c, 0_u64)).collect(); - // let mut pair_count_cold: BTreeMap = - // cold_lps.iter().copied().map(|c| (c, 0_u32)).collect(); - - // let mut leftover_total: u64 = 0; - - // for (ni, &net) in nets.iter().enumerate() { - // let pot = pots[ni]; - // let pairs = alpha_pairs_per_net.get(&net).cloned().unwrap_or_default(); - // if pot == 0 || pairs.is_empty() { - // continue; - // } - // let total_alpha: u128 = pairs.iter().map(|(_, a)| *a).sum(); - // if total_alpha == 0 { - // continue; - // } - - // let mut base_sum_net: u64 = 0; - // for ((_, cold), a) in pairs.iter().copied() { - // // quota = a * pot / total_alpha - // let prod: u128 = a.saturating_mul(pot as u128); - // let base: u64 = (prod / total_alpha) as u64; - // base_sum_net = base_sum_net.saturating_add(base); - // *base_share_cold.entry(cold).or_default() = - // base_share_cold[&cold].saturating_add(base); - // *pair_count_cold.entry(cold).or_default() += 1; - // } - // let leftover_net = pot.saturating_sub(base_sum_net); - // leftover_total = leftover_total.saturating_add(leftover_net); - // } - - // // ──────────────────────────────────────────────────────────────────── - // // 6) Seed τ pots and dissolve *all* networks (liquidates LPs + refunds) - // // ──────────────────────────────────────────────────────────────────── - // for (ni, &net) in nets.iter().enumerate() { - // SubnetTAO::::insert(net, TaoCurrency::from(pots[ni])); - // } - // for &net in nets.iter() { - // assert_ok!(SubtensorModule::do_dissolve_network(net)); - // } - - // // ──────────────────────────────────────────────────────────────────── - // // 7) Assertions: τ balances, α gone, nets removed, swap state clean - // // (Hamilton invariants enforced at cold-level without relying on tie-break) - // // ──────────────────────────────────────────────────────────────────── - // // Collect actual pot credits per cold (principal cancels out against adds when comparing before→after). - // let mut actual_pot_cold: BTreeMap = - // cold_lps.iter().copied().map(|c| (c, 0_u64)).collect(); - // for &cold in cold_lps.iter() { - // let before = tao_before[&cold]; - // let after = SubtensorModule::get_coldkey_balance(&cold); - // actual_pot_cold.insert(cold, after.saturating_sub(before)); - // } - - // // (a) Sum of actual pot credits equals total pots. - // let total_actual: u64 = actual_pot_cold.values().copied().sum(); - // let total_pots: u64 = pots.iter().copied().sum(); - // assert_eq!( - // total_actual, total_pots, - // "total τ pot credited across colds must equal sum of pots" - // ); - - // // (b) Each cold’s pot is within Hamilton bounds: base ≤ actual ≤ base + #pairs. - // let mut extra_accum: u64 = 0; - // for &cold in cold_lps.iter() { - // let base = *base_share_cold.get(&cold).unwrap_or(&0); - // let pairs = *pair_count_cold.get(&cold).unwrap_or(&0) as u64; - // let actual = *actual_pot_cold.get(&cold).unwrap_or(&0); - - // assert!( - // actual >= base, - // "cold {cold:?} actual pot {actual} is below base {base}" - // ); - // assert!( - // actual <= base.saturating_add(pairs), - // "cold {cold:?} actual pot {actual} exceeds base + pairs ({base} + {pairs})" - // ); - - // extra_accum = extra_accum.saturating_add(actual.saturating_sub(base)); - // } - - // // (c) The total “extra beyond base” equals the computed leftover_total across nets. - // assert_eq!( - // extra_accum, leftover_total, - // "sum of extras beyond base must equal total leftover" - // ); - - // // (d) τ principal was fully refunded (compare after_adds → after). - // for &cold in cold_lps.iter() { - // let before = tao_before[&cold]; - // let mid = tao_after_adds[&cold]; - // let after = SubtensorModule::get_coldkey_balance(&cold); - // let principal_actual = before.saturating_sub(mid); - // let actual_pot = after.saturating_sub(before); - // assert_eq!( - // after.saturating_sub(mid), - // principal_actual.saturating_add(actual_pot), - // "cold {cold:?} τ balance incorrect vs 'after_adds'" - // ); - // } - - // // For each dissolved net, check α ledgers gone, network removed, and swap state clean. - // for &net in nets.iter() { - // assert!( - // Alpha::::iter().all(|((_h, _c, n), _)| n != net), - // "alpha ledger not fully cleared for net {net:?}" - // ); - // assert!( - // !SubtensorModule::if_subnet_exist(net), - // "subnet {net:?} still exists" - // ); - // assert!( - // pallet_subtensor_swap::Ticks::::iter_prefix(net) - // .next() - // .is_none(), - // "ticks not cleared for net {net:?}" - // ); - // assert!( - // !pallet_subtensor_swap::Positions::::iter() - // .any(|((n, _owner, _pid), _)| n == net), - // "swap positions not fully cleared for net {net:?}" - // ); - // assert_eq!( - // pallet_subtensor_swap::FeeGlobalTao::::get(net).saturating_to_num::(), - // 0, - // "FeeGlobalTao nonzero for net {net:?}" - // ); - // assert_eq!( - // pallet_subtensor_swap::FeeGlobalAlpha::::get(net).saturating_to_num::(), - // 0, - // "FeeGlobalAlpha nonzero for net {net:?}" - // ); - // assert_eq!( - // pallet_subtensor_swap::CurrentLiquidity::::get(net), - // 0, - // "CurrentLiquidity not zero for net {net:?}" - // ); - // assert!( - // !pallet_subtensor_swap::PalSwapInitialized::::get(net), - // "PalSwapInitialized still set" - // ); - // assert!( - // !pallet_subtensor_swap::EnabledUserLiquidity::::get(net), - // "EnabledUserLiquidity still set" - // ); - // assert!( - // pallet_subtensor_swap::TickIndexBitmapWords::::iter_prefix((net,)) - // .next() - // .is_none(), - // "TickIndexBitmapWords not cleared for net {net:?}" - // ); - // } - - // // ──────────────────────────────────────────────────────────────────── - // // 8) Re-register a fresh subnet and re‑stake using the pallet’s min rule - // // Assert αΔ equals the sim-swap result for the exact τ staked. - // // ──────────────────────────────────────────────────────────────────── - // let new_owner_hot = U256::from(99_000); - // let new_owner_cold = U256::from(99_001); - // let net_new = add_dynamic_network(&new_owner_hot, &new_owner_cold); - // SubtensorModule::set_max_registrations_per_block(net_new, 1_000u16); - // SubtensorModule::set_target_registrations_per_interval(net_new, 1_000u16); - // Emission::::insert(net_new, Vec::::new()); - // SubtensorModule::set_subnet_locked_balance(net_new, TaoCurrency::from(0)); - - // assert_ok!( - // pallet_subtensor_swap::Pallet::::toggle_user_liquidity( - // RuntimeOrigin::root(), - // net_new, - // true - // ) - // ); - // let ct0 = pallet_subtensor_swap::tick::TickIndex::new_unchecked(0); - // let sqrt1 = ct0.try_to_sqrt_price().expect("sqrt(1)"); - // pallet_subtensor_swap::CurrentTick::::set(net_new, ct0); - // pallet_subtensor_swap::AlphaSqrtPrice::::set(net_new, sqrt1); - - // // Compute the exact min stake per the pallet rule: DefaultMinStake + fee(DefaultMinStake). - // let min_stake = DefaultMinStake::::get(); - // let order = GetAlphaForTao::::with_amount(min_stake); - // let fee_for_min = pallet_subtensor_swap::Pallet::::sim_swap( - // net_new, - // order, - // ) - // .map(|r| r.fee_paid) - // .unwrap_or_else(|_e| { - // as subtensor_swap_interface::SwapHandler>::approx_fee_amount(net_new, min_stake) - // }); - // let min_amount_required = min_stake.saturating_add(fee_for_min).to_u64(); - - // // Re‑stake from three coldkeys; choose a specific DISTINCT hotkey per cold. - // for &cold in &cold_lps[0..3] { - // let [hot1, _hot2] = cold_to_hots[&cold]; - // register_ok_neuron(net_new, hot1, cold, 7777); - - // let before_tao = SubtensorModule::get_coldkey_balance(&cold); - // let a_prev: u64 = Alpha::::get((hot1, cold, net_new)).saturating_to_num(); - - // // Expected α for this exact τ, using the same sim path as the pallet. - // let order = GetAlphaForTao::::with_amount(min_amount_required); - // let expected_alpha_out = pallet_subtensor_swap::Pallet::::sim_swap( - // net_new, - // order, - // ) - // .map(|r| r.amount_paid_out) - // .expect("sim_swap must succeed for fresh net and min amount"); - - // assert_ok!(SubtensorModule::do_add_stake( - // RuntimeOrigin::signed(cold), - // hot1, - // net_new, - // min_amount_required.into() - // )); - - // let after_tao = SubtensorModule::get_coldkey_balance(&cold); - // let a_new: u64 = Alpha::::get((hot1, cold, net_new)).saturating_to_num(); - // let a_delta = a_new.saturating_sub(a_prev); - - // // τ decreased by exactly the amount we sent. - // assert_eq!( - // after_tao, - // before_tao.saturating_sub(min_amount_required), - // "τ did not decrease by the min required restake amount for cold {cold:?}" - // ); - - // // α minted equals the simulated swap’s net out for that same τ. - // assert_eq!( - // a_delta, expected_alpha_out.to_u64(), - // "α minted mismatch for cold {cold:?} (hot {hot1:?}) on new net (αΔ {a_delta}, expected {expected_alpha_out})" - // ); - // } - - // // Ensure V3 still functional on new net: add a small position for the first cold using its hot1 - // let who_cold = cold_lps[0]; - // let [who_hot, _] = cold_to_hots[&who_cold]; - // add_pos(net_new, who_hot, who_cold, 8, 123_456); - // assert!( - // pallet_subtensor_swap::Positions::::iter() - // .any(|((n, owner, _pid), _)| n == net_new && owner == who_cold), - // "new position not recorded on the re-registered net" - // ); - // }); -} +// TODO: Revise when user liquidity is available +// #[allow(clippy::indexing_slicing)] +// #[test] +// fn massive_dissolve_refund_and_reregistration_flow_is_lossless_and_cleans_state() { +// new_test_ext(0).execute_with(|| { +// // ──────────────────────────────────────────────────────────────────── +// // 0) Constants and helpers (distinct hotkeys & coldkeys) +// // ──────────────────────────────────────────────────────────────────── +// const NUM_NETS: usize = 4; + +// // Six LP coldkeys +// let cold_lps: [U256; 6] = [ +// U256::from(3001), +// U256::from(3002), +// U256::from(3003), +// U256::from(3004), +// U256::from(3005), +// U256::from(3006), +// ]; + +// // For each coldkey, define two DISTINCT hotkeys it owns. +// let mut cold_to_hots: BTreeMap = BTreeMap::new(); +// for &c in cold_lps.iter() { +// let h1 = U256::from(c.low_u64().saturating_add(100_000)); +// let h2 = U256::from(c.low_u64().saturating_add(200_000)); +// cold_to_hots.insert(c, [h1, h2]); +// } + +// // Distinct τ pot sizes per net. +// let pots: [u64; NUM_NETS] = [12_345, 23_456, 34_567, 45_678]; + +// let lp_sets_per_net: [&[U256]; NUM_NETS] = [ +// &cold_lps[0..4], // net0: A,B,C,D +// &cold_lps[2..6], // net1: C,D,E,F +// &cold_lps[0..6], // net2: A..F +// &cold_lps[1..5], // net3: B,C,D,E +// ]; + +// // Multiple bands/sizes → many positions per cold across nets, using mixed hotkeys. +// let bands: [i32; 3] = [5, 13, 30]; +// let liqs: [u64; 3] = [400_000, 700_000, 1_100_000]; + +// // Helper: add a V3 position via a (hot, cold) pair. +// let add_pos = |net: NetUid, hot: U256, cold: U256, band: i32, liq: u64| { +// let ct = pallet_subtensor_swap::CurrentTick::::get(net); +// let lo = ct.saturating_sub(band); +// let hi = ct.saturating_add(band); +// assert_ok!(pallet_subtensor_swap::Pallet::::add_liquidity( +// RuntimeOrigin::signed(cold), +// hot, +// net, +// lo, +// hi, +// liq +// )); +// }; + +// // ──────────────────────────────────────────────────────────────────── +// // 1) Create many subnets, enable V3, fix price at tick=0 (sqrt≈1) +// // ──────────────────────────────────────────────────────────────────── +// let mut nets: Vec = Vec::new(); +// for i in 0..NUM_NETS { +// let owner_hot = U256::from(10_000 + (i as u64)); +// let owner_cold = U256::from(20_000 + (i as u64)); +// let net = add_dynamic_network(&owner_hot, &owner_cold); +// SubtensorModule::set_max_registrations_per_block(net, 1_000u16); +// SubtensorModule::set_target_registrations_per_interval(net, 1_000u16); +// Emission::::insert(net, Vec::::new()); +// SubtensorModule::set_subnet_locked_balance(net, TaoCurrency::from(0)); + +// assert_ok!( +// pallet_subtensor_swap::Pallet::::toggle_user_liquidity( +// RuntimeOrigin::root(), +// net, +// true +// ) +// ); + +// // Price/tick pinned so LP math stays stable (sqrt(1)). +// let ct0 = pallet_subtensor_swap::tick::TickIndex::new_unchecked(0); +// let sqrt1 = ct0.try_to_sqrt_price().expect("sqrt(1) price"); +// pallet_subtensor_swap::CurrentTick::::set(net, ct0); +// pallet_subtensor_swap::AlphaSqrtPrice::::set(net, sqrt1); + +// nets.push(net); +// } + +// // Map net → index for quick lookups. +// let mut net_index: BTreeMap = BTreeMap::new(); +// for (i, &n) in nets.iter().enumerate() { +// net_index.insert(n, i); +// } + +// // ──────────────────────────────────────────────────────────────────── +// // 2) Pre-create a handful of small (hot, cold) pairs so accounts exist +// // ──────────────────────────────────────────────────────────────────── +// for id in 0u64..10 { +// let cold_acc = U256::from(1_000_000 + id); +// let hot_acc = U256::from(2_000_000 + id); +// for &net in nets.iter() { +// register_ok_neuron(net, hot_acc, cold_acc, 100_000 + id); +// } +// } + +// // ──────────────────────────────────────────────────────────────────── +// // 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); +// } + +// // τ balances before LP adds (after staking): +// let mut tao_before: BTreeMap = BTreeMap::new(); + +// // Ordered α snapshot per net at **pair granularity** (pre‑LP): +// let mut alpha_pairs_per_net: BTreeMap> = BTreeMap::new(); + +// // Register both hotkeys for each participating cold on each net and stake τ→α. +// for (ni, &net) in nets.iter().enumerate() { +// let participants = lp_sets_per_net[ni]; +// for &cold in participants.iter() { +// let [hot1, hot2] = cold_to_hots[&cold]; + +// // Ensure (hot, cold) neurons exist on this net. +// register_ok_neuron( +// net, +// hot1, +// cold, +// (ni as u64) * 10_000 + (hot1.low_u64() % 10_000), +// ); +// register_ok_neuron( +// net, +// hot2, +// cold, +// (ni as u64) * 10_000 + (hot2.low_u64() % 10_000) + 1, +// ); + +// // Stake τ (split across the two hotkeys). +// let base: u64 = +// 5_000_000 + ((ni as u64) * 1_000_000) + ((cold.low_u64() % 10) * 250_000); +// let stake1: u64 = base.saturating_mul(3) / 5; // 60% +// let stake2: u64 = base.saturating_sub(stake1); // 40% + +// assert_ok!(SubtensorModule::do_add_stake( +// RuntimeOrigin::signed(cold), +// hot1, +// net, +// stake1.into() +// )); +// assert_ok!(SubtensorModule::do_add_stake( +// RuntimeOrigin::signed(cold), +// hot2, +// net, +// stake2.into() +// )); +// } +// } + +// // Record τ balances now (post‑stake, pre‑LP). +// for &cold in cold_lps.iter() { +// tao_before.insert(cold, SubtensorModule::get_coldkey_balance(&cold)); +// } +// // Capture **pair‑level** α snapshot per net (pre‑LP). +// for ((hot, cold, net), amt) in Alpha::::iter() { +// if let Some(&ni) = net_index.get(&net) +// && lp_sets_per_net[ni].contains(&cold) { +// let a: u128 = amt.saturating_to_num(); +// if a > 0 { +// alpha_pairs_per_net +// .entry(net) +// .or_default() +// .push(((hot, cold), a)); +// } +// } +// } + +// // ──────────────────────────────────────────────────────────────────── +// // 4) Add many V3 positions per cold across nets, alternating hotkeys +// // ──────────────────────────────────────────────────────────────────── +// for (ni, &net) in nets.iter().enumerate() { +// let participants = lp_sets_per_net[ni]; +// for (pi, &cold) in participants.iter().enumerate() { +// let [hot1, hot2] = cold_to_hots[&cold]; +// let hots = [hot1, hot2]; +// for k in 0..3 { +// let band = bands[(pi + k) % bands.len()]; +// let liq = liqs[(ni + k) % liqs.len()]; +// let hot = hots[k % hots.len()]; +// add_pos(net, hot, cold, band, liq); +// } +// } +// } + +// // Snapshot τ balances AFTER LP adds (to measure actual principal debit). +// let mut tao_after_adds: BTreeMap = BTreeMap::new(); +// for &cold in cold_lps.iter() { +// tao_after_adds.insert(cold, SubtensorModule::get_coldkey_balance(&cold)); +// } + +// // ──────────────────────────────────────────────────────────────────── +// // 5) Compute Hamilton-apportionment BASE shares per cold and total leftover +// // from the **pair-level** pre‑LP α snapshot; also count pairs per cold. +// // ──────────────────────────────────────────────────────────────────── +// let mut base_share_cold: BTreeMap = +// cold_lps.iter().copied().map(|c| (c, 0_u64)).collect(); +// let mut pair_count_cold: BTreeMap = +// cold_lps.iter().copied().map(|c| (c, 0_u32)).collect(); + +// let mut leftover_total: u64 = 0; + +// for (ni, &net) in nets.iter().enumerate() { +// let pot = pots[ni]; +// let pairs = alpha_pairs_per_net.get(&net).cloned().unwrap_or_default(); +// if pot == 0 || pairs.is_empty() { +// continue; +// } +// let total_alpha: u128 = pairs.iter().map(|(_, a)| *a).sum(); +// if total_alpha == 0 { +// continue; +// } + +// let mut base_sum_net: u64 = 0; +// for ((_, cold), a) in pairs.iter().copied() { +// // quota = a * pot / total_alpha +// let prod: u128 = a.saturating_mul(pot as u128); +// let base: u64 = (prod / total_alpha) as u64; +// base_sum_net = base_sum_net.saturating_add(base); +// *base_share_cold.entry(cold).or_default() = +// base_share_cold[&cold].saturating_add(base); +// *pair_count_cold.entry(cold).or_default() += 1; +// } +// let leftover_net = pot.saturating_sub(base_sum_net); +// leftover_total = leftover_total.saturating_add(leftover_net); +// } + +// // ──────────────────────────────────────────────────────────────────── +// // 6) Seed τ pots and dissolve *all* networks (liquidates LPs + refunds) +// // ──────────────────────────────────────────────────────────────────── +// for (ni, &net) in nets.iter().enumerate() { +// SubnetTAO::::insert(net, TaoCurrency::from(pots[ni])); +// } +// for &net in nets.iter() { +// assert_ok!(SubtensorModule::do_dissolve_network(net)); +// } + +// // ──────────────────────────────────────────────────────────────────── +// // 7) Assertions: τ balances, α gone, nets removed, swap state clean +// // (Hamilton invariants enforced at cold-level without relying on tie-break) +// // ──────────────────────────────────────────────────────────────────── +// // Collect actual pot credits per cold (principal cancels out against adds when comparing before→after). +// let mut actual_pot_cold: BTreeMap = +// cold_lps.iter().copied().map(|c| (c, 0_u64)).collect(); +// for &cold in cold_lps.iter() { +// let before = tao_before[&cold]; +// let after = SubtensorModule::get_coldkey_balance(&cold); +// actual_pot_cold.insert(cold, after.saturating_sub(before)); +// } + +// // (a) Sum of actual pot credits equals total pots. +// let total_actual: u64 = actual_pot_cold.values().copied().sum(); +// let total_pots: u64 = pots.iter().copied().sum(); +// assert_eq!( +// total_actual, total_pots, +// "total τ pot credited across colds must equal sum of pots" +// ); + +// // (b) Each cold’s pot is within Hamilton bounds: base ≤ actual ≤ base + #pairs. +// let mut extra_accum: u64 = 0; +// for &cold in cold_lps.iter() { +// let base = *base_share_cold.get(&cold).unwrap_or(&0); +// let pairs = *pair_count_cold.get(&cold).unwrap_or(&0) as u64; +// let actual = *actual_pot_cold.get(&cold).unwrap_or(&0); + +// assert!( +// actual >= base, +// "cold {cold:?} actual pot {actual} is below base {base}" +// ); +// assert!( +// actual <= base.saturating_add(pairs), +// "cold {cold:?} actual pot {actual} exceeds base + pairs ({base} + {pairs})" +// ); + +// extra_accum = extra_accum.saturating_add(actual.saturating_sub(base)); +// } + +// // (c) The total “extra beyond base” equals the computed leftover_total across nets. +// assert_eq!( +// extra_accum, leftover_total, +// "sum of extras beyond base must equal total leftover" +// ); + +// // (d) τ principal was fully refunded (compare after_adds → after). +// for &cold in cold_lps.iter() { +// let before = tao_before[&cold]; +// let mid = tao_after_adds[&cold]; +// let after = SubtensorModule::get_coldkey_balance(&cold); +// let principal_actual = before.saturating_sub(mid); +// let actual_pot = after.saturating_sub(before); +// assert_eq!( +// after.saturating_sub(mid), +// principal_actual.saturating_add(actual_pot), +// "cold {cold:?} τ balance incorrect vs 'after_adds'" +// ); +// } + +// // For each dissolved net, check α ledgers gone, network removed, and swap state clean. +// for &net in nets.iter() { +// assert!( +// Alpha::::iter().all(|((_h, _c, n), _)| n != net), +// "alpha ledger not fully cleared for net {net:?}" +// ); +// assert!( +// !SubtensorModule::if_subnet_exist(net), +// "subnet {net:?} still exists" +// ); +// assert!( +// pallet_subtensor_swap::Ticks::::iter_prefix(net) +// .next() +// .is_none(), +// "ticks not cleared for net {net:?}" +// ); +// assert!( +// !pallet_subtensor_swap::Positions::::iter() +// .any(|((n, _owner, _pid), _)| n == net), +// "swap positions not fully cleared for net {net:?}" +// ); +// assert_eq!( +// pallet_subtensor_swap::FeeGlobalTao::::get(net).saturating_to_num::(), +// 0, +// "FeeGlobalTao nonzero for net {net:?}" +// ); +// assert_eq!( +// pallet_subtensor_swap::FeeGlobalAlpha::::get(net).saturating_to_num::(), +// 0, +// "FeeGlobalAlpha nonzero for net {net:?}" +// ); +// assert_eq!( +// pallet_subtensor_swap::CurrentLiquidity::::get(net), +// 0, +// "CurrentLiquidity not zero for net {net:?}" +// ); +// assert!( +// !pallet_subtensor_swap::PalSwapInitialized::::get(net), +// "PalSwapInitialized still set" +// ); +// assert!( +// !pallet_subtensor_swap::EnabledUserLiquidity::::get(net), +// "EnabledUserLiquidity still set" +// ); +// assert!( +// pallet_subtensor_swap::TickIndexBitmapWords::::iter_prefix((net,)) +// .next() +// .is_none(), +// "TickIndexBitmapWords not cleared for net {net:?}" +// ); +// } + +// // ──────────────────────────────────────────────────────────────────── +// // 8) Re-register a fresh subnet and re‑stake using the pallet’s min rule +// // Assert αΔ equals the sim-swap result for the exact τ staked. +// // ──────────────────────────────────────────────────────────────────── +// let new_owner_hot = U256::from(99_000); +// let new_owner_cold = U256::from(99_001); +// let net_new = add_dynamic_network(&new_owner_hot, &new_owner_cold); +// SubtensorModule::set_max_registrations_per_block(net_new, 1_000u16); +// SubtensorModule::set_target_registrations_per_interval(net_new, 1_000u16); +// Emission::::insert(net_new, Vec::::new()); +// SubtensorModule::set_subnet_locked_balance(net_new, TaoCurrency::from(0)); + +// assert_ok!( +// pallet_subtensor_swap::Pallet::::toggle_user_liquidity( +// RuntimeOrigin::root(), +// net_new, +// true +// ) +// ); +// let ct0 = pallet_subtensor_swap::tick::TickIndex::new_unchecked(0); +// let sqrt1 = ct0.try_to_sqrt_price().expect("sqrt(1)"); +// pallet_subtensor_swap::CurrentTick::::set(net_new, ct0); +// pallet_subtensor_swap::AlphaSqrtPrice::::set(net_new, sqrt1); + +// // Compute the exact min stake per the pallet rule: DefaultMinStake + fee(DefaultMinStake). +// let min_stake = DefaultMinStake::::get(); +// let order = GetAlphaForTao::::with_amount(min_stake); +// let fee_for_min = pallet_subtensor_swap::Pallet::::sim_swap( +// net_new, +// order, +// ) +// .map(|r| r.fee_paid) +// .unwrap_or_else(|_e| { +// as subtensor_swap_interface::SwapHandler>::approx_fee_amount(net_new, min_stake) +// }); +// let min_amount_required = min_stake.saturating_add(fee_for_min).to_u64(); + +// // Re‑stake from three coldkeys; choose a specific DISTINCT hotkey per cold. +// for &cold in &cold_lps[0..3] { +// let [hot1, _hot2] = cold_to_hots[&cold]; +// register_ok_neuron(net_new, hot1, cold, 7777); + +// let before_tao = SubtensorModule::get_coldkey_balance(&cold); +// let a_prev: u64 = Alpha::::get((hot1, cold, net_new)).saturating_to_num(); + +// // Expected α for this exact τ, using the same sim path as the pallet. +// let order = GetAlphaForTao::::with_amount(min_amount_required); +// let expected_alpha_out = pallet_subtensor_swap::Pallet::::sim_swap( +// net_new, +// order, +// ) +// .map(|r| r.amount_paid_out) +// .expect("sim_swap must succeed for fresh net and min amount"); + +// assert_ok!(SubtensorModule::do_add_stake( +// RuntimeOrigin::signed(cold), +// hot1, +// net_new, +// min_amount_required.into() +// )); + +// let after_tao = SubtensorModule::get_coldkey_balance(&cold); +// let a_new: u64 = Alpha::::get((hot1, cold, net_new)).saturating_to_num(); +// let a_delta = a_new.saturating_sub(a_prev); + +// // τ decreased by exactly the amount we sent. +// assert_eq!( +// after_tao, +// before_tao.saturating_sub(min_amount_required), +// "τ did not decrease by the min required restake amount for cold {cold:?}" +// ); + +// // α minted equals the simulated swap’s net out for that same τ. +// assert_eq!( +// a_delta, expected_alpha_out.to_u64(), +// "α minted mismatch for cold {cold:?} (hot {hot1:?}) on new net (αΔ {a_delta}, expected {expected_alpha_out})" +// ); +// } + +// // Ensure V3 still functional on new net: add a small position for the first cold using its hot1 +// let who_cold = cold_lps[0]; +// let [who_hot, _] = cold_to_hots[&who_cold]; +// add_pos(net_new, who_hot, who_cold, 8, 123_456); +// assert!( +// pallet_subtensor_swap::Positions::::iter() +// .any(|((n, owner, _pid), _)| n == net_new && owner == who_cold), +// "new position not recorded on the re-registered net" +// ); +// }); +// } #[test] fn dissolve_clears_all_mechanism_scoped_maps_for_all_mechanisms() { diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index 3417656a3a..71f0667972 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -797,7 +797,7 @@ fn test_add_stake_insufficient_liquidity_one_side_ok() { SubtensorModule::add_balance_to_coldkey_account(&coldkey, amount_staked); // Set the liquidity at lowest possible value so that all staking requests fail - let reserve_alpha = u64::from(mock::SwapMinimumReserve::get()); + let reserve_alpha = 1_000_000_000_u64; let reserve_tao = u64::from(mock::SwapMinimumReserve::get()) - 1; mock::setup_reserves(netuid, reserve_tao.into(), reserve_alpha.into()); From 1c663f17dd265d3cd8779aeffeaeca6397011c11 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Fri, 26 Dec 2025 13:59:37 -0500 Subject: [PATCH 082/240] Fix dissolve_clears_all_per_subnet_storages --- pallets/subtensor/src/staking/stake_utils.rs | 32 ++++++---- pallets/subtensor/src/tests/claim_root.rs | 25 ++------ pallets/subtensor/src/tests/coinbase.rs | 5 +- pallets/swap/src/pallet/reserve_weights.rs | 5 +- pallets/swap/src/pallet/swap_step.rs | 5 +- pallets/swap/src/pallet/tests.rs | 61 ++++++++++++-------- 6 files changed, 70 insertions(+), 63 deletions(-) diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index 13fd286edb..704ada9f03 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -1205,9 +1205,11 @@ impl Pallet { } pub fn increase_provided_tao_reserve(netuid: NetUid, tao: TaoCurrency) { - SubnetTaoProvided::::mutate(netuid, |total| { - *total = total.saturating_add(tao); - }); + if !tao.is_zero() { + SubnetTaoProvided::::mutate(netuid, |total| { + *total = total.saturating_add(tao); + }); + } } pub fn decrease_provided_tao_reserve(netuid: NetUid, tao: TaoCurrency) { @@ -1217,17 +1219,23 @@ impl Pallet { let remainder = subnet_tao_provided.saturating_sub(tao); let carry_over = tao.saturating_sub(subnet_tao_provided); if carry_over.is_zero() { - SubnetTaoProvided::::set(netuid, remainder); + if remainder.is_zero() { + SubnetTaoProvided::::remove(netuid); + } else { + SubnetTaoProvided::::set(netuid, remainder); + } } else { - SubnetTaoProvided::::set(netuid, TaoCurrency::ZERO); + SubnetTaoProvided::::remove(netuid); SubnetTAO::::set(netuid, subnet_tao.saturating_sub(carry_over)); } } pub fn increase_provided_alpha_reserve(netuid: NetUid, alpha: AlphaCurrency) { - SubnetAlphaInProvided::::mutate(netuid, |total| { - *total = total.saturating_add(alpha); - }); + if !alpha.is_zero() { + SubnetAlphaInProvided::::mutate(netuid, |total| { + *total = total.saturating_add(alpha); + }); + } } pub fn decrease_provided_alpha_reserve(netuid: NetUid, alpha: AlphaCurrency) { @@ -1237,9 +1245,13 @@ impl Pallet { let remainder = subnet_alpha_provided.saturating_sub(alpha); let carry_over = alpha.saturating_sub(subnet_alpha_provided); if carry_over.is_zero() { - SubnetAlphaInProvided::::set(netuid, remainder); + if remainder.is_zero() { + SubnetAlphaInProvided::::remove(netuid); + } else { + SubnetAlphaInProvided::::set(netuid, remainder); + } } else { - SubnetAlphaInProvided::::set(netuid, AlphaCurrency::ZERO); + SubnetAlphaInProvided::::remove(netuid); SubnetAlphaIn::::set(netuid, subnet_alpha.saturating_sub(carry_over)); } } diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index 852f8b4abd..e5f66f6da2 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -5,26 +5,10 @@ use crate::tests::mock::{ RuntimeOrigin, SubtensorModule, Test, add_dynamic_network, new_test_ext, run_to_block, }; use crate::{ - DefaultMinRootClaimAmount, - Error, - MAX_NUM_ROOT_CLAIMS, - MAX_ROOT_CLAIM_THRESHOLD, - NetworksAdded, - NumRootClaim, - NumStakingColdkeys, - PendingRootAlphaDivs, - RootClaimable, - RootClaimableThreshold, - StakingColdkeys, - StakingColdkeysByIndex, - SubnetAlphaIn, - SubnetMechanism, - SubnetMovingPrice, - SubnetTAO, - SubnetTaoFlow, - SubtokenEnabled, - Tempo, - pallet, + DefaultMinRootClaimAmount, Error, MAX_NUM_ROOT_CLAIMS, MAX_ROOT_CLAIM_THRESHOLD, NetworksAdded, + NumRootClaim, NumStakingColdkeys, PendingRootAlphaDivs, RootClaimable, RootClaimableThreshold, + StakingColdkeys, StakingColdkeysByIndex, SubnetAlphaIn, SubnetMechanism, SubnetMovingPrice, + SubnetTAO, SubnetTaoFlow, SubtokenEnabled, Tempo, pallet, }; use crate::{RootClaimType, RootClaimTypeEnum, RootClaimed}; use approx::assert_abs_diff_eq; @@ -1017,7 +1001,6 @@ fn test_populate_staking_maps() { }); } - #[test] fn test_claim_root_coinbase_distribution() { new_test_ext(1).execute_with(|| { diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 294552c760..b6648bf7b3 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -2693,7 +2693,6 @@ fn test_run_coinbase_not_started_start_after() { }); } - // TODO: Revise when user liquidity is available // Test that coinbase updates protocol position liquidity // cargo test --package pallet-subtensor --lib -- tests::coinbase::test_coinbase_v3_liquidity_update --exact --show-output @@ -3368,7 +3367,9 @@ fn test_coinbase_subnet_terms_with_alpha_in_gt_alpha_emission() { // Set the price let tao = TaoCurrency::from(1_000_000_000_u64); - let alpha = AlphaCurrency::from((U64F64::saturating_from_num(u64::from(tao)) / price_to_set).to_num::()); + let alpha = AlphaCurrency::from( + (U64F64::saturating_from_num(u64::from(tao)) / price_to_set).to_num::(), + ); SubnetTAO::::insert(netuid0, tao); SubnetAlphaIn::::insert(netuid0, alpha); diff --git a/pallets/swap/src/pallet/reserve_weights.rs b/pallets/swap/src/pallet/reserve_weights.rs index 0e7ae7af95..16b6d79e16 100644 --- a/pallets/swap/src/pallet/reserve_weights.rs +++ b/pallets/swap/src/pallet/reserve_weights.rs @@ -78,7 +78,6 @@ impl ReserveWeight { let perquintill_scale = SafeInt::from(ACCURACY as u128); let denominator = SafeInt::from(x_plus_dx); let maybe_result_safe_int = if base_quote { - println!("x = {:?}", x); println!("dx = {:?}", dx); println!("x_safe = {:?}", x_safe); @@ -87,7 +86,7 @@ impl ReserveWeight { println!("w2_safe = {:?}", w2_safe); println!("precision = {:?}", precision); println!("perquintill_scale = {:?}", perquintill_scale); - + SafeInt::pow_ratio_scaled( &x_safe, &denominator, @@ -410,7 +409,7 @@ mod tests { let w2 = perquintill_to_f64(rw.get_quote_weight()); let e_expected = (x as f64 / (x as f64 + dx as f64)).powf(w1 / w2); let dy_expected = y as f64 * (1. - e_expected); - + println!("debug 3: dy_expected = {:?}", dy_expected); let mut eps = dy_expected / 100000.; diff --git a/pallets/swap/src/pallet/swap_step.rs b/pallets/swap/src/pallet/swap_step.rs index f4758b15f0..9db358be02 100644 --- a/pallets/swap/src/pallet/swap_step.rs +++ b/pallets/swap/src/pallet/swap_step.rs @@ -118,10 +118,7 @@ where let delta_out = Self::convert_deltas(self.netuid, self.delta_in); log::trace!("\tDelta Out : {delta_out}"); if self.delta_in > 0.into() { - ensure!( - delta_out > 0.into(), - Error::::ReservesTooLow - ); + ensure!(delta_out > 0.into(), Error::::ReservesTooLow); // Hold the fees Self::add_fees(self.netuid, self.fee); diff --git a/pallets/swap/src/pallet/tests.rs b/pallets/swap/src/pallet/tests.rs index f12742c5ce..1895a2f346 100644 --- a/pallets/swap/src/pallet/tests.rs +++ b/pallets/swap/src/pallet/tests.rs @@ -29,12 +29,14 @@ use crate::pallet::swap_step::*; #[allow(dead_code)] fn get_min_price() -> U64F64 { - U64F64::from_num(Pallet::::min_price_inner::()) / U64F64::from_num(1_000_000_000) + U64F64::from_num(Pallet::::min_price_inner::()) + / U64F64::from_num(1_000_000_000) } #[allow(dead_code)] fn get_max_price() -> U64F64 { - U64F64::from_num(Pallet::::max_price_inner::()) / U64F64::from_num(1_000_000_000) + U64F64::from_num(Pallet::::max_price_inner::()) + / U64F64::from_num(1_000_000_000) } mod dispatchables { @@ -1087,8 +1089,8 @@ fn test_swap_basic() { AlphaReserve::set_mock_reserve( netuid, AlphaCurrency::from( - (u64::from(initial_alpha_reserve) as i128 + swap_result.paid_out_reserve_delta()) - as u64, + (u64::from(initial_alpha_reserve) as i128 + + swap_result.paid_out_reserve_delta()) as u64, ), ); } else { @@ -1102,8 +1104,8 @@ fn test_swap_basic() { AlphaReserve::set_mock_reserve( netuid, AlphaCurrency::from( - (u64::from(initial_alpha_reserve) as i128 + swap_result.paid_in_reserve_delta()) - as u64, + (u64::from(initial_alpha_reserve) as i128 + + swap_result.paid_in_reserve_delta()) as u64, ), ); } @@ -1140,7 +1142,12 @@ fn test_swap_basic() { perform_test(1.into(), GetAlphaForTao::with_amount(123_456), 1000.0, true); perform_test(2.into(), GetTaoForAlpha::with_amount(1_000), 0.0001, false); perform_test(2.into(), GetTaoForAlpha::with_amount(2_000), 0.0001, false); - perform_test(2.into(), GetTaoForAlpha::with_amount(123_456), 0.0001, false); + perform_test( + 2.into(), + GetTaoForAlpha::with_amount(123_456), + 0.0001, + false, + ); perform_test( 3.into(), GetAlphaForTao::with_amount(1_000_000_000), @@ -1162,7 +1169,7 @@ fn test_swap_precision_edge_case() { // Test case: tao_reserve, alpha_reserve, swap_amount [ (1_000_u64, 1_000_u64, 999_500_u64), - (1_000_000_u64, 1_000_000_u64, 999_500_000_u64) + (1_000_000_u64, 1_000_000_u64, 999_500_000_u64), ] .into_iter() .for_each(|(tao_reserve, alpha_reserve, swap_amount)| { @@ -1179,7 +1186,8 @@ fn test_swap_precision_edge_case() { println!("limit_price = {:?}", limit_price); // Swap - let swap_result = Pallet::::do_swap(netuid, order, limit_price, false, true).unwrap(); + let swap_result = + Pallet::::do_swap(netuid, order, limit_price, false, true).unwrap(); assert!(swap_result.amount_paid_out > TaoCurrency::ZERO); }); @@ -1237,7 +1245,10 @@ fn test_convert_deltas() { TaoReserve::set_mock_reserve(netuid, TaoCurrency::from(tao)); AlphaReserve::set_mock_reserve(netuid, AlphaCurrency::from(alpha)); let w_accuracy = 1_000_000_000_f64; - let w_quote_pt = Perquintill::from_rational((w_quote as f64 * w_accuracy) as u128, w_accuracy as u128); + let w_quote_pt = Perquintill::from_rational( + (w_quote as f64 * w_accuracy) as u128, + w_accuracy as u128, + ); let rw = ReserveWeight::new(w_quote_pt).unwrap(); SwapReserveWeight::::insert(netuid, rw); @@ -1251,18 +1262,22 @@ fn test_convert_deltas() { let expected_buy = x * (1. - (y / (y + d)).powf(w2_div_w1)); assert_abs_diff_eq!( - u64::from(BasicSwapStep::::convert_deltas( - netuid, - delta_in.into() - )), + u64::from( + BasicSwapStep::::convert_deltas( + netuid, + delta_in.into() + ) + ), expected_sell as u64, epsilon = 2u64 ); assert_abs_diff_eq!( - u64::from(BasicSwapStep::::convert_deltas( - netuid, - delta_in.into() - )), + u64::from( + BasicSwapStep::::convert_deltas( + netuid, + delta_in.into() + ) + ), expected_buy as u64, epsilon = 2u64 ); @@ -1918,7 +1933,7 @@ fn test_swap_subtoken_disabled() { // } // TODO: Revise when user liquidity is available -// Non‑palswap path: PalSwap not initialized (no positions, no map values); function +// Non‑palswap path: PalSwap not initialized (no positions, no map values); function // must still clear any residual storages and succeed. // #[test] // fn test_liquidate_pal_uninitialized_ok_and_clears() { @@ -1947,7 +1962,7 @@ fn test_swap_subtoken_disabled() { // ); // // All single-key maps should not have the key after liquidation -// assert!(!FeeRate::::contains_key(netuid)); +// assert!(!FeeRate::::contains_key(netuid)); // assert!(!EnabledUserLiquidity::::contains_key(netuid)); // assert!(!FeesTao::::contains_key(netuid)); // assert!(!FeesAlpha::::contains_key(netuid)); @@ -1956,7 +1971,7 @@ fn test_swap_subtoken_disabled() { // }); // } -/// Simple palswap path: PalSwap is initialized, but no positions, only protocol; function +/// Simple palswap path: PalSwap is initialized, but no positions, only protocol; function /// must still clear any residual storages and succeed. /// TODO: Revise when user liquidity is available #[test] @@ -1965,7 +1980,7 @@ fn test_liquidate_pal_simple_ok_and_clears() { let netuid = NetUid::from(202); // Insert map values - FeeRate::::insert(netuid, 1_000); + FeeRate::::insert(netuid, 1_000); EnabledUserLiquidity::::insert(netuid, false); FeesTao::::insert(netuid, TaoCurrency::from(1_000)); FeesAlpha::::insert(netuid, AlphaCurrency::from(1_000)); @@ -1993,7 +2008,7 @@ fn test_liquidate_pal_simple_ok_and_clears() { ); // All single-key maps should not have the key after liquidation - assert!(!FeeRate::::contains_key(netuid)); + assert!(!FeeRate::::contains_key(netuid)); assert!(!EnabledUserLiquidity::::contains_key(netuid)); assert!(!FeesTao::::contains_key(netuid)); assert!(!FeesAlpha::::contains_key(netuid)); From 25a9e5d4a006a0080e52c14bdd58abed4df7b2f2 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Fri, 26 Dec 2025 14:28:23 -0500 Subject: [PATCH 083/240] Fix test_large_swap, improve error messaging --- pallets/subtensor/src/tests/staking.rs | 45 ++++++++++++++------------ pallets/swap/src/pallet/mod.rs | 3 ++ pallets/swap/src/pallet/swap_step.rs | 9 ++++++ 3 files changed, 36 insertions(+), 21 deletions(-) diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index 71f0667972..ad5069c55a 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -5348,31 +5348,34 @@ fn test_update_position_fees() { #[test] fn test_large_swap() { - todo!(); - - // new_test_ext(1).execute_with(|| { - // let owner_hotkey = U256::from(1); - // let owner_coldkey = U256::from(2); - // let coldkey = U256::from(100); + new_test_ext(1).execute_with(|| { + let owner_hotkey = U256::from(1); + let owner_coldkey = U256::from(2); + let coldkey = U256::from(100); - // // add network - // let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); - // SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_000); - // pallet_subtensor_swap::EnabledUserLiquidity::::insert(NetUid::from(netuid), true); + // add network + let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_000); + pallet_subtensor_swap::EnabledUserLiquidity::::insert(NetUid::from(netuid), true); + let tao = TaoCurrency::from(100_000_000u64); + let alpha = AlphaCurrency::from(1_000_000_000_000_000_u64); + SubnetTAO::::insert(netuid, tao); + SubnetAlphaIn::::insert(netuid, alpha); - // // Force the swap to initialize - // ::SwapInterface::init_swap(netuid); + // Force the swap to initialize + ::SwapInterface::init_swap(netuid); - // setup_positions(netuid.into()); + // TODO: Revise when user liquidity is available + // setup_positions(netuid.into()); - // let swap_amount = TaoCurrency::from(100_000_000_000_000); - // assert_ok!(SubtensorModule::add_stake( - // RuntimeOrigin::signed(coldkey), - // owner_hotkey, - // netuid, - // swap_amount, - // )); - // }); + let swap_amount = TaoCurrency::from(100_000_000_000_000); + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(coldkey), + owner_hotkey, + netuid, + swap_amount, + )); + }); } #[test] diff --git a/pallets/swap/src/pallet/mod.rs b/pallets/swap/src/pallet/mod.rs index 7c6b484f79..173db5f26b 100644 --- a/pallets/swap/src/pallet/mod.rs +++ b/pallets/swap/src/pallet/mod.rs @@ -303,6 +303,9 @@ mod pallet { /// Swap reserves are too imbalanced ReservesOutOfBalance, + + /// Swap amount is too high and target price will be out of range + AmountTooHigh, } #[pallet::call] diff --git a/pallets/swap/src/pallet/swap_step.rs b/pallets/swap/src/pallet/swap_step.rs index 9db358be02..cfa42ae39c 100644 --- a/pallets/swap/src/pallet/swap_step.rs +++ b/pallets/swap/src/pallet/swap_step.rs @@ -114,6 +114,15 @@ where /// Process a single step of a swap fn process_swap(&self) -> Result, Error> { + // Check if the target price is valid + let tao = U64F64::saturating_from_num(1_000_000_000u64); + let min_price = U64F64::saturating_from_num(Pallet::::min_price_inner::()).safe_div(tao); + let max_price = U64F64::saturating_from_num(Pallet::::max_price_inner::()).safe_div(tao); + ensure!( + (self.target_price <= max_price) && (self.target_price >= min_price), + Error::::AmountTooHigh + ); + // Convert amounts, actual swap happens here let delta_out = Self::convert_deltas(self.netuid, self.delta_in); log::trace!("\tDelta Out : {delta_out}"); From 213a984fe3c9818216bbe6ac2a9fc7335973631e Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Fri, 26 Dec 2025 14:45:18 -0500 Subject: [PATCH 084/240] Fix test_add_stake_limit_fill_or_kill --- pallets/subtensor/src/tests/staking.rs | 8 +++----- pallets/swap/src/pallet/mod.rs | 3 --- pallets/swap/src/pallet/swap_step.rs | 9 --------- 3 files changed, 3 insertions(+), 17 deletions(-) diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index ad5069c55a..3f19136882 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -3782,7 +3782,7 @@ fn test_add_stake_limit_fill_or_kill() { new_test_ext(1).execute_with(|| { let hotkey_account_id = U256::from(533453); let coldkey_account_id = U256::from(55453); - let amount = 900_000_000_000; // over the maximum + let amount = 300_000_000_000; // over the maximum // add network let netuid = add_dynamic_network(&hotkey_account_id, &coldkey_account_id); @@ -3802,9 +3802,7 @@ fn test_add_stake_limit_fill_or_kill() { SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount); // 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 - // Alpha produced will be equal to 25 = 100 - 450*100/(150+450) - let limit_price = TaoCurrency::from(24_000_000_000); + let limit_price = TaoCurrency::from(6_000_000_000); // Add stake with slippage safety and check if it fails assert_noop!( @@ -3820,7 +3818,7 @@ fn test_add_stake_limit_fill_or_kill() { ); // Lower the amount and it should succeed now - let amount_ok = TaoCurrency::from(450_000_000_000); // fits the maximum + let amount_ok = TaoCurrency::from(150_000_000_000); // fits the maximum assert_ok!(SubtensorModule::add_stake_limit( RuntimeOrigin::signed(coldkey_account_id), hotkey_account_id, diff --git a/pallets/swap/src/pallet/mod.rs b/pallets/swap/src/pallet/mod.rs index 173db5f26b..7c6b484f79 100644 --- a/pallets/swap/src/pallet/mod.rs +++ b/pallets/swap/src/pallet/mod.rs @@ -303,9 +303,6 @@ mod pallet { /// Swap reserves are too imbalanced ReservesOutOfBalance, - - /// Swap amount is too high and target price will be out of range - AmountTooHigh, } #[pallet::call] diff --git a/pallets/swap/src/pallet/swap_step.rs b/pallets/swap/src/pallet/swap_step.rs index cfa42ae39c..9db358be02 100644 --- a/pallets/swap/src/pallet/swap_step.rs +++ b/pallets/swap/src/pallet/swap_step.rs @@ -114,15 +114,6 @@ where /// Process a single step of a swap fn process_swap(&self) -> Result, Error> { - // Check if the target price is valid - let tao = U64F64::saturating_from_num(1_000_000_000u64); - let min_price = U64F64::saturating_from_num(Pallet::::min_price_inner::()).safe_div(tao); - let max_price = U64F64::saturating_from_num(Pallet::::max_price_inner::()).safe_div(tao); - ensure!( - (self.target_price <= max_price) && (self.target_price >= min_price), - Error::::AmountTooHigh - ); - // Convert amounts, actual swap happens here let delta_out = Self::convert_deltas(self.netuid, self.delta_in); log::trace!("\tDelta Out : {delta_out}"); From b755f0ed8a1d689c4bef14823f1f58e457dd008b Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Fri, 26 Dec 2025 14:52:01 -0500 Subject: [PATCH 085/240] Fix test_max_amount_add_dynamic --- pallets/subtensor/src/tests/staking.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index 3f19136882..356bd78c5a 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -2853,8 +2853,15 @@ fn test_max_amount_add_dynamic() { pallet_subtensor_swap::Error::::PriceLimitExceeded, )), ), - (150_000_000_000, 100_000_000_000, 1_500_000_000, Ok(5)), - (150_000_000_000, 100_000_000_000, 1_500_000_001, Ok(51)), + ( + 150_000_000_000, + 100_000_000_000, + 1_500_000_000, + Err(DispatchError::from( + pallet_subtensor_swap::Error::::PriceLimitExceeded, + )), + ), + (150_000_000_000, 100_000_000_000, 1_500_000_001, Ok(49)), ( 150_000_000_000, 100_000_000_000, From 26cca0b2292f4fd2dd1c666b2dc3ec6bda5b2cb7 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Fri, 26 Dec 2025 15:00:13 -0500 Subject: [PATCH 086/240] Fix test_max_amount_move_dynamic_dynamic --- pallets/subtensor/src/tests/staking.rs | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index 356bd78c5a..f07547fc60 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -3670,29 +3670,26 @@ fn test_max_amount_move_dynamic_dynamic() { expected_max_swappable, precision, )| { - let alpha_in_1 = AlphaCurrency::from(alpha_in_1); - let alpha_in_2 = AlphaCurrency::from(alpha_in_2); let expected_max_swappable = AlphaCurrency::from(expected_max_swappable); // Forse-set alpha in and tao reserve to achieve relative price of subnets SubnetTAO::::insert(origin_netuid, TaoCurrency::from(tao_in_1)); - SubnetAlphaIn::::insert(origin_netuid, alpha_in_1); + SubnetAlphaIn::::insert(origin_netuid, AlphaCurrency::from(alpha_in_1)); SubnetTAO::::insert(destination_netuid, TaoCurrency::from(tao_in_2)); - SubnetAlphaIn::::insert(destination_netuid, alpha_in_2); + SubnetAlphaIn::::insert(destination_netuid, AlphaCurrency::from(alpha_in_2)); if !alpha_in_1.is_zero() && !alpha_in_2.is_zero() { - let origin_price = - I96F32::from_num(tao_in_1) / I96F32::from_num(u64::from(alpha_in_1)); - let dest_price = - I96F32::from_num(tao_in_2) / I96F32::from_num(u64::from(alpha_in_2)); - if dest_price != 0 { + let origin_price = tao_in_1 as f64 / alpha_in_1 as f64; + let dest_price = tao_in_2 as f64 / alpha_in_2 as f64; + if dest_price != 0. { let expected_price = origin_price / dest_price; - assert_eq!( - ::SwapInterface::current_alpha_price( + assert_abs_diff_eq!( + (::SwapInterface::current_alpha_price( origin_netuid.into() ) / ::SwapInterface::current_alpha_price( destination_netuid.into() - ), - expected_price + )).to_num::(), + expected_price, + epsilon = 0.000_000_001 ); } } From 7c51712d42fce6c4503f9f88bd5fdf35f7d07f2f Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Fri, 26 Dec 2025 15:07:50 -0500 Subject: [PATCH 087/240] Re-enable balancer math test cases --- pallets/swap/src/pallet/reserve_weights.rs | 154 ++++++++++----------- 1 file changed, 77 insertions(+), 77 deletions(-) diff --git a/pallets/swap/src/pallet/reserve_weights.rs b/pallets/swap/src/pallet/reserve_weights.rs index 16b6d79e16..18a5b5f9b1 100644 --- a/pallets/swap/src/pallet/reserve_weights.rs +++ b/pallets/swap/src/pallet/reserve_weights.rs @@ -310,90 +310,90 @@ mod tests { fn test_exp_base_quote_happy_path() { // Outer test cases: w_quote [ - // Perquintill::from_rational(500_000_000_000_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(500_000_000_001_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(499_999_999_999_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(500_000_000_100_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(500_000_001_000_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(500_000_010_000_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(500_000_100_000_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(500_001_000_000_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(500_010_000_000_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(500_100_000_000_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(501_000_000_000_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(510_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(500_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(500_000_000_001_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(499_999_999_999_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(500_000_000_100_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(500_000_001_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(500_000_010_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(500_000_100_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(500_001_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(500_010_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(500_100_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(501_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(510_000_000_000_u128, 1_000_000_000_000_u128), Perquintill::from_rational(100_000_000_000_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(100_000_000_001_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(200_000_000_000_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(300_000_000_000_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(400_000_000_000_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(600_000_000_000_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(700_000_000_000_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(800_000_000_000_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(899_999_999_999_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational(900_000_000_000_u128, 1_000_000_000_000_u128), - // Perquintill::from_rational( - // 102_337_248_363_782_924_u128, - // 1_000_000_000_000_000_000_u128, - // ), + Perquintill::from_rational(100_000_000_001_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(200_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(300_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(400_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(600_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(700_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(800_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(899_999_999_999_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(900_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational( + 102_337_248_363_782_924_u128, + 1_000_000_000_000_000_000_u128, + ), ] .into_iter() .for_each(|w_quote| { // Inner test cases: y, x, ∆x [ - // (1_000_u64, 1_000_u64, 0_u64), - // (1_000_u64, 1_000_u64, 1_u64), + (1_000_u64, 1_000_u64, 0_u64), + (1_000_u64, 1_000_u64, 1_u64), (1_500_u64, 1_000_u64, 1_u64), - // ( - // 1_000_000_000_000_u64, - // 100_000_000_000_000_u64, - // 100_000_000_u64, - // ), - // ( - // 1_000_000_000_000_u64, - // 100_000_000_000_000_u64, - // 100_000_000_u64, - // ), - // ( - // 100_000_000_000_u64, - // 100_000_000_000_000_u64, - // 100_000_000_u64, - // ), - // (100_000_000_000_u64, 100_000_000_000_000_u64, 1_000_000_u64), - // ( - // 100_000_000_000_u64, - // 100_000_000_000_000_u64, - // 1_000_000_000_000_u64, - // ), - // ( - // 1_000_000_000_u64, - // 100_000_000_000_000_u64, - // 1_000_000_000_000_u64, - // ), - // ( - // 1_000_000_u64, - // 100_000_000_000_000_u64, - // 1_000_000_000_000_u64, - // ), - // (1_000_u64, 100_000_000_000_000_u64, 1_000_000_000_000_u64), - // (1_000_u64, 100_000_000_000_000_u64, 1_000_000_000_u64), - // (1_000_u64, 100_000_000_000_000_u64, 1_000_000_u64), - // (1_000_u64, 100_000_000_000_000_u64, 1_000_u64), - // (1_000_u64, 100_000_000_000_000_u64, 100_000_000_000_000_u64), - // (10_u64, 100_000_000_000_000_u64, 100_000_000_000_000_u64), - // // Extreme values of ∆x for small x - // (1_000_000_000_u64, 4_000_000_000_u64, 1_000_000_000_000_u64), - // (1_000_000_000_000_u64, 1_000_u64, 1_000_000_000_000_u64), - // ( - // 5_628_038_062_729_553_u64, - // 400_775_553_u64, - // 14_446_633_907_665_582_u64, - // ), - // ( - // 5_600_000_000_000_000_u64, - // 400_000_000_u64, - // 14_000_000_000_000_000_u64, - // ), + ( + 1_000_000_000_000_u64, + 100_000_000_000_000_u64, + 100_000_000_u64, + ), + ( + 1_000_000_000_000_u64, + 100_000_000_000_000_u64, + 100_000_000_u64, + ), + ( + 100_000_000_000_u64, + 100_000_000_000_000_u64, + 100_000_000_u64, + ), + (100_000_000_000_u64, 100_000_000_000_000_u64, 1_000_000_u64), + ( + 100_000_000_000_u64, + 100_000_000_000_000_u64, + 1_000_000_000_000_u64, + ), + ( + 1_000_000_000_u64, + 100_000_000_000_000_u64, + 1_000_000_000_000_u64, + ), + ( + 1_000_000_u64, + 100_000_000_000_000_u64, + 1_000_000_000_000_u64, + ), + (1_000_u64, 100_000_000_000_000_u64, 1_000_000_000_000_u64), + (1_000_u64, 100_000_000_000_000_u64, 1_000_000_000_u64), + (1_000_u64, 100_000_000_000_000_u64, 1_000_000_u64), + (1_000_u64, 100_000_000_000_000_u64, 1_000_u64), + (1_000_u64, 100_000_000_000_000_u64, 100_000_000_000_000_u64), + (10_u64, 100_000_000_000_000_u64, 100_000_000_000_000_u64), + // Extreme values of ∆x for small x + (1_000_000_000_u64, 4_000_000_000_u64, 1_000_000_000_000_u64), + (1_000_000_000_000_u64, 1_000_u64, 1_000_000_000_000_u64), + ( + 5_628_038_062_729_553_u64, + 400_775_553_u64, + 14_446_633_907_665_582_u64, + ), + ( + 5_600_000_000_000_000_u64, + 400_000_000_u64, + 14_000_000_000_000_000_u64, + ), ] .into_iter() .for_each(|(y, x, dx)| { From 6ce98e26a5506a2ca43657f063092a64badef4bb Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 29 Dec 2025 12:43:17 -0500 Subject: [PATCH 088/240] Fix test_max_amount_move_dynamic_stable --- pallets/subtensor/src/staking/move_stake.rs | 3 +- pallets/swap/src/pallet/reserve_weights.rs | 56 ++++++++++++--------- pallets/swap/src/pallet/swap_step.rs | 2 +- 3 files changed, 35 insertions(+), 26 deletions(-) diff --git a/pallets/subtensor/src/staking/move_stake.rs b/pallets/subtensor/src/staking/move_stake.rs index a1d9b46d5b..020981158f 100644 --- a/pallets/subtensor/src/staking/move_stake.rs +++ b/pallets/subtensor/src/staking/move_stake.rs @@ -418,7 +418,8 @@ impl Pallet { /// /// In the corner case when SubnetTAO(2) == SubnetTAO(1), no slippage is going to occur. /// - /// TODO: This formula only works for a single swap step, so it is not 100% correct for swap v3. We need an updated one. + /// TODO: This formula only works for a single swap step, so it is not 100% correct for swap v3 or balancers. + /// We need an updated one. /// pub fn get_max_amount_move( origin_netuid: NetUid, diff --git a/pallets/swap/src/pallet/reserve_weights.rs b/pallets/swap/src/pallet/reserve_weights.rs index 18a5b5f9b1..047f661eaf 100644 --- a/pallets/swap/src/pallet/reserve_weights.rs +++ b/pallets/swap/src/pallet/reserve_weights.rs @@ -203,26 +203,29 @@ impl ReserveWeight { let maybe_exp_result = SafeInt::pow_ratio_scaled( &SafeInt::from(base_numerator), &SafeInt::from(base_denominator), - // &SafeInt::from(3u128), - // &SafeInt::from(4u128), &SafeInt::from(w1_fixed), &SafeInt::from(ACCURACY), - 160, + 1024, &SafeInt::from(scale), ); if let Some(exp_result_safe_int) = maybe_exp_result { - if let Some(exp_result_u64) = exp_result_safe_int.to_u64() { - let reserve_fixed = U64F64::saturating_from_num(reserve); - let exp_result_fixed = U64F64::saturating_from_num(exp_result_u64); - let one = U64F64::saturating_from_num(1); - let scale_fixed = U64F64::saturating_from_num(scale); - return reserve_fixed - .saturating_mul(exp_result_fixed.safe_div(scale_fixed).saturating_sub(one)) - .saturating_to_num::(); - } + let reserve_fixed = U64F64::saturating_from_num(reserve); + let one = U64F64::saturating_from_num(1); + let scale_fixed = U64F64::saturating_from_num(scale); + let exp_result_fixed = if let Some(exp_result_u64) = exp_result_safe_int.to_u64() { + U64F64::saturating_from_num(exp_result_u64) + } else if SafeInt::from(u64::MAX) < exp_result_safe_int { + U64F64::saturating_from_num(u64::MAX) + } else { + U64F64::saturating_from_num(0) + }; + reserve_fixed + .saturating_mul(exp_result_fixed.safe_div(scale_fixed).saturating_sub(one)) + .saturating_to_num::() + } else { + 0u64 } - return 0u64; } /// Calculates base delta needed to reach the price down when selling @@ -242,22 +245,27 @@ impl ReserveWeight { &SafeInt::from(base_denominator), &SafeInt::from(w2_fixed), &SafeInt::from(ACCURACY), - 160, + 1024, &SafeInt::from(scale), ); if let Some(exp_result_safe_int) = maybe_exp_result { - if let Some(exp_result_u64) = exp_result_safe_int.to_u64() { - let reserve_fixed = U64F64::saturating_from_num(reserve); - let exp_result_fixed = U64F64::saturating_from_num(exp_result_u64); - let one = U64F64::saturating_from_num(1); - let scale_fixed = U64F64::saturating_from_num(scale); - return reserve_fixed - .saturating_mul(exp_result_fixed.safe_div(scale_fixed).saturating_sub(one)) - .saturating_to_num::(); - } + let one = U64F64::saturating_from_num(1); + let scale_fixed = U64F64::saturating_from_num(scale); + let reserve_fixed = U64F64::saturating_from_num(reserve); + let exp_result_fixed = if let Some(exp_result_u64) = exp_result_safe_int.to_u64() { + U64F64::saturating_from_num(exp_result_u64) + } else if SafeInt::from(u64::MAX) < exp_result_safe_int { + U64F64::saturating_from_num(u64::MAX) + } else { + U64F64::saturating_from_num(0) + }; + reserve_fixed + .saturating_mul(exp_result_fixed.safe_div(scale_fixed).saturating_sub(one)) + .saturating_to_num::() + } else { + 0u64 } - return 0u64; } } diff --git a/pallets/swap/src/pallet/swap_step.rs b/pallets/swap/src/pallet/swap_step.rs index 9db358be02..278e6337d4 100644 --- a/pallets/swap/src/pallet/swap_step.rs +++ b/pallets/swap/src/pallet/swap_step.rs @@ -184,7 +184,7 @@ impl SwapStep for BasicSwapStep { fn delta_in(netuid: NetUid, price_curr: U64F64, price_target: U64F64) -> AlphaCurrency { - let alpha_reserve = T::TaoReserve::reserve(netuid); + let alpha_reserve = T::AlphaReserve::reserve(netuid); let reserve_weight = SwapReserveWeight::::get(netuid); AlphaCurrency::from(reserve_weight.calculate_base_delta_in( price_curr, From 483dcd444f714c10821ab0fea4f000efef571e64 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 29 Dec 2025 12:45:21 -0500 Subject: [PATCH 089/240] Fix test_max_amount_move_stable_dynamic --- pallets/subtensor/src/tests/staking.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index f07547fc60..d1287ec16c 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -3300,7 +3300,7 @@ fn test_max_amount_move_stable_dynamic() { dynamic_netuid, TaoCurrency::from(2_000_000_000) ), - Err(Error::::ZeroMaxStakeAmount.into()) + Err(pallet_subtensor_swap::Error::::PriceLimitExceeded.into()) ); // 3.0 price => max is 0 From 3f152bf4698a602786b3e93ed11d681d3f82ac40 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 29 Dec 2025 13:03:06 -0500 Subject: [PATCH 090/240] Fix test_max_amount_remove_dynamic --- pallets/subtensor/src/tests/staking.rs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index d1287ec16c..e7a20b55d4 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -3020,13 +3020,16 @@ fn test_max_amount_remove_dynamic() { (10_000_000_000, 10_000_000_000, 0, Ok(u64::MAX)), // Low bounds (numbers are empirical, it is only important that result // is sharply decreasing when limit price increases) - (1_000, 1_000, 0, Ok(4_308_000_000_000)), - (1_001, 1_001, 0, Ok(4_310_000_000_000)), - (1_001, 1_001, 1, Ok(31_750_000)), - (1_001, 1_001, 2, Ok(22_500_000)), - (1_001, 1_001, 1_001, Ok(1_000_000)), - (1_001, 1_001, 10_000, Ok(316_000)), - (1_001, 1_001, 100_000, Ok(100_000)), + (1_000, 1_000, 0, Ok(u64::MAX)), + (1_001, 1_001, 0, Ok(u64::MAX)), + (1_001, 1_001, 1, Ok(17_472)), + (1_001, 1_001, 2, Ok(17_472)), + (1_001, 1_001, 1_001, Ok(17_472)), + (1_001, 1_001, 10_000, Ok(17_472)), + (1_001, 1_001, 100_000, Ok(17_472)), + (1_001, 1_001, 1_000_000, Ok(17_472)), + (1_001, 1_001, 10_000_000, Ok(9_013)), + (1_001, 1_001, 100_000_000, Ok(2_165)), // Basic math (1_000_000, 1_000_000, 250_000_000, Ok(1_000_000)), (1_000_000, 1_000_000, 62_500_000, Ok(3_000_000)), @@ -3073,7 +3076,7 @@ fn test_max_amount_remove_dynamic() { 21_000_000_000_000_000, 1_000_000, 21_000_000_000_000_000, - Ok(30_700_000), + Ok(17_455_533), ), (21_000_000_000_000_000, 1_000_000, u64::MAX, Ok(67_164)), ( @@ -3111,7 +3114,7 @@ fn test_max_amount_remove_dynamic() { SubnetAlphaIn::::insert(netuid, alpha_in); if !alpha_in.is_zero() { - let expected_price = I96F32::from_num(tao_in) / I96F32::from_num(alpha_in); + let expected_price = U64F64::from_num(tao_in) / U64F64::from_num(alpha_in); assert_eq!( ::SwapInterface::current_alpha_price(netuid.into()), expected_price From fb9d4941305ab4c8daed93c23d29360de47b8a82 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 29 Dec 2025 13:09:57 -0500 Subject: [PATCH 091/240] Fix test_stake_into_subnet_low_amount --- pallets/subtensor/src/tests/staking.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index e7a20b55d4..522556963f 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -4557,13 +4557,13 @@ fn test_stake_into_subnet_low_amount() { false, false, )); - let expected_stake = AlphaCurrency::from(((amount as f64) * 0.997 / current_price) as u64); + let expected_stake = (amount as f64) * 0.997 / current_price; // Check if stake has increased assert_abs_diff_eq!( - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid), + u64::from(SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid)) as f64, expected_stake, - epsilon = expected_stake / 100.into() + epsilon = expected_stake / 100. ); }); } From 72262b2c08a272e20e4ef500bbe2ccb38e724cdc Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 29 Dec 2025 13:14:22 -0500 Subject: [PATCH 092/240] Fix test_swap_fees_tao_correctness --- pallets/subtensor/src/tests/staking.rs | 182 ++++++++++++------------- 1 file changed, 91 insertions(+), 91 deletions(-) diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index 522556963f..87e9785af6 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -4839,100 +4839,100 @@ fn test_unstake_full_amount() { /// 1. TAO is not minted or burned /// 2. Fees match FeeRate /// +/// TODO: Revise when user liquidity is available #[test] fn test_swap_fees_tao_correctness() { - todo!(); + 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; + let owner_balance_before = amount * 10; + let user_balance_before = amount * 100; - // 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; - // let owner_balance_before = amount * 10; - // let user_balance_before = amount * 100; - - // // 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); - // let fee_rate = pallet_subtensor_swap::FeeRate::::get(NetUid::from(netuid)) as f64 - // / u16::MAX as f64; - // 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 = TaoCurrency::from(100_000_000_000); - // let alpha_in = AlphaCurrency::from(400_000_000_000); - // mock::setup_reserves(netuid, tao_reserve, alpha_in); - - // // Check starting "total TAO" - // let total_tao_before = - // user_balance_before + owner_balance_before + SubnetTAO::::get(netuid).to_u64(); - - // // Get alpha for owner - // assert_ok!(SubtensorModule::add_stake( - // RuntimeOrigin::signed(owner_coldkey), - // owner_hotkey, - // netuid, - // amount.into(), - // )); - // let mut fees = (fee_rate * amount as f64) as u64; - - // // 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.01; - // let tick_low = price_to_tick(current_price); - // let tick_high = price_to_tick(limit_price); - // let liquidity = amount; - - // assert_ok!(::SwapInterface::do_add_liquidity( - // netuid.into(), - // &owner_coldkey, - // &owner_hotkey, - // tick_low, - // tick_high, - // liquidity, - // )); - - // // Limit-buy and then sell all alpha for user to hit owner liquidity - // assert_ok!(SubtensorModule::add_stake_limit( - // RuntimeOrigin::signed(coldkey), - // owner_hotkey, - // netuid, - // amount.into(), - // ((limit_price * u64::MAX as f64) as u64).into(), - // true - // )); - // fees += (fee_rate * amount as f64) as u64; - - // let user_alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - // &owner_hotkey, - // &coldkey, - // netuid, - // ); - // remove_stake_rate_limit_for_tests(&owner_hotkey, &coldkey, netuid); - // assert_ok!(SubtensorModule::remove_stake( - // RuntimeOrigin::signed(coldkey), - // owner_hotkey, - // netuid, - // user_alpha, - // )); - // // Do not add fees because selling fees are in alpha - - // // Check ending "total TAO" - // let owner_balance_after = SubtensorModule::get_coldkey_balance(&owner_coldkey); - // let user_balance_after = SubtensorModule::get_coldkey_balance(&coldkey); - // let total_tao_after = user_balance_after - // + owner_balance_after - // + SubnetTAO::::get(netuid).to_u64() - // + fees; - - // // Total TAO does not change, leave some epsilon for rounding - // assert_abs_diff_eq!(total_tao_before, total_tao_after, epsilon = 2); - // }); + // 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); + let fee_rate = pallet_subtensor_swap::FeeRate::::get(NetUid::from(netuid)) as f64 + / u16::MAX as f64; + 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 = TaoCurrency::from(100_000_000_000); + let alpha_in = AlphaCurrency::from(400_000_000_000); + mock::setup_reserves(netuid, tao_reserve, alpha_in); + + // Check starting "total TAO" + let total_tao_before = + user_balance_before + owner_balance_before + SubnetTAO::::get(netuid).to_u64(); + + // Get alpha for owner + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(owner_coldkey), + owner_hotkey, + netuid, + amount.into(), + )); + let mut fees = (fee_rate * amount as f64) as u64; + + // TODO: Revise when user liquidity is available + // 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.01; + // let tick_low = price_to_tick(current_price); + // let tick_high = price_to_tick(limit_price); + // let liquidity = amount; + + // assert_ok!(::SwapInterface::do_add_liquidity( + // netuid.into(), + // &owner_coldkey, + // &owner_hotkey, + // tick_low, + // tick_high, + // liquidity, + // )); + + // Limit-buy and then sell all alpha for user to hit owner liquidity + assert_ok!(SubtensorModule::add_stake_limit( + RuntimeOrigin::signed(coldkey), + owner_hotkey, + netuid, + amount.into(), + ((limit_price * u64::MAX as f64) as u64).into(), + true + )); + fees += (fee_rate * amount as f64) as u64; + + let user_alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &owner_hotkey, + &coldkey, + netuid, + ); + remove_stake_rate_limit_for_tests(&owner_hotkey, &coldkey, netuid); + assert_ok!(SubtensorModule::remove_stake( + RuntimeOrigin::signed(coldkey), + owner_hotkey, + netuid, + user_alpha, + )); + // Do not add fees because selling fees are in alpha + + // Check ending "total TAO" + let owner_balance_after = SubtensorModule::get_coldkey_balance(&owner_coldkey); + let user_balance_after = SubtensorModule::get_coldkey_balance(&coldkey); + let total_tao_after = user_balance_after + + owner_balance_after + + SubnetTAO::::get(netuid).to_u64() + + fees; + + // Total TAO does not change, leave some epsilon for rounding + assert_abs_diff_eq!(total_tao_before, total_tao_after, epsilon = 2); + }); } #[test] From 5fdc9f9e07f1e2963d3086ce7c83fc66d13bb415 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 29 Dec 2025 14:03:25 -0500 Subject: [PATCH 093/240] Refactor: Rename ReserveWeights to Balancer --- pallets/subtensor/src/tests/claim_root.rs | 10 +- pallets/subtensor/src/tests/staking.rs | 271 +++++++++--------- .../{reserve_weights.rs => balancer.rs} | 78 ++--- pallets/swap/src/pallet/impls.rs | 18 +- pallets/swap/src/pallet/mod.rs | 12 +- pallets/swap/src/pallet/swap_step.rs | 12 +- pallets/swap/src/pallet/tests.rs | 24 +- 7 files changed, 212 insertions(+), 213 deletions(-) rename pallets/swap/src/pallet/{reserve_weights.rs => balancer.rs} (92%) diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index e5f66f6da2..3295653968 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -1034,14 +1034,14 @@ fn test_claim_root_coinbase_distribution() { // Set moving price > 1.0 and price > 1.0 // So we turn ON root sell SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); - let tao = TaoCurrency::from(20_000_000_000_u64); + let tao = TaoCurrency::from(10_000_000_000_u64); let alpha = AlphaCurrency::from(10_000_000_000_u64); SubnetTAO::::insert(netuid, tao); SubnetAlphaIn::::insert(netuid, alpha); - let current_price = - ::SwapInterface::current_alpha_price(netuid.into()) - .saturating_to_num::(); - assert_eq!(current_price, 2.0f64); + // let current_price = + // ::SwapInterface::current_alpha_price(netuid.into()) + // .saturating_to_num::(); + // assert_eq!(current_price, 2.0f64); RootClaimableThreshold::::insert(netuid, I96F32::from_num(0)); let initial_alpha_issuance = SubtensorModule::get_alpha_issuance(netuid); diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index 87e9785af6..e995c8a095 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -5175,142 +5175,141 @@ 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() { - todo!(); - - // // 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); - // SubtensorModule::add_balance_to_coldkey_account(&coldkey, amount * 100); - // 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 = TaoCurrency::from(100_000_000_000); - // let alpha_in = AlphaCurrency::from(400_000_000_000); - // mock::setup_reserves(netuid, tao_reserve, alpha_in); - - // // 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 - // let owner_tao_before = SubtensorModule::get_coldkey_balance(&owner_coldkey); - // let owner_alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - // &owner_hotkey, - // &owner_coldkey, - // netuid, - // ); - - // // 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 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 > owner_tao_before); - // assert!(owner_alpha_after_add > owner_alpha_before); // always greater because of claimed fees - - // // 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); - // } - // }); - // }); -} +// TODO: Revise when user liquidity is availble +// 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); +// SubtensorModule::add_balance_to_coldkey_account(&coldkey, amount * 100); +// 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 = TaoCurrency::from(100_000_000_000); +// let alpha_in = AlphaCurrency::from(400_000_000_000); +// mock::setup_reserves(netuid, tao_reserve, alpha_in); + +// // 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 +// let owner_tao_before = SubtensorModule::get_coldkey_balance(&owner_coldkey); +// let owner_alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( +// &owner_hotkey, +// &owner_coldkey, +// netuid, +// ); + +// // 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 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 > owner_tao_before); +// assert!(owner_alpha_after_add > owner_alpha_before); // always greater because of claimed fees + +// // 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); +// } +// }); +// }); +// } // fn setup_positions(netuid: NetUid) { // for (coldkey, hotkey, low_price, high_price, liquidity) in [ diff --git a/pallets/swap/src/pallet/reserve_weights.rs b/pallets/swap/src/pallet/balancer.rs similarity index 92% rename from pallets/swap/src/pallet/reserve_weights.rs rename to pallets/swap/src/pallet/balancer.rs index 047f661eaf..27240acc0c 100644 --- a/pallets/swap/src/pallet/reserve_weights.rs +++ b/pallets/swap/src/pallet/balancer.rs @@ -8,9 +8,9 @@ use sp_runtime::Saturating; use substrate_fixed::types::U64F64; use subtensor_macros::freeze_struct; -#[freeze_struct("8c6bbe52ef752203")] +#[freeze_struct("7fa3cbcf1d419808")] #[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] -pub struct ReserveWeight { +pub struct Balancer { quote: Perquintill, } @@ -20,11 +20,11 @@ pub const MIN_WEIGHT: Perquintill = Perquintill::from_parts(ACCURACY / 100); pub const ONE: Perquintill = Perquintill::from_parts(ACCURACY); #[derive(Debug)] -pub enum ReserveWeightError { +pub enum BalancerError { InvalidValue, } -impl Default for ReserveWeight { +impl Default for Balancer { fn default() -> Self { Self { quote: Perquintill::from_rational(1u128, 2u128), @@ -32,12 +32,12 @@ impl Default for ReserveWeight { } } -impl ReserveWeight { - pub fn new(quote: Perquintill) -> Result { +impl Balancer { + pub fn new(quote: Perquintill) -> Result { if Self::check_constraints(quote) { - Ok(ReserveWeight { quote }) + Ok(Balancer { quote }) } else { - Err(ReserveWeightError::InvalidValue) + Err(BalancerError::InvalidValue) } } @@ -54,11 +54,11 @@ impl ReserveWeight { ONE.saturating_sub(self.quote) } - pub fn set_quote_weight(&self, new_value: Perquintill) -> Result<(), ReserveWeightError> { + pub fn set_quote_weight(&self, new_value: Perquintill) -> Result<(), BalancerError> { if Self::check_constraints(new_value) { Ok(()) } else { - Err(ReserveWeightError::InvalidValue) + Err(BalancerError::InvalidValue) } } @@ -156,7 +156,7 @@ impl ReserveWeight { alpha_reserve: u64, tao_delta: u64, alpha_delta: u64, - ) -> Result<(), ReserveWeightError> { + ) -> Result<(), BalancerError> { // Calculate new to-be reserves (do not update here) let tao_reserve_u128 = u64::from(tao_reserve) as u128; let alpha_reserve_u128 = u64::from(alpha_reserve) as u128; @@ -269,12 +269,12 @@ impl ReserveWeight { } } -// cargo test --package pallet-subtensor-swap --lib -- pallet::reserve_weights::tests --nocapture +// cargo test --package pallet-subtensor-swap --lib -- pallet::balancer::tests --nocapture #[cfg(test)] #[cfg(feature = "std")] mod tests { - use crate::pallet::ReserveWeight; - use crate::pallet::reserve_weights::*; + use crate::pallet::Balancer; + use crate::pallet::balancer::*; use approx::assert_abs_diff_eq; use sp_arithmetic::Perquintill; @@ -405,16 +405,16 @@ mod tests { ] .into_iter() .for_each(|(y, x, dx)| { - let rw = ReserveWeight::new(w_quote).unwrap(); - let e = rw.exp_base_quote(x, dx); + let bal = Balancer::new(w_quote).unwrap(); + let e = bal.exp_base_quote(x, dx); let one = U64F64::from_num(1); let y_fixed = U64F64::from_num(y); println!("debug 1: e = {:?}", e); let dy = y_fixed * (one - e); println!("debug 2: dy = {:?}", dy); - let w1 = perquintill_to_f64(rw.get_base_weight()); - let w2 = perquintill_to_f64(rw.get_quote_weight()); + let w1 = perquintill_to_f64(bal.get_base_weight()); + let w2 = perquintill_to_f64(bal.get_quote_weight()); let e_expected = (x as f64 / (x as f64 + dx as f64)).powf(w1 / w2); let dy_expected = y as f64 * (1. - e_expected); @@ -489,11 +489,11 @@ mod tests { ] .into_iter() .for_each(|(y, x1, dx1, w_quote1, x2, dx2, w_quote2)| { - let rw1 = ReserveWeight::new(w_quote1).unwrap(); - let rw2 = ReserveWeight::new(w_quote2).unwrap(); + let bal1 = Balancer::new(w_quote1).unwrap(); + let bal2 = Balancer::new(w_quote2).unwrap(); - let exp1 = rw1.exp_base_quote(x1, dx1); - let exp2 = rw2.exp_base_quote(x2, dx2); + let exp1 = bal1.exp_base_quote(x1, dx1); + let exp2 = bal2.exp_base_quote(x2, dx2); let one = U64F64::from_num(1); let y_fixed = U64F64::from_num(y); @@ -522,8 +522,8 @@ mod tests { let stop = 900_000_000_000_u128; for num in (start..=stop).step_by(1000 as usize) { let w_quote = Perquintill::from_rational(num, 1_000_000_000_000_u128); - let rw = ReserveWeight::new(w_quote).unwrap(); - let e = rw.exp_base_quote(x, dx); + let bal = Balancer::new(w_quote).unwrap(); + let e = bal.exp_base_quote(x, dx); let one = U64F64::from_num(1); // println!("e = {:?}", e); @@ -585,15 +585,15 @@ mod tests { let w_numerator: u64 = rng.gen_range(ACCURACY / 10..=ACCURACY / 10 * 9); let w_quote = Perquintill::from_rational(w_numerator, ACCURACY); - let rw = ReserveWeight::new(w_quote).unwrap(); - let e = rw.exp_base_quote(x, dx); + let bal = Balancer::new(w_quote).unwrap(); + let e = bal.exp_base_quote(x, dx); let one = U64F64::from_num(1); let dy = U64F64::from_num(y) * (one - e); // Calculate expected in f64 and approx-assert - let w1 = perquintill_to_f64(rw.get_base_weight()); - let w2 = perquintill_to_f64(rw.get_quote_weight()); + let w1 = perquintill_to_f64(bal.get_base_weight()); + let w2 = perquintill_to_f64(bal.get_quote_weight()); let e_expected = (x as f64 / (x as f64 + dx as f64)).powf(w1 / w2); let dy_expected = y as f64 * (1. - e_expected); @@ -628,13 +628,13 @@ mod tests { fn test_calculate_quote_delta_in() { let num = 250_000_000_000_u128; // w1 = 0.75 let w_quote = Perquintill::from_rational(num, 1_000_000_000_000_u128); - let rw = ReserveWeight::new(w_quote).unwrap(); + let bal = Balancer::new(w_quote).unwrap(); let current_price: U64F64 = U64F64::from_num(0.1); let target_price: U64F64 = U64F64::from_num(0.2); let tao_reserve: u64 = 1_000_000_000; - let dy = rw.calculate_quote_delta_in(current_price, target_price, tao_reserve); + let dy = bal.calculate_quote_delta_in(current_price, target_price, tao_reserve); // ∆y = y•[(p'/p)^w1 - 1] let dy_expected = tao_reserve as f64 @@ -647,13 +647,13 @@ mod tests { fn test_calculate_base_delta_in() { let num = 250_000_000_000_u128; // w2 = 0.25 let w_quote = Perquintill::from_rational(num, 1_000_000_000_000_u128); - let rw = ReserveWeight::new(w_quote).unwrap(); + let bal = Balancer::new(w_quote).unwrap(); let current_price: U64F64 = U64F64::from_num(0.2); let target_price: U64F64 = U64F64::from_num(0.1); let alpha_reserve: u64 = 1_000_000_000; - let dx = rw.calculate_base_delta_in(current_price, target_price, alpha_reserve); + let dx = bal.calculate_base_delta_in(current_price, target_price, alpha_reserve); // ∆x = x•[(p/p')^w2 - 1] let dx_expected = alpha_reserve as f64 @@ -666,14 +666,14 @@ mod tests { fn test_calculate_quote_delta_in_impossible() { let num = 250_000_000_000_u128; // w1 = 0.75 let w_quote = Perquintill::from_rational(num, 1_000_000_000_000_u128); - let rw = ReserveWeight::new(w_quote).unwrap(); + let bal = Balancer::new(w_quote).unwrap(); // Impossible price (lower) let current_price: U64F64 = U64F64::from_num(0.1); let target_price: U64F64 = U64F64::from_num(0.05); let tao_reserve: u64 = 1_000_000_000; - let dy = rw.calculate_quote_delta_in(current_price, target_price, tao_reserve); + let dy = bal.calculate_quote_delta_in(current_price, target_price, tao_reserve); let dy_expected = 0u64; assert_eq!(dy, dy_expected as u64,); @@ -683,14 +683,14 @@ mod tests { fn test_calculate_base_delta_in_impossible() { let num = 250_000_000_000_u128; // w2 = 0.25 let w_quote = Perquintill::from_rational(num, 1_000_000_000_000_u128); - let rw = ReserveWeight::new(w_quote).unwrap(); + let bal = Balancer::new(w_quote).unwrap(); // Impossible price (higher) let current_price: U64F64 = U64F64::from_num(0.1); let target_price: U64F64 = U64F64::from_num(0.2); let alpha_reserve: u64 = 1_000_000_000; - let dx = rw.calculate_base_delta_in(current_price, target_price, alpha_reserve); + let dx = bal.calculate_base_delta_in(current_price, target_price, alpha_reserve); let dx_expected = 0u64; assert_eq!(dx, dx_expected as u64,); @@ -700,7 +700,7 @@ mod tests { fn test_calculate_delta_in_reverse_swap() { let num = 500_000_000_000_u128; let w_quote = Perquintill::from_rational(num, 1_000_000_000_000_u128); - let rw = ReserveWeight::new(w_quote).unwrap(); + let bal = Balancer::new(w_quote).unwrap(); let current_price: U64F64 = U64F64::from_num(0.1); let target_price: U64F64 = U64F64::from_num(0.2); @@ -709,14 +709,14 @@ mod tests { // Here is the simple case of w1 = w2 = 0.5, so alpha = tao / price let alpha_reserve: u64 = (tao_reserve as f64 / current_price.to_num::()) as u64; - let dy = rw.calculate_quote_delta_in(current_price, target_price, tao_reserve); + let dy = bal.calculate_quote_delta_in(current_price, target_price, tao_reserve); let dx = alpha_reserve as f64 * (1.0 - (tao_reserve as f64 / (tao_reserve as f64 + dy as f64)) .powf(num as f64 / (1_000_000_000_000 - num) as f64)); // Verify that buying with dy will in fact bring the price to target_price - let actual_price = rw.calculate_price(alpha_reserve - dx as u64, tao_reserve + dy); + let actual_price = bal.calculate_price(alpha_reserve - dx as u64, tao_reserve + dy); assert_abs_diff_eq!( actual_price.to_num::(), target_price.to_num::(), diff --git a/pallets/swap/src/pallet/impls.rs b/pallets/swap/src/pallet/impls.rs index b6a6bcba67..f5dca185ac 100644 --- a/pallets/swap/src/pallet/impls.rs +++ b/pallets/swap/src/pallet/impls.rs @@ -23,7 +23,7 @@ use subtensor_swap_interface::{ use super::pallet::*; use super::swap_step::{BasicSwapStep, SwapStep}; use crate::{ - pallet::ReserveWeight, pallet::reserve_weights::ReserveWeightError, position::PositionId, + pallet::Balancer, pallet::balancer::BalancerError, position::PositionId, }; #[derive(Debug, PartialEq)] @@ -51,7 +51,7 @@ impl Pallet { let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); if !alpha_reserve.is_zero() { let tao_reserve = T::TaoReserve::reserve(netuid.into()); - let reserve_weight = SwapReserveWeight::::get(netuid); + let reserve_weight = SwapBalancer::::get(netuid); reserve_weight.calculate_price(alpha_reserve.into(), tao_reserve.into()) } else { U64F64::saturating_from_num(0) @@ -67,13 +67,13 @@ impl Pallet { return Ok(()); } - // Insert 0.5 into SwapReserveWeight - let reserve_weight = ReserveWeight::new(Perquintill::from_rational(1_u64, 2_u64)).map_err( + // Insert 0.5 into SwapBalancer + let reserve_weight = Balancer::new(Perquintill::from_rational(1_u64, 2_u64)).map_err( |err| match err { - ReserveWeightError::InvalidValue => Error::::ReservesOutOfBalance, + BalancerError::InvalidValue => Error::::ReservesOutOfBalance, }, )?; - SwapReserveWeight::::insert(netuid, reserve_weight); + SwapBalancer::::insert(netuid, reserve_weight); // TODO: Review when/if we have user liquidity // Initialize the pal-swap: @@ -114,7 +114,7 @@ impl Pallet { // Get reserves let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); let tao_reserve = T::TaoReserve::reserve(netuid.into()); - let mut reserve_weight = SwapReserveWeight::::get(netuid); + let mut reserve_weight = SwapBalancer::::get(netuid); // Update weights and log errors if they go out of range if reserve_weight @@ -135,7 +135,7 @@ impl Pallet { alpha_delta ); } else { - SwapReserveWeight::::insert(netuid, reserve_weight); + SwapBalancer::::insert(netuid, reserve_weight); } } @@ -549,7 +549,7 @@ impl Pallet { PalSwapInitialized::::remove(netuid); FeeRate::::remove(netuid); EnabledUserLiquidity::::remove(netuid); - SwapReserveWeight::::remove(netuid); + SwapBalancer::::remove(netuid); log::debug!( "clear_protocol_liquidity: netuid={netuid:?}, protocol_burned: τ={burned_tao:?}, α={burned_alpha:?}; state cleared" diff --git a/pallets/swap/src/pallet/mod.rs b/pallets/swap/src/pallet/mod.rs index 7c6b484f79..978b26b53b 100644 --- a/pallets/swap/src/pallet/mod.rs +++ b/pallets/swap/src/pallet/mod.rs @@ -9,7 +9,7 @@ use subtensor_runtime_common::{ }; use crate::{ - pallet::reserve_weights::ReserveWeight, + pallet::balancer::Balancer, position::{Position, PositionId}, weights::WeightInfo, }; @@ -17,7 +17,7 @@ use crate::{ pub use pallet::*; mod impls; -mod reserve_weights; +mod balancer; mod swap_step; #[cfg(test)] mod tests; @@ -162,13 +162,13 @@ mod pallet { /// Default reserve weight #[pallet::type_value] - pub fn DefaultReserveWeight() -> ReserveWeight { - ReserveWeight::default() + pub fn DefaultBalancer() -> Balancer { + Balancer::default() } /// u64-normalized reserve weight #[pallet::storage] - pub type SwapReserveWeight = - StorageMap<_, Twox64Concat, NetUid, ReserveWeight, ValueQuery, DefaultReserveWeight>; + pub type SwapBalancer = + StorageMap<_, Twox64Concat, NetUid, Balancer, ValueQuery, DefaultBalancer>; /// Storage to determine whether balancer swap was initialized for a specific subnet. #[pallet::storage] diff --git a/pallets/swap/src/pallet/swap_step.rs b/pallets/swap/src/pallet/swap_step.rs index 278e6337d4..c9f0ae92a8 100644 --- a/pallets/swap/src/pallet/swap_step.rs +++ b/pallets/swap/src/pallet/swap_step.rs @@ -137,7 +137,7 @@ impl SwapStep { fn delta_in(netuid: NetUid, price_curr: U64F64, price_target: U64F64) -> TaoCurrency { let tao_reserve = T::TaoReserve::reserve(netuid.into()); - let reserve_weight = SwapReserveWeight::::get(netuid); + let reserve_weight = SwapBalancer::::get(netuid); TaoCurrency::from(reserve_weight.calculate_quote_delta_in( price_curr, price_target, @@ -148,7 +148,7 @@ impl SwapStep fn price_target(netuid: NetUid, delta_in: TaoCurrency) -> U64F64 { let tao_reserve = T::TaoReserve::reserve(netuid.into()); let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); - let reserve_weight = SwapReserveWeight::::get(netuid); + let reserve_weight = SwapBalancer::::get(netuid); let dy = delta_in; let dx = Self::convert_deltas(netuid, dy); reserve_weight.calculate_price( @@ -168,7 +168,7 @@ impl SwapStep fn convert_deltas(netuid: NetUid, delta_in: TaoCurrency) -> AlphaCurrency { let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); let tao_reserve = T::TaoReserve::reserve(netuid.into()); - let reserve_weight = SwapReserveWeight::::get(netuid); + let reserve_weight = SwapBalancer::::get(netuid); let e = reserve_weight.exp_quote_base(tao_reserve.into(), delta_in.into()); let one = U64F64::from_num(1); let alpha_reserve_fixed = U64F64::from_num(alpha_reserve); @@ -185,7 +185,7 @@ impl SwapStep { fn delta_in(netuid: NetUid, price_curr: U64F64, price_target: U64F64) -> AlphaCurrency { let alpha_reserve = T::AlphaReserve::reserve(netuid); - let reserve_weight = SwapReserveWeight::::get(netuid); + let reserve_weight = SwapBalancer::::get(netuid); AlphaCurrency::from(reserve_weight.calculate_base_delta_in( price_curr, price_target, @@ -196,7 +196,7 @@ impl SwapStep fn price_target(netuid: NetUid, delta_in: AlphaCurrency) -> U64F64 { let tao_reserve = T::TaoReserve::reserve(netuid.into()); let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); - let reserve_weight = SwapReserveWeight::::get(netuid); + let reserve_weight = SwapBalancer::::get(netuid); let dx = delta_in; let dy = Self::convert_deltas(netuid, dx); reserve_weight.calculate_price( @@ -216,7 +216,7 @@ impl SwapStep fn convert_deltas(netuid: NetUid, delta_in: AlphaCurrency) -> TaoCurrency { let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); let tao_reserve = T::TaoReserve::reserve(netuid.into()); - let reserve_weight = SwapReserveWeight::::get(netuid); + let reserve_weight = SwapBalancer::::get(netuid); let e = reserve_weight.exp_base_quote(alpha_reserve.into(), delta_in.into()); let one = U64F64::from_num(1); let tao_reserve_fixed = U64F64::from_num(u64::from(tao_reserve)); diff --git a/pallets/swap/src/pallet/tests.rs b/pallets/swap/src/pallet/tests.rs index 1895a2f346..c451701d3b 100644 --- a/pallets/swap/src/pallet/tests.rs +++ b/pallets/swap/src/pallet/tests.rs @@ -158,7 +158,7 @@ mod dispatchables { let expected_quote_weight = new_tao / (new_alpha * price_before.to_num::() + new_tao); let expected_quote_weight_delta = expected_quote_weight - 0.5; - let res_weights = SwapReserveWeight::::get(netuid); + let res_weights = SwapBalancer::::get(netuid); let actual_quote_weight_delta = perquintill_to_f64(res_weights.get_quote_weight()) - 0.5; let eps = expected_quote_weight / 1_000_000_000_000.; @@ -177,7 +177,7 @@ mod dispatchables { /// - Result in the same weight change as one large injection /// /// This is a long test that only tests validity of weights math. Run again if changing - /// ReserveWeight::update_weights_for_added_liquidity + /// Balancer::update_weights_for_added_liquidity /// /// cargo test --package pallet-subtensor-swap --lib -- pallet::tests::dispatchables::test_adjust_protocol_liquidity_deltas --exact --nocapture #[ignore] @@ -267,8 +267,8 @@ mod dispatchables { AlphaReserve::set_mock_reserve(netuid2, initial_alpha_reserve + alpha_delta_once); // Compare reserve weights for netuid 1 and 2 - let res_weights1 = SwapReserveWeight::::get(netuid1); - let res_weights2 = SwapReserveWeight::::get(netuid2); + let res_weights1 = SwapBalancer::::get(netuid1); + let res_weights2 = SwapBalancer::::get(netuid2); let actual_quote_weight1 = perquintill_to_f64(res_weights1.get_quote_weight()); let actual_quote_weight2 = perquintill_to_f64(res_weights2.get_quote_weight()); assert_abs_diff_eq!( @@ -349,7 +349,7 @@ mod dispatchables { TaoReserve::set_mock_reserve(netuid, tao + tao_delta); AlphaReserve::set_mock_reserve(netuid, alpha + alpha_delta); - let res_weights = SwapReserveWeight::::get(netuid); + let res_weights = SwapBalancer::::get(netuid); let actual_quote_weight = perquintill_to_f64(res_weights.get_quote_weight()); // Check that price didn't change @@ -432,7 +432,7 @@ fn test_swap_initialization() { ); // Verify that swap reserve weight is initialized - let reserve_weight = SwapReserveWeight::::get(netuid); + let reserve_weight = SwapBalancer::::get(netuid); assert_eq!( reserve_weight.get_quote_weight(), Perquintill::from_rational(1_u64, 2_u64), @@ -1249,8 +1249,8 @@ fn test_convert_deltas() { (w_quote as f64 * w_accuracy) as u128, w_accuracy as u128, ); - let rw = ReserveWeight::new(w_quote_pt).unwrap(); - SwapReserveWeight::::insert(netuid, rw); + let bal = Balancer::new(w_quote_pt).unwrap(); + SwapBalancer::::insert(netuid, bal); // Calculate expected swap results (buy and sell) using f64 math let y = tao as f64; @@ -1967,7 +1967,7 @@ fn test_swap_subtoken_disabled() { // assert!(!FeesTao::::contains_key(netuid)); // assert!(!FeesAlpha::::contains_key(netuid)); // assert!(!PalSwapInitialized::::contains_key(netuid)); -// assert!(!SwapReserveWeight::::contains_key(netuid)); +// assert!(!SwapBalancer::::contains_key(netuid)); // }); // } @@ -1986,8 +1986,8 @@ fn test_liquidate_pal_simple_ok_and_clears() { FeesAlpha::::insert(netuid, AlphaCurrency::from(1_000)); PalSwapInitialized::::insert(netuid, true); let w_quote_pt = Perquintill::from_rational(1u128, 2u128); - let rw = ReserveWeight::new(w_quote_pt).unwrap(); - SwapReserveWeight::::insert(netuid, rw); + let bal = Balancer::new(w_quote_pt).unwrap(); + SwapBalancer::::insert(netuid, bal); // Sanity: PalSwap is not initialized assert!(PalSwapInitialized::::get(netuid)); @@ -2013,7 +2013,7 @@ fn test_liquidate_pal_simple_ok_and_clears() { assert!(!FeesTao::::contains_key(netuid)); assert!(!FeesAlpha::::contains_key(netuid)); assert!(!PalSwapInitialized::::contains_key(netuid)); - assert!(!SwapReserveWeight::::contains_key(netuid)); + assert!(!SwapBalancer::::contains_key(netuid)); }); } From 1cc192179803970663e4fc931aa3ff716a243198 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Tue, 30 Dec 2025 13:03:33 -0500 Subject: [PATCH 094/240] Fix test_claim_root_coinbase_distribution --- pallets/subtensor/src/tests/claim_root.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index 3295653968..362a686772 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -1034,8 +1034,8 @@ fn test_claim_root_coinbase_distribution() { // Set moving price > 1.0 and price > 1.0 // So we turn ON root sell SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); - let tao = TaoCurrency::from(10_000_000_000_u64); - let alpha = AlphaCurrency::from(10_000_000_000_u64); + let tao = TaoCurrency::from(100_000_000_000_u64); + let alpha = AlphaCurrency::from(100_000_000_000_u64); SubnetTAO::::insert(netuid, tao); SubnetAlphaIn::::insert(netuid, alpha); // let current_price = From be6fd6f65a0bd31444d7ea24edf533fc71427c57 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Tue, 30 Dec 2025 13:58:52 -0500 Subject: [PATCH 095/240] Fix test_adjust_protocol_liquidity_happy --- pallets/swap/src/pallet/balancer.rs | 36 ++++++++++++---------------- pallets/swap/src/pallet/impls.rs | 10 ++++---- pallets/swap/src/pallet/swap_step.rs | 24 +++++++++---------- 3 files changed, 32 insertions(+), 38 deletions(-) diff --git a/pallets/swap/src/pallet/balancer.rs b/pallets/swap/src/pallet/balancer.rs index 27240acc0c..9892769143 100644 --- a/pallets/swap/src/pallet/balancer.rs +++ b/pallets/swap/src/pallet/balancer.rs @@ -54,8 +54,9 @@ impl Balancer { ONE.saturating_sub(self.quote) } - pub fn set_quote_weight(&self, new_value: Perquintill) -> Result<(), BalancerError> { + pub fn set_quote_weight(&mut self, new_value: Perquintill) -> Result<(), BalancerError> { if Self::check_constraints(new_value) { + self.quote = new_value; Ok(()) } else { Err(BalancerError::InvalidValue) @@ -78,14 +79,14 @@ impl Balancer { let perquintill_scale = SafeInt::from(ACCURACY as u128); let denominator = SafeInt::from(x_plus_dx); let maybe_result_safe_int = if base_quote { - println!("x = {:?}", x); - println!("dx = {:?}", dx); - println!("x_safe = {:?}", x_safe); - println!("denominator = {:?}", denominator); - println!("w1_safe = {:?}", w1_safe); - println!("w2_safe = {:?}", w2_safe); - println!("precision = {:?}", precision); - println!("perquintill_scale = {:?}", perquintill_scale); + log::debug!("x = {:?}", x); + log::debug!("dx = {:?}", dx); + log::debug!("x_safe = {:?}", x_safe); + log::debug!("denominator = {:?}", denominator); + log::debug!("w1_safe = {:?}", w1_safe); + log::debug!("w2_safe = {:?}", w2_safe); + log::debug!("precision = {:?}", precision); + log::debug!("perquintill_scale = {:?}", perquintill_scale); SafeInt::pow_ratio_scaled( &x_safe, @@ -409,17 +410,13 @@ mod tests { let e = bal.exp_base_quote(x, dx); let one = U64F64::from_num(1); let y_fixed = U64F64::from_num(y); - println!("debug 1: e = {:?}", e); let dy = y_fixed * (one - e); - println!("debug 2: dy = {:?}", dy); let w1 = perquintill_to_f64(bal.get_base_weight()); let w2 = perquintill_to_f64(bal.get_quote_weight()); let e_expected = (x as f64 / (x as f64 + dx as f64)).powf(w1 / w2); let dy_expected = y as f64 * (1. - e_expected); - println!("debug 3: dy_expected = {:?}", dy_expected); - let mut eps = dy_expected / 100000.; if eps > 1.0 { eps = 1.0; @@ -526,17 +523,13 @@ mod tests { let e = bal.exp_base_quote(x, dx); let one = U64F64::from_num(1); - // println!("e = {:?}", e); - // println!("1 - e = {:?}", one - e); - let dy = U64F64::from_num(y) * (one - e); - // println!("dy = {:?}", dy); let progress = (num as f64 - start as f64) / (stop as f64 - start as f64); - if progress - last_progress >= 0.0001 { - println!("progress = {:?}%", progress * 100.); - println!("dy = {:?}", dy); + // Replace with println for real-time progress + log::debug!("progress = {:?}%", progress * 100.); + log::debug!("dy = {:?}", dy); last_progress = progress; } @@ -619,7 +612,8 @@ mod tests { let done = counter.fetch_add(1, Ordering::Relaxed) + 1; if done % 100_000_000 == 0 { let progress = done as f64 / ITERATIONS as f64 * 100.0; - println!("progress = {progress:.4}%"); + // Replace with println for real-time progress + log::debug!("progress = {progress:.4}%"); } }); } diff --git a/pallets/swap/src/pallet/impls.rs b/pallets/swap/src/pallet/impls.rs index f5dca185ac..cc93fa9114 100644 --- a/pallets/swap/src/pallet/impls.rs +++ b/pallets/swap/src/pallet/impls.rs @@ -51,8 +51,8 @@ impl Pallet { let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); if !alpha_reserve.is_zero() { let tao_reserve = T::TaoReserve::reserve(netuid.into()); - let reserve_weight = SwapBalancer::::get(netuid); - reserve_weight.calculate_price(alpha_reserve.into(), tao_reserve.into()) + let balancer = SwapBalancer::::get(netuid); + balancer.calculate_price(alpha_reserve.into(), tao_reserve.into()) } else { U64F64::saturating_from_num(0) } @@ -114,10 +114,10 @@ impl Pallet { // Get reserves let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); let tao_reserve = T::TaoReserve::reserve(netuid.into()); - let mut reserve_weight = SwapBalancer::::get(netuid); + let mut balancer = SwapBalancer::::get(netuid); // Update weights and log errors if they go out of range - if reserve_weight + if balancer .update_weights_for_added_liquidity( u64::from(tao_reserve), u64::from(alpha_reserve), @@ -135,7 +135,7 @@ impl Pallet { alpha_delta ); } else { - SwapBalancer::::insert(netuid, reserve_weight); + SwapBalancer::::insert(netuid, balancer); } } diff --git a/pallets/swap/src/pallet/swap_step.rs b/pallets/swap/src/pallet/swap_step.rs index c9f0ae92a8..ddb6a7bccc 100644 --- a/pallets/swap/src/pallet/swap_step.rs +++ b/pallets/swap/src/pallet/swap_step.rs @@ -137,8 +137,8 @@ impl SwapStep { fn delta_in(netuid: NetUid, price_curr: U64F64, price_target: U64F64) -> TaoCurrency { let tao_reserve = T::TaoReserve::reserve(netuid.into()); - let reserve_weight = SwapBalancer::::get(netuid); - TaoCurrency::from(reserve_weight.calculate_quote_delta_in( + let balancer = SwapBalancer::::get(netuid); + TaoCurrency::from(balancer.calculate_quote_delta_in( price_curr, price_target, tao_reserve.into(), @@ -148,10 +148,10 @@ impl SwapStep fn price_target(netuid: NetUid, delta_in: TaoCurrency) -> U64F64 { let tao_reserve = T::TaoReserve::reserve(netuid.into()); let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); - let reserve_weight = SwapBalancer::::get(netuid); + let balancer = SwapBalancer::::get(netuid); let dy = delta_in; let dx = Self::convert_deltas(netuid, dy); - reserve_weight.calculate_price( + balancer.calculate_price( u64::from(alpha_reserve.saturating_sub(dx)), u64::from(tao_reserve.saturating_add(dy)), ) @@ -168,8 +168,8 @@ impl SwapStep fn convert_deltas(netuid: NetUid, delta_in: TaoCurrency) -> AlphaCurrency { let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); let tao_reserve = T::TaoReserve::reserve(netuid.into()); - let reserve_weight = SwapBalancer::::get(netuid); - let e = reserve_weight.exp_quote_base(tao_reserve.into(), delta_in.into()); + let balancer = SwapBalancer::::get(netuid); + let e = balancer.exp_quote_base(tao_reserve.into(), delta_in.into()); let one = U64F64::from_num(1); let alpha_reserve_fixed = U64F64::from_num(alpha_reserve); AlphaCurrency::from( @@ -185,8 +185,8 @@ impl SwapStep { fn delta_in(netuid: NetUid, price_curr: U64F64, price_target: U64F64) -> AlphaCurrency { let alpha_reserve = T::AlphaReserve::reserve(netuid); - let reserve_weight = SwapBalancer::::get(netuid); - AlphaCurrency::from(reserve_weight.calculate_base_delta_in( + let balancer = SwapBalancer::::get(netuid); + AlphaCurrency::from(balancer.calculate_base_delta_in( price_curr, price_target, alpha_reserve.into(), @@ -196,10 +196,10 @@ impl SwapStep fn price_target(netuid: NetUid, delta_in: AlphaCurrency) -> U64F64 { let tao_reserve = T::TaoReserve::reserve(netuid.into()); let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); - let reserve_weight = SwapBalancer::::get(netuid); + let balancer = SwapBalancer::::get(netuid); let dx = delta_in; let dy = Self::convert_deltas(netuid, dx); - reserve_weight.calculate_price( + balancer.calculate_price( u64::from(alpha_reserve.saturating_add(dx)), u64::from(tao_reserve.saturating_sub(dy)), ) @@ -216,8 +216,8 @@ impl SwapStep fn convert_deltas(netuid: NetUid, delta_in: AlphaCurrency) -> TaoCurrency { let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); let tao_reserve = T::TaoReserve::reserve(netuid.into()); - let reserve_weight = SwapBalancer::::get(netuid); - let e = reserve_weight.exp_base_quote(alpha_reserve.into(), delta_in.into()); + let balancer = SwapBalancer::::get(netuid); + let e = balancer.exp_base_quote(alpha_reserve.into(), delta_in.into()); let one = U64F64::from_num(1); let tao_reserve_fixed = U64F64::from_num(u64::from(tao_reserve)); TaoCurrency::from( From 1540d30d40966b4da8b0d851acf3e52cb3ca24b8 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Tue, 30 Dec 2025 14:06:53 -0500 Subject: [PATCH 096/240] Disable add_liquidity tests, fix test_swap_initialization --- pallets/subtensor/src/staking/move_stake.rs | 2 +- pallets/subtensor/src/tests/staking.rs | 7 +- pallets/swap/src/pallet/impls.rs | 11 +- pallets/swap/src/pallet/mod.rs | 2 +- pallets/swap/src/pallet/tests.rs | 1064 +++++++++---------- 5 files changed, 540 insertions(+), 546 deletions(-) diff --git a/pallets/subtensor/src/staking/move_stake.rs b/pallets/subtensor/src/staking/move_stake.rs index 020981158f..7071e4d55d 100644 --- a/pallets/subtensor/src/staking/move_stake.rs +++ b/pallets/subtensor/src/staking/move_stake.rs @@ -418,7 +418,7 @@ impl Pallet { /// /// In the corner case when SubnetTAO(2) == SubnetTAO(1), no slippage is going to occur. /// - /// TODO: This formula only works for a single swap step, so it is not 100% correct for swap v3 or balancers. + /// TODO: This formula only works for a single swap step, so it is not 100% correct for swap v3 or balancers. /// We need an updated one. /// pub fn get_max_amount_move( diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index e995c8a095..52f761dbaf 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -3690,7 +3690,8 @@ fn test_max_amount_move_dynamic_dynamic() { origin_netuid.into() ) / ::SwapInterface::current_alpha_price( destination_netuid.into() - )).to_num::(), + )) + .to_num::(), expected_price, epsilon = 0.000_000_001 ); @@ -4561,7 +4562,9 @@ fn test_stake_into_subnet_low_amount() { // Check if stake has increased assert_abs_diff_eq!( - u64::from(SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid)) as f64, + u64::from(SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid + )) as f64, expected_stake, epsilon = expected_stake / 100. ); diff --git a/pallets/swap/src/pallet/impls.rs b/pallets/swap/src/pallet/impls.rs index cc93fa9114..54c3c1c5f6 100644 --- a/pallets/swap/src/pallet/impls.rs +++ b/pallets/swap/src/pallet/impls.rs @@ -22,9 +22,7 @@ use subtensor_swap_interface::{ use super::pallet::*; use super::swap_step::{BasicSwapStep, SwapStep}; -use crate::{ - pallet::Balancer, pallet::balancer::BalancerError, position::PositionId, -}; +use crate::{pallet::Balancer, pallet::balancer::BalancerError, position::PositionId}; #[derive(Debug, PartialEq)] pub struct UpdateLiquidityResult { @@ -68,11 +66,10 @@ impl Pallet { } // Insert 0.5 into SwapBalancer - let reserve_weight = Balancer::new(Perquintill::from_rational(1_u64, 2_u64)).map_err( - |err| match err { + let reserve_weight = + Balancer::new(Perquintill::from_rational(1_u64, 2_u64)).map_err(|err| match err { BalancerError::InvalidValue => Error::::ReservesOutOfBalance, - }, - )?; + })?; SwapBalancer::::insert(netuid, reserve_weight); // TODO: Review when/if we have user liquidity diff --git a/pallets/swap/src/pallet/mod.rs b/pallets/swap/src/pallet/mod.rs index 978b26b53b..1ee73e0448 100644 --- a/pallets/swap/src/pallet/mod.rs +++ b/pallets/swap/src/pallet/mod.rs @@ -16,8 +16,8 @@ use crate::{ pub use pallet::*; -mod impls; mod balancer; +mod impls; mod swap_step; #[cfg(test)] mod tests; diff --git a/pallets/swap/src/pallet/tests.rs b/pallets/swap/src/pallet/tests.rs index c451701d3b..4360533cd0 100644 --- a/pallets/swap/src/pallet/tests.rs +++ b/pallets/swap/src/pallet/tests.rs @@ -416,15 +416,17 @@ fn test_swap_initialization() { let netuid = NetUid::from(1); // Get reserves from the mock provider - // let tao = TaoReserve::reserve(netuid.into()); - // let alpha = AlphaReserve::reserve(netuid.into()); + let tao = TaoCurrency::from(1_000_000_000u64); + let alpha = AlphaCurrency::from(4_000_000_000u64); + TaoReserve::set_mock_reserve(netuid, tao); + AlphaReserve::set_mock_reserve(netuid, alpha); assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); assert!(PalSwapInitialized::::get(netuid)); // Verify current price is set let price = Pallet::::current_price(netuid); - let expected_price = U64F64::from_num(0.5_f64); + let expected_price = U64F64::from_num(0.25_f64); assert_abs_diff_eq!( price.to_num::(), expected_price.to_num::(), @@ -438,6 +440,7 @@ fn test_swap_initialization() { Perquintill::from_rational(1_u64, 2_u64), ); + // TODO: Revise when user liquidity is available // // Calculate expected liquidity // let expected_liquidity = // helpers_128bit::sqrt((tao.to_u64() as u128).saturating_mul(alpha.to_u64() as u128)) @@ -455,558 +458,549 @@ fn test_swap_initialization() { // assert_eq!(position.liquidity, expected_liquidity); // assert_eq!(position.fees_tao, 0); // assert_eq!(position.fees_alpha, 0); - - todo!(); }); } +// TODO: Revise when user liquidity is available // Test adding liquidity on top of the existing protocol liquidity -#[test] -fn test_add_liquidity_basic() { - todo!(); - - // new_test_ext().execute_with(|| { - // let min_price = tick_to_price(TickIndex::MIN); - // let max_price = tick_to_price(TickIndex::MAX); - // let max_tick = price_to_tick(max_price); - // assert_eq!(max_tick, TickIndex::MAX); - - // assert_ok!(Pallet::::maybe_initialize_palswap(NetUid::from(1))); - // let current_price = Pallet::::current_price(NetUid::from(1)).to_num::(); - // let (current_price_low, current_price_high) = get_ticked_prices_around_current_price(); - - // // As a user add liquidity with all possible corner cases - // // - Initial price is 0.25 - // // - liquidity is expressed in RAO units - // // Test case is (price_low, price_high, liquidity, tao, alpha) - // [ - // // Repeat the protocol liquidity at maximum range: Expect all the same values - // ( - // min_price, - // max_price, - // 2_000_000_000_u64, - // 1_000_000_000_u64, - // 4_000_000_000_u64, - // ), - // // Repeat the protocol liquidity at current to max range: Expect the same alpha - // ( - // current_price_high, - // max_price, - // 2_000_000_000_u64, - // 0, - // 4_000_000_000, - // ), - // // Repeat the protocol liquidity at min to current range: Expect all the same tao - // ( - // min_price, - // current_price_low, - // 2_000_000_000_u64, - // 1_000_000_000, - // 0, - // ), - // // Half to double price - just some sane wothdraw amounts - // (0.125, 0.5, 2_000_000_000_u64, 293_000_000, 1_171_000_000), - // // Both below price - tao is non-zero, alpha is zero - // (0.12, 0.13, 2_000_000_000_u64, 28_270_000, 0), - // // Both above price - tao is zero, alpha is non-zero - // (0.3, 0.4, 2_000_000_000_u64, 0, 489_200_000), - // ] - // .into_iter() - // .enumerate() - // .map(|(n, v)| (NetUid::from(n as u16 + 1), v.0, v.1, v.2, v.3, v.4)) - // .for_each( - // |(netuid, price_low, price_high, liquidity, expected_tao, expected_alpha)| { - // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - - // // Calculate ticks (assuming tick math is tested separately) - // let tick_low = price_to_tick(price_low); - // let tick_high = price_to_tick(price_high); - - // // Get tick infos and liquidity before adding (to account for protocol liquidity) - // let tick_low_info_before = Ticks::::get(netuid, tick_low).unwrap_or_default(); - // let tick_high_info_before = - // Ticks::::get(netuid, tick_high).unwrap_or_default(); - // let liquidity_before = CurrentLiquidity::::get(netuid); - - // // Add liquidity - // let (position_id, tao, alpha) = Pallet::::do_add_liquidity( - // netuid, - // &OK_COLDKEY_ACCOUNT_ID, - // &OK_HOTKEY_ACCOUNT_ID, - // tick_low, - // tick_high, - // liquidity, - // ) - // .unwrap(); - - // assert_abs_diff_eq!(tao, expected_tao, epsilon = tao / 1000); - // assert_abs_diff_eq!(alpha, expected_alpha, epsilon = alpha / 1000); - - // // Check that low and high ticks appear in the state and are properly updated - // let tick_low_info = Ticks::::get(netuid, tick_low).unwrap(); - // let tick_high_info = Ticks::::get(netuid, tick_high).unwrap(); - // let expected_liquidity_net_low = liquidity as i128; - // let expected_liquidity_gross_low = liquidity; - // let expected_liquidity_net_high = -(liquidity as i128); - // let expected_liquidity_gross_high = liquidity; - - // assert_eq!( - // tick_low_info.liquidity_net - tick_low_info_before.liquidity_net, - // expected_liquidity_net_low, - // ); - // assert_eq!( - // tick_low_info.liquidity_gross - tick_low_info_before.liquidity_gross, - // expected_liquidity_gross_low, - // ); - // assert_eq!( - // tick_high_info.liquidity_net - tick_high_info_before.liquidity_net, - // expected_liquidity_net_high, - // ); - // assert_eq!( - // tick_high_info.liquidity_gross - tick_high_info_before.liquidity_gross, - // expected_liquidity_gross_high, - // ); - - // // Liquidity position at correct ticks - // assert_eq!( - // Pallet::::count_positions(netuid, &OK_COLDKEY_ACCOUNT_ID), - // 1 - // ); - - // let position = - // Positions::::get((netuid, OK_COLDKEY_ACCOUNT_ID, position_id)).unwrap(); - // assert_eq!(position.liquidity, liquidity); - // assert_eq!(position.tick_low, tick_low); - // assert_eq!(position.tick_high, tick_high); - // assert_eq!(position.fees_alpha, 0); - // assert_eq!(position.fees_tao, 0); - - // // Current liquidity is updated only when price range includes the current price - // let expected_liquidity = - // if (price_high > current_price) && (price_low <= current_price) { - // liquidity_before + liquidity - // } else { - // liquidity_before - // }; - - // assert_eq!(CurrentLiquidity::::get(netuid), expected_liquidity) - // }, - // ); - // }); -} +// #[test] +// fn test_add_liquidity_basic() { +// new_test_ext().execute_with(|| { +// let min_price = tick_to_price(TickIndex::MIN); +// let max_price = tick_to_price(TickIndex::MAX); +// let max_tick = price_to_tick(max_price); +// assert_eq!(max_tick, TickIndex::MAX); + +// assert_ok!(Pallet::::maybe_initialize_palswap(NetUid::from(1))); +// let current_price = Pallet::::current_price(NetUid::from(1)).to_num::(); +// let (current_price_low, current_price_high) = get_ticked_prices_around_current_price(); + +// // As a user add liquidity with all possible corner cases +// // - Initial price is 0.25 +// // - liquidity is expressed in RAO units +// // Test case is (price_low, price_high, liquidity, tao, alpha) +// [ +// // Repeat the protocol liquidity at maximum range: Expect all the same values +// ( +// min_price, +// max_price, +// 2_000_000_000_u64, +// 1_000_000_000_u64, +// 4_000_000_000_u64, +// ), +// // Repeat the protocol liquidity at current to max range: Expect the same alpha +// ( +// current_price_high, +// max_price, +// 2_000_000_000_u64, +// 0, +// 4_000_000_000, +// ), +// // Repeat the protocol liquidity at min to current range: Expect all the same tao +// ( +// min_price, +// current_price_low, +// 2_000_000_000_u64, +// 1_000_000_000, +// 0, +// ), +// // Half to double price - just some sane wothdraw amounts +// (0.125, 0.5, 2_000_000_000_u64, 293_000_000, 1_171_000_000), +// // Both below price - tao is non-zero, alpha is zero +// (0.12, 0.13, 2_000_000_000_u64, 28_270_000, 0), +// // Both above price - tao is zero, alpha is non-zero +// (0.3, 0.4, 2_000_000_000_u64, 0, 489_200_000), +// ] +// .into_iter() +// .enumerate() +// .map(|(n, v)| (NetUid::from(n as u16 + 1), v.0, v.1, v.2, v.3, v.4)) +// .for_each( +// |(netuid, price_low, price_high, liquidity, expected_tao, expected_alpha)| { +// assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); -#[test] -fn test_add_liquidity_max_limit_enforced() { - todo!(); - - // new_test_ext().execute_with(|| { - // let netuid = NetUid::from(1); - // let liquidity = 2_000_000_000_u64; - // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - - // let limit = MaxPositions::get() as usize; - - // for _ in 0..limit { - // Pallet::::do_add_liquidity( - // netuid, - // &OK_COLDKEY_ACCOUNT_ID, - // &OK_HOTKEY_ACCOUNT_ID, - // TickIndex::MIN, - // TickIndex::MAX, - // liquidity, - // ) - // .unwrap(); - // } - - // let test_result = Pallet::::do_add_liquidity( - // netuid, - // &OK_COLDKEY_ACCOUNT_ID, - // &OK_HOTKEY_ACCOUNT_ID, - // TickIndex::MIN, - // TickIndex::MAX, - // liquidity, - // ); - - // assert_err!(test_result, Error::::MaxPositionsExceeded); - // }); -} +// // Calculate ticks (assuming tick math is tested separately) +// let tick_low = price_to_tick(price_low); +// let tick_high = price_to_tick(price_high); -#[test] -fn test_add_liquidity_out_of_bounds() { - todo!(); - - // new_test_ext().execute_with(|| { - // [ - // // For our tests, we'll construct TickIndex values that are intentionally - // // outside the valid range for testing purposes only - // ( - // TickIndex::new_unchecked(TickIndex::MIN.get() - 1), - // TickIndex::MAX, - // 1_000_000_000_u64, - // ), - // ( - // TickIndex::MIN, - // TickIndex::new_unchecked(TickIndex::MAX.get() + 1), - // 1_000_000_000_u64, - // ), - // ( - // TickIndex::new_unchecked(TickIndex::MIN.get() - 1), - // TickIndex::new_unchecked(TickIndex::MAX.get() + 1), - // 1_000_000_000_u64, - // ), - // ( - // TickIndex::new_unchecked(TickIndex::MIN.get() - 100), - // TickIndex::new_unchecked(TickIndex::MAX.get() + 100), - // 1_000_000_000_u64, - // ), - // // Inverted ticks: high < low - // ( - // TickIndex::new_unchecked(-900), - // TickIndex::new_unchecked(-1000), - // 1_000_000_000_u64, - // ), - // // Equal ticks: high == low - // ( - // TickIndex::new_unchecked(-10_000), - // TickIndex::new_unchecked(-10_000), - // 1_000_000_000_u64, - // ), - // ] - // .into_iter() - // .enumerate() - // .map(|(n, v)| (NetUid::from(n as u16 + 1), v.0, v.1, v.2)) - // .for_each(|(netuid, tick_low, tick_high, liquidity)| { - // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - - // // Add liquidity - // assert_err!( - // Swap::do_add_liquidity( - // netuid, - // &OK_COLDKEY_ACCOUNT_ID, - // &OK_HOTKEY_ACCOUNT_ID, - // tick_low, - // tick_high, - // liquidity - // ), - // Error::::InvalidTickRange, - // ); - // }); - // }); -} +// // Get tick infos and liquidity before adding (to account for protocol liquidity) +// let tick_low_info_before = Ticks::::get(netuid, tick_low).unwrap_or_default(); +// let tick_high_info_before = +// Ticks::::get(netuid, tick_high).unwrap_or_default(); +// let liquidity_before = CurrentLiquidity::::get(netuid); -#[test] -fn test_add_liquidity_over_balance() { - todo!(); - - // new_test_ext().execute_with(|| { - // let coldkey_account_id = 3; - // let hotkey_account_id = 1002; - - // [ - // // Lower than price (not enough tao) - // (0.1, 0.2, 100_000_000_000_u64), - // // Higher than price (not enough alpha) - // (0.3, 0.4, 100_000_000_000_u64), - // // Around the price (not enough both) - // (0.1, 0.4, 100_000_000_000_u64), - // ] - // .into_iter() - // .enumerate() - // .map(|(n, v)| (NetUid::from(n as u16 + 1), v.0, v.1, v.2)) - // .for_each(|(netuid, price_low, price_high, liquidity)| { - // // Calculate ticks - // let tick_low = price_to_tick(price_low); - // let tick_high = price_to_tick(price_high); - - // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - - // // Add liquidity - // assert_err!( - // Pallet::::do_add_liquidity( - // netuid, - // &coldkey_account_id, - // &hotkey_account_id, - // tick_low, - // tick_high, - // liquidity - // ), - // Error::::InsufficientBalance, - // ); - // }); - // }); -} +// // Add liquidity +// let (position_id, tao, alpha) = Pallet::::do_add_liquidity( +// netuid, +// &OK_COLDKEY_ACCOUNT_ID, +// &OK_HOTKEY_ACCOUNT_ID, +// tick_low, +// tick_high, +// liquidity, +// ) +// .unwrap(); + +// assert_abs_diff_eq!(tao, expected_tao, epsilon = tao / 1000); +// assert_abs_diff_eq!(alpha, expected_alpha, epsilon = alpha / 1000); + +// // Check that low and high ticks appear in the state and are properly updated +// let tick_low_info = Ticks::::get(netuid, tick_low).unwrap(); +// let tick_high_info = Ticks::::get(netuid, tick_high).unwrap(); +// let expected_liquidity_net_low = liquidity as i128; +// let expected_liquidity_gross_low = liquidity; +// let expected_liquidity_net_high = -(liquidity as i128); +// let expected_liquidity_gross_high = liquidity; + +// assert_eq!( +// tick_low_info.liquidity_net - tick_low_info_before.liquidity_net, +// expected_liquidity_net_low, +// ); +// assert_eq!( +// tick_low_info.liquidity_gross - tick_low_info_before.liquidity_gross, +// expected_liquidity_gross_low, +// ); +// assert_eq!( +// tick_high_info.liquidity_net - tick_high_info_before.liquidity_net, +// expected_liquidity_net_high, +// ); +// assert_eq!( +// tick_high_info.liquidity_gross - tick_high_info_before.liquidity_gross, +// expected_liquidity_gross_high, +// ); + +// // Liquidity position at correct ticks +// assert_eq!( +// Pallet::::count_positions(netuid, &OK_COLDKEY_ACCOUNT_ID), +// 1 +// ); + +// let position = +// Positions::::get((netuid, OK_COLDKEY_ACCOUNT_ID, position_id)).unwrap(); +// assert_eq!(position.liquidity, liquidity); +// assert_eq!(position.tick_low, tick_low); +// assert_eq!(position.tick_high, tick_high); +// assert_eq!(position.fees_alpha, 0); +// assert_eq!(position.fees_tao, 0); + +// // Current liquidity is updated only when price range includes the current price +// let expected_liquidity = +// if (price_high > current_price) && (price_low <= current_price) { +// liquidity_before + liquidity +// } else { +// liquidity_before +// }; + +// assert_eq!(CurrentLiquidity::::get(netuid), expected_liquidity) +// }, +// ); +// }); +// } + +// TODO: Revise when user liquidity is available +// #[test] +// fn test_add_liquidity_max_limit_enforced() { +// new_test_ext().execute_with(|| { +// let netuid = NetUid::from(1); +// let liquidity = 2_000_000_000_u64; +// assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + +// let limit = MaxPositions::get() as usize; + +// for _ in 0..limit { +// Pallet::::do_add_liquidity( +// netuid, +// &OK_COLDKEY_ACCOUNT_ID, +// &OK_HOTKEY_ACCOUNT_ID, +// TickIndex::MIN, +// TickIndex::MAX, +// liquidity, +// ) +// .unwrap(); +// } +// let test_result = Pallet::::do_add_liquidity( +// netuid, +// &OK_COLDKEY_ACCOUNT_ID, +// &OK_HOTKEY_ACCOUNT_ID, +// TickIndex::MIN, +// TickIndex::MAX, +// liquidity, +// ); + +// assert_err!(test_result, Error::::MaxPositionsExceeded); +// }); +// } + +// TODO: Revise when user liquidity is available +// #[test] +// fn test_add_liquidity_out_of_bounds() { +// new_test_ext().execute_with(|| { +// [ +// // For our tests, we'll construct TickIndex values that are intentionally +// // outside the valid range for testing purposes only +// ( +// TickIndex::new_unchecked(TickIndex::MIN.get() - 1), +// TickIndex::MAX, +// 1_000_000_000_u64, +// ), +// ( +// TickIndex::MIN, +// TickIndex::new_unchecked(TickIndex::MAX.get() + 1), +// 1_000_000_000_u64, +// ), +// ( +// TickIndex::new_unchecked(TickIndex::MIN.get() - 1), +// TickIndex::new_unchecked(TickIndex::MAX.get() + 1), +// 1_000_000_000_u64, +// ), +// ( +// TickIndex::new_unchecked(TickIndex::MIN.get() - 100), +// TickIndex::new_unchecked(TickIndex::MAX.get() + 100), +// 1_000_000_000_u64, +// ), +// // Inverted ticks: high < low +// ( +// TickIndex::new_unchecked(-900), +// TickIndex::new_unchecked(-1000), +// 1_000_000_000_u64, +// ), +// // Equal ticks: high == low +// ( +// TickIndex::new_unchecked(-10_000), +// TickIndex::new_unchecked(-10_000), +// 1_000_000_000_u64, +// ), +// ] +// .into_iter() +// .enumerate() +// .map(|(n, v)| (NetUid::from(n as u16 + 1), v.0, v.1, v.2)) +// .for_each(|(netuid, tick_low, tick_high, liquidity)| { +// assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + +// // Add liquidity +// assert_err!( +// Swap::do_add_liquidity( +// netuid, +// &OK_COLDKEY_ACCOUNT_ID, +// &OK_HOTKEY_ACCOUNT_ID, +// tick_low, +// tick_high, +// liquidity +// ), +// Error::::InvalidTickRange, +// ); +// }); +// }); +// } + +// TODO: Revise when user liquidity is available +// #[test] +// fn test_add_liquidity_over_balance() { +// new_test_ext().execute_with(|| { +// let coldkey_account_id = 3; +// let hotkey_account_id = 1002; + +// [ +// // Lower than price (not enough tao) +// (0.1, 0.2, 100_000_000_000_u64), +// // Higher than price (not enough alpha) +// (0.3, 0.4, 100_000_000_000_u64), +// // Around the price (not enough both) +// (0.1, 0.4, 100_000_000_000_u64), +// ] +// .into_iter() +// .enumerate() +// .map(|(n, v)| (NetUid::from(n as u16 + 1), v.0, v.1, v.2)) +// .for_each(|(netuid, price_low, price_high, liquidity)| { +// // Calculate ticks +// let tick_low = price_to_tick(price_low); +// let tick_high = price_to_tick(price_high); + +// assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + +// // Add liquidity +// assert_err!( +// Pallet::::do_add_liquidity( +// netuid, +// &coldkey_account_id, +// &hotkey_account_id, +// tick_low, +// tick_high, +// liquidity +// ), +// Error::::InsufficientBalance, +// ); +// }); +// }); +// } + +// TODO: Revise when user liquidity is available // cargo test --package pallet-subtensor-swap --lib -- pallet::impls::tests::test_remove_liquidity_basic --exact --show-output -#[test] -fn test_remove_liquidity_basic() { - todo!(); - - // new_test_ext().execute_with(|| { - // let min_price = tick_to_price(TickIndex::MIN); - // let max_price = tick_to_price(TickIndex::MAX); - // let max_tick = price_to_tick(max_price); - // assert_eq!(max_tick, TickIndex::MAX); - - // let (current_price_low, current_price_high) = get_ticked_prices_around_current_price(); - - // // As a user add liquidity with all possible corner cases - // // - Initial price is 0.25 - // // - liquidity is expressed in RAO units - // // Test case is (price_low, price_high, liquidity, tao, alpha) - // [ - // // Repeat the protocol liquidity at maximum range: Expect all the same values - // ( - // min_price, - // max_price, - // 2_000_000_000_u64, - // 1_000_000_000_u64, - // 4_000_000_000_u64, - // ), - // // Repeat the protocol liquidity at current to max range: Expect the same alpha - // ( - // current_price_high, - // max_price, - // 2_000_000_000_u64, - // 0, - // 4_000_000_000, - // ), - // // Repeat the protocol liquidity at min to current range: Expect all the same tao - // ( - // min_price, - // current_price_low, - // 2_000_000_000_u64, - // 1_000_000_000, - // 0, - // ), - // // Half to double price - just some sane wothdraw amounts - // (0.125, 0.5, 2_000_000_000_u64, 293_000_000, 1_171_000_000), - // // Both below price - tao is non-zero, alpha is zero - // (0.12, 0.13, 2_000_000_000_u64, 28_270_000, 0), - // // Both above price - tao is zero, alpha is non-zero - // (0.3, 0.4, 2_000_000_000_u64, 0, 489_200_000), - // ] - // .into_iter() - // .enumerate() - // .map(|(n, v)| (NetUid::from(n as u16 + 1), v.0, v.1, v.2, v.3, v.4)) - // .for_each(|(netuid, price_low, price_high, liquidity, tao, alpha)| { - // // Calculate ticks (assuming tick math is tested separately) - // let tick_low = price_to_tick(price_low); - // let tick_high = price_to_tick(price_high); - - // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - // let liquidity_before = CurrentLiquidity::::get(netuid); - - // // Add liquidity - // let (position_id, _, _) = Pallet::::do_add_liquidity( - // netuid, - // &OK_COLDKEY_ACCOUNT_ID, - // &OK_HOTKEY_ACCOUNT_ID, - // tick_low, - // tick_high, - // liquidity, - // ) - // .unwrap(); - - // // Remove liquidity - // let remove_result = - // Pallet::::do_remove_liquidity(netuid, &OK_COLDKEY_ACCOUNT_ID, position_id) - // .unwrap(); - // assert_abs_diff_eq!(remove_result.tao.to_u64(), tao, epsilon = tao / 1000); - // assert_abs_diff_eq!( - // u64::from(remove_result.alpha), - // alpha, - // epsilon = alpha / 1000 - // ); - // assert_eq!(remove_result.fee_tao, TaoCurrency::ZERO); - // assert_eq!(remove_result.fee_alpha, AlphaCurrency::ZERO); +// #[test] +// fn test_remove_liquidity_basic() { +// new_test_ext().execute_with(|| { +// let min_price = tick_to_price(TickIndex::MIN); +// let max_price = tick_to_price(TickIndex::MAX); +// let max_tick = price_to_tick(max_price); +// assert_eq!(max_tick, TickIndex::MAX); + +// let (current_price_low, current_price_high) = get_ticked_prices_around_current_price(); + +// // As a user add liquidity with all possible corner cases +// // - Initial price is 0.25 +// // - liquidity is expressed in RAO units +// // Test case is (price_low, price_high, liquidity, tao, alpha) +// [ +// // Repeat the protocol liquidity at maximum range: Expect all the same values +// ( +// min_price, +// max_price, +// 2_000_000_000_u64, +// 1_000_000_000_u64, +// 4_000_000_000_u64, +// ), +// // Repeat the protocol liquidity at current to max range: Expect the same alpha +// ( +// current_price_high, +// max_price, +// 2_000_000_000_u64, +// 0, +// 4_000_000_000, +// ), +// // Repeat the protocol liquidity at min to current range: Expect all the same tao +// ( +// min_price, +// current_price_low, +// 2_000_000_000_u64, +// 1_000_000_000, +// 0, +// ), +// // Half to double price - just some sane wothdraw amounts +// (0.125, 0.5, 2_000_000_000_u64, 293_000_000, 1_171_000_000), +// // Both below price - tao is non-zero, alpha is zero +// (0.12, 0.13, 2_000_000_000_u64, 28_270_000, 0), +// // Both above price - tao is zero, alpha is non-zero +// (0.3, 0.4, 2_000_000_000_u64, 0, 489_200_000), +// ] +// .into_iter() +// .enumerate() +// .map(|(n, v)| (NetUid::from(n as u16 + 1), v.0, v.1, v.2, v.3, v.4)) +// .for_each(|(netuid, price_low, price_high, liquidity, tao, alpha)| { +// // Calculate ticks (assuming tick math is tested separately) +// let tick_low = price_to_tick(price_low); +// let tick_high = price_to_tick(price_high); + +// assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); +// let liquidity_before = CurrentLiquidity::::get(netuid); + +// // Add liquidity +// let (position_id, _, _) = Pallet::::do_add_liquidity( +// netuid, +// &OK_COLDKEY_ACCOUNT_ID, +// &OK_HOTKEY_ACCOUNT_ID, +// tick_low, +// tick_high, +// liquidity, +// ) +// .unwrap(); + +// // Remove liquidity +// let remove_result = +// Pallet::::do_remove_liquidity(netuid, &OK_COLDKEY_ACCOUNT_ID, position_id) +// .unwrap(); +// assert_abs_diff_eq!(remove_result.tao.to_u64(), tao, epsilon = tao / 1000); +// assert_abs_diff_eq!( +// u64::from(remove_result.alpha), +// alpha, +// epsilon = alpha / 1000 +// ); +// assert_eq!(remove_result.fee_tao, TaoCurrency::ZERO); +// assert_eq!(remove_result.fee_alpha, AlphaCurrency::ZERO); - // // Liquidity position is removed - // assert_eq!( - // Pallet::::count_positions(netuid, &OK_COLDKEY_ACCOUNT_ID), - // 0 - // ); - // assert!(Positions::::get((netuid, OK_COLDKEY_ACCOUNT_ID, position_id)).is_none()); +// // Liquidity position is removed +// assert_eq!( +// Pallet::::count_positions(netuid, &OK_COLDKEY_ACCOUNT_ID), +// 0 +// ); +// assert!(Positions::::get((netuid, OK_COLDKEY_ACCOUNT_ID, position_id)).is_none()); - // // Current liquidity is updated (back where it was) - // assert_eq!(CurrentLiquidity::::get(netuid), liquidity_before); - // }); - // }); -} +// // Current liquidity is updated (back where it was) +// assert_eq!(CurrentLiquidity::::get(netuid), liquidity_before); +// }); +// }); +// } -#[test] -fn test_remove_liquidity_nonexisting_position() { - todo!(); - - // new_test_ext().execute_with(|| { - // let min_price = tick_to_price(TickIndex::MIN); - // let max_price = tick_to_price(TickIndex::MAX); - // let max_tick = price_to_tick(max_price); - // assert_eq!(max_tick.get(), TickIndex::MAX.get()); - - // let liquidity = 2_000_000_000_u64; - // let netuid = NetUid::from(1); - - // // Calculate ticks (assuming tick math is tested separately) - // let tick_low = price_to_tick(min_price); - // let tick_high = price_to_tick(max_price); - - // // Setup swap - // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - - // // Add liquidity - // assert_ok!(Pallet::::do_add_liquidity( - // netuid, - // &OK_COLDKEY_ACCOUNT_ID, - // &OK_HOTKEY_ACCOUNT_ID, - // tick_low, - // tick_high, - // liquidity, - // )); - - // assert!(Pallet::::count_positions(netuid, &OK_COLDKEY_ACCOUNT_ID) > 0); - - // // Remove liquidity - // assert_err!( - // Pallet::::do_remove_liquidity( - // netuid, - // &OK_COLDKEY_ACCOUNT_ID, - // PositionId::new::() - // ), - // Error::::LiquidityNotFound, - // ); - // }); -} +// TODO: Revise when user liquidity is available +// #[test] +// fn test_remove_liquidity_nonexisting_position() { +// new_test_ext().execute_with(|| { +// let min_price = tick_to_price(TickIndex::MIN); +// let max_price = tick_to_price(TickIndex::MAX); +// let max_tick = price_to_tick(max_price); +// assert_eq!(max_tick.get(), TickIndex::MAX.get()); + +// let liquidity = 2_000_000_000_u64; +// let netuid = NetUid::from(1); + +// // Calculate ticks (assuming tick math is tested separately) +// let tick_low = price_to_tick(min_price); +// let tick_high = price_to_tick(max_price); + +// // Setup swap +// assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + +// // Add liquidity +// assert_ok!(Pallet::::do_add_liquidity( +// netuid, +// &OK_COLDKEY_ACCOUNT_ID, +// &OK_HOTKEY_ACCOUNT_ID, +// tick_low, +// tick_high, +// liquidity, +// )); + +// assert!(Pallet::::count_positions(netuid, &OK_COLDKEY_ACCOUNT_ID) > 0); + +// // Remove liquidity +// assert_err!( +// Pallet::::do_remove_liquidity( +// netuid, +// &OK_COLDKEY_ACCOUNT_ID, +// PositionId::new::() +// ), +// Error::::LiquidityNotFound, +// ); +// }); +// } +// TODO: Revise when user liquidity is available // cargo test --package pallet-subtensor-swap --lib -- pallet::tests::test_modify_position_basic --exact --show-output -#[test] -fn test_modify_position_basic() { - todo!(); - - // new_test_ext().execute_with(|| { - // let max_price = tick_to_price(TickIndex::MAX); - // let max_tick = price_to_tick(max_price); - // let limit_price = 1000.0_f64; - // assert_eq!(max_tick, TickIndex::MAX); - // let (current_price_low, _current_price_high) = get_ticked_prices_around_current_price(); - - // // As a user add liquidity with all possible corner cases - // // - Initial price is 0.25 - // // - liquidity is expressed in RAO units - // // Test case is (price_low, price_high, liquidity, tao, alpha) - // [ - // // Repeat the protocol liquidity at current to max range: Expect the same alpha - // ( - // current_price_low, - // max_price, - // 2_000_000_000_u64, - // 4_000_000_000, - // ), - // ] - // .into_iter() - // .enumerate() - // .map(|(n, v)| (NetUid::from(n as u16 + 1), v.0, v.1, v.2, v.3)) - // .for_each(|(netuid, price_low, price_high, liquidity, alpha)| { - // // Calculate ticks (assuming tick math is tested separately) - // let tick_low = price_to_tick(price_low); - // let tick_high = price_to_tick(price_high); - - // assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - - // // Add liquidity - // let (position_id, _, _) = Pallet::::do_add_liquidity( - // netuid, - // &OK_COLDKEY_ACCOUNT_ID, - // &OK_HOTKEY_ACCOUNT_ID, - // tick_low, - // tick_high, - // liquidity, - // ) - // .unwrap(); - - // // Get tick infos before the swap/update - // let tick_low_info_before = Ticks::::get(netuid, tick_low).unwrap(); - // let tick_high_info_before = Ticks::::get(netuid, tick_high).unwrap(); - - // // Swap to create fees on the position - // let sqrt_limit_price = SqrtPrice::from_num((limit_price).sqrt()); - // let order = GetAlphaForTao::with_amount(liquidity / 10); - // Pallet::::do_swap(netuid, order, sqrt_limit_price, false, false).unwrap(); - - // // Modify liquidity (also causes claiming of fees) - // let liquidity_before = CurrentLiquidity::::get(netuid); - // let modify_result = Pallet::::do_modify_position( - // netuid, - // &OK_COLDKEY_ACCOUNT_ID, - // &OK_HOTKEY_ACCOUNT_ID, - // position_id, - // -((liquidity / 10) as i64), - // ) - // .unwrap(); - // assert_abs_diff_eq!( - // u64::from(modify_result.alpha), - // alpha / 10, - // epsilon = alpha / 1000 - // ); - // assert!(modify_result.fee_tao > TaoCurrency::ZERO); - // assert_eq!(modify_result.fee_alpha, AlphaCurrency::ZERO); +// #[test] +// fn test_modify_position_basic() { +// new_test_ext().execute_with(|| { +// let max_price = tick_to_price(TickIndex::MAX); +// let max_tick = price_to_tick(max_price); +// let limit_price = 1000.0_f64; +// assert_eq!(max_tick, TickIndex::MAX); +// let (current_price_low, _current_price_high) = get_ticked_prices_around_current_price(); + +// // As a user add liquidity with all possible corner cases +// // - Initial price is 0.25 +// // - liquidity is expressed in RAO units +// // Test case is (price_low, price_high, liquidity, tao, alpha) +// [ +// // Repeat the protocol liquidity at current to max range: Expect the same alpha +// ( +// current_price_low, +// max_price, +// 2_000_000_000_u64, +// 4_000_000_000, +// ), +// ] +// .into_iter() +// .enumerate() +// .map(|(n, v)| (NetUid::from(n as u16 + 1), v.0, v.1, v.2, v.3)) +// .for_each(|(netuid, price_low, price_high, liquidity, alpha)| { +// // Calculate ticks (assuming tick math is tested separately) +// let tick_low = price_to_tick(price_low); +// let tick_high = price_to_tick(price_high); + +// assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + +// // Add liquidity +// let (position_id, _, _) = Pallet::::do_add_liquidity( +// netuid, +// &OK_COLDKEY_ACCOUNT_ID, +// &OK_HOTKEY_ACCOUNT_ID, +// tick_low, +// tick_high, +// liquidity, +// ) +// .unwrap(); + +// // Get tick infos before the swap/update +// let tick_low_info_before = Ticks::::get(netuid, tick_low).unwrap(); +// let tick_high_info_before = Ticks::::get(netuid, tick_high).unwrap(); + +// // Swap to create fees on the position +// let sqrt_limit_price = SqrtPrice::from_num((limit_price).sqrt()); +// let order = GetAlphaForTao::with_amount(liquidity / 10); +// Pallet::::do_swap(netuid, order, sqrt_limit_price, false, false).unwrap(); + +// // Modify liquidity (also causes claiming of fees) +// let liquidity_before = CurrentLiquidity::::get(netuid); +// let modify_result = Pallet::::do_modify_position( +// netuid, +// &OK_COLDKEY_ACCOUNT_ID, +// &OK_HOTKEY_ACCOUNT_ID, +// position_id, +// -((liquidity / 10) as i64), +// ) +// .unwrap(); +// assert_abs_diff_eq!( +// u64::from(modify_result.alpha), +// alpha / 10, +// epsilon = alpha / 1000 +// ); +// assert!(modify_result.fee_tao > TaoCurrency::ZERO); +// assert_eq!(modify_result.fee_alpha, AlphaCurrency::ZERO); - // // Liquidity position is reduced - // assert_eq!( - // Pallet::::count_positions(netuid, &OK_COLDKEY_ACCOUNT_ID), - // 1 - // ); +// // Liquidity position is reduced +// assert_eq!( +// Pallet::::count_positions(netuid, &OK_COLDKEY_ACCOUNT_ID), +// 1 +// ); - // // Current liquidity is reduced with modify_position - // assert!(CurrentLiquidity::::get(netuid) < liquidity_before); +// // Current liquidity is reduced with modify_position +// assert!(CurrentLiquidity::::get(netuid) < liquidity_before); - // // Position liquidity is reduced - // let position = - // Positions::::get((netuid, OK_COLDKEY_ACCOUNT_ID, position_id)).unwrap(); - // assert_eq!(position.liquidity, liquidity * 9 / 10); - // assert_eq!(position.tick_low, tick_low); - // assert_eq!(position.tick_high, tick_high); +// // Position liquidity is reduced +// let position = +// Positions::::get((netuid, OK_COLDKEY_ACCOUNT_ID, position_id)).unwrap(); +// assert_eq!(position.liquidity, liquidity * 9 / 10); +// assert_eq!(position.tick_low, tick_low); +// assert_eq!(position.tick_high, tick_high); - // // Tick liquidity is updated properly for low and high position ticks - // let tick_low_info_after = Ticks::::get(netuid, tick_low).unwrap(); - // let tick_high_info_after = Ticks::::get(netuid, tick_high).unwrap(); +// // Tick liquidity is updated properly for low and high position ticks +// let tick_low_info_after = Ticks::::get(netuid, tick_low).unwrap(); +// let tick_high_info_after = Ticks::::get(netuid, tick_high).unwrap(); - // assert_eq!( - // tick_low_info_before.liquidity_net - (liquidity / 10) as i128, - // tick_low_info_after.liquidity_net, - // ); - // assert_eq!( - // tick_low_info_before.liquidity_gross - (liquidity / 10), - // tick_low_info_after.liquidity_gross, - // ); - // assert_eq!( - // tick_high_info_before.liquidity_net + (liquidity / 10) as i128, - // tick_high_info_after.liquidity_net, - // ); - // assert_eq!( - // tick_high_info_before.liquidity_gross - (liquidity / 10), - // tick_high_info_after.liquidity_gross, - // ); +// assert_eq!( +// tick_low_info_before.liquidity_net - (liquidity / 10) as i128, +// tick_low_info_after.liquidity_net, +// ); +// assert_eq!( +// tick_low_info_before.liquidity_gross - (liquidity / 10), +// tick_low_info_after.liquidity_gross, +// ); +// assert_eq!( +// tick_high_info_before.liquidity_net + (liquidity / 10) as i128, +// tick_high_info_after.liquidity_net, +// ); +// assert_eq!( +// tick_high_info_before.liquidity_gross - (liquidity / 10), +// tick_high_info_after.liquidity_gross, +// ); - // // Modify liquidity again (ensure fees aren't double-collected) - // let modify_result = Pallet::::do_modify_position( - // netuid, - // &OK_COLDKEY_ACCOUNT_ID, - // &OK_HOTKEY_ACCOUNT_ID, - // position_id, - // -((liquidity / 100) as i64), - // ) - // .unwrap(); - - // assert_abs_diff_eq!( - // u64::from(modify_result.alpha), - // alpha / 100, - // epsilon = alpha / 1000 - // ); - // assert_eq!(modify_result.fee_tao, TaoCurrency::ZERO); - // assert_eq!(modify_result.fee_alpha, AlphaCurrency::ZERO); - // }); - // }); -} +// // Modify liquidity again (ensure fees aren't double-collected) +// let modify_result = Pallet::::do_modify_position( +// netuid, +// &OK_COLDKEY_ACCOUNT_ID, +// &OK_HOTKEY_ACCOUNT_ID, +// position_id, +// -((liquidity / 100) as i64), +// ) +// .unwrap(); + +// assert_abs_diff_eq!( +// u64::from(modify_result.alpha), +// alpha / 100, +// epsilon = alpha / 1000 +// ); +// assert_eq!(modify_result.fee_tao, TaoCurrency::ZERO); +// assert_eq!(modify_result.fee_alpha, AlphaCurrency::ZERO); +// }); +// }); +// } // cargo test --package pallet-subtensor-swap --lib -- pallet::tests::test_swap_basic --exact --nocapture #[test] From e75ee3a781e2f07703250edb58e797ed07c9dddf Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Fri, 2 Jan 2026 18:16:30 -0500 Subject: [PATCH 097/240] Update bigmath, cleanup balancer math, add missing balancer tests --- Cargo.lock | 16 +- Cargo.toml | 2 +- pallets/swap/src/pallet/balancer.rs | 288 +++++++++++++++++++++++++--- 3 files changed, 275 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b7d0c4be4f..259c441360 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13686,9 +13686,9 @@ dependencies = [ [[package]] name = "quoth" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3fcb67d48b8ef70eda1c84514f4ba26e4904c40c36940cec7aa2c45cd8d114b" +checksum = "76d9da82a5dc3ff2fb2eee43d2b434fb197a9bf6a2a243850505b61584f888d2" dependencies = [ "quoth-macros", "regex", @@ -13698,9 +13698,9 @@ dependencies = [ [[package]] name = "quoth-macros" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9e5bab2cb57954711cf4165382479a56c3b9d2c94f513ab2a634aea00baed4a" +checksum = "58547202bec9896e773db7ef04b4d47c444f9c97bc4386f36e55718c347db440" dependencies = [ "proc-macro2", "quote", @@ -14585,8 +14585,8 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "safe-bigmath" -version = "0.2.0" -source = "git+https://github.com/sam0x17/safe-math?rev=fd8e816#fd8e8167ff95761d9b6407e2feec47032c2311ca" +version = "0.3.0" +source = "git+https://github.com/sam0x17/safe-math#0a94bf14f8235f82d30d23d91e0b63ed28861ece" dependencies = [ "crabtime", "num-bigint", @@ -14616,9 +14616,9 @@ dependencies = [ [[package]] name = "safe-string" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "828a62cafa42d2d1b5ad4592c47946c156972e1e8d6e929e566256d841177b59" +checksum = "f2fc51f1e562058dee569383bfdb5a58752bfeb7fa7f0823f5c07c4c45381b5a" [[package]] name = "safe_arch" diff --git a/Cargo.toml b/Cargo.toml index 4b93e54e23..6ed0ca0205 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,7 +61,7 @@ pallet-subtensor-swap-runtime-api = { path = "pallets/swap/runtime-api", default pallet-subtensor-swap-rpc = { path = "pallets/swap/rpc", default-features = false } procedural-fork = { path = "support/procedural-fork", default-features = false } safe-math = { path = "primitives/safe-math", default-features = false } -safe-bigmath = { package = "safe-bigmath", git = "https://github.com/sam0x17/safe-math", rev = "fd8e816", default-features = false } +safe-bigmath = { version = "0.3.0", package = "safe-bigmath", git = "https://github.com/sam0x17/safe-math", default-features = false } share-pool = { path = "primitives/share-pool", default-features = false } subtensor-macros = { path = "support/macros", default-features = false } subtensor-custom-rpc = { default-features = false, path = "pallets/subtensor/rpc" } diff --git a/pallets/swap/src/pallet/balancer.rs b/pallets/swap/src/pallet/balancer.rs index 9892769143..a20891a9e1 100644 --- a/pallets/swap/src/pallet/balancer.rs +++ b/pallets/swap/src/pallet/balancer.rs @@ -8,23 +8,38 @@ use sp_runtime::Saturating; use substrate_fixed::types::U64F64; use subtensor_macros::freeze_struct; -#[freeze_struct("7fa3cbcf1d419808")] +/// Balancer implements all high complexity math for swap operations such as: +/// - Swapping x for y, which includes limit orders +/// - Adding and removing liquidity (including unbalanced) +/// +/// Notation used in this file: +/// - x: Base reserve (alplha reserve) +/// - y: Quote reserve (tao reserve) +/// - ∆x: Alpha paid in/out +/// - ∆y: Tao paid in/out +/// - w1: Base weight (a.k.a weight_base) +/// - w2: Quote weight (a.k.a weight_quote) +#[freeze_struct("33a4fb0774da77c7")] #[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct Balancer { quote: Perquintill, } -// Lower imit of weights is 0.01 +/// Accuracy matches to 18 decimal digits used to represent weights pub const ACCURACY: u64 = 1_000_000_000_000_000_000_u64; +/// Lower imit of weights is 0.01 pub const MIN_WEIGHT: Perquintill = Perquintill::from_parts(ACCURACY / 100); +/// 1.0 in Perquintill pub const ONE: Perquintill = Perquintill::from_parts(ACCURACY); #[derive(Debug)] pub enum BalancerError { + /// The provided weight value is out of range InvalidValue, } impl Default for Balancer { + /// The default value of weights is 0.5 for pool initialization fn default() -> Self { Self { quote: Perquintill::from_rational(1u128, 2u128), @@ -33,6 +48,7 @@ impl Default for Balancer { } impl Balancer { + /// Creates a new instance of balancer with a given quote weight pub fn new(quote: Perquintill) -> Result { if Self::check_constraints(quote) { Ok(Balancer { quote }) @@ -41,19 +57,27 @@ impl Balancer { } } + /// Constraints limit balancer weights within certain range of values: + /// - Both weights are above minimum + /// - Sum of weights is equal to 1.0 fn check_constraints(quote: Perquintill) -> bool { let base = ONE.saturating_sub(quote); (base >= MIN_WEIGHT) && (quote >= MIN_WEIGHT) } + /// We store quote weight as Perquintill pub fn get_quote_weight(&self) -> Perquintill { self.quote } + /// Base weight is calculated as 1.0 - quote_weight pub fn get_base_weight(&self) -> Perquintill { ONE.saturating_sub(self.quote) } + /// Sets quote currency weight in the balancer. + /// Because sum of weights is always 1.0, there is no need to + /// store base currency weight pub fn set_quote_weight(&mut self, new_value: Perquintill) -> Result<(), BalancerError> { if Self::check_constraints(new_value) { self.quote = new_value; @@ -63,6 +87,11 @@ impl Balancer { } } + /// If base_quote is true, calculate (x / (x + ∆x))^(weight_base / weight_quote), + /// otherwise, calculate (x / (x + ∆x))^(weight_quote / weight_base) + /// + /// Here we use SafeInt from bigmath crate for high-precision exponentiation, + /// which exposes the function pow_ratio_scaled. fn exp_scaled(&self, x: u64, dx: u64, base_quote: bool) -> U64F64 { let den = x.saturating_add(dx); if den == 0 { @@ -78,16 +107,16 @@ impl Balancer { let w2_safe = SafeInt::from(w2); let perquintill_scale = SafeInt::from(ACCURACY as u128); let denominator = SafeInt::from(x_plus_dx); - let maybe_result_safe_int = if base_quote { - log::debug!("x = {:?}", x); - log::debug!("dx = {:?}", dx); - log::debug!("x_safe = {:?}", x_safe); - log::debug!("denominator = {:?}", denominator); - log::debug!("w1_safe = {:?}", w1_safe); - log::debug!("w2_safe = {:?}", w2_safe); - log::debug!("precision = {:?}", precision); - log::debug!("perquintill_scale = {:?}", perquintill_scale); + log::debug!("x = {:?}", x); + log::debug!("dx = {:?}", dx); + log::debug!("x_safe = {:?}", x_safe); + log::debug!("denominator = {:?}", denominator); + log::debug!("w1_safe = {:?}", w1_safe); + log::debug!("w2_safe = {:?}", w2_safe); + log::debug!("precision = {:?}", precision); + log::debug!("perquintill_scale = {:?}", perquintill_scale); + let maybe_result_safe_int = if base_quote { SafeInt::pow_ratio_scaled( &x_safe, &denominator, @@ -117,16 +146,24 @@ impl Balancer { } /// Calculates exponent of (x / (x + ∆x)) ^ (w_base/w_quote) + /// This method is used in sell swaps + /// (∆x is given by user, ∆y is paid out by the pool) pub fn exp_base_quote(&self, x: u64, dx: u64) -> U64F64 { self.exp_scaled(x, dx, true) } /// Calculates exponent of (y / (y + ∆y)) ^ (w_quote/w_base) + /// This method is used in buy swaps + /// (∆y is given by user, ∆x is paid out by the pool) pub fn exp_quote_base(&self, y: u64, dy: u64) -> U64F64 { self.exp_scaled(y, dy, false) } - /// Calculates price as (w1/w2) * (y/x) + /// Calculates price as (w1/w2) * (y/x), where + /// - w1 is base weight + /// - w2 is quote weight + /// - x is base reserve + /// - y is quote reserve pub fn calculate_price(&self, x: u64, y: u64) -> U64F64 { let w2_fixed = U64F64::saturating_from_num(self.get_quote_weight().deconstruct()); let w1_fixed = U64F64::saturating_from_num(self.get_base_weight().deconstruct()); @@ -146,11 +183,20 @@ impl Balancer { let num = U256::from(value) * U256::from(parts); let den = U256::from(acc); - // add 0.5 ulp before integer division → round-to-nearest + // Add 0.5 before integer division to achieve rounding to the nearest + // integer let res = (num + den / U256::from(2u8)) / den; res.min(U256::from(u128::MAX)).as_u128() } + /// When liquidity is added to balancer swap, it may be added with arbitrary proportion, + /// not necessarily in the proportion of price, like with uniswap v2 or v3. In order to + /// stay within balancer pool invariant, the weights need to be updated. Invariant: + /// + /// L = x ^ weight_base * y ^ weight_quote + /// + /// Note that weights must remain within the proper range (both be above MIN_WEIGHT), + /// so only reasonably small disproportions of updates are appropriate. pub fn update_weights_for_added_liquidity( &mut self, tao_reserve: u64, @@ -190,6 +236,11 @@ impl Balancer { } /// Calculates quote delta needed to reach the price up when byuing + /// This method is needed for limit orders. + /// + /// Formula is: + /// ∆y = y * ((price_new / price)^weight_base - 1) + /// price_new >= price pub fn calculate_quote_delta_in( &self, current_price: U64F64, @@ -230,6 +281,11 @@ impl Balancer { } /// Calculates base delta needed to reach the price down when selling + /// This method is needed for limit orders. + /// + /// Formula is: + /// ∆x = x * ((price / price_new)^weight_quote - 1) + /// price_new <= price pub fn calculate_base_delta_in( &self, current_price: U64F64, @@ -279,11 +335,17 @@ mod tests { use approx::assert_abs_diff_eq; use sp_arithmetic::Perquintill; + // Helper: convert Perquintill to f64 for comparison fn perquintill_to_f64(p: Perquintill) -> f64 { let parts = p.deconstruct() as f64; parts / ACCURACY as f64 } + // Helper: convert U64F64 to f64 for comparison + fn f(v: U64F64) -> f64 { + v.to_num::() + } + #[test] fn test_perquintill_power() { const PRECISION: u32 = 4096; @@ -407,21 +469,33 @@ mod tests { .into_iter() .for_each(|(y, x, dx)| { let bal = Balancer::new(w_quote).unwrap(); - let e = bal.exp_base_quote(x, dx); + let e1 = bal.exp_base_quote(x, dx); + let e2 = bal.exp_quote_base(x, dx); let one = U64F64::from_num(1); let y_fixed = U64F64::from_num(y); - let dy = y_fixed * (one - e); + let dy1 = y_fixed * (one - e1); + let dy2 = y_fixed * (one - e2); let w1 = perquintill_to_f64(bal.get_base_weight()); let w2 = perquintill_to_f64(bal.get_quote_weight()); - let e_expected = (x as f64 / (x as f64 + dx as f64)).powf(w1 / w2); - let dy_expected = y as f64 * (1. - e_expected); - - let mut eps = dy_expected / 100000.; - if eps > 1.0 { - eps = 1.0; + let e1_expected = (x as f64 / (x as f64 + dx as f64)).powf(w1 / w2); + let dy1_expected = y as f64 * (1. - e1_expected); + let e2_expected = (x as f64 / (x as f64 + dx as f64)).powf(w2 / w1); + let dy2_expected = y as f64 * (1. - e2_expected); + + // Start tolerance with 0.001 rao + let mut eps1 = 0.001; + let mut eps2 = 0.001; + + // If swapping more than 100k tao/alpha, relax tolerance to 1.0 rao + if dy1_expected > 100_000_000_000_000_f64 { + eps1 = 1.0; + } + if dy2_expected > 100_000_000_000_000_f64 { + eps2 = 1.0; } - assert_abs_diff_eq!(dy.to_num::(), dy_expected, epsilon = eps); + assert_abs_diff_eq!(f(dy1), dy1_expected, epsilon = eps1); + assert_abs_diff_eq!(f(dy2), dy2_expected, epsilon = eps2); }) }); } @@ -717,4 +791,174 @@ mod tests { epsilon = target_price.to_num::() / 1_000_000_000. ); } + + #[test] + fn mul_round_zero_and_one() { + let v = 1_000_000u128; + + // p = 0 -> always 0 + assert_eq!(Balancer::mul_perquintill_round(Perquintill::zero(), v), 0); + + // p = 1 -> identity + assert_eq!(Balancer::mul_perquintill_round(Perquintill::one(), v), v); + } + + #[test] + fn mul_round_half_behaviour() { + // p = 1/2 + let p = Perquintill::from_rational(1u128, 2u128); + + // Check rounding around .5 boundaries + // value * 1/2, rounded to nearest + assert_eq!(Balancer::mul_perquintill_round(p, 0), 0); // 0.0 -> 0 + assert_eq!(Balancer::mul_perquintill_round(p, 1), 1); // 0.5 -> 1 (round up) + assert_eq!(Balancer::mul_perquintill_round(p, 2), 1); // 1.0 -> 1 + assert_eq!(Balancer::mul_perquintill_round(p, 3), 2); // 1.5 -> 2 + assert_eq!(Balancer::mul_perquintill_round(p, 4), 2); // 2.0 -> 2 + assert_eq!(Balancer::mul_perquintill_round(p, 5), 3); // 2.5 -> 3 + assert_eq!(Balancer::mul_perquintill_round(p, 1023), 512); // 511.5 -> 512 + assert_eq!(Balancer::mul_perquintill_round(p, 1025), 513); // 512.5 -> 513 + } + + #[test] + fn mul_round_third_behaviour() { + // p = 1/3 + let p = Perquintill::from_rational(1u128, 3u128); + + // value * 1/3, rounded to nearest + assert_eq!(Balancer::mul_perquintill_round(p, 3), 1); // 1.0 -> 1 + assert_eq!(Balancer::mul_perquintill_round(p, 4), 1); // 1.333... -> 1 + assert_eq!(Balancer::mul_perquintill_round(p, 5), 2); // 1.666... -> 2 + assert_eq!(Balancer::mul_perquintill_round(p, 6), 2); // 2.0 -> 2 + } + + #[test] + fn mul_round_large_values_simple_rational() { + // p = 7/10 (exact in perquintill: 0.7) + let p = Perquintill::from_rational(7u128, 10u128); + let v: u128 = 1_000_000_000_000_000_000; + + let res = Balancer::mul_perquintill_round(p, v); + + // Expected = round(0.7 * v) with pure integer math: + // round(v * 7 / 10) = (v*7 + 10/2) / 10 + let expected = (v.saturating_mul(7) + 10 / 2) / 10; + + assert_eq!(res, expected); + } + + #[test] + fn mul_round_max_value_with_one() { + let v = u128::MAX; + let p = ONE; + + // For p = 1, result must be exactly value, and must not overflow + let res = Balancer::mul_perquintill_round(p, v); + assert_eq!(res, v); + } + + #[test] + fn price_with_equal_weights_is_y_over_x() { + // quote = 0.5, base = 0.5 -> w1 / w2 = 1, so price = y/x + let quote = Perquintill::from_rational(1u128, 2u128); + let bal = Balancer::new(quote).unwrap(); + + let x = 2u64; + let y = 5u64; + + let price = bal.calculate_price(x, y); + let price_f = f(price); + + let expected_f = (y as f64) / (x as f64); + assert_abs_diff_eq!(price_f, expected_f, epsilon = 1e-12); + } + + #[test] + fn price_scales_with_weight_ratio_two_to_one() { + // Assume base = 1 - quote. + // quote = 1/3 -> base = 2/3, so w1 / w2 = 2. + // Then price = 2 * (y/x). + let quote = Perquintill::from_rational(1u128, 3u128); + let bal = Balancer::new(quote).unwrap(); + + let x = 4u64; + let y = 10u64; + + let price_f = f(bal.calculate_price(x, y)); + let expected_f = 2.0 * (y as f64 / x as f64); + + assert_abs_diff_eq!(price_f, expected_f, epsilon = 1e-10); + } + + #[test] + fn price_is_zero_when_y_is_zero() { + // If y = 0, y/x = 0 so price must be 0 regardless of weights (for x > 0). + let quote = Perquintill::from_rational(3u128, 10u128); // 0.3 + let bal = Balancer::new(quote).unwrap(); + + let x = 10u64; + let y = 0u64; + + let price_f = f(bal.calculate_price(x, y)); + assert_abs_diff_eq!(price_f, 0.0, epsilon = 0.0); + } + + #[test] + fn price_invariant_when_scaling_x_and_y_with_equal_weights() { + // For equal weights, price(x, y) == price(kx, ky). + let quote = Perquintill::from_rational(1u128, 2u128); // 0.5 + let bal = Balancer::new(quote).unwrap(); + + let x1 = 3u64; + let y1 = 7u64; + let k = 10u64; + let x2 = x1 * k; + let y2 = y1 * k; + + let p1 = f(bal.calculate_price(x1, y1)); + let p2 = f(bal.calculate_price(x2, y2)); + + assert_abs_diff_eq!(p1, p2, epsilon = 1e-12); + } + + #[test] + fn price_matches_formula_for_general_quote() { + // General check: price = (w1 / w2) * (y/x), + // where w1 = base_weight, w2 = quote_weight. + // Here we assume get_base_weight = 1 - quote. + let quote = Perquintill::from_rational(2u128, 5u128); // 0.4 + let bal = Balancer::new(quote).unwrap(); + + let x = 9u64; + let y = 25u64; + + let price_f = f(bal.calculate_price(x, y)); + + let base = Perquintill::one() - quote; + let w1 = base.deconstruct() as f64; + let w2 = quote.deconstruct() as f64; + + let expected_f = (w1 / w2) * (y as f64 / x as f64); + assert_abs_diff_eq!(price_f, expected_f, epsilon = 1e-9); + } + + #[test] + fn price_high_values_non_equal_weights() { + // Non-equal weights, high x and y (up to 21e15) + let quote = Perquintill::from_rational(3u128, 10u128); // 0.3 + let bal = Balancer::new(quote).unwrap(); + + let x: u64 = 21_000_000_000_000_000; + let y: u64 = 15_000_000_000_000_000; + + let price = bal.calculate_price(x, y); + let price_f = f(price); + + // Expected: (w1 / w2) * (y / x), using Balancer's actual weights + let w1 = bal.get_base_weight().deconstruct() as f64; + let w2 = bal.get_quote_weight().deconstruct() as f64; + let expected_f = (w1 / w2) * (y as f64 / x as f64); + + assert_abs_diff_eq!(price_f, expected_f, epsilon = 1e-9); + } } From 2b5b7dc255998c832e1773b7c221a41e498f873e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 5 Jan 2026 12:05:45 +0000 Subject: [PATCH 098/240] auto-update benchmark weights --- pallets/subtensor/src/macros/dispatches.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index a5fdeb4b9e..b8102d1c8e 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2365,9 +2365,9 @@ mod dispatches { /// #[pallet::call_index(125)] #[pallet::weight( - Weight::from_parts(16_150_000, 0) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) + Weight::from_parts(55_700_000, 0) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) )] pub fn announce_coldkey_swap( origin: OriginFor, @@ -2407,9 +2407,9 @@ mod dispatches { /// The `ColdkeySwapped` event is emitted on successful swap. #[pallet::call_index(126)] #[pallet::weight( - Weight::from_parts(207_300_000, 0) - .saturating_add(T::DbWeight::get().reads(19_u64)) - .saturating_add(T::DbWeight::get().writes(9_u64)) + Weight::from_parts(110_700_000, 0) + .saturating_add(T::DbWeight::get().reads(16_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) )] pub fn swap_coldkey_announced( origin: OriginFor, From 49accc1a836f40009444b83c024a9abe296fcb3d Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 5 Jan 2026 18:17:02 -0500 Subject: [PATCH 099/240] Remove provided reserve maps, add liquidity calculation method to balancer, add migrations from swap v3 to balancer, initialize current liquidity. --- pallets/subtensor/src/coinbase/root.rs | 1 - pallets/subtensor/src/lib.rs | 14 +- .../src/migrations/migrate_cleanup_swap_v3.rs | 70 ++++++++++ pallets/subtensor/src/migrations/mod.rs | 1 + pallets/subtensor/src/staking/move_stake.rs | 12 +- pallets/subtensor/src/staking/remove_stake.rs | 1 - pallets/subtensor/src/staking/stake_utils.rs | 44 ++---- pallets/subtensor/src/subnets/subnet.rs | 2 - pallets/subtensor/src/tests/migration.rs | 48 +++++++ pallets/subtensor/src/tests/networks.rs | 4 - pallets/subtensor/src/tests/staking.rs | 6 +- pallets/swap/src/pallet/balancer.rs | 127 ++++++++++++++++-- pallets/swap/src/pallet/hooks.rs | 30 +++++ pallets/swap/src/pallet/impls.rs | 15 ++- .../migrations/migrate_swapv3_to_balancer.rs | 71 ++++++++++ pallets/swap/src/pallet/migrations/mod.rs | 25 ++++ pallets/swap/src/pallet/mod.rs | 38 ++---- pallets/swap/src/pallet/tests.rs | 46 +++++-- 18 files changed, 440 insertions(+), 115 deletions(-) create mode 100644 pallets/subtensor/src/migrations/migrate_cleanup_swap_v3.rs create mode 100644 pallets/swap/src/pallet/hooks.rs create mode 100644 pallets/swap/src/pallet/migrations/migrate_swapv3_to_balancer.rs create mode 100644 pallets/swap/src/pallet/migrations/mod.rs diff --git a/pallets/subtensor/src/coinbase/root.rs b/pallets/subtensor/src/coinbase/root.rs index 328ce3805c..62a1fa9766 100644 --- a/pallets/subtensor/src/coinbase/root.rs +++ b/pallets/subtensor/src/coinbase/root.rs @@ -300,7 +300,6 @@ impl Pallet { SubnetMovingPrice::::remove(netuid); SubnetTaoFlow::::remove(netuid); SubnetEmaTaoFlow::::remove(netuid); - SubnetTaoProvided::::remove(netuid); // --- 13. Token / mechanism / registration toggles. TokenSymbol::::remove(netuid); diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index ef2d44e68b..9e726c3f8c 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1305,11 +1305,6 @@ pub mod pallet { pub type SubnetTAO = StorageMap<_, Identity, NetUid, TaoCurrency, ValueQuery, DefaultZeroTao>; - /// --- MAP ( netuid ) --> tao_in_user_subnet | Returns the amount of TAO in the subnet reserve provided by users as liquidity. - #[pallet::storage] - pub type SubnetTaoProvided = - StorageMap<_, Identity, NetUid, TaoCurrency, ValueQuery, DefaultZeroTao>; - /// --- MAP ( netuid ) --> alpha_in_emission | Returns the amount of alph in emission into the pool per block. #[pallet::storage] pub type SubnetAlphaInEmission = @@ -1330,11 +1325,6 @@ pub mod pallet { pub type SubnetAlphaIn = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultZeroAlpha>; - /// --- MAP ( netuid ) --> alpha_supply_user_in_pool | Returns the amount of alpha in the pool provided by users as liquidity. - #[pallet::storage] - pub type SubnetAlphaInProvided = - StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultZeroAlpha>; - /// --- MAP ( netuid ) --> alpha_supply_in_subnet | Returns the amount of alpha in the subnet. #[pallet::storage] pub type SubnetAlphaOut = @@ -2506,7 +2496,7 @@ pub struct TaoCurrencyReserve(PhantomData); impl CurrencyReserve for TaoCurrencyReserve { #![deny(clippy::expect_used)] fn reserve(netuid: NetUid) -> TaoCurrency { - SubnetTAO::::get(netuid).saturating_add(SubnetTaoProvided::::get(netuid)) + SubnetTAO::::get(netuid) } fn increase_provided(netuid: NetUid, tao: TaoCurrency) { @@ -2524,7 +2514,7 @@ pub struct AlphaCurrencyReserve(PhantomData); impl CurrencyReserve for AlphaCurrencyReserve { #![deny(clippy::expect_used)] fn reserve(netuid: NetUid) -> AlphaCurrency { - SubnetAlphaIn::::get(netuid).saturating_add(SubnetAlphaInProvided::::get(netuid)) + SubnetAlphaIn::::get(netuid) } fn increase_provided(netuid: NetUid, alpha: AlphaCurrency) { diff --git a/pallets/subtensor/src/migrations/migrate_cleanup_swap_v3.rs b/pallets/subtensor/src/migrations/migrate_cleanup_swap_v3.rs new file mode 100644 index 0000000000..13edf21033 --- /dev/null +++ b/pallets/subtensor/src/migrations/migrate_cleanup_swap_v3.rs @@ -0,0 +1,70 @@ +use super::*; +use crate::HasMigrationRun; +use frame_support::{storage_alias, traits::Get, weights::Weight}; +use scale_info::prelude::string::String; + +pub mod deprecated_swap_maps { + use super::*; + + /// --- MAP ( netuid ) --> tao_in_user_subnet | Returns the amount of TAO in the subnet reserve provided by users as liquidity. + #[storage_alias] + pub type SubnetTaoProvided = + StorageMap, Identity, NetUid, TaoCurrency, ValueQuery>; + + /// --- MAP ( netuid ) --> alpha_supply_user_in_pool | Returns the amount of alpha in the pool provided by users as liquidity. + #[storage_alias] + pub type SubnetAlphaInProvided = + StorageMap, Identity, NetUid, AlphaCurrency, ValueQuery>; +} + +pub fn migrate_cleanup_swap_v3() -> Weight { + let migration_name = b"migrate_cleanup_swap_v3".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), + ); + + // ------------------------------ + // Step 1: Move provided to reserves + // ------------------------------ + for (netuid, tao_provided) in deprecated_swap_maps::SubnetTaoProvided::::iter() { + SubnetTAO::::mutate(netuid, |total| { + *total = total.saturating_add(tao_provided); + }); + } + for (netuid, alpha_provided) in deprecated_swap_maps::SubnetAlphaInProvided::::iter() { + SubnetAlphaIn::::mutate(netuid, |total| { + *total = total.saturating_add(alpha_provided); + }); + } + + // ------------------------------ + // Step 2: Remove Map entries + // ------------------------------ + remove_prefix::("SubtensorModule", "SubnetTaoProvided", &mut weight); + remove_prefix::("SubtensorModule", "SubnetAlphaInProvided", &mut weight); + + // ------------------------------ + // Step 3: Mark Migration as Completed + // ------------------------------ + + HasMigrationRun::::insert(&migration_name, true); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + + log::info!( + "Migration '{:?}' completed successfully.", + String::from_utf8_lossy(&migration_name) + ); + + weight +} diff --git a/pallets/subtensor/src/migrations/mod.rs b/pallets/subtensor/src/migrations/mod.rs index 4c9d5f01d1..f36c65dcc6 100644 --- a/pallets/subtensor/src/migrations/mod.rs +++ b/pallets/subtensor/src/migrations/mod.rs @@ -5,6 +5,7 @@ use sp_io::KillStorageResult; use sp_io::hashing::twox_128; use sp_io::storage::clear_prefix; pub mod migrate_auto_stake_destination; +pub mod migrate_cleanup_swap_v3; pub mod migrate_clear_rank_trust_pruning_maps; pub mod migrate_coldkey_swap_scheduled; pub mod migrate_commit_reveal_settings; diff --git a/pallets/subtensor/src/staking/move_stake.rs b/pallets/subtensor/src/staking/move_stake.rs index 7071e4d55d..76d9c95d3a 100644 --- a/pallets/subtensor/src/staking/move_stake.rs +++ b/pallets/subtensor/src/staking/move_stake.rs @@ -472,10 +472,8 @@ impl Pallet { } // Corner case: SubnetTAO for any of two subnets is zero - let subnet_tao_1 = SubnetTAO::::get(origin_netuid) - .saturating_add(SubnetTaoProvided::::get(origin_netuid)); - let subnet_tao_2 = SubnetTAO::::get(destination_netuid) - .saturating_add(SubnetTaoProvided::::get(destination_netuid)); + let subnet_tao_1 = SubnetTAO::::get(origin_netuid); + let subnet_tao_2 = SubnetTAO::::get(destination_netuid); if subnet_tao_1.is_zero() || subnet_tao_2.is_zero() { return Err(Error::::ZeroMaxStakeAmount.into()); } @@ -483,10 +481,8 @@ impl Pallet { let subnet_tao_2_float: U64F64 = U64F64::saturating_from_num(subnet_tao_2); // Corner case: SubnetAlphaIn for any of two subnets is zero - let alpha_in_1 = SubnetAlphaIn::::get(origin_netuid) - .saturating_add(SubnetAlphaInProvided::::get(origin_netuid)); - let alpha_in_2 = SubnetAlphaIn::::get(destination_netuid) - .saturating_add(SubnetAlphaInProvided::::get(destination_netuid)); + let alpha_in_1 = SubnetAlphaIn::::get(origin_netuid); + let alpha_in_2 = SubnetAlphaIn::::get(destination_netuid); if alpha_in_1.is_zero() || alpha_in_2.is_zero() { return Err(Error::::ZeroMaxStakeAmount.into()); } diff --git a/pallets/subtensor/src/staking/remove_stake.rs b/pallets/subtensor/src/staking/remove_stake.rs index 01427545b4..46aac452d6 100644 --- a/pallets/subtensor/src/staking/remove_stake.rs +++ b/pallets/subtensor/src/staking/remove_stake.rs @@ -587,7 +587,6 @@ impl Pallet { } // 7.c) Remove α‑in/α‑out counters (fully destroyed). SubnetAlphaIn::::remove(netuid); - SubnetAlphaInProvided::::remove(netuid); SubnetAlphaOut::::remove(netuid); // Clear the locked balance on the subnet. diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index 704ada9f03..eb3a826a87 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -18,9 +18,7 @@ impl Pallet { /// # Returns /// * `u64` - The total alpha issuance for the specified subnet. pub fn get_alpha_issuance(netuid: NetUid) -> AlphaCurrency { - SubnetAlphaIn::::get(netuid) - .saturating_add(SubnetAlphaInProvided::::get(netuid)) - .saturating_add(SubnetAlphaOut::::get(netuid)) + SubnetAlphaIn::::get(netuid).saturating_add(SubnetAlphaOut::::get(netuid)) } pub fn get_protocol_tao(netuid: NetUid) -> TaoCurrency { @@ -1206,53 +1204,33 @@ impl Pallet { pub fn increase_provided_tao_reserve(netuid: NetUid, tao: TaoCurrency) { if !tao.is_zero() { - SubnetTaoProvided::::mutate(netuid, |total| { + SubnetTAO::::mutate(netuid, |total| { *total = total.saturating_add(tao); }); } } pub fn decrease_provided_tao_reserve(netuid: NetUid, tao: TaoCurrency) { - // First, decrease SubnetTaoProvided, then deduct the rest from SubnetTAO - let subnet_tao = SubnetTAO::::get(netuid); - let subnet_tao_provided = SubnetTaoProvided::::get(netuid); - let remainder = subnet_tao_provided.saturating_sub(tao); - let carry_over = tao.saturating_sub(subnet_tao_provided); - if carry_over.is_zero() { - if remainder.is_zero() { - SubnetTaoProvided::::remove(netuid); - } else { - SubnetTaoProvided::::set(netuid, remainder); - } - } else { - SubnetTaoProvided::::remove(netuid); - SubnetTAO::::set(netuid, subnet_tao.saturating_sub(carry_over)); + if !tao.is_zero() { + SubnetTAO::::mutate(netuid, |total| { + *total = total.saturating_sub(tao); + }); } } pub fn increase_provided_alpha_reserve(netuid: NetUid, alpha: AlphaCurrency) { if !alpha.is_zero() { - SubnetAlphaInProvided::::mutate(netuid, |total| { + SubnetAlphaIn::::mutate(netuid, |total| { *total = total.saturating_add(alpha); }); } } pub fn decrease_provided_alpha_reserve(netuid: NetUid, alpha: AlphaCurrency) { - // First, decrease SubnetAlphaInProvided, then deduct the rest from SubnetAlphaIn - let subnet_alpha = SubnetAlphaIn::::get(netuid); - let subnet_alpha_provided = SubnetAlphaInProvided::::get(netuid); - let remainder = subnet_alpha_provided.saturating_sub(alpha); - let carry_over = alpha.saturating_sub(subnet_alpha_provided); - if carry_over.is_zero() { - if remainder.is_zero() { - SubnetAlphaInProvided::::remove(netuid); - } else { - SubnetAlphaInProvided::::set(netuid, remainder); - } - } else { - SubnetAlphaInProvided::::remove(netuid); - SubnetAlphaIn::::set(netuid, subnet_alpha.saturating_sub(carry_over)); + if !alpha.is_zero() { + SubnetAlphaIn::::mutate(netuid, |total| { + *total = total.saturating_sub(alpha); + }); } } diff --git a/pallets/subtensor/src/subnets/subnet.rs b/pallets/subtensor/src/subnets/subnet.rs index 4185aee624..50eb379cd3 100644 --- a/pallets/subtensor/src/subnets/subnet.rs +++ b/pallets/subtensor/src/subnets/subnet.rs @@ -218,8 +218,6 @@ impl Pallet { SubnetOwner::::insert(netuid_to_register, coldkey.clone()); SubnetOwnerHotkey::::insert(netuid_to_register, hotkey.clone()); SubnetLocked::::insert(netuid_to_register, actual_tao_lock_amount); - SubnetTaoProvided::::insert(netuid_to_register, TaoCurrency::ZERO); - SubnetAlphaInProvided::::insert(netuid_to_register, AlphaCurrency::ZERO); SubnetAlphaOut::::insert(netuid_to_register, AlphaCurrency::ZERO); SubnetVolume::::insert(netuid_to_register, 0u128); RAORecycledForRegistration::::insert( diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index aa35751aa0..4c52f9b9fa 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -2927,3 +2927,51 @@ fn test_migrate_remove_unknown_neuron_axon_cert_prom() { } } } + +// cargo test --package pallet-subtensor --lib -- tests::migration::test_migrate_cleanup_swap_v3 --exact --nocapture +#[test] +fn test_migrate_cleanup_swap_v3() { + use crate::migrations::migrate_cleanup_swap_v3::deprecated_swap_maps; + use substrate_fixed::types::U64F64; + + new_test_ext(1).execute_with(|| { + let migration = crate::migrations::migrate_cleanup_swap_v3::migrate_cleanup_swap_v3::; + + const MIGRATION_NAME: &str = "migrate_cleanup_swap_v3"; + + let provided: u64 = 9876; + let reserves: u64 = 1_000_000; + + SubnetTAO::::insert(NetUid::from(1), TaoCurrency::from(reserves)); + SubnetAlphaIn::::insert(NetUid::from(1), AlphaCurrency::from(reserves)); + + // Insert deprecated maps values + deprecated_swap_maps::SubnetTaoProvided::::insert( + NetUid::from(1), + TaoCurrency::from(provided), + ); + deprecated_swap_maps::SubnetAlphaInProvided::::insert( + NetUid::from(1), + AlphaCurrency::from(provided), + ); + + // Run migration + let weight = migration(); + + // Test that values are removed from state + assert!(!deprecated_swap_maps::SubnetTaoProvided::::contains_key(NetUid::from(1)),); + assert!( + !deprecated_swap_maps::SubnetAlphaInProvided::::contains_key(NetUid::from(1)), + ); + + // Provided got added to reserves + assert_eq!( + u64::from(SubnetTAO::::get(NetUid::from(1))), + reserves + provided + ); + assert_eq!( + u64::from(SubnetAlphaIn::::get(NetUid::from(1))), + reserves + provided + ); + }); +} diff --git a/pallets/subtensor/src/tests/networks.rs b/pallets/subtensor/src/tests/networks.rs index 303b5a8b26..d1198a64f0 100644 --- a/pallets/subtensor/src/tests/networks.rs +++ b/pallets/subtensor/src/tests/networks.rs @@ -372,8 +372,6 @@ fn dissolve_clears_all_per_subnet_storages() { // Token / price / provided reserves TokenSymbol::::insert(net, b"XX".to_vec()); SubnetMovingPrice::::insert(net, substrate_fixed::types::I96F32::from_num(1)); - SubnetTaoProvided::::insert(net, TaoCurrency::from(1)); - SubnetAlphaInProvided::::insert(net, AlphaCurrency::from(1)); // TAO Flow SubnetTaoFlow::::insert(net, 0i64); @@ -536,8 +534,6 @@ fn dissolve_clears_all_per_subnet_storages() { // Token / price / provided reserves assert!(!TokenSymbol::::contains_key(net)); assert!(!SubnetMovingPrice::::contains_key(net)); - assert!(!SubnetTaoProvided::::contains_key(net)); - assert!(!SubnetAlphaInProvided::::contains_key(net)); // Subnet locks assert!(!TransferToggle::::contains_key(net)); diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index 52f761dbaf..e61df01106 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -881,9 +881,9 @@ fn test_remove_stake_insufficient_liquidity() { Error::::InsufficientLiquidity ); - // Mock provided liquidity - remove becomes successful - SubnetTaoProvided::::insert(netuid, TaoCurrency::from(amount_staked + 1)); - SubnetAlphaInProvided::::insert(netuid, AlphaCurrency::from(1)); + // Mock more liquidity - remove becomes successful + SubnetTAO::::insert(netuid, TaoCurrency::from(amount_staked + 1)); + SubnetAlphaIn::::insert(netuid, AlphaCurrency::from(1)); assert_ok!(SubtensorModule::remove_stake( RuntimeOrigin::signed(coldkey), hotkey, diff --git a/pallets/swap/src/pallet/balancer.rs b/pallets/swap/src/pallet/balancer.rs index a20891a9e1..ff0d049145 100644 --- a/pallets/swap/src/pallet/balancer.rs +++ b/pallets/swap/src/pallet/balancer.rs @@ -324,6 +324,40 @@ impl Balancer { 0u64 } } + + pub fn calculate_current_liquidity(&self, tao_reserve: u64, alpha_reserve: u64) -> u64 { + let base_numerator_x: u128 = alpha_reserve as u128; + let base_numerator_y: u128 = tao_reserve as u128; + let base_denominator: u128 = 1_u128; + let w1_fixed: u128 = self.get_base_weight().deconstruct() as u128; + let w2_fixed: u128 = self.get_quote_weight().deconstruct() as u128; + let scale: u128 = 10u128.pow(18); + + let accuracy = SafeInt::from(ACCURACY); + let exp_x = SafeInt::pow_ratio_scaled( + &SafeInt::from(base_numerator_x), + &SafeInt::from(base_denominator), + &SafeInt::from(w1_fixed), + &SafeInt::from(ACCURACY), + 1024, + &SafeInt::from(scale), + ) + .unwrap_or(SafeInt::from(0)); + let exp_y = SafeInt::pow_ratio_scaled( + &SafeInt::from(base_numerator_y), + &SafeInt::from(base_denominator), + &SafeInt::from(w2_fixed), + &SafeInt::from(ACCURACY), + 1024, + &SafeInt::from(scale), + ) + .unwrap_or(SafeInt::from(0)); + + ((exp_x * exp_y / accuracy.clone()).unwrap_or_default() / accuracy) + .unwrap_or_default() + .to_u64() + .unwrap_or(0) + } } // cargo test --package pallet-subtensor-swap --lib -- pallet::balancer::tests --nocapture @@ -793,7 +827,7 @@ mod tests { } #[test] - fn mul_round_zero_and_one() { + fn test_mul_round_zero_and_one() { let v = 1_000_000u128; // p = 0 -> always 0 @@ -804,7 +838,7 @@ mod tests { } #[test] - fn mul_round_half_behaviour() { + fn test_mul_round_half_behaviour() { // p = 1/2 let p = Perquintill::from_rational(1u128, 2u128); @@ -821,7 +855,7 @@ mod tests { } #[test] - fn mul_round_third_behaviour() { + fn test_mul_round_third_behaviour() { // p = 1/3 let p = Perquintill::from_rational(1u128, 3u128); @@ -833,7 +867,7 @@ mod tests { } #[test] - fn mul_round_large_values_simple_rational() { + fn test_mul_round_large_values_simple_rational() { // p = 7/10 (exact in perquintill: 0.7) let p = Perquintill::from_rational(7u128, 10u128); let v: u128 = 1_000_000_000_000_000_000; @@ -848,7 +882,7 @@ mod tests { } #[test] - fn mul_round_max_value_with_one() { + fn test_mul_round_max_value_with_one() { let v = u128::MAX; let p = ONE; @@ -858,7 +892,7 @@ mod tests { } #[test] - fn price_with_equal_weights_is_y_over_x() { + fn test_price_with_equal_weights_is_y_over_x() { // quote = 0.5, base = 0.5 -> w1 / w2 = 1, so price = y/x let quote = Perquintill::from_rational(1u128, 2u128); let bal = Balancer::new(quote).unwrap(); @@ -874,7 +908,7 @@ mod tests { } #[test] - fn price_scales_with_weight_ratio_two_to_one() { + fn test_price_scales_with_weight_ratio_two_to_one() { // Assume base = 1 - quote. // quote = 1/3 -> base = 2/3, so w1 / w2 = 2. // Then price = 2 * (y/x). @@ -891,7 +925,7 @@ mod tests { } #[test] - fn price_is_zero_when_y_is_zero() { + fn test_price_is_zero_when_y_is_zero() { // If y = 0, y/x = 0 so price must be 0 regardless of weights (for x > 0). let quote = Perquintill::from_rational(3u128, 10u128); // 0.3 let bal = Balancer::new(quote).unwrap(); @@ -904,7 +938,7 @@ mod tests { } #[test] - fn price_invariant_when_scaling_x_and_y_with_equal_weights() { + fn test_price_invariant_when_scaling_x_and_y_with_equal_weights() { // For equal weights, price(x, y) == price(kx, ky). let quote = Perquintill::from_rational(1u128, 2u128); // 0.5 let bal = Balancer::new(quote).unwrap(); @@ -922,7 +956,7 @@ mod tests { } #[test] - fn price_matches_formula_for_general_quote() { + fn test_price_matches_formula_for_general_quote() { // General check: price = (w1 / w2) * (y/x), // where w1 = base_weight, w2 = quote_weight. // Here we assume get_base_weight = 1 - quote. @@ -943,7 +977,7 @@ mod tests { } #[test] - fn price_high_values_non_equal_weights() { + fn test_price_high_values_non_equal_weights() { // Non-equal weights, high x and y (up to 21e15) let quote = Perquintill::from_rational(3u128, 10u128); // 0.3 let bal = Balancer::new(quote).unwrap(); @@ -961,4 +995,75 @@ mod tests { assert_abs_diff_eq!(price_f, expected_f, epsilon = 1e-9); } + + #[test] + fn test_calculate_current_liquidity() { + // Test case: quote weight (numerator), alpha, tao + // Outer test cases: w_quote + [ + 500_000_000_000_000_000_u64, + 500_000_000_001_000_000, + 499_999_999_999_000_000, + 500_000_000_100_000_000, + 500_000_001_000_000_000, + 500_000_010_000_000_000, + 500_000_100_000_000_000, + 500_001_000_000_000_000, + 500_010_000_000_000_000, + 500_100_000_000_000_000, + 501_000_000_000_000_000, + 510_000_000_000_000_000, + 100_000_000_000_000_000, + 100_000_000_001_000_000, + 200_000_000_000_000_000, + 300_000_000_000_000_000, + 400_000_000_000_000_000, + 600_000_000_000_000_000, + 700_000_000_000_000_000, + 800_000_000_000_000_000, + 899_999_999_999_000_000, + 900_000_000_000_000_000, + 102_337_248_363_782_924, + ] + .into_iter() + .for_each(|w_quote| { + [ + (0_u64, 0_u64), + (1_000_u64, 0_u64), + (0_u64, 1_000_u64), + (1_u64, 1_u64), + (2_u64, 1_u64), + (1_u64, 2_u64), + (1_000_u64, 1_000_u64), + (2_000_u64, 1_000_u64), + (1_000_u64, 2_000_u64), + (1_000_000_u64, 1_000_000_u64), + (2_000_000_u64, 1_000_000_u64), + (1_000_000_u64, 2_000_000_u64), + (1_000_000_000_u64, 1_000_000_000_u64), + (2_000_000_000_u64, 1_000_000_000_u64), + (1_000_000_000_u64, 2_000_000_000_u64), + (1_000_000_000_000_u64, 1_000_u64), + (1_000_u64, 1_000_000_000_000_u64), + (1_000_000_000_000_000_u64, 1_u64), + (1_u64, 1_000_000_000_000_000_u64), + (1_000_000_000_000_000_u64, 1_000_u64), + (1_000_u64, 1_000_000_000_000_000_u64), + (21_000_000_000_000_000_u64, 21_000_000_000_000_000_u64), + ] + .into_iter() + .for_each(|(alpha, tao)| { + let quote = Perquintill::from_rational(w_quote, ACCURACY); + let bal = Balancer::new(quote).unwrap(); + + let actual = bal.calculate_current_liquidity(tao, alpha); + + let w1 = w_quote as f64 / ACCURACY as f64; + let w2 = (ACCURACY - w_quote) as f64 / ACCURACY as f64; + let expected = ((alpha as f64).powf(w1) * (tao as f64).powf(w2)) as u64; + + assert_abs_diff_eq!(actual, expected, epsilon = expected / 1_000_000_000); + }); + }); + } } diff --git a/pallets/swap/src/pallet/hooks.rs b/pallets/swap/src/pallet/hooks.rs new file mode 100644 index 0000000000..90989d5f52 --- /dev/null +++ b/pallets/swap/src/pallet/hooks.rs @@ -0,0 +1,30 @@ +use frame_support::pallet_macros::pallet_section; + +#[pallet_section] +mod hooks { + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_initialize(block_number: BlockNumberFor) -> Weight { + Weight::from_parts(0, 0) + } + + fn on_finalize(_block_number: BlockNumberFor) {} + + fn on_runtime_upgrade() -> Weight { + // --- Migrate storage + let mut weight = Weight::from_parts(0, 0); + + weight = weight + // Cleanup uniswap v3 and migrate to balancer + .saturating_add( + migrations::migrate_swapv3_to_balancer::migrate_swapv3_to_balancer::(), + ); + weight + } + + #[cfg(feature = "try-runtime")] + fn try_state(_n: BlockNumberFor) -> Result<(), sp_runtime::TryRuntimeError> { + Ok(()) + } + } +} diff --git a/pallets/swap/src/pallet/impls.rs b/pallets/swap/src/pallet/impls.rs index 54c3c1c5f6..e559e5aad2 100644 --- a/pallets/swap/src/pallet/impls.rs +++ b/pallets/swap/src/pallet/impls.rs @@ -66,18 +66,21 @@ impl Pallet { } // Insert 0.5 into SwapBalancer - let reserve_weight = + let balancer = Balancer::new(Perquintill::from_rational(1_u64, 2_u64)).map_err(|err| match err { BalancerError::InvalidValue => Error::::ReservesOutOfBalance, })?; - SwapBalancer::::insert(netuid, reserve_weight); + SwapBalancer::::insert(netuid, balancer.clone()); + + // Insert current liquidity + let tao_reserve = T::TaoReserve::reserve(netuid.into()); + let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); + let liquidity = + balancer.calculate_current_liquidity(u64::from(tao_reserve), u64::from(alpha_reserve)); + CurrentLiquidity::::insert(netuid, liquidity); // TODO: Review when/if we have user liquidity // Initialize the pal-swap: - // Reserves are re-purposed, nothing to set, just query values for creation - // of protocol position - // let tao_reserve = T::TaoReserve::reserve(netuid.into()); - // let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); // Set initial (protocol owned) liquidity and positions // Protocol liquidity makes one position diff --git a/pallets/swap/src/pallet/migrations/migrate_swapv3_to_balancer.rs b/pallets/swap/src/pallet/migrations/migrate_swapv3_to_balancer.rs new file mode 100644 index 0000000000..5a339ed2c6 --- /dev/null +++ b/pallets/swap/src/pallet/migrations/migrate_swapv3_to_balancer.rs @@ -0,0 +1,71 @@ +use super::*; +use crate::HasMigrationRun; +use frame_support::{storage_alias, traits::Get, weights::Weight}; +use scale_info::prelude::string::String; +use substrate_fixed::types::U64F64; + +pub mod deprecated_swap_maps { + use super::*; + + #[storage_alias] + pub type AlphaSqrtPrice = + StorageMap, Twox64Concat, NetUid, U64F64, ValueQuery>; + + /// TAO reservoir for scraps of protocol claimed fees. + #[storage_alias] + pub type ScrapReservoirTao = + StorageMap, Twox64Concat, NetUid, TaoCurrency, ValueQuery>; + + /// Alpha reservoir for scraps of protocol claimed fees. + #[storage_alias] + pub type ScrapReservoirAlpha = + StorageMap, Twox64Concat, NetUid, AlphaCurrency, ValueQuery>; +} + +pub fn migrate_swapv3_to_balancer() -> Weight { + let migration_name = BoundedVec::truncate_from(b"migrate_swapv3_to_balancer".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), + ); + + // ------------------------------ + // Step 1: Clear Map entries + // ------------------------------ + remove_prefix::("Swap", "AlphaSqrtPrice", &mut weight); + remove_prefix::("Swap", "CurrentTick", &mut weight); + remove_prefix::("Swap", "FeeGlobalTao", &mut weight); + remove_prefix::("Swap", "FeeGlobalAlpha", &mut weight); + // Scrap reservoirs can be just cleaned because they are already included in reserves + remove_prefix::("Swap", "ScrapReservoirTao", &mut weight); + remove_prefix::("Swap", "ScrapReservoirAlpha", &mut weight); + remove_prefix::("Swap", "Ticks", &mut weight); + remove_prefix::("Swap", "TickIndexBitmapWords", &mut weight); + remove_prefix::("Swap", "SwapV3Initialized", &mut weight); + remove_prefix::("Swap", "CurrentLiquidity", &mut weight); + remove_prefix::("Swap", "Positions", &mut weight); + + // ------------------------------ + // Step 2: Mark Migration as Completed + // ------------------------------ + + HasMigrationRun::::insert(&migration_name, true); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + + log::info!( + "Migration '{:?}' completed successfully.", + String::from_utf8_lossy(&migration_name) + ); + + weight +} diff --git a/pallets/swap/src/pallet/migrations/mod.rs b/pallets/swap/src/pallet/migrations/mod.rs new file mode 100644 index 0000000000..3da637331d --- /dev/null +++ b/pallets/swap/src/pallet/migrations/mod.rs @@ -0,0 +1,25 @@ +use super::*; +use frame_support::pallet_prelude::Weight; +use sp_io::KillStorageResult; +use sp_io::hashing::twox_128; +use sp_io::storage::clear_prefix; + +pub mod migrate_swapv3_to_balancer; + +pub(crate) fn remove_prefix(module: &str, old_map: &str, weight: &mut Weight) { + let mut prefix = Vec::new(); + prefix.extend_from_slice(&twox_128(module.as_bytes())); + prefix.extend_from_slice(&twox_128(old_map.as_bytes())); + + let removal_results = clear_prefix(&prefix, Some(u32::MAX)); + let removed_entries_count = match removal_results { + KillStorageResult::AllRemoved(removed) => removed as u64, + KillStorageResult::SomeRemaining(removed) => { + log::info!("Failed To Remove Some Items During migration"); + println!("Failed To Remove Some Items During migration"); + removed as u64 + } + }; + + *weight = (*weight).saturating_add(T::DbWeight::get().writes(removed_entries_count)); +} diff --git a/pallets/swap/src/pallet/mod.rs b/pallets/swap/src/pallet/mod.rs index 1ee73e0448..c46fa59d93 100644 --- a/pallets/swap/src/pallet/mod.rs +++ b/pallets/swap/src/pallet/mod.rs @@ -17,11 +17,16 @@ use crate::{ pub use pallet::*; mod balancer; +mod hooks; mod impls; +pub mod migrations; mod swap_step; #[cfg(test)] mod tests; +// Define a maximum length for the migration key +type MigrationKeyMaxLen = ConstU32<128>; + #[allow(clippy::module_inception)] #[frame_support::pallet] #[allow(clippy::expect_used)] @@ -107,9 +112,9 @@ mod pallet { // #[pallet::storage] // pub type CurrentTick = StorageMap<_, Twox64Concat, NetUid, TickIndex, ValueQuery>; - // /// Storage for the current liquidity amount for each subnet. - // #[pallet::storage] - // pub type CurrentLiquidity = StorageMap<_, Twox64Concat, NetUid, u64, ValueQuery>; + /// Storage for the current liquidity amount for each subnet. + #[pallet::storage] + pub type CurrentLiquidity = StorageMap<_, Twox64Concat, NetUid, u64, ValueQuery>; /// Indicates whether a subnet has been switched to V3 swap from V2. /// If `true`, the subnet is permanently on V3 swap mode allowing add/remove liquidity @@ -135,28 +140,6 @@ mod pallet { #[pallet::storage] pub type LastPositionId = StorageValue<_, u128, ValueQuery>; - // /// Tick index bitmap words storage - // #[pallet::storage] - // pub type TickIndexBitmapWords = StorageNMap< - // _, - // ( - // NMapKey, // Subnet ID - // NMapKey, // Layer level - // NMapKey, // word index - // ), - // u128, - // ValueQuery, - // >; - - // /// TAO reservoir for scraps of protocol claimed fees. - // #[pallet::storage] - // pub type ScrapReservoirTao = StorageMap<_, Twox64Concat, NetUid, TaoCurrency, ValueQuery>; - - // /// Alpha reservoir for scraps of protocol claimed fees. - // #[pallet::storage] - // pub type ScrapReservoirAlpha = - // StorageMap<_, Twox64Concat, NetUid, AlphaCurrency, ValueQuery>; - //////////////////////////////////////////////////// // Balancer (PalSwap) maps and variables @@ -182,6 +165,11 @@ mod pallet { #[pallet::storage] pub type FeesAlpha = StorageMap<_, Twox64Concat, NetUid, AlphaCurrency, ValueQuery>; + /// --- Storage for migration run status + #[pallet::storage] + pub type HasMigrationRun = + StorageMap<_, Identity, BoundedVec, bool, ValueQuery>; + #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { diff --git a/pallets/swap/src/pallet/tests.rs b/pallets/swap/src/pallet/tests.rs index 4360533cd0..572fb4cbe6 100644 --- a/pallets/swap/src/pallet/tests.rs +++ b/pallets/swap/src/pallet/tests.rs @@ -6,15 +6,8 @@ )] use approx::assert_abs_diff_eq; -use frame_support::{ - //assert_err, - assert_noop, - assert_ok, -}; -use sp_arithmetic::{ - //helpers_128bit, - Perquintill, -}; +use frame_support::{assert_noop, assert_ok}; +use sp_arithmetic::Perquintill; use sp_runtime::DispatchError; use substrate_fixed::types::U64F64; use subtensor_runtime_common::NetUid; @@ -2596,3 +2589,38 @@ fn as_tuple( u64::from(a_rem), ) } + +// cargo test --package pallet-subtensor-swap --lib -- pallet::tests::test_migrate_swapv3_to_balancer --exact --nocapture +#[test] +fn test_migrate_swapv3_to_balancer() { + use crate::migrations::migrate_swapv3_to_balancer::deprecated_swap_maps; + use substrate_fixed::types::U64F64; + + new_test_ext().execute_with(|| { + let migration = + crate::migrations::migrate_swapv3_to_balancer::migrate_swapv3_to_balancer::; + + // Insert deprecated maps values + deprecated_swap_maps::AlphaSqrtPrice::::insert( + NetUid::from(1), + U64F64::from_num(1.23), + ); + deprecated_swap_maps::ScrapReservoirTao::::insert( + NetUid::from(1), + TaoCurrency::from(9876), + ); + deprecated_swap_maps::ScrapReservoirAlpha::::insert( + NetUid::from(1), + AlphaCurrency::from(9876), + ); + + // Run migration + migration(); + + // Test that values are removed from state + assert!(!deprecated_swap_maps::AlphaSqrtPrice::::contains_key( + NetUid::from(1) + ),); + assert!(!deprecated_swap_maps::ScrapReservoirAlpha::::contains_key(NetUid::from(1)),); + }); +} From c4e21e716c95b73627526ab9a48d0aa8e2278881 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 5 Jan 2026 18:26:08 -0500 Subject: [PATCH 100/240] Add test for CurrentLiquidity initialization --- pallets/swap/src/pallet/tests.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/pallets/swap/src/pallet/tests.rs b/pallets/swap/src/pallet/tests.rs index 572fb4cbe6..5df4fa29f0 100644 --- a/pallets/swap/src/pallet/tests.rs +++ b/pallets/swap/src/pallet/tests.rs @@ -7,7 +7,7 @@ use approx::assert_abs_diff_eq; use frame_support::{assert_noop, assert_ok}; -use sp_arithmetic::Perquintill; +use sp_arithmetic::{Perquintill, helpers_128bit}; use sp_runtime::DispatchError; use substrate_fixed::types::U64F64; use subtensor_runtime_common::NetUid; @@ -433,6 +433,16 @@ fn test_swap_initialization() { Perquintill::from_rational(1_u64, 2_u64), ); + // Current liquidity is initialized + let expected_liquidity = + helpers_128bit::sqrt((tao.to_u64() as u128).saturating_mul(alpha.to_u64() as u128)) + as u64; + assert_abs_diff_eq!( + CurrentLiquidity::::get(netuid), + expected_liquidity, + epsilon = 1 + ); + // TODO: Revise when user liquidity is available // // Calculate expected liquidity // let expected_liquidity = From f97e56c8d6282f5e9e7bc7771619f606eb54f848 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Tue, 6 Jan 2026 17:41:48 -0500 Subject: [PATCH 101/240] Fix exponentiation in liquidity calculation --- Cargo.lock | 2 +- Cargo.toml | 2 +- pallets/swap/src/pallet/balancer.rs | 41 ++++++++++++++++++++--------- pallets/swap/src/position.rs | 5 +--- 4 files changed, 32 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e6759bb91b..e2d4b2897f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14586,7 +14586,7 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "safe-bigmath" version = "0.3.0" -source = "git+https://github.com/sam0x17/safe-math#0a94bf14f8235f82d30d23d91e0b63ed28861ece" +source = "git+https://github.com/gztensor/safe-bigmath.git?rev=2a2b6e4#2a2b6e4e404948082c99e6ec62d5b052b1cdd9d1" dependencies = [ "crabtime", "num-bigint", diff --git a/Cargo.toml b/Cargo.toml index 6ed0ca0205..b2347303c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,7 +61,7 @@ pallet-subtensor-swap-runtime-api = { path = "pallets/swap/runtime-api", default pallet-subtensor-swap-rpc = { path = "pallets/swap/rpc", default-features = false } procedural-fork = { path = "support/procedural-fork", default-features = false } safe-math = { path = "primitives/safe-math", default-features = false } -safe-bigmath = { version = "0.3.0", package = "safe-bigmath", git = "https://github.com/sam0x17/safe-math", default-features = false } +safe-bigmath = { rev = "2a2b6e4", package = "safe-bigmath", git = "https://github.com/gztensor/safe-bigmath.git", default-features = false } share-pool = { path = "primitives/share-pool", default-features = false } subtensor-macros = { path = "support/macros", default-features = false } subtensor-custom-rpc = { default-features = false, path = "pallets/subtensor/rpc" } diff --git a/pallets/swap/src/pallet/balancer.rs b/pallets/swap/src/pallet/balancer.rs index ff0d049145..e56426acd3 100644 --- a/pallets/swap/src/pallet/balancer.rs +++ b/pallets/swap/src/pallet/balancer.rs @@ -325,35 +325,41 @@ impl Balancer { } } + /// Calculates current liquidity from alpha and tao reserves using the formula: + /// L = x^w1 * y^w2 + /// where + /// x - alpha reserve + /// y - tao reserve + /// w1 - base weight + /// w2 - quote weight pub fn calculate_current_liquidity(&self, tao_reserve: u64, alpha_reserve: u64) -> u64 { let base_numerator_x: u128 = alpha_reserve as u128; let base_numerator_y: u128 = tao_reserve as u128; - let base_denominator: u128 = 1_u128; + // let base_denominator: u128 = 1_u128; let w1_fixed: u128 = self.get_base_weight().deconstruct() as u128; let w2_fixed: u128 = self.get_quote_weight().deconstruct() as u128; - let scale: u128 = 10u128.pow(18); + let scale = SafeInt::from(10u128.pow(18)); - let accuracy = SafeInt::from(ACCURACY); - let exp_x = SafeInt::pow_ratio_scaled( + let exp_x = SafeInt::pow_bigint_base( &SafeInt::from(base_numerator_x), - &SafeInt::from(base_denominator), &SafeInt::from(w1_fixed), &SafeInt::from(ACCURACY), 1024, - &SafeInt::from(scale), + &scale, ) .unwrap_or(SafeInt::from(0)); - let exp_y = SafeInt::pow_ratio_scaled( + let exp_y = SafeInt::pow_bigint_base( &SafeInt::from(base_numerator_y), - &SafeInt::from(base_denominator), &SafeInt::from(w2_fixed), &SafeInt::from(ACCURACY), 1024, - &SafeInt::from(scale), + &scale, ) .unwrap_or(SafeInt::from(0)); - ((exp_x * exp_y / accuracy.clone()).unwrap_or_default() / accuracy) + // 0.5 scaled for rounding to the nearest integer + let round_nearest_offset = (scale.clone() / SafeInt::from(2)).unwrap_or_default(); + ((((exp_x * exp_y) / scale.clone()).unwrap_or_default() + round_nearest_offset) / scale) .unwrap_or_default() .to_u64() .unwrap_or(0) @@ -1049,7 +1055,18 @@ mod tests { (1_u64, 1_000_000_000_000_000_u64), (1_000_000_000_000_000_u64, 1_000_u64), (1_000_u64, 1_000_000_000_000_000_u64), + (1_000_u64, 21_000_000_000_000_000_u64), + (21_000_000_000_000_000_u64, 1_000_u64), + (1_u64, 21_000_000_000_000_000_u64), + (21_000_000_000_000_000_u64, 1_u64), + (2_u64, 21_000_000_000_000_000_u64), + (21_000_000_000_000_000_u64, 2_u64), (21_000_000_000_000_000_u64, 21_000_000_000_000_000_u64), + (2, u64::MAX), + (u64::MAX, 2), + (2, u64::MAX - 1), + (u64::MAX - 1, 2), + (u64::MAX, u64::MAX), ] .into_iter() .for_each(|(alpha, tao)| { @@ -1060,9 +1077,9 @@ mod tests { let w1 = w_quote as f64 / ACCURACY as f64; let w2 = (ACCURACY - w_quote) as f64 / ACCURACY as f64; - let expected = ((alpha as f64).powf(w1) * (tao as f64).powf(w2)) as u64; + let expected = (((alpha as f64).powf(w2) * (tao as f64).powf(w1)) + 0.5) as u64; - assert_abs_diff_eq!(actual, expected, epsilon = expected / 1_000_000_000); + assert_abs_diff_eq!(actual, expected, epsilon = expected / 1_000_000_000_000); }); }); } diff --git a/pallets/swap/src/position.rs b/pallets/swap/src/position.rs index a514e60e8f..071d11c3d9 100644 --- a/pallets/swap/src/position.rs +++ b/pallets/swap/src/position.rs @@ -10,10 +10,7 @@ use subtensor_runtime_common::NetUid; use crate::pallet::{Config, Error, LastPositionId}; /// Position designates one liquidity position. -/// -/// Alpha price is expressed in rao units per one 10^9 unit. For example, -/// price 1_000_000 is equal to 0.001 TAO per Alpha. -#[freeze_struct("3f68e54e8969f976")] +#[freeze_struct("6d7ff015e0a73860")] #[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen, Default)] #[scale_info(skip_type_params(T))] pub struct Position { From 7634aee9851bf44892332cfdf220b1bb98c3cafe Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Wed, 7 Jan 2026 10:48:13 -0500 Subject: [PATCH 102/240] Update to most recent bigmath --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e2d4b2897f..e42f5d6723 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14586,7 +14586,7 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "safe-bigmath" version = "0.3.0" -source = "git+https://github.com/gztensor/safe-bigmath.git?rev=2a2b6e4#2a2b6e4e404948082c99e6ec62d5b052b1cdd9d1" +source = "git+https://github.com/sam0x17/safe-math?rev=1a2aab0#1a2aab0456a80e06a6d2228ef18f96d380d413b4" dependencies = [ "crabtime", "num-bigint", diff --git a/Cargo.toml b/Cargo.toml index b2347303c9..9c0dc8c837 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,7 +61,7 @@ pallet-subtensor-swap-runtime-api = { path = "pallets/swap/runtime-api", default pallet-subtensor-swap-rpc = { path = "pallets/swap/rpc", default-features = false } procedural-fork = { path = "support/procedural-fork", default-features = false } safe-math = { path = "primitives/safe-math", default-features = false } -safe-bigmath = { rev = "2a2b6e4", package = "safe-bigmath", git = "https://github.com/gztensor/safe-bigmath.git", default-features = false } +safe-bigmath = { rev = "1a2aab0", package = "safe-bigmath", git = "https://github.com/sam0x17/safe-math", default-features = false } share-pool = { path = "primitives/share-pool", default-features = false } subtensor-macros = { path = "support/macros", default-features = false } subtensor-custom-rpc = { default-features = false, path = "pallets/subtensor/rpc" } From 0bd3c78cf9c6a1d6d6edf140123ab061686083ef Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 7 Jan 2026 14:08:46 -0500 Subject: [PATCH 103/240] local --- Cargo.lock | 3 +-- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e42f5d6723..9d9787c3c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8663,7 +8663,7 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" dependencies = [ - "proc-macro-crate 3.4.0", + "proc-macro-crate 1.1.3", "proc-macro2", "quote", "syn 2.0.106", @@ -14586,7 +14586,6 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "safe-bigmath" version = "0.3.0" -source = "git+https://github.com/sam0x17/safe-math?rev=1a2aab0#1a2aab0456a80e06a6d2228ef18f96d380d413b4" dependencies = [ "crabtime", "num-bigint", diff --git a/Cargo.toml b/Cargo.toml index 9c0dc8c837..3b127e7b58 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,7 +61,7 @@ pallet-subtensor-swap-runtime-api = { path = "pallets/swap/runtime-api", default pallet-subtensor-swap-rpc = { path = "pallets/swap/rpc", default-features = false } procedural-fork = { path = "support/procedural-fork", default-features = false } safe-math = { path = "primitives/safe-math", default-features = false } -safe-bigmath = { rev = "1a2aab0", package = "safe-bigmath", git = "https://github.com/sam0x17/safe-math", default-features = false } +safe-bigmath = { package = "safe-bigmath", default-features = false, path = "../safe-bigmath" } share-pool = { path = "primitives/share-pool", default-features = false } subtensor-macros = { path = "support/macros", default-features = false } subtensor-custom-rpc = { default-features = false, path = "pallets/subtensor/rpc" } From 9fed25e91967191093ba2c6a4b02300e848b7161 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Thu, 8 Jan 2026 11:49:08 -0500 Subject: [PATCH 104/240] Collect protocol fees in adjust_protocol_liquidity and add to reserves --- .../subtensor/src/coinbase/run_coinbase.rs | 9 +++-- pallets/swap-interface/src/lib.rs | 2 +- pallets/swap/src/pallet/impls.rs | 29 ++++++++++---- pallets/swap/src/pallet/mod.rs | 24 ------------ pallets/swap/src/pallet/tests.rs | 39 +++++++++++++++++++ 5 files changed, 68 insertions(+), 35 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 2e7153e407..19a074fa47 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -67,7 +67,8 @@ impl Pallet { let tao_to_swap_with: TaoCurrency = tou64!(excess_tao.get(netuid_i).unwrap_or(&asfloat!(0))).into(); - T::SwapInterface::adjust_protocol_liquidity(*netuid_i, tao_in_i, alpha_in_i); + let (actal_injected_tao, actal_injected_alpha) = + T::SwapInterface::adjust_protocol_liquidity(*netuid_i, tao_in_i, alpha_in_i); if tao_to_swap_with > TaoCurrency::ZERO { let buy_swap_result = Self::swap_tao_for_alpha( @@ -87,7 +88,8 @@ impl Pallet { AlphaCurrency::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); + // Reserves also received fees in addition to alpha_in_i + *total = total.saturating_add(actal_injected_alpha); }); // Inject TAO in. @@ -95,7 +97,8 @@ impl Pallet { 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); + // Reserves also received fees in addition to injected_tao + *total = total.saturating_add(actal_injected_tao); }); TotalStake::::mutate(|total| { *total = total.saturating_add(injected_tao); diff --git a/pallets/swap-interface/src/lib.rs b/pallets/swap-interface/src/lib.rs index 4b2069b119..992192127f 100644 --- a/pallets/swap-interface/src/lib.rs +++ b/pallets/swap-interface/src/lib.rs @@ -46,7 +46,7 @@ pub trait SwapHandler { netuid: NetUid, tao_delta: TaoCurrency, alpha_delta: AlphaCurrency, - ); + ) -> (TaoCurrency, AlphaCurrency); fn is_user_liquidity_enabled(netuid: NetUid) -> bool; fn dissolve_all_liquidity_providers(netuid: NetUid) -> DispatchResult; fn toggle_user_liquidity(netuid: NetUid, enabled: bool); diff --git a/pallets/swap/src/pallet/impls.rs b/pallets/swap/src/pallet/impls.rs index e559e5aad2..bbba296d59 100644 --- a/pallets/swap/src/pallet/impls.rs +++ b/pallets/swap/src/pallet/impls.rs @@ -106,11 +106,21 @@ impl Pallet { } /// Adjusts protocol liquidity with new values of TAO and Alpha reserve + /// Returns actually added Tao and Alpha, which includes fees pub(super) fn adjust_protocol_liquidity( netuid: NetUid, tao_delta: TaoCurrency, alpha_delta: AlphaCurrency, - ) { + ) -> (TaoCurrency, AlphaCurrency) { + // Collect fees + // TODO: Revise when user liquidity is available + let tao_fees = FeesTao::::get(netuid); + let alpha_fees = FeesAlpha::::get(netuid); + FeesTao::::insert(netuid, TaoCurrency::ZERO); + FeesAlpha::::insert(netuid, AlphaCurrency::ZERO); + let actual_tao_delta = tao_delta.saturating_add(tao_fees); + let actual_alpha_delta = alpha_delta.saturating_add(alpha_fees); + // Get reserves let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); let tao_reserve = T::TaoReserve::reserve(netuid.into()); @@ -121,8 +131,8 @@ impl Pallet { .update_weights_for_added_liquidity( u64::from(tao_reserve), u64::from(alpha_reserve), - u64::from(tao_delta), - u64::from(alpha_delta), + u64::from(actual_tao_delta), + u64::from(actual_alpha_delta), ) .is_err() { @@ -131,11 +141,16 @@ impl Pallet { netuid, tao_reserve, alpha_reserve, - tao_delta, - alpha_delta + actual_tao_delta, + actual_alpha_delta ); + // Return fees back into fee storage and return zeroes + FeesTao::::insert(netuid, tao_fees); + FeesAlpha::::insert(netuid, alpha_fees); + (TaoCurrency::ZERO, AlphaCurrency::ZERO) } else { SwapBalancer::::insert(netuid, balancer); + (actual_tao_delta, actual_alpha_delta) } } @@ -681,8 +696,8 @@ impl SwapHandler for Pallet { netuid: NetUid, tao_delta: TaoCurrency, alpha_delta: AlphaCurrency, - ) { - Self::adjust_protocol_liquidity(netuid, tao_delta, alpha_delta); + ) -> (TaoCurrency, AlphaCurrency) { + Self::adjust_protocol_liquidity(netuid, tao_delta, alpha_delta) } fn is_user_liquidity_enabled(netuid: NetUid) -> bool { diff --git a/pallets/swap/src/pallet/mod.rs b/pallets/swap/src/pallet/mod.rs index c46fa59d93..e4c6bbae2f 100644 --- a/pallets/swap/src/pallet/mod.rs +++ b/pallets/swap/src/pallet/mod.rs @@ -88,30 +88,6 @@ mod pallet { #[pallet::storage] pub type FeeRate = StorageMap<_, Twox64Concat, NetUid, u16, ValueQuery, DefaultFeeRate>; - // // Global accrued fees in tao per subnet - // #[pallet::storage] - // pub type FeeGlobalTao = StorageMap<_, Twox64Concat, NetUid, U64F64, ValueQuery>; - - // // Global accrued fees in alpha per subnet - // #[pallet::storage] - // pub type FeeGlobalAlpha = StorageMap<_, Twox64Concat, NetUid, U64F64, ValueQuery>; - - // /// Storage for all ticks, using subnet ID as the primary key and tick index as the secondary key - // #[pallet::storage] - // pub type Ticks = StorageDoubleMap<_, Twox64Concat, NetUid, Twox64Concat, TickIndex, Tick>; - - // /// Storage to determine whether swap V3 was initialized for a specific subnet. - // #[pallet::storage] - // pub type SwapV3Initialized = StorageMap<_, Twox64Concat, NetUid, bool, ValueQuery>; - - // /// Storage for the square root price of Alpha token for each subnet. - // #[pallet::storage] - // pub type AlphaSqrtPrice = StorageMap<_, Twox64Concat, NetUid, U64F64, ValueQuery>; - - // /// Storage for the current price tick. - // #[pallet::storage] - // pub type CurrentTick = StorageMap<_, Twox64Concat, NetUid, TickIndex, ValueQuery>; - /// Storage for the current liquidity amount for each subnet. #[pallet::storage] pub type CurrentLiquidity = StorageMap<_, Twox64Concat, NetUid, u64, ValueQuery>; diff --git a/pallets/swap/src/pallet/tests.rs b/pallets/swap/src/pallet/tests.rs index 5df4fa29f0..c4bb5c143c 100644 --- a/pallets/swap/src/pallet/tests.rs +++ b/pallets/swap/src/pallet/tests.rs @@ -365,6 +365,45 @@ mod dispatchables { }); } + /// Collects the fees and adds them to protocol liquidity + /// cargo test --package pallet-subtensor-swap --lib -- pallet::tests::dispatchables::test_adjust_protocol_liquidity_collects_fees --exact --nocapture + #[test] + fn test_adjust_protocol_liquidity_collects_fees() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1); + + let tao_delta = TaoCurrency::ZERO; + let alpha_delta = AlphaCurrency::ZERO; + + // Initialize reserves and price + // 0.1 price + let tao = TaoCurrency::from(1_000_000_000_u64); + let alpha = AlphaCurrency::from(10_000_000_000_u64); + TaoReserve::set_mock_reserve(netuid, tao); + AlphaReserve::set_mock_reserve(netuid, alpha); + + // Insert fees + let tao_fees = TaoCurrency::from(1_000); + let alpha_fees = AlphaCurrency::from(1_000); + FeesTao::::insert(netuid, tao_fees); + FeesAlpha::::insert(netuid, alpha_fees); + + // Adjust reserves + let (actual_tao_delta, actual_alpha_delta) = + Swap::adjust_protocol_liquidity(netuid, tao_delta, alpha_delta); + TaoReserve::set_mock_reserve(netuid, tao + tao_delta); + AlphaReserve::set_mock_reserve(netuid, alpha + alpha_delta); + + // Check that returned reserve deltas are correct (include fees) + assert_eq!(actual_tao_delta, tao_fees); + assert_eq!(actual_alpha_delta, alpha_fees); + + // Check that fees got reset + assert_eq!(FeesTao::::get(netuid), TaoCurrency::ZERO); + assert_eq!(FeesAlpha::::get(netuid), AlphaCurrency::ZERO); + }); + } + // #[test] // fn test_toggle_user_liquidity() { // new_test_ext().execute_with(|| { From 71995f07cac61c9440a4f97d37443c19c27fe1f7 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 8 Jan 2026 13:54:13 -0500 Subject: [PATCH 105/240] fix --- Cargo.lock | 24 +---------------------- Cargo.toml | 2 +- pallets/swap/src/pallet/migrations/mod.rs | 2 +- 3 files changed, 3 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9d9787c3c8..3f1716f52e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2520,28 +2520,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crabtime" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81605e11aed454fb3838bc408f091d17f2f6d31613fb897f561a45bc8fb98378" -dependencies = [ - "crabtime-internal", -] - -[[package]] -name = "crabtime-internal" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e49bfb43e6dfb9026c045fc1fd5cde79f4927ea6d1d89a2d346c55879e85900c" -dependencies = [ - "proc-macro2", - "quote", - "rustc_version 0.4.1", - "syn 2.0.106", - "toml 0.8.23", -] - [[package]] name = "cranelift-bforest" version = "0.95.1" @@ -14586,8 +14564,8 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "safe-bigmath" version = "0.3.0" +source = "git+https://github.com/sam0x17/safe-bigmath#1da0a09c5bcf143fa7c464b431bfaeff5476b080" dependencies = [ - "crabtime", "num-bigint", "num-integer", "num-traits", diff --git a/Cargo.toml b/Cargo.toml index 3b127e7b58..7b3cf277ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,7 +61,7 @@ pallet-subtensor-swap-runtime-api = { path = "pallets/swap/runtime-api", default pallet-subtensor-swap-rpc = { path = "pallets/swap/rpc", default-features = false } procedural-fork = { path = "support/procedural-fork", default-features = false } safe-math = { path = "primitives/safe-math", default-features = false } -safe-bigmath = { package = "safe-bigmath", default-features = false, path = "../safe-bigmath" } +safe-bigmath = { package = "safe-bigmath", default-features = false, git = "https://github.com/sam0x17/safe-bigmath" } share-pool = { path = "primitives/share-pool", default-features = false } subtensor-macros = { path = "support/macros", default-features = false } subtensor-custom-rpc = { default-features = false, path = "pallets/subtensor/rpc" } diff --git a/pallets/swap/src/pallet/migrations/mod.rs b/pallets/swap/src/pallet/migrations/mod.rs index 3da637331d..d34626f05e 100644 --- a/pallets/swap/src/pallet/migrations/mod.rs +++ b/pallets/swap/src/pallet/migrations/mod.rs @@ -3,6 +3,7 @@ use frame_support::pallet_prelude::Weight; use sp_io::KillStorageResult; use sp_io::hashing::twox_128; use sp_io::storage::clear_prefix; +use sp_std::vec::Vec; pub mod migrate_swapv3_to_balancer; @@ -16,7 +17,6 @@ pub(crate) fn remove_prefix(module: &str, old_map: &str, weight: &mut KillStorageResult::AllRemoved(removed) => removed as u64, KillStorageResult::SomeRemaining(removed) => { log::info!("Failed To Remove Some Items During migration"); - println!("Failed To Remove Some Items During migration"); removed as u64 } }; From b2744c84c7fd431562cf8ab26f91b489212755dd Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Thu, 8 Jan 2026 15:59:51 -0500 Subject: [PATCH 106/240] Remove todo() macros from position.rs file --- pallets/swap/src/position.rs | 49 +++--------------------------------- 1 file changed, 4 insertions(+), 45 deletions(-) diff --git a/pallets/swap/src/position.rs b/pallets/swap/src/position.rs index 071d11c3d9..21148d8b52 100644 --- a/pallets/swap/src/position.rs +++ b/pallets/swap/src/position.rs @@ -54,56 +54,15 @@ impl Position { /// returns tuple of (TAO, Alpha) /// pub fn to_token_amounts(&self, _price_curr: U64F64) -> Result<(u64, u64), Error> { - // let one = U64F64::saturating_from_num(1); - - // let sqrt_price_low = self - // .tick_low - // .try_to_sqrt_price() - // .map_err(|_| Error::::InvalidTickRange)?; - // let sqrt_price_high = self - // .tick_high - // .try_to_sqrt_price() - // .map_err(|_| Error::::InvalidTickRange)?; - // let liquidity_fixed = U64F64::saturating_from_num(self.liquidity); - - // Ok(if sqrt_price_curr < sqrt_price_low { - // ( - // 0, - // liquidity_fixed - // .saturating_mul( - // one.safe_div(sqrt_price_low) - // .saturating_sub(one.safe_div(sqrt_price_high)), - // ) - // .saturating_to_num::(), - // ) - // } else if sqrt_price_curr > sqrt_price_high { - // ( - // liquidity_fixed - // .saturating_mul(sqrt_price_high.saturating_sub(sqrt_price_low)) - // .saturating_to_num::(), - // 0, - // ) - // } else { - // ( - // liquidity_fixed - // .saturating_mul(sqrt_price_curr.saturating_sub(sqrt_price_low)) - // .saturating_to_num::(), - // liquidity_fixed - // .saturating_mul( - // one.safe_div(sqrt_price_curr) - // .saturating_sub(one.safe_div(sqrt_price_high)), - // ) - // .saturating_to_num::(), - // ) - // }) - - todo!() + // TODO: Revise when user liquidity is available + Ok((0, 0)) } /// Collect fees for a position /// Updates the position pub fn collect_fees(&mut self) -> (u64, u64) { - todo!() + // TODO: Revise when user liquidity is available + (0, 0) } } From 1dd3e672e700271b5046f7320ef0a32327af637a Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Thu, 8 Jan 2026 16:14:18 -0500 Subject: [PATCH 107/240] Address a todo in test_remove_stake_edge_alpha --- pallets/transaction-fee/src/tests/mod.rs | 148 +++++++++++------------ 1 file changed, 74 insertions(+), 74 deletions(-) diff --git a/pallets/transaction-fee/src/tests/mod.rs b/pallets/transaction-fee/src/tests/mod.rs index 4c79c368cb..7212c91077 100644 --- a/pallets/transaction-fee/src/tests/mod.rs +++ b/pallets/transaction-fee/src/tests/mod.rs @@ -386,80 +386,80 @@ fn test_remove_stake_not_enough_balance_for_fees() { #[test] #[ignore] fn test_remove_stake_edge_alpha() { - todo!(); - - // new_test_ext().execute_with(|| { - // let stake_amount = TAO; - // let sn = setup_subnets(1, 1); - // setup_stake( - // sn.subnets[0].netuid, - // &sn.coldkey, - // &sn.hotkeys[0], - // stake_amount, - // ); - - // // Simulate stake removal to get how much TAO should we get for unstaked Alpha - // let current_stake = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - // &sn.hotkeys[0], - // &sn.coldkey, - // sn.subnets[0].netuid, - // ); - - // // Forse-set signer balance to ED - // let current_balance = Balances::free_balance(sn.coldkey); - // let _ = SubtensorModule::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 = AlphaCurrency::from(1_000_000); - // SubtensorModule::decrease_stake_for_hotkey_and_coldkey_on_subnet( - // &sn.hotkeys[0], - // &sn.coldkey, - // sn.subnets[0].netuid, - // current_stake - new_current_stake, - // ); - - // // Remove stake - // let call = RuntimeCall::SubtensorModule(pallet_subtensor::Call::remove_stake { - // hotkey: sn.hotkeys[0], - // netuid: sn.subnets[0].netuid, - // amount_unstaked: new_current_stake, - // }); - - // // Dispatch the extrinsic with ChargeTransactionPayment extension - // let info = call.get_dispatch_info(); - // let ext = pallet_transaction_payment::ChargeTransactionPayment::::from(0); - // let result = ext.validate( - // RuntimeOrigin::signed(sn.coldkey).into(), - // &call.clone(), - // &info, - // 10, - // (), - // &TxBaseImplication(()), - // TransactionSource::External, - // ); - - // // Ok - Validation passed - // assert_ok!(result); - - // // Lower Alpha price to 0.0001 so that there is not enough alpha to cover tx fees - // AlphaSqrtPrice::::insert(sn.subnets[0].netuid, U64F64::from_num(0.01)); - // let result_low_alpha_price = ext.validate( - // RuntimeOrigin::signed(sn.coldkey).into(), - // &call.clone(), - // &info, - // 10, - // (), - // &TxBaseImplication(()), - // TransactionSource::External, - // ); - // assert_eq!( - // result_low_alpha_price.unwrap_err(), - // TransactionValidityError::Invalid(InvalidTransaction::Payment) - // ); - // }); + new_test_ext().execute_with(|| { + let stake_amount = TAO; + let sn = setup_subnets(1, 1); + setup_stake( + sn.subnets[0].netuid, + &sn.coldkey, + &sn.hotkeys[0], + stake_amount, + ); + + // Simulate stake removal to get how much TAO should we get for unstaked Alpha + let current_stake = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &sn.hotkeys[0], + &sn.coldkey, + sn.subnets[0].netuid, + ); + + // Forse-set signer balance to ED + let current_balance = Balances::free_balance(sn.coldkey); + let _ = SubtensorModule::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 = AlphaCurrency::from(1_000_000); + SubtensorModule::decrease_stake_for_hotkey_and_coldkey_on_subnet( + &sn.hotkeys[0], + &sn.coldkey, + sn.subnets[0].netuid, + current_stake - new_current_stake, + ); + + // Remove stake + let call = RuntimeCall::SubtensorModule(pallet_subtensor::Call::remove_stake { + hotkey: sn.hotkeys[0], + netuid: sn.subnets[0].netuid, + amount_unstaked: new_current_stake, + }); + + // Dispatch the extrinsic with ChargeTransactionPayment extension + let info = call.get_dispatch_info(); + let ext = pallet_transaction_payment::ChargeTransactionPayment::::from(0); + let result = ext.validate( + RuntimeOrigin::signed(sn.coldkey).into(), + &call.clone(), + &info, + 10, + (), + &TxBaseImplication(()), + TransactionSource::External, + ); + + // Ok - Validation passed + assert_ok!(result); + + // Lower Alpha price to 0.0001 so that there is not enough alpha to cover tx fees + SubnetTAO::::insert(sn.subnets[0].netuid, TaoCurrency::from(1_000_000)); + SubnetAlphaIn::::insert(sn.subnets[0].netuid, AlphaCurrency::from(10_000_000_000)); + + let result_low_alpha_price = ext.validate( + RuntimeOrigin::signed(sn.coldkey).into(), + &call.clone(), + &info, + 10, + (), + &TxBaseImplication(()), + TransactionSource::External, + ); + assert_eq!( + result_low_alpha_price.unwrap_err(), + TransactionValidityError::Invalid(InvalidTransaction::Payment) + ); + }); } // Validation passes, but transaction fails => TAO fees are paid From 56c001e212abdefb5983bafe77321192edef75eb Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Thu, 8 Jan 2026 16:20:39 -0500 Subject: [PATCH 108/240] Remove todo macros from liquidity extrinsics, make them return error --- pallets/swap/src/pallet/impls.rs | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/pallets/swap/src/pallet/impls.rs b/pallets/swap/src/pallet/impls.rs index bbba296d59..e2176babbd 100644 --- a/pallets/swap/src/pallet/impls.rs +++ b/pallets/swap/src/pallet/impls.rs @@ -318,17 +318,18 @@ impl Pallet { /// - [`SwapError::InvalidTickRange`] if `tick_low` is greater than or equal to `tick_high`. /// - Other [`SwapError`] variants as applicable. pub fn do_add_liquidity( - netuid: NetUid, + _netuid: NetUid, _coldkey_account_id: &T::AccountId, _hotkey_account_id: &T::AccountId, _liquidity: u64, ) -> Result<(PositionId, u64, u64), Error> { - ensure!( - EnabledUserLiquidity::::get(netuid), - Error::::UserLiquidityDisabled - ); + // ensure!( + // EnabledUserLiquidity::::get(netuid), + // Error::::UserLiquidityDisabled + // ); - todo!(); + // TODO: Revise when user liquidity is enabled + Err(Error::::UserLiquidityDisabled) } /// Remove liquidity and credit balances back to (coldkey_account_id, hotkey_account_id) stake. @@ -340,22 +341,24 @@ impl Pallet { _coldkey_account_id: &T::AccountId, _position_id: PositionId, ) -> Result> { - todo!(); + // TODO: Revise when user liquidity is enabled + Err(Error::::UserLiquidityDisabled) } pub fn do_modify_position( - netuid: NetUid, + _netuid: NetUid, _coldkey_account_id: &T::AccountId, _hotkey_account_id: &T::AccountId, _position_id: PositionId, _liquidity_delta: i64, ) -> Result> { - ensure!( - EnabledUserLiquidity::::get(netuid), - Error::::UserLiquidityDisabled - ); + // ensure!( + // EnabledUserLiquidity::::get(netuid), + // Error::::UserLiquidityDisabled + // ); - todo!(); + // TODO: Revise when user liquidity is enabled + Err(Error::::UserLiquidityDisabled) } // /// Returns the number of positions for an account in a specific subnet From c2bbe9925fd1f401aa694c32e0a4060fdf5d5c92 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Thu, 8 Jan 2026 17:10:35 -0500 Subject: [PATCH 109/240] Fix clippy (all except SafeInt) --- pallets/swap/src/pallet/balancer.rs | 39 ++++++++++++++--------------- pallets/swap/src/pallet/tests.rs | 10 +++----- pallets/swap/src/position.rs | 9 ++----- 3 files changed, 25 insertions(+), 33 deletions(-) diff --git a/pallets/swap/src/pallet/balancer.rs b/pallets/swap/src/pallet/balancer.rs index e56426acd3..6b452a6c41 100644 --- a/pallets/swap/src/pallet/balancer.rs +++ b/pallets/swap/src/pallet/balancer.rs @@ -136,13 +136,13 @@ impl Balancer { ) }; - if let Some(result_safe_int) = maybe_result_safe_int { - if let Some(result_u64) = result_safe_int.to_u64() { - return U64F64::saturating_from_num(result_u64) - .safe_div(U64F64::saturating_from_num(ACCURACY)); - } + if let Some(result_safe_int) = maybe_result_safe_int + && let Some(result_u64) = result_safe_int.to_u64() + { + return U64F64::saturating_from_num(result_u64) + .safe_div(U64F64::saturating_from_num(ACCURACY)); } - return U64F64::saturating_from_num(0); + U64F64::saturating_from_num(0) } /// Calculates exponent of (x / (x + ∆x)) ^ (w_base/w_quote) @@ -180,12 +180,16 @@ impl Balancer { let parts = p.deconstruct() as u128; let acc = ACCURACY as u128; - let num = U256::from(value) * U256::from(parts); + let num = U256::from(value).saturating_mul(U256::from(parts)); let den = U256::from(acc); // Add 0.5 before integer division to achieve rounding to the nearest // integer - let res = (num + den / U256::from(2u8)) / den; + let zero = U256::from(0); + let res = num + .saturating_add(den.checked_div(U256::from(2u8)).unwrap_or(zero)) + .checked_div(den) + .unwrap_or(zero); res.min(U256::from(u128::MAX)).as_u128() } @@ -267,7 +271,7 @@ impl Balancer { let scale_fixed = U64F64::saturating_from_num(scale); let exp_result_fixed = if let Some(exp_result_u64) = exp_result_safe_int.to_u64() { U64F64::saturating_from_num(exp_result_u64) - } else if SafeInt::from(u64::MAX) < exp_result_safe_int { + } else if u64::MAX < exp_result_safe_int { U64F64::saturating_from_num(u64::MAX) } else { U64F64::saturating_from_num(0) @@ -312,7 +316,7 @@ impl Balancer { let reserve_fixed = U64F64::saturating_from_num(reserve); let exp_result_fixed = if let Some(exp_result_u64) = exp_result_safe_int.to_u64() { U64F64::saturating_from_num(exp_result_u64) - } else if SafeInt::from(u64::MAX) < exp_result_safe_int { + } else if u64::MAX < exp_result_safe_int { U64F64::saturating_from_num(u64::MAX) } else { U64F64::saturating_from_num(0) @@ -368,6 +372,7 @@ impl Balancer { // cargo test --package pallet-subtensor-swap --lib -- pallet::balancer::tests --nocapture #[cfg(test)] +#[allow(clippy::expect_used, clippy::unwrap_used)] #[cfg(feature = "std")] mod tests { use crate::pallet::Balancer; @@ -631,7 +636,7 @@ mod tests { let mut last_progress = 0.; let start = 100_000_000_000_u128; let stop = 900_000_000_000_u128; - for num in (start..=stop).step_by(1000 as usize) { + for num in (start..=stop).step_by(1000_usize) { let w_quote = Perquintill::from_rational(num, 1_000_000_000_000_u128); let bal = Balancer::new(w_quote).unwrap(); let e = bal.exp_base_quote(x, dx); @@ -705,13 +710,7 @@ mod tests { let dy_expected = y as f64 * (1. - e_expected); let actual = dy.to_num::(); - let mut eps = dy_expected / 1_000_000.; - if eps > 1000.0 { - eps = 1000.0; - } - if eps < 1.0 { - eps = 1.0; - } + let eps = (dy_expected / 1_000_000.).clamp(1.0, 1000.0); assert!( (actual - dy_expected).abs() <= eps, @@ -784,7 +783,7 @@ mod tests { let dy = bal.calculate_quote_delta_in(current_price, target_price, tao_reserve); let dy_expected = 0u64; - assert_eq!(dy, dy_expected as u64,); + assert_eq!(dy, dy_expected); } #[test] @@ -801,7 +800,7 @@ mod tests { let dx = bal.calculate_base_delta_in(current_price, target_price, alpha_reserve); let dx_expected = 0u64; - assert_eq!(dx, dx_expected as u64,); + assert_eq!(dx, dx_expected); } #[test] diff --git a/pallets/swap/src/pallet/tests.rs b/pallets/swap/src/pallet/tests.rs index c4bb5c143c..2c61a07986 100644 --- a/pallets/swap/src/pallet/tests.rs +++ b/pallets/swap/src/pallet/tests.rs @@ -1097,14 +1097,14 @@ fn test_swap_basic() { Pallet::::do_swap(netuid, order.clone(), limit_price_fixed, false, false) .unwrap(); assert_abs_diff_eq!( - swap_result.amount_paid_out.to_u64() as u64, + swap_result.amount_paid_out.to_u64(), expected_output_amount as u64, epsilon = 1 ); assert_abs_diff_eq!( swap_result.paid_in_reserve_delta() as u64, - (swap_amount - expected_fee) as u64, + (swap_amount - expected_fee), epsilon = 1 ); assert_abs_diff_eq!( @@ -1281,10 +1281,8 @@ fn test_convert_deltas() { TaoReserve::set_mock_reserve(netuid, TaoCurrency::from(tao)); AlphaReserve::set_mock_reserve(netuid, AlphaCurrency::from(alpha)); let w_accuracy = 1_000_000_000_f64; - let w_quote_pt = Perquintill::from_rational( - (w_quote as f64 * w_accuracy) as u128, - w_accuracy as u128, - ); + let w_quote_pt = + Perquintill::from_rational((w_quote * w_accuracy) as u128, w_accuracy as u128); let bal = Balancer::new(w_quote_pt).unwrap(); SwapBalancer::::insert(netuid, bal); diff --git a/pallets/swap/src/position.rs b/pallets/swap/src/position.rs index 21148d8b52..864d63a2ab 100644 --- a/pallets/swap/src/position.rs +++ b/pallets/swap/src/position.rs @@ -34,19 +34,14 @@ impl Position { netuid: NetUid, // liquidity: u64, ) -> Self { - let position = Position { + Position { id, netuid, // liquidity, // fees_tao: I64F64::saturating_from_num(0), // fees_alpha: I64F64::saturating_from_num(0), _phantom: PhantomData, - }; - - // position.fees_tao = position.fees_in_range(true); - // position.fees_alpha = position.fees_in_range(false); - - position + } } /// Converts position to token amounts From 244fbfc0bb2a053490659db6cbdd15cf80c95ae2 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Fri, 9 Jan 2026 10:38:12 -0500 Subject: [PATCH 110/240] Add more test cases for test_convert_deltas, correct order of swap initialization in tests --- pallets/swap/src/pallet/tests.rs | 34 ++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/pallets/swap/src/pallet/tests.rs b/pallets/swap/src/pallet/tests.rs index 2c61a07986..2c31915da0 100644 --- a/pallets/swap/src/pallet/tests.rs +++ b/pallets/swap/src/pallet/tests.rs @@ -1061,13 +1061,12 @@ fn test_swap_basic() { let swap_amount = order.amount().to_u64(); // Setup swap - assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - // Price is 0.25 let initial_tao_reserve = TaoCurrency::from(1_000_000_000_u64); let initial_alpha_reserve = AlphaCurrency::from(4_000_000_000_u64); TaoReserve::set_mock_reserve(netuid, initial_tao_reserve); AlphaReserve::set_mock_reserve(netuid, initial_alpha_reserve); + assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); // Get current price let current_price_before = Pallet::::current_price(netuid); @@ -1233,10 +1232,6 @@ fn test_swap_precision_edge_case() { #[test] fn test_convert_deltas() { new_test_ext().execute_with(|| { - let netuid = NetUid::from(1); - assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - - // TODO: Add more test cases with different weights and edge cases for reserves for (tao, alpha, w_quote, delta_in) in [ (1500, 1000, 0.5, 1), (1500, 1000, 0.5, 10000), @@ -1250,6 +1245,30 @@ fn test_convert_deltas() { (1000000, 1, 0.5, 10000), (1000000, 1, 0.5, 1000000), (1000000, 1, 0.5, u64::MAX), + (1500, 1000, 0.50000001, 1), + (1500, 1000, 0.50000001, 10000), + (1500, 1000, 0.50000001, 1000000), + (1500, 1000, 0.50000001, u64::MAX), + (1, 1000000, 0.50000001, 1), + (1, 1000000, 0.50000001, 10000), + (1, 1000000, 0.50000001, 1000000), + (1, 1000000, 0.50000001, u64::MAX), + (1000000, 1, 0.50000001, 1), + (1000000, 1, 0.50000001, 10000), + (1000000, 1, 0.50000001, 1000000), + (1000000, 1, 0.50000001, u64::MAX), + (1500, 1000, 0.49999999, 1), + (1500, 1000, 0.49999999, 10000), + (1500, 1000, 0.49999999, 1000000), + (1500, 1000, 0.49999999, u64::MAX), + (1, 1000000, 0.49999999, 1), + (1, 1000000, 0.49999999, 10000), + (1, 1000000, 0.49999999, 1000000), + (1, 1000000, 0.49999999, u64::MAX), + (1000000, 1, 0.49999999, 1), + (1000000, 1, 0.49999999, 10000), + (1000000, 1, 0.49999999, 1000000), + (1000000, 1, 0.49999999, u64::MAX), // Low quote weight (1500, 1000, 0.1, 1), (1500, 1000, 0.1, 10000), @@ -1278,8 +1297,11 @@ fn test_convert_deltas() { (1000000, 1, 0.9, u64::MAX), ] { // Initialize reserves and weights + let netuid = NetUid::from(1); TaoReserve::set_mock_reserve(netuid, TaoCurrency::from(tao)); AlphaReserve::set_mock_reserve(netuid, AlphaCurrency::from(alpha)); + assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + let w_accuracy = 1_000_000_000_f64; let w_quote_pt = Perquintill::from_rational((w_quote * w_accuracy) as u128, w_accuracy as u128); From 8d19821798b1ffe61c39a2d6a3efe94266468140 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Fri, 9 Jan 2026 12:47:18 -0500 Subject: [PATCH 111/240] Disable add/remove/modify liquidity benchmarks --- pallets/swap/src/benchmarking.rs | 126 +++++++++++++++---------------- 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/pallets/swap/src/benchmarking.rs b/pallets/swap/src/benchmarking.rs index 1ff029c281..8acce9d59b 100644 --- a/pallets/swap/src/benchmarking.rs +++ b/pallets/swap/src/benchmarking.rs @@ -29,76 +29,76 @@ mod benchmarks { set_fee_rate(RawOrigin::Root, netuid, rate); } - #[benchmark] - fn add_liquidity() { - let netuid = NetUid::from(1); - - init_swap::(netuid); - - let caller: T::AccountId = whitelisted_caller(); - let hotkey: T::AccountId = account("hotkey", 0, 0); - - #[extrinsic_call] - add_liquidity(RawOrigin::Signed(caller), hotkey, netuid, 1000); - } - - #[benchmark] - fn remove_liquidity() { - let netuid = NetUid::from(1); - - init_swap::(netuid); + // #[benchmark] + // fn add_liquidity() { + // let netuid = NetUid::from(1); - let caller: T::AccountId = whitelisted_caller(); - let hotkey: T::AccountId = account("hotkey", 0, 0); - let id = PositionId::from(1u128); + // init_swap::(netuid); - PositionsV2::::insert( - (netuid, caller.clone(), id), - Position { - id, - netuid, - // liquidity: 1000, - // fees_tao: I64F64::from_num(0), - // fees_alpha: I64F64::from_num(0), - _phantom: PhantomData, - }, - ); + // let caller: T::AccountId = whitelisted_caller(); + // let hotkey: T::AccountId = account("hotkey", 0, 0); - #[extrinsic_call] - remove_liquidity(RawOrigin::Signed(caller), hotkey, netuid.into(), id.into()); - } - - #[benchmark] - fn modify_position() { - let netuid = NetUid::from(1); + // #[extrinsic_call] + // add_liquidity(RawOrigin::Signed(caller), hotkey, netuid, 1000); + // } - init_swap::(netuid); + // #[benchmark] + // fn remove_liquidity() { + // let netuid = NetUid::from(1); + + // init_swap::(netuid); + + // let caller: T::AccountId = whitelisted_caller(); + // let hotkey: T::AccountId = account("hotkey", 0, 0); + // let id = PositionId::from(1u128); + + // PositionsV2::::insert( + // (netuid, caller.clone(), id), + // Position { + // id, + // netuid, + // // liquidity: 1000, + // // fees_tao: I64F64::from_num(0), + // // fees_alpha: I64F64::from_num(0), + // _phantom: PhantomData, + // }, + // ); - let caller: T::AccountId = whitelisted_caller(); - let hotkey: T::AccountId = account("hotkey", 0, 0); - let id = PositionId::from(1u128); + // #[extrinsic_call] + // remove_liquidity(RawOrigin::Signed(caller), hotkey, netuid.into(), id.into()); + // } - PositionsV2::::insert( - (netuid, caller.clone(), id), - Position { - id, - netuid, - // liquidity: 10000, - // fees_tao: I64F64::from_num(0), - // fees_alpha: I64F64::from_num(0), - _phantom: PhantomData, - }, - ); + // #[benchmark] + // fn modify_position() { + // let netuid = NetUid::from(1); + + // init_swap::(netuid); + + // let caller: T::AccountId = whitelisted_caller(); + // let hotkey: T::AccountId = account("hotkey", 0, 0); + // let id = PositionId::from(1u128); + + // PositionsV2::::insert( + // (netuid, caller.clone(), id), + // Position { + // id, + // netuid, + // // liquidity: 10000, + // // fees_tao: I64F64::from_num(0), + // // fees_alpha: I64F64::from_num(0), + // _phantom: PhantomData, + // }, + // ); - #[extrinsic_call] - modify_position( - RawOrigin::Signed(caller), - hotkey, - netuid.into(), - id.into(), - -5000, - ); - } + // #[extrinsic_call] + // modify_position( + // RawOrigin::Signed(caller), + // hotkey, + // netuid.into(), + // id.into(), + // -5000, + // ); + // } // #[benchmark] // fn toggle_user_liquidity() { From 80e9cecf5c6bf1aa071a1e5ea37023e089db15eb Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Fri, 9 Jan 2026 12:47:57 -0500 Subject: [PATCH 112/240] Add reminders to removed benchmarks --- pallets/swap/src/benchmarking.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pallets/swap/src/benchmarking.rs b/pallets/swap/src/benchmarking.rs index 8acce9d59b..0407139ba6 100644 --- a/pallets/swap/src/benchmarking.rs +++ b/pallets/swap/src/benchmarking.rs @@ -29,6 +29,7 @@ mod benchmarks { set_fee_rate(RawOrigin::Root, netuid, rate); } + // TODO: Revise when user liquidity is available // #[benchmark] // fn add_liquidity() { // let netuid = NetUid::from(1); @@ -42,6 +43,7 @@ mod benchmarks { // add_liquidity(RawOrigin::Signed(caller), hotkey, netuid, 1000); // } + // TODO: Revise when user liquidity is available // #[benchmark] // fn remove_liquidity() { // let netuid = NetUid::from(1); @@ -68,6 +70,7 @@ mod benchmarks { // remove_liquidity(RawOrigin::Signed(caller), hotkey, netuid.into(), id.into()); // } + // TODO: Revise when user liquidity is available // #[benchmark] // fn modify_position() { // let netuid = NetUid::from(1); From fbb015cfcf92ab4296c4ada0d773189bed5d38b5 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Fri, 9 Jan 2026 12:52:09 -0500 Subject: [PATCH 113/240] Fix custom lints --- pallets/swap/src/pallet/balancer.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pallets/swap/src/pallet/balancer.rs b/pallets/swap/src/pallet/balancer.rs index 6b452a6c41..39648ebbf0 100644 --- a/pallets/swap/src/pallet/balancer.rs +++ b/pallets/swap/src/pallet/balancer.rs @@ -190,7 +190,9 @@ impl Balancer { .saturating_add(den.checked_div(U256::from(2u8)).unwrap_or(zero)) .checked_div(den) .unwrap_or(zero); - res.min(U256::from(u128::MAX)).as_u128() + res.min(U256::from(u128::MAX)) + .try_into() + .unwrap_or_default() } /// When liquidity is added to balancer swap, it may be added with arbitrary proportion, From 65bb1c23a58c3759e2ed344a2f98934b212e8bc0 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Fri, 9 Jan 2026 14:38:25 -0500 Subject: [PATCH 114/240] Fix zepter ci --- pallets/swap/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pallets/swap/Cargo.toml b/pallets/swap/Cargo.toml index cb3fd6a1f2..45389874a1 100644 --- a/pallets/swap/Cargo.toml +++ b/pallets/swap/Cargo.toml @@ -45,6 +45,7 @@ std = [ "frame-system/std", "log/std", "pallet-subtensor-swap-runtime-api/std", + "rand/std", "safe-math/std", "safe-bigmath/std", "scale-info/std", From 98e2429fae95ac7ce2fc831e28ad532ed6b28eba Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Fri, 9 Jan 2026 16:52:43 -0300 Subject: [PATCH 115/240] simplify pallet_shield config --- pallets/shield/src/lib.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pallets/shield/src/lib.rs b/pallets/shield/src/lib.rs index 22cf3240a1..ce3f16c59a 100644 --- a/pallets/shield/src/lib.rs +++ b/pallets/shield/src/lib.rs @@ -86,9 +86,7 @@ pub mod pallet { #[pallet::config] pub trait Config: - frame_system::Config>> - + pallet_timestamp::Config - + pallet_aura::Config + frame_system::Config>> + pallet_aura::Config { type RuntimeCall: Parameter + sp_runtime::traits::Dispatchable< @@ -96,7 +94,7 @@ pub mod pallet { PostInfo = PostDispatchInfo, > + GetDispatchInfo; - type AuthorityOrigin: AuthorityOriginExt; + type AuthorityOrigin: AuthorityOriginExt; } #[pallet::pallet] From 31ac0ad636b045c79cc774b6596cfdf63f3fd980 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Fri, 9 Jan 2026 16:53:11 -0300 Subject: [PATCH 116/240] added pallet_shield to subtensor mock --- Cargo.lock | 4 ++ pallets/subtensor/Cargo.toml | 8 +++- pallets/subtensor/src/tests/mock.rs | 61 +++++++++++++++++++++++------ 3 files changed, 61 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eeffdabd0f..3350bd39ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10775,15 +10775,18 @@ dependencies = [ "log", "ndarray", "num-traits", + "pallet-aura", "pallet-balances", "pallet-commitments", "pallet-crowdloan", "pallet-drand", "pallet-preimage", "pallet-scheduler", + "pallet-shield", "pallet-subtensor-proxy", "pallet-subtensor-swap", "pallet-subtensor-utility", + "pallet-timestamp", "pallet-transaction-payment", "parity-scale-codec", "polkadot-runtime-common", @@ -10795,6 +10798,7 @@ dependencies = [ "serde_json", "sha2 0.10.9", "share-pool", + "sp-consensus-aura", "sp-core", "sp-io", "sp-keyring", diff --git a/pallets/subtensor/Cargo.toml b/pallets/subtensor/Cargo.toml index 2e35a89d19..8457444e2c 100644 --- a/pallets/subtensor/Cargo.toml +++ b/pallets/subtensor/Cargo.toml @@ -55,11 +55,15 @@ sha2.workspace = true rand_chacha.workspace = true pallet-crowdloan.workspace = true pallet-subtensor-proxy.workspace = true +pallet-shield.workspace = true [dev-dependencies] pallet-balances = { workspace = true, features = ["std"] } pallet-scheduler.workspace = true pallet-subtensor-proxy.workspace = true +pallet-aura.workspace = true +pallet-timestamp.workspace = true +sp-consensus-aura.workspace = true subtensor-runtime-common.workspace = true pallet-subtensor-swap.workspace = true sp-version.workspace = true @@ -115,6 +119,7 @@ std = [ "pallet-drand/std", "pallet-subtensor-proxy/std", "pallet-subtensor-swap/std", + "pallet-shield/std", "subtensor-swap-interface/std", "pallet-subtensor-utility/std", "safe-math/std", @@ -147,7 +152,8 @@ runtime-benchmarks = [ "pallet-drand/runtime-benchmarks", "pallet-subtensor-proxy/runtime-benchmarks", "pallet-subtensor-swap/runtime-benchmarks", - "pallet-subtensor-utility/runtime-benchmarks" + "pallet-subtensor-utility/runtime-benchmarks", + "pallet-shield/runtime-benchmarks" ] pow-faucet = [] fast-runtime = ["subtensor-runtime-common/fast-runtime"] diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index f021d9d721..d512ab736d 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -20,11 +20,12 @@ use frame_system as system; use frame_system::{EnsureRoot, RawOrigin, limits, offchain::CreateTransactionBase}; use pallet_subtensor_proxy as pallet_proxy; use pallet_subtensor_utility as pallet_utility; +use sp_consensus_aura::sr25519::AuthorityId as AuraId; use sp_core::{ConstU64, Get, H256, U256, offchain::KeyTypeId}; use sp_runtime::Perbill; use sp_runtime::{ BuildStorage, Percent, - traits::{BlakeTwo256, IdentityLookup}, + traits::{BadOrigin, BlakeTwo256, IdentityLookup}, }; use sp_std::{cell::RefCell, cmp::Ordering, sync::OnceLock}; use sp_tracing::tracing_subscriber; @@ -37,16 +38,19 @@ type Block = frame_system::mocking::MockBlock; frame_support::construct_runtime!( pub enum Test { - System: frame_system::{Pallet, Call, Config, Storage, Event} = 1, - Balances: pallet_balances::{Pallet, Call, Config, Storage, Event} = 2, - SubtensorModule: crate::{Pallet, Call, Storage, Event} = 7, - Utility: pallet_utility::{Pallet, Call, Storage, Event} = 8, - Scheduler: pallet_scheduler::{Pallet, Call, Storage, Event} = 9, - Preimage: pallet_preimage::{Pallet, Call, Storage, Event} = 10, - Drand: pallet_drand::{Pallet, Call, Storage, Event} = 11, - Swap: pallet_subtensor_swap::{Pallet, Call, Storage, Event} = 12, - Crowdloan: pallet_crowdloan::{Pallet, Call, Storage, Event} = 13, - Proxy: pallet_subtensor_proxy = 14, + System: frame_system = 1, + Balances: pallet_balances = 2, + Timestamp: pallet_timestamp = 3, + Aura: pallet_aura = 4, + Shield: pallet_shield = 5, + SubtensorModule: crate = 6, + Utility: pallet_utility = 7, + Scheduler: pallet_scheduler = 8, + Preimage: pallet_preimage = 9, + Drand: pallet_drand = 10, + Swap: pallet_subtensor_swap = 11, + Crowdloan: pallet_crowdloan = 12, + Proxy: pallet_subtensor_proxy = 13, } ); @@ -551,6 +555,41 @@ where } } +#[derive_impl(pallet_timestamp::config_preludes::TestDefaultConfig)] +impl pallet_timestamp::Config for Test { + type MinimumPeriod = ConstU64<0>; +} + +parameter_types! { + pub const MaxAuthorities: u32 = 32; + pub const AllowMultipleBlocksPerSlot: bool = false; + pub const SlotDuration: u64 = 6000; +} + +impl pallet_aura::Config for Test { + type AuthorityId = AuraId; + // For tests we don't need dynamic disabling; just use unit type. + type DisabledValidators = (); + type MaxAuthorities = MaxAuthorities; + type AllowMultipleBlocksPerSlot = AllowMultipleBlocksPerSlot; + type SlotDuration = SlotDuration; +} + +pub struct TestAuthorityOrigin; + +impl pallet_shield::AuthorityOriginExt for TestAuthorityOrigin { + type AccountId = U256; + + fn ensure_validator(_origin: RuntimeOrigin) -> Result { + Ok(U256::from(0)) + } +} + +impl pallet_shield::Config for Test { + type RuntimeCall = RuntimeCall; + type AuthorityOrigin = TestAuthorityOrigin; +} + static TEST_LOGS_INIT: OnceLock<()> = OnceLock::new(); pub fn init_logs_for_tests() { From df326fbba1fd755b47059098cd2d9c5bb261a473 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Fri, 9 Jan 2026 16:53:32 -0300 Subject: [PATCH 117/240] allow mev transaction to go through while ongoing swap --- pallets/subtensor/src/tests/swap_coldkey.rs | 55 ++++++++++++------- .../subtensor/src/transaction_extension.rs | 23 +++++--- 2 files changed, 51 insertions(+), 27 deletions(-) diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index d59e469f0a..5437b9cc85 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -30,6 +30,15 @@ use crate::transaction_extension::SubtensorTransactionExtension; use crate::*; use crate::{Call, Error}; +fn run_to_block(n: u64) { + System::run_to_block_with::( + n, + frame_system::RunToBlockHooks::default().before_finalize(|bn| { + Timestamp::set_timestamp(bn); + }), + ); +} + #[test] fn test_announce_coldkey_swap_works() { new_test_ext(1).execute_with(|| { @@ -96,7 +105,7 @@ fn test_announce_coldkey_swap_with_existing_announcement_past_delay_works() { ); let reannouncement_delay = ColdkeySwapReannouncementDelay::::get(); - System::run_to_block::(now + delay + reannouncement_delay); + run_to_block(now + delay + reannouncement_delay); assert_ok!(SubtensorModule::announce_coldkey_swap( RuntimeOrigin::signed(who), @@ -137,7 +146,7 @@ fn test_announce_coldkey_swap_only_pays_swap_cost_if_no_announcement_exists() { let now = System::block_number(); let base_delay = ColdkeySwapAnnouncementDelay::::get(); let reannouncement_delay = ColdkeySwapReannouncementDelay::::get(); - System::run_to_block::(now + base_delay + reannouncement_delay); + run_to_block(now + base_delay + reannouncement_delay); assert_ok!(SubtensorModule::announce_coldkey_swap( RuntimeOrigin::signed(who), @@ -218,7 +227,7 @@ fn test_swap_coldkey_announced_works() { // Run some blocks for the announcement to be past the delay let delay = ColdkeySwapAnnouncementDelay::::get() + 1; - System::run_to_block::(now + delay); + run_to_block(now + delay); let ( netuid1, @@ -358,7 +367,7 @@ fn test_swap_coldkey_announced_with_already_associated_coldkey_fails() { let now = System::block_number(); let delay = ColdkeySwapAnnouncementDelay::::get() + 1; - System::run_to_block::(now + delay); + run_to_block(now + delay); SubtensorModule::create_account_if_non_existent(&new_coldkey, &hotkey); @@ -385,7 +394,7 @@ fn test_swap_coldkey_announced_with_hotkey_fails() { let now = System::block_number(); let delay = ColdkeySwapAnnouncementDelay::::get() + 1; - System::run_to_block::(now + delay); + run_to_block(now + delay); SubtensorModule::create_account_if_non_existent(&new_coldkey, &hotkey); @@ -1383,20 +1392,28 @@ fn test_subtensor_extension_rejects_any_call_that_is_not_announce_or_swap() { ); } - // Reannounce coldkey swap should succeed - let call = RuntimeCall::SubtensorModule(SubtensorCall::announce_coldkey_swap { - new_coldkey_hash: another_coldkey_hash, - }); - let info = call.get_dispatch_info(); - let ext = SubtensorTransactionExtension::::new(); - assert_ok!(ext.dispatch_transaction(RuntimeOrigin::signed(who).into(), call, &info, 0, 0)); - - // Swap coldkey announced should succeed - let call = - RuntimeCall::SubtensorModule(SubtensorCall::swap_coldkey_announced { new_coldkey }); - let info = call.get_dispatch_info(); - let ext = SubtensorTransactionExtension::::new(); - assert_ok!(ext.dispatch_transaction(RuntimeOrigin::signed(who).into(), call, &info, 0, 0)); + let authorized_calls: Vec = vec![ + RuntimeCall::SubtensorModule(SubtensorCall::announce_coldkey_swap { + new_coldkey_hash: another_coldkey_hash, + }), + RuntimeCall::SubtensorModule(SubtensorCall::swap_coldkey_announced { new_coldkey }), + RuntimeCall::Shield(pallet_shield::Call::submit_encrypted { + commitment: ::Hashing::hash_of(&new_coldkey), + ciphertext: BoundedVec::truncate_from(vec![1, 2, 3, 4]), + }), + ]; + + for call in authorized_calls { + let info = call.get_dispatch_info(); + let ext = SubtensorTransactionExtension::::new(); + assert_ok!(ext.dispatch_transaction( + RuntimeOrigin::signed(who).into(), + call, + &info, + 0, + 0 + )); + } }); } diff --git a/pallets/subtensor/src/transaction_extension.rs b/pallets/subtensor/src/transaction_extension.rs index 7647a9d6ad..e842a9b6ce 100644 --- a/pallets/subtensor/src/transaction_extension.rs +++ b/pallets/subtensor/src/transaction_extension.rs @@ -85,13 +85,14 @@ where } } -impl +impl TransactionExtension<::RuntimeCall> for SubtensorTransactionExtension where CallOf: Dispatchable + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, OriginOf: AsSystemOriginSigner + Clone, { const IDENTIFIER: &'static str = "SubtensorTransactionExtension"; @@ -115,15 +116,21 @@ where return Ok((Default::default(), (), origin)); }; - // Ensure the origin coldkey is not announced for a swap. - if ColdkeySwapAnnouncements::::contains_key(who) - && !matches!( + if ColdkeySwapAnnouncements::::contains_key(who) { + let is_allowed_direct = matches!( call.is_sub_type(), Some(Call::announce_coldkey_swap { .. }) | Some(Call::swap_coldkey_announced { .. }) - ) - { - return Err(CustomTransactionError::ColdkeySwapAnnounced.into()); + ); + + let is_mev_protected = matches!( + IsSubType::>::is_sub_type(call), + Some(pallet_shield::Call::submit_encrypted { .. }) + ); + + if !is_allowed_direct && !is_mev_protected { + return Err(CustomTransactionError::ColdkeySwapAnnounced.into()); + } } match call.is_sub_type() { From 7d466f1d6d11dd8f95a0e744444770501b6ce7af Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Fri, 9 Jan 2026 17:20:33 -0300 Subject: [PATCH 118/240] fix benchmarks bounds --- pallets/shield/src/benchmarking.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pallets/shield/src/benchmarking.rs b/pallets/shield/src/benchmarking.rs index a88061287e..a78ea7c49c 100644 --- a/pallets/shield/src/benchmarking.rs +++ b/pallets/shield/src/benchmarking.rs @@ -41,6 +41,8 @@ where ::RuntimeCall: From>, // Needed so we can seed Authorities from a dev sr25519 pubkey. ::AuthorityId: From, + ::AccountId: From + Into, + ::RuntimeOrigin: From> )] mod benches { use super::*; From fb050fd1b194ca5249c2488925becd884fb4620a Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Fri, 9 Jan 2026 17:26:53 -0300 Subject: [PATCH 119/240] fix zepter --- pallets/subtensor/Cargo.toml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/Cargo.toml b/pallets/subtensor/Cargo.toml index 8457444e2c..a6fadec1f4 100644 --- a/pallets/subtensor/Cargo.toml +++ b/pallets/subtensor/Cargo.toml @@ -91,7 +91,10 @@ try-runtime = [ "pallet-crowdloan/try-runtime", "pallet-drand/try-runtime", "pallet-subtensor-proxy/try-runtime", - "pallet-subtensor-utility/try-runtime" + "pallet-subtensor-utility/try-runtime", + "pallet-shield/try-runtime", + "pallet-timestamp/try-runtime", + "pallet-aura/try-runtime", ] default = ["std"] std = [ @@ -120,6 +123,9 @@ std = [ "pallet-subtensor-proxy/std", "pallet-subtensor-swap/std", "pallet-shield/std", + "pallet-timestamp/std", + "pallet-aura/std", + "sp-consensus-aura/std", "subtensor-swap-interface/std", "pallet-subtensor-utility/std", "safe-math/std", @@ -153,7 +159,8 @@ runtime-benchmarks = [ "pallet-subtensor-proxy/runtime-benchmarks", "pallet-subtensor-swap/runtime-benchmarks", "pallet-subtensor-utility/runtime-benchmarks", - "pallet-shield/runtime-benchmarks" + "pallet-shield/runtime-benchmarks", + "pallet-timestamp/runtime-benchmarks" ] pow-faucet = [] fast-runtime = ["subtensor-runtime-common/fast-runtime"] From 87fa1dc9eb200da43da12c39b5319011d7676f82 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Fri, 9 Jan 2026 15:29:20 -0500 Subject: [PATCH 120/240] Fix benchmarks --- pallets/subtensor/src/benchmarks.rs | 64 ++++++++++++++++++++--------- pallets/swap/src/benchmarking.rs | 7 +--- 2 files changed, 46 insertions(+), 25 deletions(-) diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index 476be905e9..180f5af871 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -16,7 +16,9 @@ use sp_runtime::{ }; use sp_std::collections::btree_set::BTreeSet; use sp_std::vec; +use substrate_fixed::types::U64F64; use subtensor_runtime_common::{AlphaCurrency, NetUid, TaoCurrency}; +use subtensor_swap_interface::SwapHandler; #[frame_benchmarking::v2::benchmarks] mod pallet_benchmarks { @@ -62,6 +64,8 @@ mod pallet_benchmarks { Subtensor::::set_max_registrations_per_block(netuid, 4096); Subtensor::::set_target_registrations_per_interval(netuid, 4096); Subtensor::::set_commit_reveal_weights_enabled(netuid, false); + SubnetTAO::::insert(netuid, TaoCurrency::from(1_000_000_000_000_u64)); + SubnetAlphaIn::::insert(netuid, AlphaCurrency::from(1_000_000_000_000_000_u64)); let mut seed: u32 = 1; let mut dests = Vec::new(); @@ -674,13 +678,12 @@ mod pallet_benchmarks { let coldkey: T::AccountId = account("Test", 0, seed); let hotkey: T::AccountId = account("Alice", 0, seed); - let amount = 900_000_000_000; - let limit = TaoCurrency::from(6_000_000_000); - let amount_to_be_staked = TaoCurrency::from(44_000_000_000); - Subtensor::::add_balance_to_coldkey_account(&coldkey.clone(), amount); + let initial_balance = 900_000_000_000; + Subtensor::::add_balance_to_coldkey_account(&coldkey.clone(), initial_balance); - let tao_reserve = TaoCurrency::from(150_000_000_000); - let alpha_in = AlphaCurrency::from(100_000_000_000); + // Price = 0.01 + let tao_reserve = TaoCurrency::from(1_000_000_000_000); + let alpha_in = AlphaCurrency::from(100_000_000_000_000); SubnetTAO::::insert(netuid, tao_reserve); SubnetAlphaIn::::insert(netuid, alpha_in); @@ -690,14 +693,23 @@ mod pallet_benchmarks { hotkey.clone() )); + // Read current price and set limit price 0.1% higher, which is certainly getting hit + // by swapping 100 TAO + let current_price = T::SwapInterface::current_alpha_price(netuid); + let limit = current_price + .saturating_mul(U64F64::saturating_from_num(1_001_000_000)) + .saturating_to_num::(); + let amount_to_be_staked = TaoCurrency::from(100_000_000_000); + + // Allow partial (worst case) #[extrinsic_call] _( RawOrigin::Signed(coldkey.clone()), hotkey, netuid, amount_to_be_staked, - limit, - false, + limit.into(), + true, ); } @@ -774,9 +786,9 @@ mod pallet_benchmarks { let hotkey: T::AccountId = account("Alice", 0, seed); Subtensor::::set_burn(netuid, 1.into()); - let limit = TaoCurrency::from(1_000_000_000); - let tao_reserve = TaoCurrency::from(150_000_000_000); - let alpha_in = AlphaCurrency::from(100_000_000_000); + // Price = 0.01 + let tao_reserve = TaoCurrency::from(1_000_000_000_000); + let alpha_in = AlphaCurrency::from(100_000_000_000_000); SubnetTAO::::insert(netuid, tao_reserve); SubnetAlphaIn::::insert(netuid, alpha_in); @@ -799,7 +811,13 @@ mod pallet_benchmarks { u64_staked_amt.into() )); - let amount_unstaked = AlphaCurrency::from(30_000_000_000); + // Read current price and set limit price 0.01% lower, which is certainly getting hit + // by swapping 100 Alpha + let current_price = T::SwapInterface::current_alpha_price(netuid); + let limit = current_price + .saturating_mul(U64F64::saturating_from_num(999_900_000)) + .saturating_to_num::(); + let amount_unstaked = AlphaCurrency::from(100_000_000_000); // Remove stake limit for benchmark StakingOperationRateLimiter::::remove((hotkey.clone(), coldkey.clone(), netuid)); @@ -810,8 +828,8 @@ mod pallet_benchmarks { hotkey.clone(), netuid, amount_unstaked, - limit, - false, + limit.into(), + true, ); } @@ -1265,8 +1283,9 @@ mod pallet_benchmarks { let hotkey: T::AccountId = account("Alice", 0, seed); Subtensor::::set_burn(netuid, 1.into()); - SubnetTAO::::insert(netuid, TaoCurrency::from(150_000_000_000)); - SubnetAlphaIn::::insert(netuid, AlphaCurrency::from(100_000_000_000)); + // Price = 0.01 + SubnetTAO::::insert(netuid, TaoCurrency::from(1_000_000_000_000)); + SubnetAlphaIn::::insert(netuid, AlphaCurrency::from(100_000_000_000_000)); Subtensor::::add_balance_to_coldkey_account(&coldkey.clone(), 1000000u32.into()); @@ -1313,9 +1332,8 @@ mod pallet_benchmarks { let hotkey: T::AccountId = account("Alice", 0, seed); Subtensor::::set_burn(netuid, 1.into()); - let limit = TaoCurrency::from(1_000_000_000); - let tao_reserve = TaoCurrency::from(150_000_000_000); - let alpha_in = AlphaCurrency::from(100_000_000_000); + let tao_reserve = TaoCurrency::from(1_000_000_000_000); + let alpha_in = AlphaCurrency::from(100_000_000_000_000); SubnetTAO::::insert(netuid, tao_reserve); SubnetAlphaIn::::insert(netuid, alpha_in); @@ -1328,6 +1346,12 @@ mod pallet_benchmarks { hotkey.clone() )); + // Read current price and set limit price 0.1% higher, which is certainly getting hit + // by swapping 100 TAO + let current_price = T::SwapInterface::current_alpha_price(netuid); + let limit = current_price + .saturating_mul(U64F64::saturating_from_num(1_001_000_000)) + .saturating_to_num::(); let u64_staked_amt = 100_000_000_000; Subtensor::::add_balance_to_coldkey_account(&coldkey.clone(), u64_staked_amt); @@ -1345,7 +1369,7 @@ mod pallet_benchmarks { RawOrigin::Signed(coldkey.clone()), hotkey.clone(), netuid, - Some(limit), + Some(limit.into()), ); } diff --git a/pallets/swap/src/benchmarking.rs b/pallets/swap/src/benchmarking.rs index 0407139ba6..2082af4f19 100644 --- a/pallets/swap/src/benchmarking.rs +++ b/pallets/swap/src/benchmarking.rs @@ -2,16 +2,13 @@ #![allow(clippy::unwrap_used)] #![allow(clippy::multiple_bound_locations)] -use core::marker::PhantomData; use frame_benchmarking::v2::*; use frame_system::RawOrigin; use subtensor_runtime_common::NetUid; -use crate::{ - pallet::{Call, Config, Pallet, PositionsV2}, - position::{Position, PositionId}, -}; +use crate::pallet::{Call, Config, Pallet}; +#[allow(dead_code)] fn init_swap(netuid: NetUid) { let _ = Pallet::::maybe_initialize_palswap(netuid); } From 3a913e077ebf6640d022ebb213514b05a29ac17a Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Fri, 9 Jan 2026 17:49:22 -0300 Subject: [PATCH 121/240] fix precompiles bounds --- Cargo.lock | 1 + precompiles/Cargo.toml | 1 + precompiles/src/balance_transfer.rs | 8 ++++++-- precompiles/src/crowdloan.rs | 8 ++++++-- precompiles/src/extensions.rs | 4 +++- precompiles/src/leasing.rs | 8 ++++++-- precompiles/src/lib.rs | 10 ++++++++-- precompiles/src/neuron.rs | 8 ++++++-- precompiles/src/proxy.rs | 8 ++++++-- precompiles/src/staking.rs | 16 ++++++++++++---- precompiles/src/subnet.rs | 8 ++++++-- 11 files changed, 61 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6df115fef1..e5be665236 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18109,6 +18109,7 @@ dependencies = [ "pallet-evm-precompile-modexp", "pallet-evm-precompile-sha3fips", "pallet-evm-precompile-simple", + "pallet-shield", "pallet-subtensor", "pallet-subtensor-proxy", "pallet-subtensor-swap", diff --git a/precompiles/Cargo.toml b/precompiles/Cargo.toml index 400e1caa9f..41f50343d1 100644 --- a/precompiles/Cargo.toml +++ b/precompiles/Cargo.toml @@ -38,6 +38,7 @@ pallet-subtensor-swap.workspace = true pallet-admin-utils.workspace = true subtensor-swap-interface.workspace = true pallet-crowdloan.workspace = true +pallet-shield.workspace = true [lints] workspace = true diff --git a/precompiles/src/balance_transfer.rs b/precompiles/src/balance_transfer.rs index c1cdab6ca5..54be7c551f 100644 --- a/precompiles/src/balance_transfer.rs +++ b/precompiles/src/balance_transfer.rs @@ -18,6 +18,7 @@ where + pallet_balances::Config + pallet_evm::Config + pallet_subtensor::Config + + pallet_shield::Config + Send + Sync + scale_info::TypeInfo, @@ -26,7 +27,8 @@ where ::RuntimeCall: GetDispatchInfo + Dispatchable + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, ::RuntimeCall: From> + GetDispatchInfo + Dispatchable, @@ -43,6 +45,7 @@ where + pallet_balances::Config + pallet_evm::Config + pallet_subtensor::Config + + pallet_shield::Config + Send + Sync + scale_info::TypeInfo, @@ -51,7 +54,8 @@ where ::RuntimeCall: GetDispatchInfo + Dispatchable + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, ::RuntimeCall: From> + GetDispatchInfo + Dispatchable, diff --git a/precompiles/src/crowdloan.rs b/precompiles/src/crowdloan.rs index f0971015d9..731da22da6 100644 --- a/precompiles/src/crowdloan.rs +++ b/precompiles/src/crowdloan.rs @@ -25,6 +25,7 @@ where + pallet_evm::Config + pallet_proxy::Config + pallet_subtensor::Config + + pallet_shield::Config + Send + Sync + scale_info::TypeInfo, @@ -34,7 +35,8 @@ where + GetDispatchInfo + Dispatchable + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, ::AddressMapping: AddressMapping, { const INDEX: u64 = 2057; @@ -49,6 +51,7 @@ where + pallet_evm::Config + pallet_proxy::Config + pallet_subtensor::Config + + pallet_shield::Config + Send + Sync + scale_info::TypeInfo, @@ -58,7 +61,8 @@ where + GetDispatchInfo + Dispatchable + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, ::RuntimeCall: From> + GetDispatchInfo + Dispatchable, diff --git a/precompiles/src/extensions.rs b/precompiles/src/extensions.rs index df5ebf3fa2..0055a5a0a7 100644 --- a/precompiles/src/extensions.rs +++ b/precompiles/src/extensions.rs @@ -58,6 +58,7 @@ pub(crate) trait PrecompileHandleExt: PrecompileHandle { + pallet_balances::Config + pallet_evm::Config + pallet_subtensor::Config + + pallet_shield::Config + Send + Sync + TypeInfo, @@ -65,7 +66,8 @@ pub(crate) trait PrecompileHandleExt: PrecompileHandle { ::RuntimeCall: GetDispatchInfo + Dispatchable + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, ::RuntimeOrigin: From> + AsSystemOriginSigner + Clone, { diff --git a/precompiles/src/leasing.rs b/precompiles/src/leasing.rs index 01a8db4354..9d83e8da8f 100644 --- a/precompiles/src/leasing.rs +++ b/precompiles/src/leasing.rs @@ -26,6 +26,7 @@ where + pallet_evm::Config + pallet_subtensor::Config + pallet_crowdloan::Config + + pallet_shield::Config + Send + Sync + scale_info::TypeInfo, @@ -36,7 +37,8 @@ where + GetDispatchInfo + Dispatchable + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, ::AddressMapping: AddressMapping, { const INDEX: u64 = 2058; @@ -50,6 +52,7 @@ where + pallet_evm::Config + pallet_subtensor::Config + pallet_crowdloan::Config + + pallet_shield::Config + Send + Sync + scale_info::TypeInfo, @@ -60,7 +63,8 @@ where + GetDispatchInfo + Dispatchable + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, ::AddressMapping: AddressMapping, { #[precompile::public("getLease(uint32)")] diff --git a/precompiles/src/lib.rs b/precompiles/src/lib.rs index dedeebe249..a45369d51a 100644 --- a/precompiles/src/lib.rs +++ b/precompiles/src/lib.rs @@ -68,6 +68,7 @@ where + pallet_subtensor_swap::Config + pallet_proxy::Config + pallet_crowdloan::Config + + pallet_shield::Config + Send + Sync + scale_info::TypeInfo, @@ -81,7 +82,8 @@ where + GetDispatchInfo + Dispatchable + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, ::AddressMapping: AddressMapping, ::Balance: TryFrom, <::Lookup as StaticLookup>::Source: From, @@ -101,6 +103,7 @@ where + pallet_subtensor_swap::Config + pallet_proxy::Config + pallet_crowdloan::Config + + pallet_shield::Config + Send + Sync + scale_info::TypeInfo, @@ -114,7 +117,8 @@ where + GetDispatchInfo + Dispatchable + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, ::AddressMapping: AddressMapping, ::Balance: TryFrom, <::Lookup as StaticLookup>::Source: From, @@ -163,6 +167,7 @@ where + pallet_subtensor_swap::Config + pallet_proxy::Config + pallet_crowdloan::Config + + pallet_shield::Config + Send + Sync + scale_info::TypeInfo, @@ -177,6 +182,7 @@ where + Dispatchable + IsSubType> + IsSubType> + + IsSubType> + Decode, <::RuntimeCall as Dispatchable>::RuntimeOrigin: From>>, diff --git a/precompiles/src/neuron.rs b/precompiles/src/neuron.rs index 0b998b3c07..b4ed6a25c4 100644 --- a/precompiles/src/neuron.rs +++ b/precompiles/src/neuron.rs @@ -19,6 +19,7 @@ where + pallet_balances::Config + pallet_evm::Config + pallet_subtensor::Config + + pallet_shield::Config + Send + Sync + scale_info::TypeInfo, @@ -28,7 +29,8 @@ where + GetDispatchInfo + Dispatchable + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, ::AddressMapping: AddressMapping, { const INDEX: u64 = 2052; @@ -41,6 +43,7 @@ where + pallet_balances::Config + pallet_evm::Config + pallet_subtensor::Config + + pallet_shield::Config + Send + Sync + scale_info::TypeInfo, @@ -50,7 +53,8 @@ where + GetDispatchInfo + Dispatchable + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, ::AddressMapping: AddressMapping, { #[precompile::public("setWeights(uint16,uint16[],uint16[],uint64)")] diff --git a/precompiles/src/proxy.rs b/precompiles/src/proxy.rs index 5139477f00..b387402322 100644 --- a/precompiles/src/proxy.rs +++ b/precompiles/src/proxy.rs @@ -30,6 +30,7 @@ where + pallet_evm::Config + pallet_subtensor::Config + pallet_proxy::Config + + pallet_shield::Config + Send + Sync + scale_info::TypeInfo, @@ -41,7 +42,8 @@ where + GetDispatchInfo + Dispatchable + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, ::AddressMapping: AddressMapping, <::Lookup as StaticLookup>::Source: From, { @@ -56,6 +58,7 @@ where + pallet_evm::Config + pallet_subtensor::Config + pallet_proxy::Config + + pallet_shield::Config + Send + Sync + scale_info::TypeInfo, @@ -67,7 +70,8 @@ where + GetDispatchInfo + Dispatchable + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, <::Lookup as StaticLookup>::Source: From, { #[precompile::public("createPureProxy(uint8,uint32,uint16)")] diff --git a/precompiles/src/staking.rs b/precompiles/src/staking.rs index 862c59219c..4d014d2d23 100644 --- a/precompiles/src/staking.rs +++ b/precompiles/src/staking.rs @@ -57,6 +57,7 @@ where + pallet_evm::Config + pallet_subtensor::Config + pallet_proxy::Config + + pallet_shield::Config + Send + Sync + scale_info::TypeInfo, @@ -67,7 +68,8 @@ where + GetDispatchInfo + Dispatchable + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, ::AddressMapping: AddressMapping, <::Lookup as StaticLookup>::Source: From, { @@ -82,6 +84,7 @@ where + pallet_evm::Config + pallet_subtensor::Config + pallet_proxy::Config + + pallet_shield::Config + Send + Sync + scale_info::TypeInfo, @@ -92,7 +95,8 @@ where + GetDispatchInfo + Dispatchable + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, ::AddressMapping: AddressMapping, <::Lookup as StaticLookup>::Source: From, { @@ -434,6 +438,7 @@ where + pallet_subtensor::Config + pallet_proxy::Config + pallet_balances::Config + + pallet_shield::Config + Send + Sync + scale_info::TypeInfo, @@ -445,7 +450,8 @@ where + GetDispatchInfo + Dispatchable + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, ::AddressMapping: AddressMapping, ::Balance: TryFrom, <::Lookup as StaticLookup>::Source: From, @@ -461,6 +467,7 @@ where + pallet_subtensor::Config + pallet_proxy::Config + pallet_balances::Config + + pallet_shield::Config + Send + Sync + scale_info::TypeInfo, @@ -472,7 +479,8 @@ where + GetDispatchInfo + Dispatchable + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, ::AddressMapping: AddressMapping, ::Balance: TryFrom, <::Lookup as StaticLookup>::Source: From, diff --git a/precompiles/src/subnet.rs b/precompiles/src/subnet.rs index 4df373c116..7ec65fbc07 100644 --- a/precompiles/src/subnet.rs +++ b/precompiles/src/subnet.rs @@ -22,6 +22,7 @@ where + pallet_evm::Config + pallet_subtensor::Config + pallet_admin_utils::Config + + pallet_shield::Config + Send + Sync + scale_info::TypeInfo, @@ -32,7 +33,8 @@ where + GetDispatchInfo + Dispatchable + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, ::AddressMapping: AddressMapping, { const INDEX: u64 = 2051; @@ -45,6 +47,7 @@ where + pallet_balances::Config + pallet_evm::Config + pallet_subtensor::Config + + pallet_shield::Config + pallet_admin_utils::Config + Send + Sync @@ -56,7 +59,8 @@ where + GetDispatchInfo + Dispatchable + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, ::AddressMapping: AddressMapping, { #[precompile::public("registerNetwork(bytes32)")] From c160f7b4707925181f1e25c89cea75ae8fb11c88 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Fri, 9 Jan 2026 17:55:07 -0300 Subject: [PATCH 122/240] fix zepter --- precompiles/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/precompiles/Cargo.toml b/precompiles/Cargo.toml index 41f50343d1..be1824cf91 100644 --- a/precompiles/Cargo.toml +++ b/precompiles/Cargo.toml @@ -64,6 +64,7 @@ std = [ "pallet-subtensor-proxy/std", "pallet-subtensor-swap/std", "pallet-subtensor/std", + "pallet-shield/std", "precompile-utils/std", "scale-info/std", "sp-core/std", From b021c56cacf883d198e616999c97886399c3832b Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Fri, 9 Jan 2026 17:18:04 -0500 Subject: [PATCH 123/240] Fix remove_stake_full_limit --- pallets/subtensor/src/benchmarks.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index 180f5af871..d289978020 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -1346,13 +1346,13 @@ mod pallet_benchmarks { hotkey.clone() )); - // Read current price and set limit price 0.1% higher, which is certainly getting hit - // by swapping 100 TAO + // Read current price and set limit price 50% higher, which is not getting hit + // by swapping 1 TAO let current_price = T::SwapInterface::current_alpha_price(netuid); let limit = current_price .saturating_mul(U64F64::saturating_from_num(1_001_000_000)) .saturating_to_num::(); - let u64_staked_amt = 100_000_000_000; + let u64_staked_amt = 1_000_000_000; Subtensor::::add_balance_to_coldkey_account(&coldkey.clone(), u64_staked_amt); assert_ok!(Subtensor::::add_stake( From b1067d49a24112e80a1fc8ce04f52a34f9bb6cff Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Fri, 9 Jan 2026 21:45:48 -0300 Subject: [PATCH 124/240] fix js test --- contract-tests/package-lock.json | 5919 +++++++++++++++++ contract-tests/package.json | 4 +- .../subnet.precompile.hyperparameter.test.ts | 16 +- contract-tests/yarn.lock | 788 +-- 4 files changed, 6324 insertions(+), 403 deletions(-) create mode 100644 contract-tests/package-lock.json diff --git a/contract-tests/package-lock.json b/contract-tests/package-lock.json new file mode 100644 index 0000000000..bf5d2f89d9 --- /dev/null +++ b/contract-tests/package-lock.json @@ -0,0 +1,5919 @@ +{ + "name": "contract-tests", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "license": "ISC", + "dependencies": { + "@noble/hashes": "^2.0.1", + "@polkadot-api/descriptors": "file:.papi/descriptors", + "@polkadot-api/ink-contracts": "^0.4.1", + "@polkadot-api/sdk-ink": "^0.5.1", + "@polkadot-labs/hdkd": "^0.0.25", + "@polkadot-labs/hdkd-helpers": "^0.0.25", + "@polkadot/api": "^16.4.6", + "@types/mocha": "^10.0.10", + "dotenv": "17.2.1", + "ethers": "^6.13.5", + "mocha": "^11.1.0", + "polkadot-api": "^1.22.0", + "rxjs": "^7.8.2", + "scale-ts": "^1.6.1", + "viem": "2.23.4", + "ws": "^8.18.2" + }, + "devDependencies": { + "@types/chai": "^5.0.1", + "@types/node": "^22.18.0", + "assert": "^2.1.0", + "chai": "^6.0.1", + "prettier": "^3.3.3", + "ts-node": "^10.9.2", + "typescript": "^5.7.2" + } + }, + ".papi/descriptors": { + "name": "@polkadot-api/descriptors", + "version": "0.1.0-autogenerated.9476216756280928360", + "peerDependencies": { + "polkadot-api": ">=1.21.0" + } + }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.11.1.tgz", + "integrity": "sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==", + "license": "MIT" + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@commander-js/extra-typings": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@commander-js/extra-typings/-/extra-typings-14.0.0.tgz", + "integrity": "sha512-hIn0ncNaJRLkZrxBIp5AsW/eXEHNKYQBh0aPdoUqNgD+Io3NIykQqpKFyKcuasZhicGaEZJX/JBSIkZ4e5x8Dg==", + "license": "MIT", + "peerDependencies": { + "commander": "~14.0.0" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@ethereumjs/rlp": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-10.1.0.tgz", + "integrity": "sha512-r67BJbwilammAqYI4B5okA66cNdTlFzeWxPNJOolKV52ZS/flo0tUBf4x4gxWXBgh48OgsdFV1Qp5pRoSe8IhQ==", + "license": "MPL-2.0", + "bin": { + "rlp": "bin/rlp.cjs" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/gen-mapping/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@noble/ciphers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", + "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", + "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves/node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.0.1.tgz", + "integrity": "sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@polkadot-api/cli": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/cli/-/cli-0.16.3.tgz", + "integrity": "sha512-s+p3dFw1vOeyMMqhUbt1RFyqPZdR7vg6joS0v9wBvK3qX5xU+QfOOaMxXJ8fl0mJEbwoJnJsvVl4MzjsABaKCg==", + "license": "MIT", + "dependencies": { + "@commander-js/extra-typings": "^14.0.0", + "@polkadot-api/codegen": "0.20.0", + "@polkadot-api/ink-contracts": "0.4.3", + "@polkadot-api/json-rpc-provider": "0.0.4", + "@polkadot-api/known-chains": "0.9.15", + "@polkadot-api/legacy-provider": "0.3.6", + "@polkadot-api/metadata-compatibility": "0.4.1", + "@polkadot-api/observable-client": "0.17.0", + "@polkadot-api/polkadot-sdk-compat": "2.3.3", + "@polkadot-api/sm-provider": "0.1.14", + "@polkadot-api/smoldot": "0.3.14", + "@polkadot-api/substrate-bindings": "0.16.5", + "@polkadot-api/substrate-client": "0.4.7", + "@polkadot-api/utils": "0.2.0", + "@polkadot-api/wasm-executor": "^0.2.2", + "@polkadot-api/ws-provider": "0.7.4", + "@types/node": "^24.10.1", + "commander": "^14.0.2", + "execa": "^9.6.0", + "fs.promises.exists": "^1.1.4", + "ora": "^9.0.0", + "read-pkg": "^10.0.0", + "rxjs": "^7.8.2", + "tsc-prog": "^2.3.0", + "tsup": "8.5.0", + "typescript": "^5.9.3", + "write-package": "^7.2.0" + }, + "bin": { + "papi": "dist/main.js", + "polkadot-api": "dist/main.js" + } + }, + "node_modules/@polkadot-api/cli/node_modules/@types/node": { + "version": "24.10.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", + "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@polkadot-api/cli/node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT" + }, + "node_modules/@polkadot-api/codegen": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/codegen/-/codegen-0.20.0.tgz", + "integrity": "sha512-akwPArm35UZcebUFtTKcEkdBLCjYyKweGw3/tT04p/EtM4OsQ1FxhRdXZ51ScBC3JVGCFQTUO2hNsd1E6YXvlw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/ink-contracts": "0.4.3", + "@polkadot-api/metadata-builders": "0.13.7", + "@polkadot-api/metadata-compatibility": "0.4.1", + "@polkadot-api/substrate-bindings": "0.16.5", + "@polkadot-api/utils": "0.2.0" + } + }, + "node_modules/@polkadot-api/common-sdk-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/common-sdk-utils/-/common-sdk-utils-0.1.0.tgz", + "integrity": "sha512-cgA9fh8dfBai9b46XaaQmj9vwzyHStQjc/xrAvQksgF6SqvZ0yAfxVqLvGrsz/Xi3dsAdKLg09PybC7MUAMv9w==", + "license": "MIT", + "peerDependencies": { + "polkadot-api": "^1.8.1", + "rxjs": ">=7.8.1" + } + }, + "node_modules/@polkadot-api/descriptors": { + "resolved": ".papi/descriptors", + "link": true + }, + "node_modules/@polkadot-api/ink-contracts": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/ink-contracts/-/ink-contracts-0.4.3.tgz", + "integrity": "sha512-Wl+4Dxjt0GAl+rADZEgrrqEesqX/xygTpX18TmzmspcKhb9QIZf9FJI8A5Sgtq0TKAOwsd1d/hbHVX3LgbXFXg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@polkadot-api/metadata-builders": "0.13.7", + "@polkadot-api/substrate-bindings": "0.16.5", + "@polkadot-api/utils": "0.2.0" + } + }, + "node_modules/@polkadot-api/json-rpc-provider": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider/-/json-rpc-provider-0.0.4.tgz", + "integrity": "sha512-9cDijLIxzHOBuq6yHqpqjJ9jBmXrctjc1OFqU+tQrS96adQze3mTIH6DTgfb/0LMrqxzxffz1HQGrIlEH00WrA==", + "license": "MIT" + }, + "node_modules/@polkadot-api/json-rpc-provider-proxy": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider-proxy/-/json-rpc-provider-proxy-0.2.7.tgz", + "integrity": "sha512-+HM4JQXzO2GPUD2++4GOLsmFL6LO8RoLvig0HgCLuypDgfdZMlwd8KnyGHjRnVEHA5X+kvXbk84TDcAXVxTazQ==", + "license": "MIT" + }, + "node_modules/@polkadot-api/known-chains": { + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/@polkadot-api/known-chains/-/known-chains-0.9.15.tgz", + "integrity": "sha512-VQGu2Anvnx0y0Ltd6sQB3aYzQFGsaQwf2znh+w4Oflaxln5lsjO/+trpXz/rdrdgyi0iafkhpeho/p/EGBwJ+A==", + "license": "MIT" + }, + "node_modules/@polkadot-api/legacy-provider": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@polkadot-api/legacy-provider/-/legacy-provider-0.3.6.tgz", + "integrity": "sha512-JZQg0HVtBowFKxNrZdnMBKXmeSBD4yFlz6egEpvE97RXRvjaBzTaVuFFhBchngq9YmgFQewuWSoX5XSUW6hcEg==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.0.4", + "@polkadot-api/raw-client": "0.1.1", + "@polkadot-api/substrate-bindings": "0.16.5", + "@polkadot-api/utils": "0.2.0" + }, + "peerDependencies": { + "rxjs": ">=7.8.0" + } + }, + "node_modules/@polkadot-api/logs-provider": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@polkadot-api/logs-provider/-/logs-provider-0.0.6.tgz", + "integrity": "sha512-4WgHlvy+xee1ADaaVf6+MlK/+jGMtsMgAzvbQOJZnP4PfQuagoTqaeayk8HYKxXGphogLlPbD06tANxcb+nvAg==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.0.4" + } + }, + "node_modules/@polkadot-api/merkleize-metadata": { + "version": "1.1.27", + "resolved": "https://registry.npmjs.org/@polkadot-api/merkleize-metadata/-/merkleize-metadata-1.1.27.tgz", + "integrity": "sha512-OdKwOzzrLL0Ju3pQA9LjeQEquMcD+KtLybUAO3fVxwjxD5cyI0RwillGoAIBJvfMaZpNxnxJnD+WzNjRcr7FiQ==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.13.7", + "@polkadot-api/substrate-bindings": "0.16.5", + "@polkadot-api/utils": "0.2.0" + } + }, + "node_modules/@polkadot-api/metadata-builders": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/@polkadot-api/metadata-builders/-/metadata-builders-0.13.7.tgz", + "integrity": "sha512-xwggY8F/gtX7qGzz+jzP3DZvWgBWIIFQhk+r2MJ431CR+tNKeTtzGdwNocVrb9NYTK2naC9ckJS14nrNM6LWLw==", + "license": "MIT", + "dependencies": { + "@polkadot-api/substrate-bindings": "0.16.5", + "@polkadot-api/utils": "0.2.0" + } + }, + "node_modules/@polkadot-api/metadata-compatibility": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/metadata-compatibility/-/metadata-compatibility-0.4.1.tgz", + "integrity": "sha512-mZt4Af6oPXEHAprrckJiSZkWRVf0mqwF+Bm+703rPsezLptQid9AjSzh1hkgIkOrPbg6IhWbmMhbuJVjx9VeQA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.13.7", + "@polkadot-api/substrate-bindings": "0.16.5" + } + }, + "node_modules/@polkadot-api/observable-client": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/observable-client/-/observable-client-0.17.0.tgz", + "integrity": "sha512-hilb12Fg1JrlM/0nucMT85//EQltB53fmoh7YNBsZMiNpavn/3qGTO4s0JMlC/LBbddYg0nxA+DMkSVlapo7cQ==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.13.7", + "@polkadot-api/substrate-bindings": "0.16.5", + "@polkadot-api/substrate-client": "0.4.7", + "@polkadot-api/utils": "0.2.0" + }, + "peerDependencies": { + "rxjs": ">=7.8.0" + } + }, + "node_modules/@polkadot-api/pjs-signer": { + "version": "0.6.17", + "resolved": "https://registry.npmjs.org/@polkadot-api/pjs-signer/-/pjs-signer-0.6.17.tgz", + "integrity": "sha512-bxFtyiNOchV0osh6m+1CaN4tkWF7Mo4IT9XPLZBwSybpHZgwmu2wbhgqBkVL98QMyGzud7NHfrJsTCgFU6jHGg==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.13.7", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/signers-common": "0.1.18", + "@polkadot-api/substrate-bindings": "0.16.5", + "@polkadot-api/utils": "0.2.0" + } + }, + "node_modules/@polkadot-api/polkadot-sdk-compat": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/polkadot-sdk-compat/-/polkadot-sdk-compat-2.3.3.tgz", + "integrity": "sha512-p30po+iv4trniSJ7UZiIt/rFInvtA9Tzg65EzuRkCaQAnh54a3MPp9w/q+x+SNLEcfzVLvf8LyPnMPOIpKuj5w==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.0.4" + } + }, + "node_modules/@polkadot-api/polkadot-signer": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@polkadot-api/polkadot-signer/-/polkadot-signer-0.1.6.tgz", + "integrity": "sha512-X7ghAa4r7doETtjAPTb50IpfGtrBmy3BJM5WCfNKa1saK04VFY9w+vDn+hwEcM4p0PcDHt66Ts74hzvHq54d9A==", + "license": "MIT" + }, + "node_modules/@polkadot-api/raw-client": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/raw-client/-/raw-client-0.1.1.tgz", + "integrity": "sha512-HxalpNEo8JCYXfxKM5p3TrK8sEasTGMkGjBNLzD4TLye9IK2smdb5oTvp2yfkU1iuVBdmjr69uif4NaukOYo2g==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.0.4" + } + }, + "node_modules/@polkadot-api/sdk-ink": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/sdk-ink/-/sdk-ink-0.5.1.tgz", + "integrity": "sha512-9pRnghjigivvgq7375hzkoazstvPDbc0YB01Jzw1/MYKcX+YJn1p/H8SAQTWbKlz2ohFgi1nwU52a0bsmKqb/Q==", + "license": "MIT", + "dependencies": { + "@ethereumjs/rlp": "^10.0.0", + "@polkadot-api/common-sdk-utils": "0.1.0", + "@polkadot-api/substrate-bindings": "^0.16.3", + "abitype": "^1.1.1", + "viem": "^2.37.9" + }, + "peerDependencies": { + "@polkadot-api/ink-contracts": ">=0.4.0", + "polkadot-api": ">=1.19.0", + "rxjs": ">=7.8.0" + } + }, + "node_modules/@polkadot-api/sdk-ink/node_modules/@noble/curves": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.1.tgz", + "integrity": "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@polkadot-api/sdk-ink/node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@polkadot-api/sdk-ink/node_modules/isows": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.7.tgz", + "integrity": "sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "peerDependencies": { + "ws": "*" + } + }, + "node_modules/@polkadot-api/sdk-ink/node_modules/ox": { + "version": "0.9.6", + "resolved": "https://registry.npmjs.org/ox/-/ox-0.9.6.tgz", + "integrity": "sha512-8SuCbHPvv2eZLYXrNmC0EC12rdzXQLdhnOMlHDW2wiCPLxBrOOJwX5L5E61by+UjTPOryqQiRSnjIKCI+GykKg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@adraffy/ens-normalize": "^1.11.0", + "@noble/ciphers": "^1.3.0", + "@noble/curves": "1.9.1", + "@noble/hashes": "^1.8.0", + "@scure/bip32": "^1.7.0", + "@scure/bip39": "^1.6.0", + "abitype": "^1.0.9", + "eventemitter3": "5.0.1" + }, + "peerDependencies": { + "typescript": ">=5.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@polkadot-api/sdk-ink/node_modules/viem": { + "version": "2.41.2", + "resolved": "https://registry.npmjs.org/viem/-/viem-2.41.2.tgz", + "integrity": "sha512-LYliajglBe1FU6+EH9mSWozp+gRA/QcHfxeD9Odf83AdH5fwUS7DroH4gHvlv6Sshqi1uXrYFA2B/EOczxd15g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@noble/curves": "1.9.1", + "@noble/hashes": "1.8.0", + "@scure/bip32": "1.7.0", + "@scure/bip39": "1.6.0", + "abitype": "1.1.0", + "isows": "1.0.7", + "ox": "0.9.6", + "ws": "8.18.3" + }, + "peerDependencies": { + "typescript": ">=5.0.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@polkadot-api/sdk-ink/node_modules/viem/node_modules/abitype": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.1.0.tgz", + "integrity": "sha512-6Vh4HcRxNMLA0puzPjM5GBgT4aAcFGKZzSgAXvuZ27shJP6NEpielTuqbBmZILR5/xd0PizkBGy5hReKz9jl5A==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "typescript": ">=5.0.4", + "zod": "^3.22.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/@polkadot-api/signer": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/@polkadot-api/signer/-/signer-0.2.11.tgz", + "integrity": "sha512-32tqbJo6JDfc/lHg+nTveeunFRULonWoTQX9xbs70arr/tAyyZfljupdECRK8CVRx1777es/CQO3QVj8EpWtYg==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^2.0.1", + "@polkadot-api/merkleize-metadata": "1.1.27", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/signers-common": "0.1.18", + "@polkadot-api/substrate-bindings": "0.16.5", + "@polkadot-api/utils": "0.2.0" + } + }, + "node_modules/@polkadot-api/signers-common": { + "version": "0.1.18", + "resolved": "https://registry.npmjs.org/@polkadot-api/signers-common/-/signers-common-0.1.18.tgz", + "integrity": "sha512-UQXuRZoQ+jMolEpIPF0mVXcoqQ/382fHrSOgfK5sIvjeH0HPf4P+s3IwcnwyAdpHY2gdHXYlHd/SAw7Q1gJ4EA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/metadata-builders": "0.13.7", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/substrate-bindings": "0.16.5", + "@polkadot-api/utils": "0.2.0" + } + }, + "node_modules/@polkadot-api/sm-provider": { + "version": "0.1.14", + "resolved": "https://registry.npmjs.org/@polkadot-api/sm-provider/-/sm-provider-0.1.14.tgz", + "integrity": "sha512-QQvoeBSIwnEm8IUhGA6sBU6LNh2v7SOuVOnF77ZD7P5ELTrdmQH2Tcn0W15qGTmTG45b3Z52XsKpuQbIJ7c7XA==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.0.4", + "@polkadot-api/json-rpc-provider-proxy": "0.2.7" + }, + "peerDependencies": { + "@polkadot-api/smoldot": ">=0.3" + } + }, + "node_modules/@polkadot-api/smoldot": { + "version": "0.3.14", + "resolved": "https://registry.npmjs.org/@polkadot-api/smoldot/-/smoldot-0.3.14.tgz", + "integrity": "sha512-eWqO0xFQaKzqY5mRYxYuZcj1IiaLcQP+J38UQyuJgEorm+9yHVEQ/XBWoM83P+Y8TwE5IWTICp1LCVeiFQTGPQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "^24.5.2", + "smoldot": "2.0.39" + } + }, + "node_modules/@polkadot-api/smoldot/node_modules/@types/node": { + "version": "24.10.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", + "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@polkadot-api/smoldot/node_modules/smoldot": { + "version": "2.0.39", + "resolved": "https://registry.npmjs.org/smoldot/-/smoldot-2.0.39.tgz", + "integrity": "sha512-yFMSzI6nkqWFTNao99lBA/TguUFU+bR3A5UGTDd/QqqB12jqzvZnmW/No6l2rKmagt8Qx/KybMNowV/E28znhA==", + "license": "GPL-3.0-or-later WITH Classpath-exception-2.0", + "dependencies": { + "ws": "^8.8.1" + } + }, + "node_modules/@polkadot-api/smoldot/node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT" + }, + "node_modules/@polkadot-api/substrate-bindings": { + "version": "0.16.5", + "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-bindings/-/substrate-bindings-0.16.5.tgz", + "integrity": "sha512-QFgNlBmtLtiUGTCTurxcE6UZrbI2DaQ5/gyIiC2FYfEhStL8tl20b09FRYHcSjY+lxN42Rcf9HVX+MCFWLYlpQ==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^2.0.1", + "@polkadot-api/utils": "0.2.0", + "@scure/base": "^2.0.0", + "scale-ts": "^1.6.1" + } + }, + "node_modules/@polkadot-api/substrate-bindings/node_modules/@scure/base": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-2.0.0.tgz", + "integrity": "sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@polkadot-api/substrate-client": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-client/-/substrate-client-0.4.7.tgz", + "integrity": "sha512-Mmx9VKincVqfVQmq89gzDk4DN3uKwf8CxoqYvq+EiPUZ1QmMUc7X4QMwG1MXIlYdnm5LSXzn+2Jn8ik8xMgL+w==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.0.4", + "@polkadot-api/raw-client": "0.1.1", + "@polkadot-api/utils": "0.2.0" + } + }, + "node_modules/@polkadot-api/utils": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/utils/-/utils-0.2.0.tgz", + "integrity": "sha512-nY3i5fQJoAxU4n3bD7Fs208/KR2J95SGfVc58kDjbRYN5a84kWaGEqzjBNtP9oqht49POM8Bm9mbIrkvC1Bzuw==", + "license": "MIT" + }, + "node_modules/@polkadot-api/wasm-executor": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@polkadot-api/wasm-executor/-/wasm-executor-0.2.3.tgz", + "integrity": "sha512-B2h1o+Qlo9idpASaHvMSoViB2I5ko5OAfwfhYF8LQDkTADK0B+SeStzNj1Qn+FG34wqTuv7HzBCdjaUgzYINJQ==", + "license": "MIT" + }, + "node_modules/@polkadot-api/ws-provider": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@polkadot-api/ws-provider/-/ws-provider-0.7.4.tgz", + "integrity": "sha512-mkk2p8wPht+ljU1xULCPMsLpNF7NHuGaufuDCIZZgopALaZpfVFJxc3qa9s6Xv8X3hM+TRoC5WknuD1ykRY99A==", + "license": "MIT", + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.0.4", + "@polkadot-api/json-rpc-provider-proxy": "0.2.7", + "@types/ws": "^8.18.1", + "ws": "^8.18.3" + } + }, + "node_modules/@polkadot-labs/hdkd": { + "version": "0.0.25", + "resolved": "https://registry.npmjs.org/@polkadot-labs/hdkd/-/hdkd-0.0.25.tgz", + "integrity": "sha512-+yZJC1TE4ZKdfoILw8nGxu3H/klrYXm9GdVB0kcyQDecq320ThUmM1M4l8d1F/3QD0Nez9NwHi9t5B++OgJU5A==", + "license": "MIT", + "dependencies": { + "@polkadot-labs/hdkd-helpers": "~0.0.26" + } + }, + "node_modules/@polkadot-labs/hdkd-helpers": { + "version": "0.0.25", + "resolved": "https://registry.npmjs.org/@polkadot-labs/hdkd-helpers/-/hdkd-helpers-0.0.25.tgz", + "integrity": "sha512-GwHayBuyHKfzvGD0vG47NbjFeiK6rRQHQAn1syut9nt0mhXMg4yb3tJ//IyM317qWuDU3HbD2OIp5jKDEQz2/A==", + "license": "MIT", + "dependencies": { + "@noble/curves": "^2.0.0", + "@noble/hashes": "^2.0.0", + "@scure/base": "^2.0.0", + "@scure/sr25519": "^0.3.0", + "scale-ts": "^1.6.1" + } + }, + "node_modules/@polkadot-labs/hdkd-helpers/node_modules/@noble/curves": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-2.0.1.tgz", + "integrity": "sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "2.0.1" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@polkadot-labs/hdkd-helpers/node_modules/@scure/base": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-2.0.0.tgz", + "integrity": "sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@polkadot-labs/hdkd/node_modules/@noble/curves": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-2.0.1.tgz", + "integrity": "sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "2.0.1" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@polkadot-labs/hdkd/node_modules/@polkadot-labs/hdkd-helpers": { + "version": "0.0.26", + "resolved": "https://registry.npmjs.org/@polkadot-labs/hdkd-helpers/-/hdkd-helpers-0.0.26.tgz", + "integrity": "sha512-mp3GCSiOQeh4aPt+DYBQq6UnX/tKgYUH5F75knjW3ATSA90ifEEWWjRan0Bddt4QKYKamaDGadK9GbVREgzQFw==", + "license": "MIT", + "dependencies": { + "@noble/curves": "^2.0.1", + "@noble/hashes": "^2.0.1", + "@scure/base": "^2.0.0", + "@scure/sr25519": "^0.3.0", + "scale-ts": "^1.6.1" + } + }, + "node_modules/@polkadot-labs/hdkd/node_modules/@scure/base": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-2.0.0.tgz", + "integrity": "sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@polkadot/api": { + "version": "16.5.3", + "resolved": "https://registry.npmjs.org/@polkadot/api/-/api-16.5.3.tgz", + "integrity": "sha512-Ptwo0f5Qonmus7KIklsbFcGTdHtNjbTAwl5GGI8Mp0dmBc7Y/ISJpIJX49UrG6FhW6COMa0ItsU87XIWMRwI/Q==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/api-augment": "16.5.3", + "@polkadot/api-base": "16.5.3", + "@polkadot/api-derive": "16.5.3", + "@polkadot/keyring": "^13.5.9", + "@polkadot/rpc-augment": "16.5.3", + "@polkadot/rpc-core": "16.5.3", + "@polkadot/rpc-provider": "16.5.3", + "@polkadot/types": "16.5.3", + "@polkadot/types-augment": "16.5.3", + "@polkadot/types-codec": "16.5.3", + "@polkadot/types-create": "16.5.3", + "@polkadot/types-known": "16.5.3", + "@polkadot/util": "^13.5.9", + "@polkadot/util-crypto": "^13.5.9", + "eventemitter3": "^5.0.1", + "rxjs": "^7.8.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/api-augment": { + "version": "16.5.3", + "resolved": "https://registry.npmjs.org/@polkadot/api-augment/-/api-augment-16.5.3.tgz", + "integrity": "sha512-9+8YKSS66x9qpWS+ZQ/FSm9P4mgE+icD53oAmeIykriPW2gcSTAiNufLwAjmAJAkOLcqbTD7LPjFW6xFlmtYsA==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/api-base": "16.5.3", + "@polkadot/rpc-augment": "16.5.3", + "@polkadot/types": "16.5.3", + "@polkadot/types-augment": "16.5.3", + "@polkadot/types-codec": "16.5.3", + "@polkadot/util": "^13.5.9", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/api-base": { + "version": "16.5.3", + "resolved": "https://registry.npmjs.org/@polkadot/api-base/-/api-base-16.5.3.tgz", + "integrity": "sha512-M1+pY6OFQ1uOB73VQMt2JAGq/UVISVQJISqyfjiUllUc0qIzaDMkcZxRqE34Lwaib3fD3RuIpG6dXqCL9rdzJQ==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/rpc-core": "16.5.3", + "@polkadot/types": "16.5.3", + "@polkadot/util": "^13.5.9", + "rxjs": "^7.8.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/api-derive": { + "version": "16.5.3", + "resolved": "https://registry.npmjs.org/@polkadot/api-derive/-/api-derive-16.5.3.tgz", + "integrity": "sha512-nMsnSC/N1SK1kNhgh2FhrrR1S8bTVH+3WsuBHFRzl+txKHq232IeIn9LpebSvgZdd77PaKaYBxbhYcNaA8Ypew==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/api": "16.5.3", + "@polkadot/api-augment": "16.5.3", + "@polkadot/api-base": "16.5.3", + "@polkadot/rpc-core": "16.5.3", + "@polkadot/types": "16.5.3", + "@polkadot/types-codec": "16.5.3", + "@polkadot/util": "^13.5.9", + "@polkadot/util-crypto": "^13.5.9", + "rxjs": "^7.8.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/keyring": { + "version": "13.5.9", + "resolved": "https://registry.npmjs.org/@polkadot/keyring/-/keyring-13.5.9.tgz", + "integrity": "sha512-bMCpHDN7U8ytxawjBZ89/he5s3AmEZuOdkM/ABcorh/flXNPfyghjFK27Gy4OKoFxX52yJ2sTHR4NxM87GuFXQ==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/util": "13.5.9", + "@polkadot/util-crypto": "13.5.9", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "13.5.9", + "@polkadot/util-crypto": "13.5.9" + } + }, + "node_modules/@polkadot/networks": { + "version": "13.5.9", + "resolved": "https://registry.npmjs.org/@polkadot/networks/-/networks-13.5.9.tgz", + "integrity": "sha512-nmKUKJjiLgcih0MkdlJNMnhEYdwEml2rv/h59ll2+rAvpsVWMTLCb6Cq6q7UC44+8kiWK2UUJMkFU+3PFFxndA==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/util": "13.5.9", + "@substrate/ss58-registry": "^1.51.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/rpc-augment": { + "version": "16.5.3", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-augment/-/rpc-augment-16.5.3.tgz", + "integrity": "sha512-q3Y+b0FSwbYe8Qopd4In+9KCL3eH5QmGVvimX7Z8+cvQ9+h+JUA6TP1bfpWBmYJRKlolaljsBQPBWoubchmxSw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/rpc-core": "16.5.3", + "@polkadot/types": "16.5.3", + "@polkadot/types-codec": "16.5.3", + "@polkadot/util": "^13.5.9", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/rpc-core": { + "version": "16.5.3", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-core/-/rpc-core-16.5.3.tgz", + "integrity": "sha512-UYEIRhO/1uTz/rpWLwUN9Re3c4fuTs0I9RR8dHKpKsH3jZTs1M3CtqME3NNzpGqApY1xb9tZemU/0GfHjCpeBQ==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/rpc-augment": "16.5.3", + "@polkadot/rpc-provider": "16.5.3", + "@polkadot/types": "16.5.3", + "@polkadot/util": "^13.5.9", + "rxjs": "^7.8.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/rpc-provider": { + "version": "16.5.3", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-provider/-/rpc-provider-16.5.3.tgz", + "integrity": "sha512-O7hD82HwjT4XJ4i/G58B52RSDM7arHXSpzahZKz4/wtb4x6d6b4JVdfZoskInadARFi5RwIWCrftwPtpRH81Fw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/keyring": "^13.5.9", + "@polkadot/types": "16.5.3", + "@polkadot/types-support": "16.5.3", + "@polkadot/util": "^13.5.9", + "@polkadot/util-crypto": "^13.5.9", + "@polkadot/x-fetch": "^13.5.9", + "@polkadot/x-global": "^13.5.9", + "@polkadot/x-ws": "^13.5.9", + "eventemitter3": "^5.0.1", + "mock-socket": "^9.3.1", + "nock": "^13.5.5", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@substrate/connect": "0.8.11" + } + }, + "node_modules/@polkadot/types": { + "version": "16.5.3", + "resolved": "https://registry.npmjs.org/@polkadot/types/-/types-16.5.3.tgz", + "integrity": "sha512-xy9uv/X4iT7uJ7TNCoqbcMkR8ePHwNW6DgpOU+1y1zc/KSu9ZC5i+haFOL68BpmR/QXk99YfuHoKwXvteDmykw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/keyring": "^13.5.9", + "@polkadot/types-augment": "16.5.3", + "@polkadot/types-codec": "16.5.3", + "@polkadot/types-create": "16.5.3", + "@polkadot/util": "^13.5.9", + "@polkadot/util-crypto": "^13.5.9", + "rxjs": "^7.8.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-augment": { + "version": "16.5.3", + "resolved": "https://registry.npmjs.org/@polkadot/types-augment/-/types-augment-16.5.3.tgz", + "integrity": "sha512-SfS4arJUxW6BeCEhLMVPrZwWOLte69k5+/lvEKOKHQA8Mz0MEkD4uqGZGibDjgBgdnu8N+3b+rs+Fn3YfZu4yA==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/types": "16.5.3", + "@polkadot/types-codec": "16.5.3", + "@polkadot/util": "^13.5.9", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-codec": { + "version": "16.5.3", + "resolved": "https://registry.npmjs.org/@polkadot/types-codec/-/types-codec-16.5.3.tgz", + "integrity": "sha512-b+oKMrIZrsFH4pPwvGQ6lMS8oFrYAGMy9QSbytA+KDmXAgTCtShz5XGvdQabvsGCjJ45EKgkKpKynVcYh3gk8g==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/util": "^13.5.9", + "@polkadot/x-bigint": "^13.5.9", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-create": { + "version": "16.5.3", + "resolved": "https://registry.npmjs.org/@polkadot/types-create/-/types-create-16.5.3.tgz", + "integrity": "sha512-XGnBLNamPh7eQGcHNGFghA/prH7z2BsQ+9EVSbHCvw9ENr/Ow24mmmkZyMG5WM/5I6/4HRdfwFJucYt1GL/p9g==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/types-codec": "16.5.3", + "@polkadot/util": "^13.5.9", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-known": { + "version": "16.5.3", + "resolved": "https://registry.npmjs.org/@polkadot/types-known/-/types-known-16.5.3.tgz", + "integrity": "sha512-ZLAZI24bQD0C9CJWYHxrLG8QSmzRzfWa51rlSNwZ9Atsc3R+GeX1YZGc9IljpQxYJCHrCqd6X8TXpAmEJdnbKw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/networks": "^13.5.9", + "@polkadot/types": "16.5.3", + "@polkadot/types-codec": "16.5.3", + "@polkadot/types-create": "16.5.3", + "@polkadot/util": "^13.5.9", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-support": { + "version": "16.5.3", + "resolved": "https://registry.npmjs.org/@polkadot/types-support/-/types-support-16.5.3.tgz", + "integrity": "sha512-ggyIRV+4Kn+aG1PiVT0PE00pAqMveyS3CuFsW9gJnKxeev4VrGfr08R4vw/61D7uIfpilkQdkXNgXAbeN09Mxg==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/util": "^13.5.9", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/util": { + "version": "13.5.9", + "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-13.5.9.tgz", + "integrity": "sha512-pIK3XYXo7DKeFRkEBNYhf3GbCHg6dKQisSvdzZwuyzA6m7YxQq4DFw4IE464ve4Z7WsJFt3a6C9uII36hl9EWw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@polkadot/x-bigint": "13.5.9", + "@polkadot/x-global": "13.5.9", + "@polkadot/x-textdecoder": "13.5.9", + "@polkadot/x-textencoder": "13.5.9", + "@types/bn.js": "^5.1.6", + "bn.js": "^5.2.1", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/util-crypto": { + "version": "13.5.9", + "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-13.5.9.tgz", + "integrity": "sha512-foUesMhxkTk8CZ0/XEcfvHk6I0O+aICqqVJllhOpyp/ZVnrTBKBf59T6RpsXx2pCtBlMsLRvg/6Mw7RND1HqDg==", + "license": "Apache-2.0", + "dependencies": { + "@noble/curves": "^1.3.0", + "@noble/hashes": "^1.3.3", + "@polkadot/networks": "13.5.9", + "@polkadot/util": "13.5.9", + "@polkadot/wasm-crypto": "^7.5.3", + "@polkadot/wasm-util": "^7.5.3", + "@polkadot/x-bigint": "13.5.9", + "@polkadot/x-randomvalues": "13.5.9", + "@scure/base": "^1.1.7", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "13.5.9" + } + }, + "node_modules/@polkadot/util-crypto/node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@polkadot/wasm-bridge": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-bridge/-/wasm-bridge-7.5.3.tgz", + "integrity": "sha512-mUvwwNH+uP1wqpMuHjmEwHxRIaVc5csmb+ukycWQGhzwhpXe/0fvBEU2TQ8kwgqO2MU0FS3hN/QcIWKfPRJgxQ==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/wasm-util": "7.5.3", + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*", + "@polkadot/x-randomvalues": "*" + } + }, + "node_modules/@polkadot/wasm-crypto": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto/-/wasm-crypto-7.5.3.tgz", + "integrity": "sha512-dmKUM9vw1wrnCHGuIeOtQo1pwuSF7fkyF4TYimTn3tAa0+3cDctYBErtGxgUeqP0Bo4Q0Of4/vnHlSk5Rbt9Uw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/wasm-bridge": "7.5.3", + "@polkadot/wasm-crypto-asmjs": "7.5.3", + "@polkadot/wasm-crypto-init": "7.5.3", + "@polkadot/wasm-crypto-wasm": "7.5.3", + "@polkadot/wasm-util": "7.5.3", + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*", + "@polkadot/x-randomvalues": "*" + } + }, + "node_modules/@polkadot/wasm-crypto-asmjs": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-7.5.3.tgz", + "integrity": "sha512-fSbbjI+4p0U3PQ8nOz/3p7euHriSdh+2CSywNuXHa8fMaYlMqCKt9K7+HI8CQ4RZNvZWDq+Py1nEDEkM4rZrvw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*" + } + }, + "node_modules/@polkadot/wasm-crypto-init": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-init/-/wasm-crypto-init-7.5.3.tgz", + "integrity": "sha512-KvUpxqvW70XhuDiw/N6rM8fQ7zRjIFblw+vdJ0/wwyagwg9jrYNA9TMei5ksQd9sxGCGXN/xJmwHJXuUjkocmg==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/wasm-bridge": "7.5.3", + "@polkadot/wasm-crypto-asmjs": "7.5.3", + "@polkadot/wasm-crypto-wasm": "7.5.3", + "@polkadot/wasm-util": "7.5.3", + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*", + "@polkadot/x-randomvalues": "*" + } + }, + "node_modules/@polkadot/wasm-crypto-wasm": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-7.5.3.tgz", + "integrity": "sha512-fc88+HyVxebB/40GVgGUOLBqyO3C571DXWPTFmtt5EX9H8gw7Jg0Bkitz7hgSVP2x4FjXpqS9UNTJ8trVH0x1A==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/wasm-util": "7.5.3", + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*" + } + }, + "node_modules/@polkadot/wasm-util": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-util/-/wasm-util-7.5.3.tgz", + "integrity": "sha512-hBr9bbjS+Yr7DrDUSkIIuvlTSoAlI8WXuo9YEB4C76j130u/cl+zyq6Iy/WnaTE6QH+8i9DhM8QTety6TqYnUQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*" + } + }, + "node_modules/@polkadot/x-bigint": { + "version": "13.5.9", + "resolved": "https://registry.npmjs.org/@polkadot/x-bigint/-/x-bigint-13.5.9.tgz", + "integrity": "sha512-JVW6vw3e8fkcRyN9eoc6JIl63MRxNQCP/tuLdHWZts1tcAYao0hpWUzteqJY93AgvmQ91KPsC1Kf3iuuZCi74g==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "13.5.9", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-fetch": { + "version": "13.5.9", + "resolved": "https://registry.npmjs.org/@polkadot/x-fetch/-/x-fetch-13.5.9.tgz", + "integrity": "sha512-urwXQZtT4yYROiRdJS6zHu18J/jCoAGpbgPIAjwdqjT11t9XIq4SjuPMxD19xBRhbYe9ocWV8i1KHuoMbZgKbA==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "13.5.9", + "node-fetch": "^3.3.2", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-global": { + "version": "13.5.9", + "resolved": "https://registry.npmjs.org/@polkadot/x-global/-/x-global-13.5.9.tgz", + "integrity": "sha512-zSRWvELHd3Q+bFkkI1h2cWIqLo1ETm+MxkNXLec3lB56iyq/MjWBxfXnAFFYFayvlEVneo7CLHcp+YTFd9aVSA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-randomvalues": { + "version": "13.5.9", + "resolved": "https://registry.npmjs.org/@polkadot/x-randomvalues/-/x-randomvalues-13.5.9.tgz", + "integrity": "sha512-Uuuz3oubf1JCCK97fsnVUnHvk4BGp/W91mQWJlgl5TIOUSSTIRr+lb5GurCfl4kgnQq53Zi5fJV+qR9YumbnZw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@polkadot/x-global": "13.5.9", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "13.5.9", + "@polkadot/wasm-util": "*" + } + }, + "node_modules/@polkadot/x-textdecoder": { + "version": "13.5.9", + "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-13.5.9.tgz", + "integrity": "sha512-W2HhVNUbC/tuFdzNMbnXAWsIHSg9SC9QWDNmFD3nXdSzlXNgL8NmuiwN2fkYvCQBtp/XSoy0gDLx0C+Fo19cfw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "13.5.9", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-textencoder": { + "version": "13.5.9", + "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-13.5.9.tgz", + "integrity": "sha512-SG0MHnLUgn1ZxFdm0KzMdTHJ47SfqFhdIPMcGA0Mg/jt2rwrfrP3jtEIJMsHfQpHvfsNPfv55XOMmoPWuQnP/Q==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "13.5.9", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-ws": { + "version": "13.5.9", + "resolved": "https://registry.npmjs.org/@polkadot/x-ws/-/x-ws-13.5.9.tgz", + "integrity": "sha512-NKVgvACTIvKT8CjaQu9d0dERkZsWIZngX/4NVSjc01WHmln4F4y/zyBdYn/Z2V0Zw28cISx+lB4qxRmqTe7gbg==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "13.5.9", + "tslib": "^2.8.0", + "ws": "^8.18.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz", + "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz", + "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz", + "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz", + "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz", + "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz", + "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz", + "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz", + "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz", + "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz", + "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz", + "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz", + "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz", + "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz", + "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz", + "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz", + "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz", + "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz", + "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz", + "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz", + "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz", + "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz", + "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rx-state/core": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@rx-state/core/-/core-0.1.4.tgz", + "integrity": "sha512-Z+3hjU2xh1HisLxt+W5hlYX/eGSDaXXP+ns82gq/PLZpkXLu0uwcNUh9RLY3Clq4zT+hSsA3vcpIGt6+UAb8rQ==", + "license": "MIT", + "peerDependencies": { + "rxjs": ">=7" + } + }, + "node_modules/@scure/base": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz", + "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz", + "integrity": "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==", + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.9.0", + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.6.0.tgz", + "integrity": "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39/node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/sr25519": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@scure/sr25519/-/sr25519-0.3.0.tgz", + "integrity": "sha512-SKsinX2sImunfcsH3seGrwH/OayBwwaJqVN8J1cJBNRCfbBq5q0jyTKGa9PcW1HWv9vXT6Yuq41JsxFLvF59ew==", + "license": "MIT", + "dependencies": { + "@noble/curves": "~2.0.0", + "@noble/hashes": "~2.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/sr25519/node_modules/@noble/curves": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-2.0.1.tgz", + "integrity": "sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "2.0.1" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@sec-ant/readable-stream": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", + "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", + "license": "MIT" + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@substrate/connect": { + "version": "0.8.11", + "resolved": "https://registry.npmjs.org/@substrate/connect/-/connect-0.8.11.tgz", + "integrity": "sha512-ofLs1PAO9AtDdPbdyTYj217Pe+lBfTLltdHDs3ds8no0BseoLeAGxpz1mHfi7zB4IxI3YyAiLjH6U8cw4pj4Nw==", + "license": "GPL-3.0-only", + "optional": true, + "dependencies": { + "@substrate/connect-extension-protocol": "^2.0.0", + "@substrate/connect-known-chains": "^1.1.5", + "@substrate/light-client-extension-helpers": "^1.0.0", + "smoldot": "2.0.26" + } + }, + "node_modules/@substrate/connect-extension-protocol": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@substrate/connect-extension-protocol/-/connect-extension-protocol-2.2.2.tgz", + "integrity": "sha512-t66jwrXA0s5Goq82ZtjagLNd7DPGCNjHeehRlE/gcJmJ+G56C0W+2plqOMRicJ8XGR1/YFnUSEqUFiSNbjGrAA==", + "license": "GPL-3.0-only", + "optional": true + }, + "node_modules/@substrate/connect-known-chains": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/@substrate/connect-known-chains/-/connect-known-chains-1.10.3.tgz", + "integrity": "sha512-OJEZO1Pagtb6bNE3wCikc2wrmvEU5x7GxFFLqqbz1AJYYxSlrPCGu4N2og5YTExo4IcloNMQYFRkBGue0BKZ4w==", + "license": "GPL-3.0-only", + "optional": true + }, + "node_modules/@substrate/light-client-extension-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@substrate/light-client-extension-helpers/-/light-client-extension-helpers-1.0.0.tgz", + "integrity": "sha512-TdKlni1mBBZptOaeVrKnusMg/UBpWUORNDv5fdCaJklP4RJiFOzBCrzC+CyVI5kQzsXBisZ+2pXm+rIjS38kHg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@polkadot-api/json-rpc-provider": "^0.0.1", + "@polkadot-api/json-rpc-provider-proxy": "^0.1.0", + "@polkadot-api/observable-client": "^0.3.0", + "@polkadot-api/substrate-client": "^0.1.2", + "@substrate/connect-extension-protocol": "^2.0.0", + "@substrate/connect-known-chains": "^1.1.5", + "rxjs": "^7.8.1" + }, + "peerDependencies": { + "smoldot": "2.x" + } + }, + "node_modules/@substrate/light-client-extension-helpers/node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "optional": true, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@substrate/light-client-extension-helpers/node_modules/@polkadot-api/json-rpc-provider": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider/-/json-rpc-provider-0.0.1.tgz", + "integrity": "sha512-/SMC/l7foRjpykLTUTacIH05H3mr9ip8b5xxfwXlVezXrNVLp3Cv0GX6uItkKd+ZjzVPf3PFrDF2B2/HLSNESA==", + "license": "MIT", + "optional": true + }, + "node_modules/@substrate/light-client-extension-helpers/node_modules/@polkadot-api/json-rpc-provider-proxy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider-proxy/-/json-rpc-provider-proxy-0.1.0.tgz", + "integrity": "sha512-8GSFE5+EF73MCuLQm8tjrbCqlgclcHBSRaswvXziJ0ZW7iw3UEMsKkkKvELayWyBuOPa2T5i1nj6gFOeIsqvrg==", + "license": "MIT", + "optional": true + }, + "node_modules/@substrate/light-client-extension-helpers/node_modules/@polkadot-api/metadata-builders": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@polkadot-api/metadata-builders/-/metadata-builders-0.3.2.tgz", + "integrity": "sha512-TKpfoT6vTb+513KDzMBTfCb/ORdgRnsS3TDFpOhAhZ08ikvK+hjHMt5plPiAX/OWkm1Wc9I3+K6W0hX5Ab7MVg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@polkadot-api/substrate-bindings": "0.6.0", + "@polkadot-api/utils": "0.1.0" + } + }, + "node_modules/@substrate/light-client-extension-helpers/node_modules/@polkadot-api/observable-client": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@polkadot-api/observable-client/-/observable-client-0.3.2.tgz", + "integrity": "sha512-HGgqWgEutVyOBXoGOPp4+IAq6CNdK/3MfQJmhCJb8YaJiaK4W6aRGrdQuQSTPHfERHCARt9BrOmEvTXAT257Ug==", + "license": "MIT", + "optional": true, + "dependencies": { + "@polkadot-api/metadata-builders": "0.3.2", + "@polkadot-api/substrate-bindings": "0.6.0", + "@polkadot-api/utils": "0.1.0" + }, + "peerDependencies": { + "@polkadot-api/substrate-client": "0.1.4", + "rxjs": ">=7.8.0" + } + }, + "node_modules/@substrate/light-client-extension-helpers/node_modules/@polkadot-api/substrate-bindings": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-bindings/-/substrate-bindings-0.6.0.tgz", + "integrity": "sha512-lGuhE74NA1/PqdN7fKFdE5C1gNYX357j1tWzdlPXI0kQ7h3kN0zfxNOpPUN7dIrPcOFZ6C0tRRVrBylXkI6xPw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@noble/hashes": "^1.3.1", + "@polkadot-api/utils": "0.1.0", + "@scure/base": "^1.1.1", + "scale-ts": "^1.6.0" + } + }, + "node_modules/@substrate/light-client-extension-helpers/node_modules/@polkadot-api/utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/utils/-/utils-0.1.0.tgz", + "integrity": "sha512-MXzWZeuGxKizPx2Xf/47wx9sr/uxKw39bVJUptTJdsaQn/TGq+z310mHzf1RCGvC1diHM8f593KrnDgc9oNbJA==", + "license": "MIT", + "optional": true + }, + "node_modules/@substrate/ss58-registry": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/@substrate/ss58-registry/-/ss58-registry-1.51.0.tgz", + "integrity": "sha512-TWDurLiPxndFgKjVavCniytBIw+t4ViOi7TYp9h/D0NMmkEc9klFTo+827eyEJ0lELpqO207Ey7uGxUa+BS1jQ==", + "license": "Apache-2.0" + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", + "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-DLbJ1BPqxvQhIGbeu8VbUC1DiAiahHtAYvA0ZEAa4P31F7IaArc8z3C3BRQdWX4mtLQuABG4yzp76ZrS02Ui1Q==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/bn.js/node_modules/@types/node": { + "version": "24.10.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", + "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/bn.js/node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT" + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" + }, + "node_modules/@types/mocha": { + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", + "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.19.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.1.tgz", + "integrity": "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/ws/node_modules/@types/node": { + "version": "24.10.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", + "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/ws/node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT" + }, + "node_modules/abitype": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.2.0.tgz", + "integrity": "sha512-fD3ROjckUrWsybaSor2AdWxzA0e/DSyV2dA4aYd7bd8orHsoJjl09fOgKfUkTDfk0BsDGBf4NBgu/c7JoS2Npw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "typescript": ">=5.0.4", + "zod": "^3.22.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/aes-js": { + "version": "4.0.0-beta.5", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", + "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==", + "license": "MIT" + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "license": "MIT" + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/assert": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", + "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "is-nan": "^1.3.2", + "object-is": "^1.1.5", + "object.assign": "^4.1.4", + "util": "^0.12.5" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/bn.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.2.tgz", + "integrity": "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "license": "ISC" + }, + "node_modules/bundle-require": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz", + "integrity": "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==", + "license": "MIT", + "dependencies": { + "load-tsconfig": "^0.2.3" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "esbuild": ">=0.18" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chai": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.1.tgz", + "integrity": "sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-3.3.0.tgz", + "integrity": "sha512-/+40ljC3ONVnYIttjMWrlL51nItDAbBrq2upN8BPyvGU/2n5Oxw3tbNwORCaNuNqLJnxGqOfjUuhsv7l5Q4IsQ==", + "license": "MIT", + "engines": { + "node": ">=18.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/commander": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", + "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=20" + } + }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deepmerge-ts": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", + "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/detect-indent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-7.0.2.tgz", + "integrity": "sha512-y+8xyqdGLL+6sh0tVeHcfP/QDd8gUgbasolJJpY7NgeQGSZ739bDtSiaiDgtoicy+mtYB81dKLxO9xRhCyIB3A==", + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dotenv": { + "version": "17.2.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.1.tgz", + "integrity": "sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "hasInstallScript": true, + "license": "MIT", + "peer": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ethers": { + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.16.0.tgz", + "integrity": "sha512-U1wulmetNymijEhpSEQ7Ct/P/Jw9/e7R1j5XIbPRydgV2DjLVMsULDlNksq3RQnFgKoLlZf88ijYtWEXcPa07A==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/ethers-io/" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@adraffy/ens-normalize": "1.10.1", + "@noble/curves": "1.2.0", + "@noble/hashes": "1.3.2", + "@types/node": "22.7.5", + "aes-js": "4.0.0-beta.5", + "tslib": "2.7.0", + "ws": "8.17.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/ethers/node_modules/@adraffy/ens-normalize": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz", + "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==", + "license": "MIT" + }, + "node_modules/ethers/node_modules/@noble/curves": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ethers/node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ethers/node_modules/@types/node": { + "version": "22.7.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", + "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/ethers/node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "license": "0BSD" + }, + "node_modules/ethers/node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "license": "MIT" + }, + "node_modules/ethers/node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, + "node_modules/execa": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.6.1.tgz", + "integrity": "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==", + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.6", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.1", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.2.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.1.1" + }, + "engines": { + "node": "^18.19.0 || >=20.5.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fix-dts-default-cjs-exports": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fix-dts-default-cjs-exports/-/fix-dts-default-cjs-exports-1.0.1.tgz", + "integrity": "sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==", + "license": "MIT", + "dependencies": { + "magic-string": "^0.30.17", + "mlly": "^1.7.4", + "rollup": "^4.34.8" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/fs.promises.exists": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/fs.promises.exists/-/fs.promises.exists-1.1.4.tgz", + "integrity": "sha512-lJzUGWbZn8vhGWBedA+RYjB/BeJ+3458ljUfmplqhIeb6ewzTFWNPCR1HCiYCkXV9zxcHz9zXkJzMsEgDLzh3Q==", + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/fs.promises.exists?sponsor=1" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", + "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "license": "MIT", + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/hosted-git-info": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.2.tgz", + "integrity": "sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg==", + "license": "ISC", + "dependencies": { + "lru-cache": "^11.1.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/human-signals": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz", + "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/index-to-position": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.2.0.tgz", + "integrity": "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-arguments": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", + "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-nan": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/isows": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.6.tgz", + "integrity": "sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "peerDependencies": { + "ws": "*" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "license": "ISC" + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/load-tsconfig": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", + "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mlly": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz", + "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==", + "license": "MIT", + "dependencies": { + "acorn": "^8.15.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.1" + } + }, + "node_modules/mocha": { + "version": "11.7.5", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.5.tgz", + "integrity": "sha512-mTT6RgopEYABzXWFx+GcJ+ZQ32kp4fMf0xvpZIIfSq9Z8lC/++MtcCnQ9t5FP2veYEP95FIYSvW+U9fV4xrlig==", + "license": "MIT", + "dependencies": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/mocha/node_modules/diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/mock-socket": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/mock-socket/-/mock-socket-9.3.1.tgz", + "integrity": "sha512-qxBgB7Qa2sEQgHFjj0dSigq7fX4k6Saisd5Nelwp2q8mlbAFh5dHV9JTTlF8viYJLSSWgMCZFUom8PJcMNBoJw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nock": { + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.6.tgz", + "integrity": "sha512-o2zOYiCpzRqSzPj0Zt/dQ/DqZeYoaQ7TUonc/xUPjCGl9WeHpNbxgVvOquXYAaJzI0M9BXV3HTzG0p8IUAbBTQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "propagate": "^2.0.0" + }, + "engines": { + "node": ">= 10.13" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/normalize-package-data": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-8.0.0.tgz", + "integrity": "sha512-RWk+PI433eESQ7ounYxIp67CYuVsS1uYSonX3kA6ps/3LWfjVQa/ptEg6Y3T6uAMq1mWpX9PQ+qx+QaHpsc7gQ==", + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^9.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-run-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-9.0.0.tgz", + "integrity": "sha512-m0pg2zscbYgWbqRR6ABga5c3sZdEon7bSgjnlXC64kxtxLOyjRcbbUkLj7HFyy/FTD+P2xdBWu8snGhYI0jc4A==", + "license": "MIT", + "dependencies": { + "chalk": "^5.6.2", + "cli-cursor": "^5.0.0", + "cli-spinners": "^3.2.0", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.1.0", + "log-symbols": "^7.0.1", + "stdin-discarder": "^0.2.2", + "string-width": "^8.1.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ora/node_modules/log-symbols": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-7.0.1.tgz", + "integrity": "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==", + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0", + "yoctocolors": "^2.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/string-width": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.1.0.tgz", + "integrity": "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==", + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/ox": { + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/ox/-/ox-0.6.7.tgz", + "integrity": "sha512-17Gk/eFsFRAZ80p5eKqv89a57uXjd3NgIf1CaXojATPBuujVc/fQSVhBeAU9JCRB+k7J50WQAyWTxK19T9GgbA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@adraffy/ens-normalize": "^1.10.1", + "@noble/curves": "^1.6.0", + "@noble/hashes": "^1.5.0", + "@scure/bip32": "^1.5.0", + "@scure/bip39": "^1.4.0", + "abitype": "^1.0.6", + "eventemitter3": "5.0.1" + }, + "peerDependencies": { + "typescript": ">=5.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/ox/node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/parse-json": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", + "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "index-to-position": "^1.1.0", + "type-fest": "^4.39.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/polkadot-api": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/polkadot-api/-/polkadot-api-1.22.0.tgz", + "integrity": "sha512-uREBLroPbnJxBBQ+qSkKLF493qukX4PAg32iThlELrZdxfNNgro6nvWRdVmBv73tFHvf+nyWWHKTx1c57nbixg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@polkadot-api/cli": "0.16.3", + "@polkadot-api/ink-contracts": "0.4.3", + "@polkadot-api/json-rpc-provider": "0.0.4", + "@polkadot-api/known-chains": "0.9.15", + "@polkadot-api/logs-provider": "0.0.6", + "@polkadot-api/metadata-builders": "0.13.7", + "@polkadot-api/metadata-compatibility": "0.4.1", + "@polkadot-api/observable-client": "0.17.0", + "@polkadot-api/pjs-signer": "0.6.17", + "@polkadot-api/polkadot-sdk-compat": "2.3.3", + "@polkadot-api/polkadot-signer": "0.1.6", + "@polkadot-api/signer": "0.2.11", + "@polkadot-api/sm-provider": "0.1.14", + "@polkadot-api/smoldot": "0.3.14", + "@polkadot-api/substrate-bindings": "0.16.5", + "@polkadot-api/substrate-client": "0.4.7", + "@polkadot-api/utils": "0.2.0", + "@polkadot-api/ws-provider": "0.7.4", + "@rx-state/core": "^0.1.4" + }, + "bin": { + "papi": "bin/cli.mjs", + "polkadot-api": "bin/cli.mjs" + }, + "peerDependencies": { + "rxjs": ">=7.8.0" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/prettier": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz", + "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-ms": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.0.tgz", + "integrity": "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==", + "license": "MIT", + "dependencies": { + "parse-ms": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/read-pkg": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-10.0.0.tgz", + "integrity": "sha512-A70UlgfNdKI5NSvTTfHzLQj7NJRpJ4mT5tGafkllJ4wh71oYuGm/pzphHcmW4s35iox56KSK721AihodoXSc/A==", + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.4", + "normalize-package-data": "^8.0.0", + "parse-json": "^8.3.0", + "type-fest": "^5.2.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.3.0.tgz", + "integrity": "sha512-d9CwU93nN0IA1QL+GSNDdwLAu1Ew5ZjTwupvedwg3WdfoH6pIDvYQ2hV0Uc2nKBLPq7NB5apCx57MLS5qlmO5g==", + "license": "(MIT OR CC0-1.0)", + "dependencies": { + "tagged-tag": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rollup": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", + "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.53.3", + "@rollup/rollup-android-arm64": "4.53.3", + "@rollup/rollup-darwin-arm64": "4.53.3", + "@rollup/rollup-darwin-x64": "4.53.3", + "@rollup/rollup-freebsd-arm64": "4.53.3", + "@rollup/rollup-freebsd-x64": "4.53.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", + "@rollup/rollup-linux-arm-musleabihf": "4.53.3", + "@rollup/rollup-linux-arm64-gnu": "4.53.3", + "@rollup/rollup-linux-arm64-musl": "4.53.3", + "@rollup/rollup-linux-loong64-gnu": "4.53.3", + "@rollup/rollup-linux-ppc64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-musl": "4.53.3", + "@rollup/rollup-linux-s390x-gnu": "4.53.3", + "@rollup/rollup-linux-x64-gnu": "4.53.3", + "@rollup/rollup-linux-x64-musl": "4.53.3", + "@rollup/rollup-openharmony-arm64": "4.53.3", + "@rollup/rollup-win32-arm64-msvc": "4.53.3", + "@rollup/rollup-win32-ia32-msvc": "4.53.3", + "@rollup/rollup-win32-x64-gnu": "4.53.3", + "@rollup/rollup-win32-x64-msvc": "4.53.3", + "fsevents": "~2.3.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/scale-ts": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/scale-ts/-/scale-ts-1.6.1.tgz", + "integrity": "sha512-PBMc2AWc6wSEqJYBDPcyCLUj9/tMKnLX70jLOSndMtcUoLQucP/DM0vnQo1wJAYjTrQiq8iG9rD0q6wFzgjH7g==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sort-keys": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-5.1.0.tgz", + "integrity": "sha512-aSbHV0DaBcr7u0PVHXzM6NbZNAtrr9sF6+Qfs9UUVG7Ll3jQ6hHi8F/xqIIcn2rvIVbr0v/2zyjSdwSV47AgLQ==", + "license": "MIT", + "dependencies": { + "is-plain-obj": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/source-map": { + "version": "0.8.0-beta.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", + "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", + "license": "BSD-3-Clause", + "dependencies": { + "whatwg-url": "^7.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.22", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz", + "integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==", + "license": "CC0-1.0" + }, + "node_modules/stdin-discarder": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", + "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/tagged-tag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz", + "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", + "license": "MIT", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "license": "Apache-2.0" + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/tsc-prog": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tsc-prog/-/tsc-prog-2.3.0.tgz", + "integrity": "sha512-ycET2d75EgcX7y8EmG4KiZkLAwUzbY4xRhA6NU0uVbHkY4ZjrAAuzTMxXI85kOwATqPnBI5C/7y7rlpY0xdqHA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "typescript": ">=4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tsup": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.5.0.tgz", + "integrity": "sha512-VmBp77lWNQq6PfuMqCHD3xWl22vEoWsKajkF8t+yMBawlUS8JzEI+vOVMeuNZIuMML8qXRizFKi9oD5glKQVcQ==", + "license": "MIT", + "dependencies": { + "bundle-require": "^5.1.0", + "cac": "^6.7.14", + "chokidar": "^4.0.3", + "consola": "^3.4.0", + "debug": "^4.4.0", + "esbuild": "^0.25.0", + "fix-dts-default-cjs-exports": "^1.0.0", + "joycon": "^3.1.1", + "picocolors": "^1.1.1", + "postcss-load-config": "^6.0.1", + "resolve-from": "^5.0.0", + "rollup": "^4.34.8", + "source-map": "0.8.0-beta.0", + "sucrase": "^3.35.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.11", + "tree-kill": "^1.2.2" + }, + "bin": { + "tsup": "dist/cli-default.js", + "tsup-node": "dist/cli-node.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@microsoft/api-extractor": "^7.36.0", + "@swc/core": "^1", + "postcss": "^8.4.12", + "typescript": ">=4.5.0" + }, + "peerDependenciesMeta": { + "@microsoft/api-extractor": { + "optional": true + }, + "@swc/core": { + "optional": true + }, + "postcss": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ufo": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", + "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/viem": { + "version": "2.23.4", + "resolved": "https://registry.npmjs.org/viem/-/viem-2.23.4.tgz", + "integrity": "sha512-UQquuolKlS1w5H5e0Fd1KKoUlIPJryIEBzY5AUhGyV1ka+9O6+3uYVhUzj6RbvGK0PtsMKn2ddwPZFwjNDVU/A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@noble/curves": "1.8.1", + "@noble/hashes": "1.7.1", + "@scure/bip32": "1.6.2", + "@scure/bip39": "1.5.4", + "abitype": "1.0.8", + "isows": "1.0.6", + "ox": "0.6.7", + "ws": "8.18.0" + }, + "peerDependencies": { + "typescript": ">=5.0.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/viem/node_modules/@noble/curves": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.1.tgz", + "integrity": "sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.7.1" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/viem/node_modules/@noble/hashes": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz", + "integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/viem/node_modules/@scure/bip32": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.6.2.tgz", + "integrity": "sha512-t96EPDMbtGgtb7onKKqxRLfE5g05k7uHnHRM2xdE6BP/ZmxaLtPek4J4KfVn/90IQNrU1IOAqMgiDtUdtbe3nw==", + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.8.1", + "@noble/hashes": "~1.7.1", + "@scure/base": "~1.2.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/viem/node_modules/@scure/bip32/node_modules/@noble/curves": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.2.tgz", + "integrity": "sha512-vnI7V6lFNe0tLAuJMu+2sX+FcL14TaCWy1qiczg1VwRmPrpQCdq5ESXQMqUc2tluRNf6irBXrWbl1mGN8uaU/g==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.7.2" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/viem/node_modules/@scure/bip32/node_modules/@noble/hashes": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.2.tgz", + "integrity": "sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/viem/node_modules/@scure/bip39": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.5.4.tgz", + "integrity": "sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.7.1", + "@scure/base": "~1.2.4" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/viem/node_modules/abitype": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.0.8.tgz", + "integrity": "sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "typescript": ">=5.0.4", + "zod": "^3 >=3.22.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/viem/node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "license": "MIT", + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/workerpool": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", + "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", + "license": "Apache-2.0" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/write-json-file": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/write-json-file/-/write-json-file-6.0.0.tgz", + "integrity": "sha512-MNHcU3f9WxnNyR6MxsYSj64Jz0+dwIpisWKWq9gqLj/GwmA9INg3BZ3vt70/HB3GEwrnDQWr4RPrywnhNzmUFA==", + "license": "MIT", + "dependencies": { + "detect-indent": "^7.0.1", + "is-plain-obj": "^4.1.0", + "sort-keys": "^5.0.0", + "write-file-atomic": "^5.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/write-package": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/write-package/-/write-package-7.2.0.tgz", + "integrity": "sha512-uMQTubF/vcu+Wd0b5BGtDmiXePd/+44hUWQz2nZPbs92/BnxRo74tqs+hqDo12RLiEd+CXFKUwxvvIZvtt34Jw==", + "license": "MIT", + "dependencies": { + "deepmerge-ts": "^7.1.0", + "read-pkg": "^9.0.1", + "sort-keys": "^5.0.0", + "type-fest": "^4.23.0", + "write-json-file": "^6.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/write-package/node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/write-package/node_modules/normalize-package-data": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/write-package/node_modules/read-pkg": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz", + "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==", + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.3", + "normalize-package-data": "^6.0.0", + "parse-json": "^8.0.0", + "type-fest": "^4.6.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/write-package/node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "license": "MIT", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser/node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", + "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/contract-tests/package.json b/contract-tests/package.json index 6af55b1d9e..0bf4adac9d 100644 --- a/contract-tests/package.json +++ b/contract-tests/package.json @@ -1,6 +1,6 @@ { "scripts": { - "test": "TS_NODE_PREFER_TS_EXTS=1 TS_NODE_TRANSPILE_ONLY=1 mocha --timeout 999999 --retries 3 --file src/setup.ts --require ts-node/register --extension ts \"test/**/*.ts\"" + "test": "TS_NODE_PREFER_TS_EXTS=1 TS_NODE_TRANSPILE_ONLY=1 mocha --timeout 999999 --retries 3 --file src/setup.ts --require ts-node/register --extension ts \"test/subnet.precompile.hyperparameter.test.ts\"" }, "keywords": [], "author": "", @@ -11,12 +11,12 @@ "@polkadot-api/sdk-ink": "^0.5.1", "@polkadot-labs/hdkd": "^0.0.25", "@polkadot-labs/hdkd-helpers": "^0.0.25", + "@polkadot/api": "^16.4.6", "@types/mocha": "^10.0.10", "dotenv": "17.2.1", "ethers": "^6.13.5", "mocha": "^11.1.0", "polkadot-api": "^1.22.0", - "@polkadot/api": "^16.4.6", "rxjs": "^7.8.2", "scale-ts": "^1.6.1", "viem": "2.23.4", diff --git a/contract-tests/test/subnet.precompile.hyperparameter.test.ts b/contract-tests/test/subnet.precompile.hyperparameter.test.ts index 8598b45a81..75d361a77f 100644 --- a/contract-tests/test/subnet.precompile.hyperparameter.test.ts +++ b/contract-tests/test/subnet.precompile.hyperparameter.test.ts @@ -2,12 +2,13 @@ import * as assert from "assert"; import { getAliceSigner, getDevnetApi, getRandomSubstrateKeypair, waitForTransactionWithRetry } from "../src/substrate" import { devnet } from "@polkadot-api/descriptors" -import { Binary, TypedApi, getTypedCodecs } from "polkadot-api"; +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 @@ -552,16 +553,16 @@ describe("Test the Subnet precompile contract", () => { const netuid = totalNetwork - 1; const coldkeySs58 = convertH160ToSS58(wallet.address) - const newColdkeySs58 = convertPublicKeyToSs58(hotkey1.publicKey) + 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.ColdkeySwapScheduled.value.enc([ + const valueBytes = codec.query.SubtensorModule.ColdkeySwapAnnouncements.value.enc([ executionBlock, - newColdkeySs58, + newColdkeyHash ]) - const key = await api.query.SubtensorModule.ColdkeySwapScheduled.getKey(coldkeySs58); + 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({ @@ -570,8 +571,9 @@ describe("Test the Subnet precompile contract", () => { const sudoTx = api.tx.Sudo.sudo({ call: setStorageCall.decodedCall }) await waitForTransactionWithRetry(api, sudoTx, getAliceSigner()) - const storedValue = await api.query.SubtensorModule.ColdkeySwapScheduled.getValue(coldkeySs58) - assert.deepStrictEqual(storedValue, [executionBlock, newColdkeySs58]) + 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); diff --git a/contract-tests/yarn.lock b/contract-tests/yarn.lock index 625b65c07d..6688f4fb8a 100644 --- a/contract-tests/yarn.lock +++ b/contract-tests/yarn.lock @@ -4,17 +4,17 @@ "@adraffy/ens-normalize@1.10.1": version "1.10.1" - resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz#63430d04bd8c5e74f8d7d049338f1cd9d4f02069" + resolved "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz" integrity sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw== "@adraffy/ens-normalize@^1.10.1", "@adraffy/ens-normalize@^1.11.0": version "1.11.1" - resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.11.1.tgz#6c2d657d4b2dfb37f8ea811dcb3e60843d4ac24a" + resolved "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.11.1.tgz" integrity sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ== "@babel/code-frame@^7.26.2": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz" integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg== dependencies: "@babel/helper-validator-identifier" "^7.27.1" @@ -23,17 +23,17 @@ "@babel/helper-validator-identifier@^7.27.1": version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz" integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q== "@commander-js/extra-typings@^14.0.0": version "14.0.0" - resolved "https://registry.yarnpkg.com/@commander-js/extra-typings/-/extra-typings-14.0.0.tgz#a48b73e8e9c80d5c7538d361f9c1fb9b231643d7" + resolved "https://registry.npmjs.org/@commander-js/extra-typings/-/extra-typings-14.0.0.tgz" integrity sha512-hIn0ncNaJRLkZrxBIp5AsW/eXEHNKYQBh0aPdoUqNgD+Io3NIykQqpKFyKcuasZhicGaEZJX/JBSIkZ4e5x8Dg== "@cspotcode/source-map-support@^0.8.0": version "0.8.1" - resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz" integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== dependencies: "@jridgewell/trace-mapping" "0.3.9" @@ -60,7 +60,7 @@ "@esbuild/darwin-arm64@0.25.12": version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz#79197898ec1ff745d21c071e1c7cc3c802f0c1fd" + resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz" integrity sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg== "@esbuild/darwin-x64@0.25.12": @@ -170,12 +170,12 @@ "@ethereumjs/rlp@^10.0.0": version "10.1.0" - resolved "https://registry.yarnpkg.com/@ethereumjs/rlp/-/rlp-10.1.0.tgz#fe681ed0fd2f55ed8623c0d445353d1411703b5d" + resolved "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-10.1.0.tgz" integrity sha512-r67BJbwilammAqYI4B5okA66cNdTlFzeWxPNJOolKV52ZS/flo0tUBf4x4gxWXBgh48OgsdFV1Qp5pRoSe8IhQ== "@isaacs/cliui@^8.0.2": version "8.0.2" - resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + resolved "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz" integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== dependencies: string-width "^5.1.2" @@ -187,7 +187,7 @@ "@jridgewell/gen-mapping@^0.3.2": version "0.3.13" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f" + resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz" integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA== dependencies: "@jridgewell/sourcemap-codec" "^1.5.0" @@ -195,17 +195,17 @@ "@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": version "3.1.2" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz" integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== "@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0", "@jridgewell/sourcemap-codec@^1.5.5": version "1.5.5" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz" integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== "@jridgewell/trace-mapping@0.3.9": version "0.3.9" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz" integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== dependencies: "@jridgewell/resolve-uri" "^3.0.3" @@ -213,7 +213,7 @@ "@jridgewell/trace-mapping@^0.3.24": version "0.3.31" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz" integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== dependencies: "@jridgewell/resolve-uri" "^3.1.0" @@ -221,84 +221,84 @@ "@noble/ciphers@^1.3.0": version "1.3.0" - resolved "https://registry.yarnpkg.com/@noble/ciphers/-/ciphers-1.3.0.tgz#f64b8ff886c240e644e5573c097f86e5b43676dc" + resolved "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz" integrity sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw== "@noble/curves@1.2.0": version "1.2.0" - resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.2.0.tgz#92d7e12e4e49b23105a2555c6984d41733d65c35" + resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz" integrity sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw== dependencies: "@noble/hashes" "1.3.2" "@noble/curves@1.8.1": version "1.8.1" - resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.8.1.tgz#19bc3970e205c99e4bdb1c64a4785706bce497ff" + resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.8.1.tgz" integrity sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ== dependencies: "@noble/hashes" "1.7.1" "@noble/curves@1.9.1": version "1.9.1" - resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.9.1.tgz#9654a0bc6c13420ae252ddcf975eaf0f58f0a35c" + resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.9.1.tgz" integrity sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA== dependencies: "@noble/hashes" "1.8.0" "@noble/curves@^1.3.0", "@noble/curves@^1.6.0", "@noble/curves@~1.9.0": version "1.9.7" - resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.9.7.tgz#79d04b4758a43e4bca2cbdc62e7771352fa6b951" + resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz" integrity sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw== dependencies: "@noble/hashes" "1.8.0" "@noble/curves@^2.0.0", "@noble/curves@^2.0.1", "@noble/curves@~2.0.0": version "2.0.1" - resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-2.0.1.tgz#64ba8bd5e8564a02942655602515646df1cdb3ad" + resolved "https://registry.npmjs.org/@noble/curves/-/curves-2.0.1.tgz" integrity sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw== dependencies: "@noble/hashes" "2.0.1" "@noble/curves@~1.8.1": version "1.8.2" - resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.8.2.tgz#8f24c037795e22b90ae29e222a856294c1d9ffc7" + resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.8.2.tgz" integrity sha512-vnI7V6lFNe0tLAuJMu+2sX+FcL14TaCWy1qiczg1VwRmPrpQCdq5ESXQMqUc2tluRNf6irBXrWbl1mGN8uaU/g== dependencies: "@noble/hashes" "1.7.2" "@noble/hashes@1.3.2": version "1.3.2" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" + resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz" integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== -"@noble/hashes@1.7.1": +"@noble/hashes@1.7.1", "@noble/hashes@~1.7.1": version "1.7.1" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.7.1.tgz#5738f6d765710921e7a751e00c20ae091ed8db0f" + resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz" integrity sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ== -"@noble/hashes@1.7.2", "@noble/hashes@~1.7.1": +"@noble/hashes@1.7.2": version "1.7.2" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.7.2.tgz#d53c65a21658fb02f3303e7ee3ba89d6754c64b4" + resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.2.tgz" integrity sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ== "@noble/hashes@1.8.0", "@noble/hashes@^1.3.1", "@noble/hashes@^1.3.3", "@noble/hashes@^1.5.0", "@noble/hashes@^1.8.0", "@noble/hashes@~1.8.0": version "1.8.0" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.8.0.tgz#cee43d801fcef9644b11b8194857695acd5f815a" + resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz" integrity sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A== "@noble/hashes@2.0.1", "@noble/hashes@^2.0.0", "@noble/hashes@^2.0.1", "@noble/hashes@~2.0.0": version "2.0.1" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-2.0.1.tgz#fc1a928061d1232b0a52bb754393c37a5216c89e" + resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-2.0.1.tgz" integrity sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw== "@pkgjs/parseargs@^0.11.0": version "0.11.0" - resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + resolved "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== "@polkadot-api/cli@0.16.3": version "0.16.3" - resolved "https://registry.yarnpkg.com/@polkadot-api/cli/-/cli-0.16.3.tgz#d4cf7cca802d473c68612899586f721cdfac7e88" + resolved "https://registry.npmjs.org/@polkadot-api/cli/-/cli-0.16.3.tgz" integrity sha512-s+p3dFw1vOeyMMqhUbt1RFyqPZdR7vg6joS0v9wBvK3qX5xU+QfOOaMxXJ8fl0mJEbwoJnJsvVl4MzjsABaKCg== dependencies: "@commander-js/extra-typings" "^14.0.0" @@ -331,7 +331,7 @@ "@polkadot-api/codegen@0.20.0": version "0.20.0" - resolved "https://registry.yarnpkg.com/@polkadot-api/codegen/-/codegen-0.20.0.tgz#b411c9243813a6adaa53df957ee775b339ab6738" + resolved "https://registry.npmjs.org/@polkadot-api/codegen/-/codegen-0.20.0.tgz" integrity sha512-akwPArm35UZcebUFtTKcEkdBLCjYyKweGw3/tT04p/EtM4OsQ1FxhRdXZ51ScBC3JVGCFQTUO2hNsd1E6YXvlw== dependencies: "@polkadot-api/ink-contracts" "0.4.3" @@ -342,15 +342,15 @@ "@polkadot-api/common-sdk-utils@0.1.0": version "0.1.0" - resolved "https://registry.yarnpkg.com/@polkadot-api/common-sdk-utils/-/common-sdk-utils-0.1.0.tgz#01e62f512c9e9bff2c938ecc69f508040521e64c" + resolved "https://registry.npmjs.org/@polkadot-api/common-sdk-utils/-/common-sdk-utils-0.1.0.tgz" integrity sha512-cgA9fh8dfBai9b46XaaQmj9vwzyHStQjc/xrAvQksgF6SqvZ0yAfxVqLvGrsz/Xi3dsAdKLg09PybC7MUAMv9w== "@polkadot-api/descriptors@file:.papi/descriptors": - version "0.1.0-autogenerated.17248082825697549564" + version "0.1.0-autogenerated.9476216756280928360" "@polkadot-api/ink-contracts@0.4.3", "@polkadot-api/ink-contracts@^0.4.1": version "0.4.3" - resolved "https://registry.yarnpkg.com/@polkadot-api/ink-contracts/-/ink-contracts-0.4.3.tgz#1df23db2aba67d293578cf2f5eb3b0b336994024" + resolved "https://registry.npmjs.org/@polkadot-api/ink-contracts/-/ink-contracts-0.4.3.tgz" integrity sha512-Wl+4Dxjt0GAl+rADZEgrrqEesqX/xygTpX18TmzmspcKhb9QIZf9FJI8A5Sgtq0TKAOwsd1d/hbHVX3LgbXFXg== dependencies: "@polkadot-api/metadata-builders" "0.13.7" @@ -359,32 +359,32 @@ "@polkadot-api/json-rpc-provider-proxy@0.2.7": version "0.2.7" - resolved "https://registry.yarnpkg.com/@polkadot-api/json-rpc-provider-proxy/-/json-rpc-provider-proxy-0.2.7.tgz#b6d639c98ea667e36e99d8d743465b5b3247d2ba" + resolved "https://registry.npmjs.org/@polkadot-api/json-rpc-provider-proxy/-/json-rpc-provider-proxy-0.2.7.tgz" integrity sha512-+HM4JQXzO2GPUD2++4GOLsmFL6LO8RoLvig0HgCLuypDgfdZMlwd8KnyGHjRnVEHA5X+kvXbk84TDcAXVxTazQ== "@polkadot-api/json-rpc-provider-proxy@^0.1.0": version "0.1.0" - resolved "https://registry.yarnpkg.com/@polkadot-api/json-rpc-provider-proxy/-/json-rpc-provider-proxy-0.1.0.tgz#6e191f28e7d0fbbe8b540fc51d12a0adaeba297e" + resolved "https://registry.npmjs.org/@polkadot-api/json-rpc-provider-proxy/-/json-rpc-provider-proxy-0.1.0.tgz" integrity sha512-8GSFE5+EF73MCuLQm8tjrbCqlgclcHBSRaswvXziJ0ZW7iw3UEMsKkkKvELayWyBuOPa2T5i1nj6gFOeIsqvrg== "@polkadot-api/json-rpc-provider@0.0.1", "@polkadot-api/json-rpc-provider@^0.0.1": version "0.0.1" - resolved "https://registry.yarnpkg.com/@polkadot-api/json-rpc-provider/-/json-rpc-provider-0.0.1.tgz#333645d40ccd9bccfd1f32503f17e4e63e76e297" + resolved "https://registry.npmjs.org/@polkadot-api/json-rpc-provider/-/json-rpc-provider-0.0.1.tgz" integrity sha512-/SMC/l7foRjpykLTUTacIH05H3mr9ip8b5xxfwXlVezXrNVLp3Cv0GX6uItkKd+ZjzVPf3PFrDF2B2/HLSNESA== "@polkadot-api/json-rpc-provider@0.0.4": version "0.0.4" - resolved "https://registry.yarnpkg.com/@polkadot-api/json-rpc-provider/-/json-rpc-provider-0.0.4.tgz#15d0c6a7ec14aa6d0dd64039f931bebea83ffdb3" + resolved "https://registry.npmjs.org/@polkadot-api/json-rpc-provider/-/json-rpc-provider-0.0.4.tgz" integrity sha512-9cDijLIxzHOBuq6yHqpqjJ9jBmXrctjc1OFqU+tQrS96adQze3mTIH6DTgfb/0LMrqxzxffz1HQGrIlEH00WrA== "@polkadot-api/known-chains@0.9.15": version "0.9.15" - resolved "https://registry.yarnpkg.com/@polkadot-api/known-chains/-/known-chains-0.9.15.tgz#04ea0cc19483392b8a9e0ed34187580e82700a5b" + resolved "https://registry.npmjs.org/@polkadot-api/known-chains/-/known-chains-0.9.15.tgz" integrity sha512-VQGu2Anvnx0y0Ltd6sQB3aYzQFGsaQwf2znh+w4Oflaxln5lsjO/+trpXz/rdrdgyi0iafkhpeho/p/EGBwJ+A== "@polkadot-api/legacy-provider@0.3.6": version "0.3.6" - resolved "https://registry.yarnpkg.com/@polkadot-api/legacy-provider/-/legacy-provider-0.3.6.tgz#f4a644dd40b6c30136d73cc1f53dba5a88ada018" + resolved "https://registry.npmjs.org/@polkadot-api/legacy-provider/-/legacy-provider-0.3.6.tgz" integrity sha512-JZQg0HVtBowFKxNrZdnMBKXmeSBD4yFlz6egEpvE97RXRvjaBzTaVuFFhBchngq9YmgFQewuWSoX5XSUW6hcEg== dependencies: "@polkadot-api/json-rpc-provider" "0.0.4" @@ -394,14 +394,14 @@ "@polkadot-api/logs-provider@0.0.6": version "0.0.6" - resolved "https://registry.yarnpkg.com/@polkadot-api/logs-provider/-/logs-provider-0.0.6.tgz#a22f6abf937208cea44c6722a80ce0e6eadfd116" + resolved "https://registry.npmjs.org/@polkadot-api/logs-provider/-/logs-provider-0.0.6.tgz" integrity sha512-4WgHlvy+xee1ADaaVf6+MlK/+jGMtsMgAzvbQOJZnP4PfQuagoTqaeayk8HYKxXGphogLlPbD06tANxcb+nvAg== dependencies: "@polkadot-api/json-rpc-provider" "0.0.4" "@polkadot-api/merkleize-metadata@1.1.27": version "1.1.27" - resolved "https://registry.yarnpkg.com/@polkadot-api/merkleize-metadata/-/merkleize-metadata-1.1.27.tgz#5b1cfabeb1e0b318eeb4e2c3a7c0e9037b9cb588" + resolved "https://registry.npmjs.org/@polkadot-api/merkleize-metadata/-/merkleize-metadata-1.1.27.tgz" integrity sha512-OdKwOzzrLL0Ju3pQA9LjeQEquMcD+KtLybUAO3fVxwjxD5cyI0RwillGoAIBJvfMaZpNxnxJnD+WzNjRcr7FiQ== dependencies: "@polkadot-api/metadata-builders" "0.13.7" @@ -410,7 +410,7 @@ "@polkadot-api/metadata-builders@0.13.7": version "0.13.7" - resolved "https://registry.yarnpkg.com/@polkadot-api/metadata-builders/-/metadata-builders-0.13.7.tgz#10f5c2b15a02f0a5a790174e6ff2129c8c00aab0" + resolved "https://registry.npmjs.org/@polkadot-api/metadata-builders/-/metadata-builders-0.13.7.tgz" integrity sha512-xwggY8F/gtX7qGzz+jzP3DZvWgBWIIFQhk+r2MJ431CR+tNKeTtzGdwNocVrb9NYTK2naC9ckJS14nrNM6LWLw== dependencies: "@polkadot-api/substrate-bindings" "0.16.5" @@ -418,7 +418,7 @@ "@polkadot-api/metadata-builders@0.3.2": version "0.3.2" - resolved "https://registry.yarnpkg.com/@polkadot-api/metadata-builders/-/metadata-builders-0.3.2.tgz#007f158c9e0546cf79ba440befc0c753ab1a6629" + resolved "https://registry.npmjs.org/@polkadot-api/metadata-builders/-/metadata-builders-0.3.2.tgz" integrity sha512-TKpfoT6vTb+513KDzMBTfCb/ORdgRnsS3TDFpOhAhZ08ikvK+hjHMt5plPiAX/OWkm1Wc9I3+K6W0hX5Ab7MVg== dependencies: "@polkadot-api/substrate-bindings" "0.6.0" @@ -426,7 +426,7 @@ "@polkadot-api/metadata-compatibility@0.4.1": version "0.4.1" - resolved "https://registry.yarnpkg.com/@polkadot-api/metadata-compatibility/-/metadata-compatibility-0.4.1.tgz#5f1acc47df2e8e1336d7baee8b902efb5b8919ca" + resolved "https://registry.npmjs.org/@polkadot-api/metadata-compatibility/-/metadata-compatibility-0.4.1.tgz" integrity sha512-mZt4Af6oPXEHAprrckJiSZkWRVf0mqwF+Bm+703rPsezLptQid9AjSzh1hkgIkOrPbg6IhWbmMhbuJVjx9VeQA== dependencies: "@polkadot-api/metadata-builders" "0.13.7" @@ -434,7 +434,7 @@ "@polkadot-api/observable-client@0.17.0": version "0.17.0" - resolved "https://registry.yarnpkg.com/@polkadot-api/observable-client/-/observable-client-0.17.0.tgz#317bb8e30410c753b2eba62a56cb0d6d6d5989d8" + resolved "https://registry.npmjs.org/@polkadot-api/observable-client/-/observable-client-0.17.0.tgz" integrity sha512-hilb12Fg1JrlM/0nucMT85//EQltB53fmoh7YNBsZMiNpavn/3qGTO4s0JMlC/LBbddYg0nxA+DMkSVlapo7cQ== dependencies: "@polkadot-api/metadata-builders" "0.13.7" @@ -444,7 +444,7 @@ "@polkadot-api/observable-client@^0.3.0": version "0.3.2" - resolved "https://registry.yarnpkg.com/@polkadot-api/observable-client/-/observable-client-0.3.2.tgz#fd91efee350595a6e0ecfd3f294cc80de86c0cf7" + resolved "https://registry.npmjs.org/@polkadot-api/observable-client/-/observable-client-0.3.2.tgz" integrity sha512-HGgqWgEutVyOBXoGOPp4+IAq6CNdK/3MfQJmhCJb8YaJiaK4W6aRGrdQuQSTPHfERHCARt9BrOmEvTXAT257Ug== dependencies: "@polkadot-api/metadata-builders" "0.3.2" @@ -453,7 +453,7 @@ "@polkadot-api/pjs-signer@0.6.17": version "0.6.17" - resolved "https://registry.yarnpkg.com/@polkadot-api/pjs-signer/-/pjs-signer-0.6.17.tgz#5a886c9b8fea936a02ab71d88580aa85b31c471a" + resolved "https://registry.npmjs.org/@polkadot-api/pjs-signer/-/pjs-signer-0.6.17.tgz" integrity sha512-bxFtyiNOchV0osh6m+1CaN4tkWF7Mo4IT9XPLZBwSybpHZgwmu2wbhgqBkVL98QMyGzud7NHfrJsTCgFU6jHGg== dependencies: "@polkadot-api/metadata-builders" "0.13.7" @@ -464,26 +464,26 @@ "@polkadot-api/polkadot-sdk-compat@2.3.3": version "2.3.3" - resolved "https://registry.yarnpkg.com/@polkadot-api/polkadot-sdk-compat/-/polkadot-sdk-compat-2.3.3.tgz#3ce0bb5ab6857f82d78c9d2d207fc82ce7ac084d" + resolved "https://registry.npmjs.org/@polkadot-api/polkadot-sdk-compat/-/polkadot-sdk-compat-2.3.3.tgz" integrity sha512-p30po+iv4trniSJ7UZiIt/rFInvtA9Tzg65EzuRkCaQAnh54a3MPp9w/q+x+SNLEcfzVLvf8LyPnMPOIpKuj5w== dependencies: "@polkadot-api/json-rpc-provider" "0.0.4" "@polkadot-api/polkadot-signer@0.1.6": version "0.1.6" - resolved "https://registry.yarnpkg.com/@polkadot-api/polkadot-signer/-/polkadot-signer-0.1.6.tgz#6870fd9827b282838a074380ba1a02fb3bdd5e83" + resolved "https://registry.npmjs.org/@polkadot-api/polkadot-signer/-/polkadot-signer-0.1.6.tgz" integrity sha512-X7ghAa4r7doETtjAPTb50IpfGtrBmy3BJM5WCfNKa1saK04VFY9w+vDn+hwEcM4p0PcDHt66Ts74hzvHq54d9A== "@polkadot-api/raw-client@0.1.1": version "0.1.1" - resolved "https://registry.yarnpkg.com/@polkadot-api/raw-client/-/raw-client-0.1.1.tgz#4b4aac274b3de60f5d838ec5d1b2d8b041cd682d" + resolved "https://registry.npmjs.org/@polkadot-api/raw-client/-/raw-client-0.1.1.tgz" integrity sha512-HxalpNEo8JCYXfxKM5p3TrK8sEasTGMkGjBNLzD4TLye9IK2smdb5oTvp2yfkU1iuVBdmjr69uif4NaukOYo2g== dependencies: "@polkadot-api/json-rpc-provider" "0.0.4" "@polkadot-api/sdk-ink@^0.5.1": version "0.5.1" - resolved "https://registry.yarnpkg.com/@polkadot-api/sdk-ink/-/sdk-ink-0.5.1.tgz#a19c5d18e1adcfa2ceb8da07265c1d82d3c828f6" + resolved "https://registry.npmjs.org/@polkadot-api/sdk-ink/-/sdk-ink-0.5.1.tgz" integrity sha512-9pRnghjigivvgq7375hzkoazstvPDbc0YB01Jzw1/MYKcX+YJn1p/H8SAQTWbKlz2ohFgi1nwU52a0bsmKqb/Q== dependencies: "@ethereumjs/rlp" "^10.0.0" @@ -494,7 +494,7 @@ "@polkadot-api/signer@0.2.11": version "0.2.11" - resolved "https://registry.yarnpkg.com/@polkadot-api/signer/-/signer-0.2.11.tgz#1ba8f97a6e0b503d5827c0904141fe73e05a4f06" + resolved "https://registry.npmjs.org/@polkadot-api/signer/-/signer-0.2.11.tgz" integrity sha512-32tqbJo6JDfc/lHg+nTveeunFRULonWoTQX9xbs70arr/tAyyZfljupdECRK8CVRx1777es/CQO3QVj8EpWtYg== dependencies: "@noble/hashes" "^2.0.1" @@ -506,7 +506,7 @@ "@polkadot-api/signers-common@0.1.18": version "0.1.18" - resolved "https://registry.yarnpkg.com/@polkadot-api/signers-common/-/signers-common-0.1.18.tgz#1e717937547b65c52da24447f0b37d8def59f4fc" + resolved "https://registry.npmjs.org/@polkadot-api/signers-common/-/signers-common-0.1.18.tgz" integrity sha512-UQXuRZoQ+jMolEpIPF0mVXcoqQ/382fHrSOgfK5sIvjeH0HPf4P+s3IwcnwyAdpHY2gdHXYlHd/SAw7Q1gJ4EA== dependencies: "@polkadot-api/metadata-builders" "0.13.7" @@ -516,7 +516,7 @@ "@polkadot-api/sm-provider@0.1.14": version "0.1.14" - resolved "https://registry.yarnpkg.com/@polkadot-api/sm-provider/-/sm-provider-0.1.14.tgz#c45a6e82729bc518c4061d7cc2a519be01451a22" + resolved "https://registry.npmjs.org/@polkadot-api/sm-provider/-/sm-provider-0.1.14.tgz" integrity sha512-QQvoeBSIwnEm8IUhGA6sBU6LNh2v7SOuVOnF77ZD7P5ELTrdmQH2Tcn0W15qGTmTG45b3Z52XsKpuQbIJ7c7XA== dependencies: "@polkadot-api/json-rpc-provider" "0.0.4" @@ -524,7 +524,7 @@ "@polkadot-api/smoldot@0.3.14": version "0.3.14" - resolved "https://registry.yarnpkg.com/@polkadot-api/smoldot/-/smoldot-0.3.14.tgz#a43b2a5e057607d4746d7e679106851ffae06581" + resolved "https://registry.npmjs.org/@polkadot-api/smoldot/-/smoldot-0.3.14.tgz" integrity sha512-eWqO0xFQaKzqY5mRYxYuZcj1IiaLcQP+J38UQyuJgEorm+9yHVEQ/XBWoM83P+Y8TwE5IWTICp1LCVeiFQTGPQ== dependencies: "@types/node" "^24.5.2" @@ -532,7 +532,7 @@ "@polkadot-api/substrate-bindings@0.16.5", "@polkadot-api/substrate-bindings@^0.16.3": version "0.16.5" - resolved "https://registry.yarnpkg.com/@polkadot-api/substrate-bindings/-/substrate-bindings-0.16.5.tgz#96540f01c5bb834cc2529cf19fc11b093ca15926" + resolved "https://registry.npmjs.org/@polkadot-api/substrate-bindings/-/substrate-bindings-0.16.5.tgz" integrity sha512-QFgNlBmtLtiUGTCTurxcE6UZrbI2DaQ5/gyIiC2FYfEhStL8tl20b09FRYHcSjY+lxN42Rcf9HVX+MCFWLYlpQ== dependencies: "@noble/hashes" "^2.0.1" @@ -542,7 +542,7 @@ "@polkadot-api/substrate-bindings@0.6.0": version "0.6.0" - resolved "https://registry.yarnpkg.com/@polkadot-api/substrate-bindings/-/substrate-bindings-0.6.0.tgz#889b0c3ba19dc95282286506bf6e370a43ce119a" + resolved "https://registry.npmjs.org/@polkadot-api/substrate-bindings/-/substrate-bindings-0.6.0.tgz" integrity sha512-lGuhE74NA1/PqdN7fKFdE5C1gNYX357j1tWzdlPXI0kQ7h3kN0zfxNOpPUN7dIrPcOFZ6C0tRRVrBylXkI6xPw== dependencies: "@noble/hashes" "^1.3.1" @@ -552,7 +552,7 @@ "@polkadot-api/substrate-client@0.4.7": version "0.4.7" - resolved "https://registry.yarnpkg.com/@polkadot-api/substrate-client/-/substrate-client-0.4.7.tgz#257a9acd3dc4ad64a097c99cce9523ba04093b03" + resolved "https://registry.npmjs.org/@polkadot-api/substrate-client/-/substrate-client-0.4.7.tgz" integrity sha512-Mmx9VKincVqfVQmq89gzDk4DN3uKwf8CxoqYvq+EiPUZ1QmMUc7X4QMwG1MXIlYdnm5LSXzn+2Jn8ik8xMgL+w== dependencies: "@polkadot-api/json-rpc-provider" "0.0.4" @@ -569,22 +569,22 @@ "@polkadot-api/utils@0.1.0": version "0.1.0" - resolved "https://registry.yarnpkg.com/@polkadot-api/utils/-/utils-0.1.0.tgz#d36937cdc465c2ea302f3278cf53157340ab33a0" + resolved "https://registry.npmjs.org/@polkadot-api/utils/-/utils-0.1.0.tgz" integrity sha512-MXzWZeuGxKizPx2Xf/47wx9sr/uxKw39bVJUptTJdsaQn/TGq+z310mHzf1RCGvC1diHM8f593KrnDgc9oNbJA== "@polkadot-api/utils@0.2.0": version "0.2.0" - resolved "https://registry.yarnpkg.com/@polkadot-api/utils/-/utils-0.2.0.tgz#812d4c4ee282691440aed4b6ddf863651e804444" + resolved "https://registry.npmjs.org/@polkadot-api/utils/-/utils-0.2.0.tgz" integrity sha512-nY3i5fQJoAxU4n3bD7Fs208/KR2J95SGfVc58kDjbRYN5a84kWaGEqzjBNtP9oqht49POM8Bm9mbIrkvC1Bzuw== "@polkadot-api/wasm-executor@^0.2.2": version "0.2.3" - resolved "https://registry.yarnpkg.com/@polkadot-api/wasm-executor/-/wasm-executor-0.2.3.tgz#a77d74bf95dbdec2dfa815b278a78af1cf628635" + resolved "https://registry.npmjs.org/@polkadot-api/wasm-executor/-/wasm-executor-0.2.3.tgz" integrity sha512-B2h1o+Qlo9idpASaHvMSoViB2I5ko5OAfwfhYF8LQDkTADK0B+SeStzNj1Qn+FG34wqTuv7HzBCdjaUgzYINJQ== "@polkadot-api/ws-provider@0.7.4": version "0.7.4" - resolved "https://registry.yarnpkg.com/@polkadot-api/ws-provider/-/ws-provider-0.7.4.tgz#cffdf03beaced6e201b8904c20c8e588e4417e8b" + resolved "https://registry.npmjs.org/@polkadot-api/ws-provider/-/ws-provider-0.7.4.tgz" integrity sha512-mkk2p8wPht+ljU1xULCPMsLpNF7NHuGaufuDCIZZgopALaZpfVFJxc3qa9s6Xv8X3hM+TRoC5WknuD1ykRY99A== dependencies: "@polkadot-api/json-rpc-provider" "0.0.4" @@ -594,7 +594,7 @@ "@polkadot-labs/hdkd-helpers@^0.0.25": version "0.0.25" - resolved "https://registry.yarnpkg.com/@polkadot-labs/hdkd-helpers/-/hdkd-helpers-0.0.25.tgz#6f70f4836acc3f821521babd17d52ab1a92ef13a" + resolved "https://registry.npmjs.org/@polkadot-labs/hdkd-helpers/-/hdkd-helpers-0.0.25.tgz" integrity sha512-GwHayBuyHKfzvGD0vG47NbjFeiK6rRQHQAn1syut9nt0mhXMg4yb3tJ//IyM317qWuDU3HbD2OIp5jKDEQz2/A== dependencies: "@noble/curves" "^2.0.0" @@ -605,7 +605,7 @@ "@polkadot-labs/hdkd-helpers@~0.0.26": version "0.0.26" - resolved "https://registry.yarnpkg.com/@polkadot-labs/hdkd-helpers/-/hdkd-helpers-0.0.26.tgz#06d74f16f0e4c131fd1b1cc004e4411030f33610" + resolved "https://registry.npmjs.org/@polkadot-labs/hdkd-helpers/-/hdkd-helpers-0.0.26.tgz" integrity sha512-mp3GCSiOQeh4aPt+DYBQq6UnX/tKgYUH5F75knjW3ATSA90ifEEWWjRan0Bddt4QKYKamaDGadK9GbVREgzQFw== dependencies: "@noble/curves" "^2.0.1" @@ -616,14 +616,14 @@ "@polkadot-labs/hdkd@^0.0.25": version "0.0.25" - resolved "https://registry.yarnpkg.com/@polkadot-labs/hdkd/-/hdkd-0.0.25.tgz#cb7725792485ee5dcf0a7a8491dff1989adf5cd3" + resolved "https://registry.npmjs.org/@polkadot-labs/hdkd/-/hdkd-0.0.25.tgz" integrity sha512-+yZJC1TE4ZKdfoILw8nGxu3H/klrYXm9GdVB0kcyQDecq320ThUmM1M4l8d1F/3QD0Nez9NwHi9t5B++OgJU5A== dependencies: "@polkadot-labs/hdkd-helpers" "~0.0.26" "@polkadot/api-augment@16.5.3": version "16.5.3" - resolved "https://registry.yarnpkg.com/@polkadot/api-augment/-/api-augment-16.5.3.tgz#af4bf6aa7a72a82093e9a46b095bf8288d89de1e" + resolved "https://registry.npmjs.org/@polkadot/api-augment/-/api-augment-16.5.3.tgz" integrity sha512-9+8YKSS66x9qpWS+ZQ/FSm9P4mgE+icD53oAmeIykriPW2gcSTAiNufLwAjmAJAkOLcqbTD7LPjFW6xFlmtYsA== dependencies: "@polkadot/api-base" "16.5.3" @@ -636,7 +636,7 @@ "@polkadot/api-base@16.5.3": version "16.5.3" - resolved "https://registry.yarnpkg.com/@polkadot/api-base/-/api-base-16.5.3.tgz#16e4d89df2ccd3c0872a1115bf9ba6a7ed87cf82" + resolved "https://registry.npmjs.org/@polkadot/api-base/-/api-base-16.5.3.tgz" integrity sha512-M1+pY6OFQ1uOB73VQMt2JAGq/UVISVQJISqyfjiUllUc0qIzaDMkcZxRqE34Lwaib3fD3RuIpG6dXqCL9rdzJQ== dependencies: "@polkadot/rpc-core" "16.5.3" @@ -647,7 +647,7 @@ "@polkadot/api-derive@16.5.3": version "16.5.3" - resolved "https://registry.yarnpkg.com/@polkadot/api-derive/-/api-derive-16.5.3.tgz#e27f768d8603b251acb6bcf4f486147aab09d0ba" + resolved "https://registry.npmjs.org/@polkadot/api-derive/-/api-derive-16.5.3.tgz" integrity sha512-nMsnSC/N1SK1kNhgh2FhrrR1S8bTVH+3WsuBHFRzl+txKHq232IeIn9LpebSvgZdd77PaKaYBxbhYcNaA8Ypew== dependencies: "@polkadot/api" "16.5.3" @@ -663,7 +663,7 @@ "@polkadot/api@16.5.3", "@polkadot/api@^16.4.6": version "16.5.3" - resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-16.5.3.tgz#d45b45a7ae33889cc774661275a7db7d30f06cf9" + resolved "https://registry.npmjs.org/@polkadot/api/-/api-16.5.3.tgz" integrity sha512-Ptwo0f5Qonmus7KIklsbFcGTdHtNjbTAwl5GGI8Mp0dmBc7Y/ISJpIJX49UrG6FhW6COMa0ItsU87XIWMRwI/Q== dependencies: "@polkadot/api-augment" "16.5.3" @@ -686,7 +686,7 @@ "@polkadot/keyring@^13.5.9": version "13.5.9" - resolved "https://registry.yarnpkg.com/@polkadot/keyring/-/keyring-13.5.9.tgz#257a7614879c2674836def56776348d690902df6" + resolved "https://registry.npmjs.org/@polkadot/keyring/-/keyring-13.5.9.tgz" integrity sha512-bMCpHDN7U8ytxawjBZ89/he5s3AmEZuOdkM/ABcorh/flXNPfyghjFK27Gy4OKoFxX52yJ2sTHR4NxM87GuFXQ== dependencies: "@polkadot/util" "13.5.9" @@ -695,7 +695,7 @@ "@polkadot/networks@13.5.9", "@polkadot/networks@^13.5.9": version "13.5.9" - resolved "https://registry.yarnpkg.com/@polkadot/networks/-/networks-13.5.9.tgz#d5ed8fa0478956835ba489bd3ed10da40881b9c2" + resolved "https://registry.npmjs.org/@polkadot/networks/-/networks-13.5.9.tgz" integrity sha512-nmKUKJjiLgcih0MkdlJNMnhEYdwEml2rv/h59ll2+rAvpsVWMTLCb6Cq6q7UC44+8kiWK2UUJMkFU+3PFFxndA== dependencies: "@polkadot/util" "13.5.9" @@ -704,7 +704,7 @@ "@polkadot/rpc-augment@16.5.3": version "16.5.3" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-augment/-/rpc-augment-16.5.3.tgz#edd776278e848fca297fb9271da9480301d8ff91" + resolved "https://registry.npmjs.org/@polkadot/rpc-augment/-/rpc-augment-16.5.3.tgz" integrity sha512-q3Y+b0FSwbYe8Qopd4In+9KCL3eH5QmGVvimX7Z8+cvQ9+h+JUA6TP1bfpWBmYJRKlolaljsBQPBWoubchmxSw== dependencies: "@polkadot/rpc-core" "16.5.3" @@ -715,7 +715,7 @@ "@polkadot/rpc-core@16.5.3": version "16.5.3" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-core/-/rpc-core-16.5.3.tgz#113e7b43caea3b8adc002ec8dc95782ee9cdd79f" + resolved "https://registry.npmjs.org/@polkadot/rpc-core/-/rpc-core-16.5.3.tgz" integrity sha512-UYEIRhO/1uTz/rpWLwUN9Re3c4fuTs0I9RR8dHKpKsH3jZTs1M3CtqME3NNzpGqApY1xb9tZemU/0GfHjCpeBQ== dependencies: "@polkadot/rpc-augment" "16.5.3" @@ -727,7 +727,7 @@ "@polkadot/rpc-provider@16.5.3": version "16.5.3" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-provider/-/rpc-provider-16.5.3.tgz#6bbf6b86bb2b0cef3f8a467ebce69e2739df3f8f" + resolved "https://registry.npmjs.org/@polkadot/rpc-provider/-/rpc-provider-16.5.3.tgz" integrity sha512-O7hD82HwjT4XJ4i/G58B52RSDM7arHXSpzahZKz4/wtb4x6d6b4JVdfZoskInadARFi5RwIWCrftwPtpRH81Fw== dependencies: "@polkadot/keyring" "^13.5.9" @@ -747,7 +747,7 @@ "@polkadot/types-augment@16.5.3": version "16.5.3" - resolved "https://registry.yarnpkg.com/@polkadot/types-augment/-/types-augment-16.5.3.tgz#2a02b179d06c0e941358603045c503dc81ef6557" + resolved "https://registry.npmjs.org/@polkadot/types-augment/-/types-augment-16.5.3.tgz" integrity sha512-SfS4arJUxW6BeCEhLMVPrZwWOLte69k5+/lvEKOKHQA8Mz0MEkD4uqGZGibDjgBgdnu8N+3b+rs+Fn3YfZu4yA== dependencies: "@polkadot/types" "16.5.3" @@ -757,7 +757,7 @@ "@polkadot/types-codec@16.5.3": version "16.5.3" - resolved "https://registry.yarnpkg.com/@polkadot/types-codec/-/types-codec-16.5.3.tgz#b44e54dcc7929e8b3990364868c541e4eed61abf" + resolved "https://registry.npmjs.org/@polkadot/types-codec/-/types-codec-16.5.3.tgz" integrity sha512-b+oKMrIZrsFH4pPwvGQ6lMS8oFrYAGMy9QSbytA+KDmXAgTCtShz5XGvdQabvsGCjJ45EKgkKpKynVcYh3gk8g== dependencies: "@polkadot/util" "^13.5.9" @@ -766,7 +766,7 @@ "@polkadot/types-create@16.5.3": version "16.5.3" - resolved "https://registry.yarnpkg.com/@polkadot/types-create/-/types-create-16.5.3.tgz#f2b6b23be130674a2b3fe6b1d2df1a8a0a49e554" + resolved "https://registry.npmjs.org/@polkadot/types-create/-/types-create-16.5.3.tgz" integrity sha512-XGnBLNamPh7eQGcHNGFghA/prH7z2BsQ+9EVSbHCvw9ENr/Ow24mmmkZyMG5WM/5I6/4HRdfwFJucYt1GL/p9g== dependencies: "@polkadot/types-codec" "16.5.3" @@ -775,7 +775,7 @@ "@polkadot/types-known@16.5.3": version "16.5.3" - resolved "https://registry.yarnpkg.com/@polkadot/types-known/-/types-known-16.5.3.tgz#0a323f8bddcb1a21dcfc70cde689f23831efa506" + resolved "https://registry.npmjs.org/@polkadot/types-known/-/types-known-16.5.3.tgz" integrity sha512-ZLAZI24bQD0C9CJWYHxrLG8QSmzRzfWa51rlSNwZ9Atsc3R+GeX1YZGc9IljpQxYJCHrCqd6X8TXpAmEJdnbKw== dependencies: "@polkadot/networks" "^13.5.9" @@ -787,7 +787,7 @@ "@polkadot/types-support@16.5.3": version "16.5.3" - resolved "https://registry.yarnpkg.com/@polkadot/types-support/-/types-support-16.5.3.tgz#577effc098a2739622ff052cff2eba4f4b77a21b" + resolved "https://registry.npmjs.org/@polkadot/types-support/-/types-support-16.5.3.tgz" integrity sha512-ggyIRV+4Kn+aG1PiVT0PE00pAqMveyS3CuFsW9gJnKxeev4VrGfr08R4vw/61D7uIfpilkQdkXNgXAbeN09Mxg== dependencies: "@polkadot/util" "^13.5.9" @@ -795,7 +795,7 @@ "@polkadot/types@16.5.3": version "16.5.3" - resolved "https://registry.yarnpkg.com/@polkadot/types/-/types-16.5.3.tgz#00d3ad05584ced089c8ec6e374a86d0fccc2b66d" + resolved "https://registry.npmjs.org/@polkadot/types/-/types-16.5.3.tgz" integrity sha512-xy9uv/X4iT7uJ7TNCoqbcMkR8ePHwNW6DgpOU+1y1zc/KSu9ZC5i+haFOL68BpmR/QXk99YfuHoKwXvteDmykw== dependencies: "@polkadot/keyring" "^13.5.9" @@ -809,7 +809,7 @@ "@polkadot/util-crypto@13.5.9", "@polkadot/util-crypto@^13.5.9": version "13.5.9" - resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-13.5.9.tgz#13dfa31f83527b0d486298bd85bbf604304caa20" + resolved "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-13.5.9.tgz" integrity sha512-foUesMhxkTk8CZ0/XEcfvHk6I0O+aICqqVJllhOpyp/ZVnrTBKBf59T6RpsXx2pCtBlMsLRvg/6Mw7RND1HqDg== dependencies: "@noble/curves" "^1.3.0" @@ -825,7 +825,7 @@ "@polkadot/util@13.5.9", "@polkadot/util@^13.5.9": version "13.5.9" - resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-13.5.9.tgz#26fb10b6479a6a884101d3ad8a3198c35903481c" + resolved "https://registry.npmjs.org/@polkadot/util/-/util-13.5.9.tgz" integrity sha512-pIK3XYXo7DKeFRkEBNYhf3GbCHg6dKQisSvdzZwuyzA6m7YxQq4DFw4IE464ve4Z7WsJFt3a6C9uII36hl9EWw== dependencies: "@polkadot/x-bigint" "13.5.9" @@ -838,7 +838,7 @@ "@polkadot/wasm-bridge@7.5.3": version "7.5.3" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-bridge/-/wasm-bridge-7.5.3.tgz#f4701e10848c80fe4bb3fcd1720ffda51ce322af" + resolved "https://registry.npmjs.org/@polkadot/wasm-bridge/-/wasm-bridge-7.5.3.tgz" integrity sha512-mUvwwNH+uP1wqpMuHjmEwHxRIaVc5csmb+ukycWQGhzwhpXe/0fvBEU2TQ8kwgqO2MU0FS3hN/QcIWKfPRJgxQ== dependencies: "@polkadot/wasm-util" "7.5.3" @@ -846,14 +846,14 @@ "@polkadot/wasm-crypto-asmjs@7.5.3": version "7.5.3" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-7.5.3.tgz#7b5f7f6d58b3cdf81d56b627864c8207f1eb7a48" + resolved "https://registry.npmjs.org/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-7.5.3.tgz" integrity sha512-fSbbjI+4p0U3PQ8nOz/3p7euHriSdh+2CSywNuXHa8fMaYlMqCKt9K7+HI8CQ4RZNvZWDq+Py1nEDEkM4rZrvw== dependencies: tslib "^2.7.0" "@polkadot/wasm-crypto-init@7.5.3": version "7.5.3" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-init/-/wasm-crypto-init-7.5.3.tgz#9ad66e44e3a95bfd66df4f33ea59176230e6649f" + resolved "https://registry.npmjs.org/@polkadot/wasm-crypto-init/-/wasm-crypto-init-7.5.3.tgz" integrity sha512-KvUpxqvW70XhuDiw/N6rM8fQ7zRjIFblw+vdJ0/wwyagwg9jrYNA9TMei5ksQd9sxGCGXN/xJmwHJXuUjkocmg== dependencies: "@polkadot/wasm-bridge" "7.5.3" @@ -864,7 +864,7 @@ "@polkadot/wasm-crypto-wasm@7.5.3": version "7.5.3" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-7.5.3.tgz#f10e77063bacb70f4e20b8c2427816e14bb81a6b" + resolved "https://registry.npmjs.org/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-7.5.3.tgz" integrity sha512-fc88+HyVxebB/40GVgGUOLBqyO3C571DXWPTFmtt5EX9H8gw7Jg0Bkitz7hgSVP2x4FjXpqS9UNTJ8trVH0x1A== dependencies: "@polkadot/wasm-util" "7.5.3" @@ -872,7 +872,7 @@ "@polkadot/wasm-crypto@^7.5.3": version "7.5.3" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto/-/wasm-crypto-7.5.3.tgz#2b21670ff90594d1dc520d5ced149afab0e28dd2" + resolved "https://registry.npmjs.org/@polkadot/wasm-crypto/-/wasm-crypto-7.5.3.tgz" integrity sha512-dmKUM9vw1wrnCHGuIeOtQo1pwuSF7fkyF4TYimTn3tAa0+3cDctYBErtGxgUeqP0Bo4Q0Of4/vnHlSk5Rbt9Uw== dependencies: "@polkadot/wasm-bridge" "7.5.3" @@ -884,14 +884,14 @@ "@polkadot/wasm-util@7.5.3", "@polkadot/wasm-util@^7.5.3": version "7.5.3" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-util/-/wasm-util-7.5.3.tgz#0619487cad6ffbe67243fff5da53669b42b397ca" + resolved "https://registry.npmjs.org/@polkadot/wasm-util/-/wasm-util-7.5.3.tgz" integrity sha512-hBr9bbjS+Yr7DrDUSkIIuvlTSoAlI8WXuo9YEB4C76j130u/cl+zyq6Iy/WnaTE6QH+8i9DhM8QTety6TqYnUQ== dependencies: tslib "^2.7.0" "@polkadot/x-bigint@13.5.9", "@polkadot/x-bigint@^13.5.9": version "13.5.9" - resolved "https://registry.yarnpkg.com/@polkadot/x-bigint/-/x-bigint-13.5.9.tgz#a0b665a7c120dba02fbe4f2272762895b39e9d0f" + resolved "https://registry.npmjs.org/@polkadot/x-bigint/-/x-bigint-13.5.9.tgz" integrity sha512-JVW6vw3e8fkcRyN9eoc6JIl63MRxNQCP/tuLdHWZts1tcAYao0hpWUzteqJY93AgvmQ91KPsC1Kf3iuuZCi74g== dependencies: "@polkadot/x-global" "13.5.9" @@ -899,7 +899,7 @@ "@polkadot/x-fetch@^13.5.9": version "13.5.9" - resolved "https://registry.yarnpkg.com/@polkadot/x-fetch/-/x-fetch-13.5.9.tgz#396a5a884d13d26a04c61b6e43240ca69fd72190" + resolved "https://registry.npmjs.org/@polkadot/x-fetch/-/x-fetch-13.5.9.tgz" integrity sha512-urwXQZtT4yYROiRdJS6zHu18J/jCoAGpbgPIAjwdqjT11t9XIq4SjuPMxD19xBRhbYe9ocWV8i1KHuoMbZgKbA== dependencies: "@polkadot/x-global" "13.5.9" @@ -908,14 +908,14 @@ "@polkadot/x-global@13.5.9", "@polkadot/x-global@^13.5.9": version "13.5.9" - resolved "https://registry.yarnpkg.com/@polkadot/x-global/-/x-global-13.5.9.tgz#22d680f036a879a5aef15963f96d71dd115927a3" + resolved "https://registry.npmjs.org/@polkadot/x-global/-/x-global-13.5.9.tgz" integrity sha512-zSRWvELHd3Q+bFkkI1h2cWIqLo1ETm+MxkNXLec3lB56iyq/MjWBxfXnAFFYFayvlEVneo7CLHcp+YTFd9aVSA== dependencies: tslib "^2.8.0" "@polkadot/x-randomvalues@13.5.9": version "13.5.9" - resolved "https://registry.yarnpkg.com/@polkadot/x-randomvalues/-/x-randomvalues-13.5.9.tgz#5d5e2bdef2bbd6ded99a1ff081d7d67af500c8e7" + resolved "https://registry.npmjs.org/@polkadot/x-randomvalues/-/x-randomvalues-13.5.9.tgz" integrity sha512-Uuuz3oubf1JCCK97fsnVUnHvk4BGp/W91mQWJlgl5TIOUSSTIRr+lb5GurCfl4kgnQq53Zi5fJV+qR9YumbnZw== dependencies: "@polkadot/x-global" "13.5.9" @@ -923,7 +923,7 @@ "@polkadot/x-textdecoder@13.5.9": version "13.5.9" - resolved "https://registry.yarnpkg.com/@polkadot/x-textdecoder/-/x-textdecoder-13.5.9.tgz#ce6dc1177c2d89549ad5bdfe5409cb6b42182c14" + resolved "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-13.5.9.tgz" integrity sha512-W2HhVNUbC/tuFdzNMbnXAWsIHSg9SC9QWDNmFD3nXdSzlXNgL8NmuiwN2fkYvCQBtp/XSoy0gDLx0C+Fo19cfw== dependencies: "@polkadot/x-global" "13.5.9" @@ -931,7 +931,7 @@ "@polkadot/x-textencoder@13.5.9": version "13.5.9" - resolved "https://registry.yarnpkg.com/@polkadot/x-textencoder/-/x-textencoder-13.5.9.tgz#c70f7add6e2c40c04d190160bc5c574674def78f" + resolved "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-13.5.9.tgz" integrity sha512-SG0MHnLUgn1ZxFdm0KzMdTHJ47SfqFhdIPMcGA0Mg/jt2rwrfrP3jtEIJMsHfQpHvfsNPfv55XOMmoPWuQnP/Q== dependencies: "@polkadot/x-global" "13.5.9" @@ -939,7 +939,7 @@ "@polkadot/x-ws@^13.5.9": version "13.5.9" - resolved "https://registry.yarnpkg.com/@polkadot/x-ws/-/x-ws-13.5.9.tgz#796564254e64809619993e8ff77ad098b3b95023" + resolved "https://registry.npmjs.org/@polkadot/x-ws/-/x-ws-13.5.9.tgz" integrity sha512-NKVgvACTIvKT8CjaQu9d0dERkZsWIZngX/4NVSjc01WHmln4F4y/zyBdYn/Z2V0Zw28cISx+lB4qxRmqTe7gbg== dependencies: "@polkadot/x-global" "13.5.9" @@ -958,7 +958,7 @@ "@rollup/rollup-darwin-arm64@4.53.3": version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz#3577c38af68ccf34c03e84f476bfd526abca10a0" + resolved "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz" integrity sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA== "@rollup/rollup-darwin-x64@4.53.3": @@ -1058,22 +1058,22 @@ "@rx-state/core@^0.1.4": version "0.1.4" - resolved "https://registry.yarnpkg.com/@rx-state/core/-/core-0.1.4.tgz#586dde80be9dbdac31844006a0dcaa2bc7f35a5c" + resolved "https://registry.npmjs.org/@rx-state/core/-/core-0.1.4.tgz" integrity sha512-Z+3hjU2xh1HisLxt+W5hlYX/eGSDaXXP+ns82gq/PLZpkXLu0uwcNUh9RLY3Clq4zT+hSsA3vcpIGt6+UAb8rQ== "@scure/base@^1.1.1", "@scure/base@^1.1.7", "@scure/base@~1.2.2", "@scure/base@~1.2.4", "@scure/base@~1.2.5": version "1.2.6" - resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.2.6.tgz#ca917184b8231394dd8847509c67a0be522e59f6" + resolved "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz" integrity sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg== "@scure/base@^2.0.0": version "2.0.0" - resolved "https://registry.yarnpkg.com/@scure/base/-/base-2.0.0.tgz#ba6371fddf92c2727e88ad6ab485db6e624f9a98" + resolved "https://registry.npmjs.org/@scure/base/-/base-2.0.0.tgz" integrity sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w== "@scure/bip32@1.6.2": version "1.6.2" - resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.6.2.tgz#093caa94961619927659ed0e711a6e4bf35bffd0" + resolved "https://registry.npmjs.org/@scure/bip32/-/bip32-1.6.2.tgz" integrity sha512-t96EPDMbtGgtb7onKKqxRLfE5g05k7uHnHRM2xdE6BP/ZmxaLtPek4J4KfVn/90IQNrU1IOAqMgiDtUdtbe3nw== dependencies: "@noble/curves" "~1.8.1" @@ -1082,7 +1082,7 @@ "@scure/bip32@1.7.0", "@scure/bip32@^1.5.0", "@scure/bip32@^1.7.0": version "1.7.0" - resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.7.0.tgz#b8683bab172369f988f1589640e53c4606984219" + resolved "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz" integrity sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw== dependencies: "@noble/curves" "~1.9.0" @@ -1091,7 +1091,7 @@ "@scure/bip39@1.5.4": version "1.5.4" - resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.5.4.tgz#07fd920423aa671be4540d59bdd344cc1461db51" + resolved "https://registry.npmjs.org/@scure/bip39/-/bip39-1.5.4.tgz" integrity sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA== dependencies: "@noble/hashes" "~1.7.1" @@ -1099,7 +1099,7 @@ "@scure/bip39@1.6.0", "@scure/bip39@^1.4.0", "@scure/bip39@^1.6.0": version "1.6.0" - resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.6.0.tgz#475970ace440d7be87a6086cbee77cb8f1a684f9" + resolved "https://registry.npmjs.org/@scure/bip39/-/bip39-1.6.0.tgz" integrity sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A== dependencies: "@noble/hashes" "~1.8.0" @@ -1107,7 +1107,7 @@ "@scure/sr25519@^0.3.0": version "0.3.0" - resolved "https://registry.yarnpkg.com/@scure/sr25519/-/sr25519-0.3.0.tgz#1fb075ef05086c1dc59f16bdda1327627a552352" + resolved "https://registry.npmjs.org/@scure/sr25519/-/sr25519-0.3.0.tgz" integrity sha512-SKsinX2sImunfcsH3seGrwH/OayBwwaJqVN8J1cJBNRCfbBq5q0jyTKGa9PcW1HWv9vXT6Yuq41JsxFLvF59ew== dependencies: "@noble/curves" "~2.0.0" @@ -1115,27 +1115,27 @@ "@sec-ant/readable-stream@^0.4.1": version "0.4.1" - resolved "https://registry.yarnpkg.com/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz#60de891bb126abfdc5410fdc6166aca065f10a0c" + resolved "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz" integrity sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg== "@sindresorhus/merge-streams@^4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz#abb11d99aeb6d27f1b563c38147a72d50058e339" + resolved "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz" integrity sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ== "@substrate/connect-extension-protocol@^2.0.0": version "2.2.2" - resolved "https://registry.yarnpkg.com/@substrate/connect-extension-protocol/-/connect-extension-protocol-2.2.2.tgz#2cf8f2eaf1879308d307a1a08df83cd5db918fe0" + resolved "https://registry.npmjs.org/@substrate/connect-extension-protocol/-/connect-extension-protocol-2.2.2.tgz" integrity sha512-t66jwrXA0s5Goq82ZtjagLNd7DPGCNjHeehRlE/gcJmJ+G56C0W+2plqOMRicJ8XGR1/YFnUSEqUFiSNbjGrAA== "@substrate/connect-known-chains@^1.1.5": version "1.10.3" - resolved "https://registry.yarnpkg.com/@substrate/connect-known-chains/-/connect-known-chains-1.10.3.tgz#71a89864f13626c412fa0a9d0ffc4f6ca39fdcec" + resolved "https://registry.npmjs.org/@substrate/connect-known-chains/-/connect-known-chains-1.10.3.tgz" integrity sha512-OJEZO1Pagtb6bNE3wCikc2wrmvEU5x7GxFFLqqbz1AJYYxSlrPCGu4N2og5YTExo4IcloNMQYFRkBGue0BKZ4w== "@substrate/connect@0.8.11": version "0.8.11" - resolved "https://registry.yarnpkg.com/@substrate/connect/-/connect-0.8.11.tgz#983ec69a05231636e217b573b8130a6b942af69f" + resolved "https://registry.npmjs.org/@substrate/connect/-/connect-0.8.11.tgz" integrity sha512-ofLs1PAO9AtDdPbdyTYj217Pe+lBfTLltdHDs3ds8no0BseoLeAGxpz1mHfi7zB4IxI3YyAiLjH6U8cw4pj4Nw== dependencies: "@substrate/connect-extension-protocol" "^2.0.0" @@ -1145,7 +1145,7 @@ "@substrate/light-client-extension-helpers@^1.0.0": version "1.0.0" - resolved "https://registry.yarnpkg.com/@substrate/light-client-extension-helpers/-/light-client-extension-helpers-1.0.0.tgz#7b60368c57e06e5cf798c6557422d12e6d81f1ff" + resolved "https://registry.npmjs.org/@substrate/light-client-extension-helpers/-/light-client-extension-helpers-1.0.0.tgz" integrity sha512-TdKlni1mBBZptOaeVrKnusMg/UBpWUORNDv5fdCaJklP4RJiFOzBCrzC+CyVI5kQzsXBisZ+2pXm+rIjS38kHg== dependencies: "@polkadot-api/json-rpc-provider" "^0.0.1" @@ -1158,39 +1158,39 @@ "@substrate/ss58-registry@^1.51.0": version "1.51.0" - resolved "https://registry.yarnpkg.com/@substrate/ss58-registry/-/ss58-registry-1.51.0.tgz#39e0341eb4069c2d3e684b93f0d8cb0bec572383" + resolved "https://registry.npmjs.org/@substrate/ss58-registry/-/ss58-registry-1.51.0.tgz" integrity sha512-TWDurLiPxndFgKjVavCniytBIw+t4ViOi7TYp9h/D0NMmkEc9klFTo+827eyEJ0lELpqO207Ey7uGxUa+BS1jQ== "@tsconfig/node10@^1.0.7": version "1.0.12" - resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.12.tgz#be57ceac1e4692b41be9de6be8c32a106636dba4" + resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz" integrity sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ== "@tsconfig/node12@^1.0.7": version "1.0.11" - resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + resolved "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz" integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== "@tsconfig/node14@^1.0.0": version "1.0.3" - resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + resolved "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz" integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== "@tsconfig/node16@^1.0.2": version "1.0.4" - resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" + resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz" integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== "@types/bn.js@^5.1.6": version "5.2.0" - resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.2.0.tgz#4349b9710e98f9ab3cdc50f1c5e4dcbd8ef29c80" + resolved "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.2.0.tgz" integrity sha512-DLbJ1BPqxvQhIGbeu8VbUC1DiAiahHtAYvA0ZEAa4P31F7IaArc8z3C3BRQdWX4mtLQuABG4yzp76ZrS02Ui1Q== dependencies: "@types/node" "*" "@types/chai@^5.0.1": version "5.2.3" - resolved "https://registry.yarnpkg.com/@types/chai/-/chai-5.2.3.tgz#8e9cd9e1c3581fa6b341a5aed5588eb285be0b4a" + resolved "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz" integrity sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA== dependencies: "@types/deep-eql" "*" @@ -1198,124 +1198,124 @@ "@types/deep-eql@*": version "4.0.2" - resolved "https://registry.yarnpkg.com/@types/deep-eql/-/deep-eql-4.0.2.tgz#334311971d3a07121e7eb91b684a605e7eea9cbd" + resolved "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz" integrity sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw== "@types/estree@1.0.8": version "1.0.8" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" + resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz" integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== "@types/mocha@^10.0.10": version "10.0.10" - resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.10.tgz#91f62905e8d23cbd66225312f239454a23bebfa0" + resolved "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz" integrity sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q== -"@types/node@*", "@types/node@^24.10.1", "@types/node@^24.5.2": - version "24.10.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-24.10.1.tgz#91e92182c93db8bd6224fca031e2370cef9a8f01" - integrity sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ== +"@types/node@*", "@types/node@^22.18.0": + version "22.19.1" + resolved "https://registry.npmjs.org/@types/node/-/node-22.19.1.tgz" + integrity sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ== dependencies: - undici-types "~7.16.0" + undici-types "~6.21.0" "@types/node@22.7.5": version "22.7.5" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.7.5.tgz#cfde981727a7ab3611a481510b473ae54442b92b" + resolved "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz" integrity sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ== dependencies: undici-types "~6.19.2" -"@types/node@^22.18.0": - version "22.19.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.19.1.tgz#1188f1ddc9f46b4cc3aec76749050b4e1f459b7b" - integrity sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ== +"@types/node@^24.10.1", "@types/node@^24.5.2": + version "24.10.1" + resolved "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz" + integrity sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ== dependencies: - undici-types "~6.21.0" + undici-types "~7.16.0" "@types/normalize-package-data@^2.4.3", "@types/normalize-package-data@^2.4.4": version "2.4.4" - resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz#56e2cc26c397c038fab0e3a917a12d5c5909e901" + resolved "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz" integrity sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA== "@types/ws@^8.18.1": version "8.18.1" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.18.1.tgz#48464e4bf2ddfd17db13d845467f6070ffea4aa9" + resolved "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz" integrity sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg== dependencies: "@types/node" "*" abitype@1.0.8: version "1.0.8" - resolved "https://registry.yarnpkg.com/abitype/-/abitype-1.0.8.tgz#3554f28b2e9d6e9f35eb59878193eabd1b9f46ba" + resolved "https://registry.npmjs.org/abitype/-/abitype-1.0.8.tgz" integrity sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg== abitype@1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/abitype/-/abitype-1.1.0.tgz#510c5b3f92901877977af5e864841f443bf55406" + resolved "https://registry.npmjs.org/abitype/-/abitype-1.1.0.tgz" integrity sha512-6Vh4HcRxNMLA0puzPjM5GBgT4aAcFGKZzSgAXvuZ27shJP6NEpielTuqbBmZILR5/xd0PizkBGy5hReKz9jl5A== abitype@^1.0.6, abitype@^1.0.9, abitype@^1.1.1: version "1.2.0" - resolved "https://registry.yarnpkg.com/abitype/-/abitype-1.2.0.tgz#aeb24a323c3d28d4e78f2ada9bf2c7610907737a" + resolved "https://registry.npmjs.org/abitype/-/abitype-1.2.0.tgz" integrity sha512-fD3ROjckUrWsybaSor2AdWxzA0e/DSyV2dA4aYd7bd8orHsoJjl09fOgKfUkTDfk0BsDGBf4NBgu/c7JoS2Npw== acorn-walk@^8.1.1: version "8.3.4" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7" + resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz" integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g== dependencies: acorn "^8.11.0" acorn@^8.11.0, acorn@^8.15.0, acorn@^8.4.1: version "8.15.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz" integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== aes-js@4.0.0-beta.5: version "4.0.0-beta.5" - resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-4.0.0-beta.5.tgz#8d2452c52adedebc3a3e28465d858c11ca315873" + resolved "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz" integrity sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q== ansi-regex@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== ansi-regex@^6.0.1: version "6.2.2" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.2.2.tgz#60216eea464d864597ce2832000738a0589650c1" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz" integrity sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg== ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" ansi-styles@^6.1.0: version "6.2.3" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.3.tgz#c044d5dcc521a076413472597a1acb1f103c4041" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz" integrity sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg== any-promise@^1.0.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + resolved "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz" integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== arg@^4.1.0: version "4.1.3" - resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== argparse@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== assert@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/assert/-/assert-2.1.0.tgz#6d92a238d05dc02e7427c881fb8be81c8448b2dd" + resolved "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz" integrity sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw== dependencies: call-bind "^1.0.2" @@ -1326,53 +1326,53 @@ assert@^2.1.0: assertion-error@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-2.0.1.tgz#f641a196b335690b1070bf00b6e7593fec190bf7" + resolved "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz" integrity sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA== available-typed-arrays@^1.0.7: version "1.0.7" - resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" + resolved "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz" integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== dependencies: possible-typed-array-names "^1.0.0" balanced-match@^1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== bn.js@^5.2.1: version "5.2.2" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.2.tgz#82c09f9ebbb17107cd72cb7fd39bd1f9d0aaa566" + resolved "https://registry.npmjs.org/bn.js/-/bn.js-5.2.2.tgz" integrity sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw== brace-expansion@^2.0.1: version "2.0.2" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz" integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ== dependencies: balanced-match "^1.0.0" browser-stdout@^1.3.1: version "1.3.1" - resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + resolved "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== bundle-require@^5.1.0: version "5.1.0" - resolved "https://registry.yarnpkg.com/bundle-require/-/bundle-require-5.1.0.tgz#8db66f41950da3d77af1ef3322f4c3e04009faee" + resolved "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz" integrity sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA== dependencies: load-tsconfig "^0.2.3" cac@^6.7.14: version "6.7.14" - resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" + resolved "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz" integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + resolved "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz" integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== dependencies: es-errors "^1.3.0" @@ -1380,7 +1380,7 @@ call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1, call-bind-apply- call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.7, call-bind@^1.0.8: version "1.0.8" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.8.tgz#0736a9660f537e3388826f440d5ec45f744eaa4c" + resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz" integrity sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww== dependencies: call-bind-apply-helpers "^1.0.0" @@ -1390,7 +1390,7 @@ call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.7, call-bind@^1.0.8: call-bound@^1.0.2, call-bound@^1.0.3, call-bound@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" + resolved "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz" integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== dependencies: call-bind-apply-helpers "^1.0.2" @@ -1398,17 +1398,17 @@ call-bound@^1.0.2, call-bound@^1.0.3, call-bound@^1.0.4: camelcase@^6.0.0: version "6.3.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== chai@^6.0.1: version "6.2.1" - resolved "https://registry.yarnpkg.com/chai/-/chai-6.2.1.tgz#d1e64bc42433fbee6175ad5346799682060b5b6a" + resolved "https://registry.npmjs.org/chai/-/chai-6.2.1.tgz" integrity sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg== chalk@^4.1.0: version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== dependencies: ansi-styles "^4.1.0" @@ -1416,31 +1416,31 @@ chalk@^4.1.0: chalk@^5.6.2: version "5.6.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.6.2.tgz#b1238b6e23ea337af71c7f8a295db5af0c158aea" + resolved "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz" integrity sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA== chokidar@^4.0.1, chokidar@^4.0.3: version "4.0.3" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.3.tgz#7be37a4c03c9aee1ecfe862a4a23b2c70c205d30" + resolved "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz" integrity sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA== dependencies: readdirp "^4.0.1" cli-cursor@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-5.0.0.tgz#24a4831ecf5a6b01ddeb32fb71a4b2088b0dce38" + resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz" integrity sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw== dependencies: restore-cursor "^5.0.0" cli-spinners@^3.2.0: version "3.3.0" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-3.3.0.tgz#2ba7c98b4f4662e67315b5634365661be8574440" + resolved "https://registry.npmjs.org/cli-spinners/-/cli-spinners-3.3.0.tgz" integrity sha512-/+40ljC3ONVnYIttjMWrlL51nItDAbBrq2upN8BPyvGU/2n5Oxw3tbNwORCaNuNqLJnxGqOfjUuhsv7l5Q4IsQ== cliui@^8.0.1: version "8.0.1" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + resolved "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz" integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== dependencies: string-width "^4.2.0" @@ -1449,44 +1449,44 @@ cliui@^8.0.1: color-convert@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== dependencies: color-name "~1.1.4" color-name@~1.1.4: version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== commander@^14.0.2: version "14.0.2" - resolved "https://registry.yarnpkg.com/commander/-/commander-14.0.2.tgz#b71fd37fe4069e4c3c7c13925252ada4eba14e8e" + resolved "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz" integrity sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ== commander@^4.0.0: version "4.1.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" + resolved "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz" integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== confbox@^0.1.8: version "0.1.8" - resolved "https://registry.yarnpkg.com/confbox/-/confbox-0.1.8.tgz#820d73d3b3c82d9bd910652c5d4d599ef8ff8b06" + resolved "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz" integrity sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w== consola@^3.4.0: version "3.4.2" - resolved "https://registry.yarnpkg.com/consola/-/consola-3.4.2.tgz#5af110145397bb67afdab77013fdc34cae590ea7" + resolved "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz" integrity sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA== create-require@^1.1.0: version "1.1.1" - resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== cross-spawn@^7.0.6: version "7.0.6" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz" integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== dependencies: path-key "^3.1.0" @@ -1495,29 +1495,29 @@ cross-spawn@^7.0.6: data-uri-to-buffer@^4.0.0: version "4.0.1" - resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e" + resolved "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz" integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A== debug@^4.1.0, debug@^4.3.5, debug@^4.4.0: version "4.4.3" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + resolved "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz" integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== dependencies: ms "^2.1.3" decamelize@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" + resolved "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz" integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== deepmerge-ts@^7.1.0: version "7.1.5" - resolved "https://registry.yarnpkg.com/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz#ff818564007f5c150808d2b7b732cac83aa415ab" + resolved "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz" integrity sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw== define-data-property@^1.0.1, define-data-property@^1.1.4: version "1.1.4" - resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + resolved "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz" integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== dependencies: es-define-property "^1.0.0" @@ -1526,7 +1526,7 @@ define-data-property@^1.0.1, define-data-property@^1.1.4: define-properties@^1.1.3, define-properties@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz" integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== dependencies: define-data-property "^1.0.1" @@ -1535,27 +1535,27 @@ define-properties@^1.1.3, define-properties@^1.2.1: detect-indent@^7.0.1: version "7.0.2" - resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-7.0.2.tgz#16c516bf75d4b2f759f68214554996d467c8d648" + resolved "https://registry.npmjs.org/detect-indent/-/detect-indent-7.0.2.tgz" integrity sha512-y+8xyqdGLL+6sh0tVeHcfP/QDd8gUgbasolJJpY7NgeQGSZ739bDtSiaiDgtoicy+mtYB81dKLxO9xRhCyIB3A== diff@^4.0.1: version "4.0.2" - resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== diff@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-7.0.0.tgz#3fb34d387cd76d803f6eebea67b921dab0182a9a" + resolved "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz" integrity sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw== dotenv@17.2.1: version "17.2.1" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-17.2.1.tgz#6f32e10faf014883515538dc922a0fb8765d9b32" + resolved "https://registry.npmjs.org/dotenv/-/dotenv-17.2.1.tgz" integrity sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ== dunder-proto@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + resolved "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz" integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== dependencies: call-bind-apply-helpers "^1.0.1" @@ -1564,39 +1564,39 @@ dunder-proto@^1.0.1: eastasianwidth@^0.2.0: version "0.2.0" - resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz" integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== emoji-regex@^8.0.0: version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== emoji-regex@^9.2.2: version "9.2.2" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== es-define-property@^1.0.0, es-define-property@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + resolved "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz" integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== es-errors@^1.3.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + resolved "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz" integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + resolved "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz" integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== dependencies: es-errors "^1.3.0" esbuild@^0.25.0: version "0.25.12" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.12.tgz#97a1d041f4ab00c2fce2f838d2b9969a2d2a97a5" + resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz" integrity sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg== optionalDependencies: "@esbuild/aix-ppc64" "0.25.12" @@ -1628,17 +1628,17 @@ esbuild@^0.25.0: escalade@^3.1.1: version "3.2.0" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + resolved "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz" integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== escape-string-regexp@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== ethers@^6.13.5: version "6.16.0" - resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.16.0.tgz#fff9b4f05d7a359c774ad6e91085a800f7fccf65" + resolved "https://registry.npmjs.org/ethers/-/ethers-6.16.0.tgz" integrity sha512-U1wulmetNymijEhpSEQ7Ct/P/Jw9/e7R1j5XIbPRydgV2DjLVMsULDlNksq3RQnFgKoLlZf88ijYtWEXcPa07A== dependencies: "@adraffy/ens-normalize" "1.10.1" @@ -1651,12 +1651,12 @@ ethers@^6.13.5: eventemitter3@5.0.1, eventemitter3@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" + resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz" integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== execa@^9.6.0: version "9.6.1" - resolved "https://registry.yarnpkg.com/execa/-/execa-9.6.1.tgz#5b90acedc6bdc0fa9b9a6ddf8f9cbb0c75a7c471" + resolved "https://registry.npmjs.org/execa/-/execa-9.6.1.tgz" integrity sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA== dependencies: "@sindresorhus/merge-streams" "^4.0.0" @@ -1674,12 +1674,12 @@ execa@^9.6.0: fdir@^6.5.0: version "6.5.0" - resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350" + resolved "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz" integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== fetch-blob@^3.1.2, fetch-blob@^3.1.4: version "3.2.0" - resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9" + resolved "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz" integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ== dependencies: node-domexception "^1.0.0" @@ -1687,14 +1687,14 @@ fetch-blob@^3.1.2, fetch-blob@^3.1.4: figures@^6.1.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-6.1.0.tgz#935479f51865fa7479f6fa94fc6fc7ac14e62c4a" + resolved "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz" integrity sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg== dependencies: is-unicode-supported "^2.0.0" find-up@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== dependencies: locate-path "^6.0.0" @@ -1702,7 +1702,7 @@ find-up@^5.0.0: fix-dts-default-cjs-exports@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/fix-dts-default-cjs-exports/-/fix-dts-default-cjs-exports-1.0.1.tgz#955cb6b3d519691c57828b078adadf2cb92e9549" + resolved "https://registry.npmjs.org/fix-dts-default-cjs-exports/-/fix-dts-default-cjs-exports-1.0.1.tgz" integrity sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg== dependencies: magic-string "^0.30.17" @@ -1711,19 +1711,19 @@ fix-dts-default-cjs-exports@^1.0.0: flat@^5.0.2: version "5.0.2" - resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + resolved "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz" integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== for-each@^0.3.5: version "0.3.5" - resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.5.tgz#d650688027826920feeb0af747ee7b9421a41d47" + resolved "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz" integrity sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg== dependencies: is-callable "^1.2.7" foreground-child@^3.1.0: version "3.3.1" - resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f" + resolved "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz" integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw== dependencies: cross-spawn "^7.0.6" @@ -1731,44 +1731,44 @@ foreground-child@^3.1.0: formdata-polyfill@^4.0.10: version "4.0.10" - resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423" + resolved "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz" integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g== dependencies: fetch-blob "^3.1.2" fs.promises.exists@^1.1.4: version "1.1.4" - resolved "https://registry.yarnpkg.com/fs.promises.exists/-/fs.promises.exists-1.1.4.tgz#6a1d8fd24df79248eda19a8ba9dd7fd68b941921" + resolved "https://registry.npmjs.org/fs.promises.exists/-/fs.promises.exists-1.1.4.tgz" integrity sha512-lJzUGWbZn8vhGWBedA+RYjB/BeJ+3458ljUfmplqhIeb6ewzTFWNPCR1HCiYCkXV9zxcHz9zXkJzMsEgDLzh3Q== fsevents@~2.3.2: version "2.3.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== function-bind@^1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== generator-function@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/generator-function/-/generator-function-2.0.1.tgz#0e75dd410d1243687a0ba2e951b94eedb8f737a2" + resolved "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz" integrity sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g== get-caller-file@^2.0.5: version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== get-east-asian-width@^1.3.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz#9bc4caa131702b4b61729cb7e42735bc550c9ee6" + resolved "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz" integrity sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q== get-intrinsic@^1.2.4, get-intrinsic@^1.3.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz" integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== dependencies: call-bind-apply-helpers "^1.0.2" @@ -1784,7 +1784,7 @@ get-intrinsic@^1.2.4, get-intrinsic@^1.3.0: get-proto@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + resolved "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz" integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== dependencies: dunder-proto "^1.0.1" @@ -1792,7 +1792,7 @@ get-proto@^1.0.1: get-stream@^9.0.0: version "9.0.1" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-9.0.1.tgz#95157d21df8eb90d1647102b63039b1df60ebd27" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz" integrity sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA== dependencies: "@sec-ant/readable-stream" "^0.4.1" @@ -1800,7 +1800,7 @@ get-stream@^9.0.0: glob@^10.4.5: version "10.5.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-10.5.0.tgz#8ec0355919cd3338c28428a23d4f24ecc5fe738c" + resolved "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz" integrity sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg== dependencies: foreground-child "^3.1.0" @@ -1812,82 +1812,82 @@ glob@^10.4.5: gopd@^1.0.1, gopd@^1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + resolved "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz" integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== has-flag@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + resolved "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz" integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== dependencies: es-define-property "^1.0.0" has-symbols@^1.0.3, has-symbols@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz" integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== has-tostringtag@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz" integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== dependencies: has-symbols "^1.0.3" hasown@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz" integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== dependencies: function-bind "^1.1.2" he@^1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== hosted-git-info@^7.0.0: version "7.0.2" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-7.0.2.tgz#9b751acac097757667f30114607ef7b661ff4f17" + resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz" integrity sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w== dependencies: lru-cache "^10.0.1" hosted-git-info@^9.0.0: version "9.0.2" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-9.0.2.tgz#b38c8a802b274e275eeeccf9f4a1b1a0a8557ada" + resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.2.tgz" integrity sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg== dependencies: lru-cache "^11.1.0" human-signals@^8.0.1: version "8.0.1" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-8.0.1.tgz#f08bb593b6d1db353933d06156cedec90abe51fb" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz" integrity sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ== imurmurhash@^0.1.4: version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== index-to-position@^1.1.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/index-to-position/-/index-to-position-1.2.0.tgz#c800eb34dacf4dbf96b9b06c7eb78d5f704138b4" + resolved "https://registry.npmjs.org/index-to-position/-/index-to-position-1.2.0.tgz" integrity sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw== inherits@^2.0.3: version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== is-arguments@^1.0.4: version "1.2.0" - resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.2.0.tgz#ad58c6aecf563b78ef2bf04df540da8f5d7d8e1b" + resolved "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz" integrity sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA== dependencies: call-bound "^1.0.2" @@ -1895,17 +1895,17 @@ is-arguments@^1.0.4: is-callable@^1.2.7: version "1.2.7" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== is-fullwidth-code-point@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== is-generator-function@^1.0.7: version "1.1.2" - resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.1.2.tgz#ae3b61e3d5ea4e4839b90bad22b02335051a17d5" + resolved "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz" integrity sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA== dependencies: call-bound "^1.0.4" @@ -1916,12 +1916,12 @@ is-generator-function@^1.0.7: is-interactive@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-2.0.0.tgz#40c57614593826da1100ade6059778d597f16e90" + resolved "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz" integrity sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ== is-nan@^1.3.2: version "1.3.2" - resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d" + resolved "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz" integrity sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w== dependencies: call-bind "^1.0.0" @@ -1929,22 +1929,22 @@ is-nan@^1.3.2: is-path-inside@^3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== is-plain-obj@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz" integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== is-plain-obj@^4.0.0, is-plain-obj@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0" + resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz" integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg== is-regex@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.2.1.tgz#76d70a3ed10ef9be48eb577887d74205bf0cad22" + resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz" integrity sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g== dependencies: call-bound "^1.0.2" @@ -1954,44 +1954,44 @@ is-regex@^1.2.1: is-stream@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-4.0.1.tgz#375cf891e16d2e4baec250b85926cffc14720d9b" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz" integrity sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A== is-typed-array@^1.1.3: version "1.1.15" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.15.tgz#4bfb4a45b61cee83a5a46fba778e4e8d59c0ce0b" + resolved "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz" integrity sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ== dependencies: which-typed-array "^1.1.16" is-unicode-supported@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== is-unicode-supported@^2.0.0, is-unicode-supported@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz#09f0ab0de6d3744d48d265ebb98f65d11f2a9b3a" + resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz" integrity sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ== isexe@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== isows@1.0.6: version "1.0.6" - resolved "https://registry.yarnpkg.com/isows/-/isows-1.0.6.tgz#0da29d706fa51551c663c627ace42769850f86e7" + resolved "https://registry.npmjs.org/isows/-/isows-1.0.6.tgz" integrity sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw== isows@1.0.7: version "1.0.7" - resolved "https://registry.yarnpkg.com/isows/-/isows-1.0.7.tgz#1c06400b7eed216fbba3bcbd68f12490fc342915" + resolved "https://registry.npmjs.org/isows/-/isows-1.0.7.tgz" integrity sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg== jackspeak@^3.1.2: version "3.4.3" - resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" + resolved "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz" integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== dependencies: "@isaacs/cliui" "^8.0.2" @@ -2000,56 +2000,56 @@ jackspeak@^3.1.2: joycon@^3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/joycon/-/joycon-3.1.1.tgz#bce8596d6ae808f8b68168f5fc69280996894f03" + resolved "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz" integrity sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw== js-tokens@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-yaml@^4.1.0: version "4.1.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.1.tgz#854c292467705b699476e1a2decc0c8a3458806b" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz" integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA== dependencies: argparse "^2.0.1" json-stringify-safe@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + resolved "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== lilconfig@^3.1.1: version "3.1.3" - resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.1.3.tgz#a1bcfd6257f9585bf5ae14ceeebb7b559025e4c4" + resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz" integrity sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw== lines-and-columns@^1.1.6: version "1.2.4" - resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== load-tsconfig@^0.2.3: version "0.2.5" - resolved "https://registry.yarnpkg.com/load-tsconfig/-/load-tsconfig-0.2.5.tgz#453b8cd8961bfb912dea77eb6c168fe8cca3d3a1" + resolved "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz" integrity sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg== locate-path@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== dependencies: p-locate "^5.0.0" lodash.sortby@^4.7.0: version "4.7.0" - resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" + resolved "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz" integrity sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA== log-symbols@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz" integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== dependencies: chalk "^4.1.0" @@ -2057,7 +2057,7 @@ log-symbols@^4.1.0: log-symbols@^7.0.1: version "7.0.1" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-7.0.1.tgz#f52e68037d96f589fc572ff2193dc424d48c195b" + resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-7.0.1.tgz" integrity sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg== dependencies: is-unicode-supported "^2.0.0" @@ -2065,51 +2065,51 @@ log-symbols@^7.0.1: lru-cache@^10.0.1, lru-cache@^10.2.0: version "10.4.3" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz" integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== lru-cache@^11.1.0: version "11.2.4" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.2.4.tgz#ecb523ebb0e6f4d837c807ad1abaea8e0619770d" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz" integrity sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg== magic-string@^0.30.17: version "0.30.21" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.21.tgz#56763ec09a0fa8091df27879fd94d19078c00d91" + resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz" integrity sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ== dependencies: "@jridgewell/sourcemap-codec" "^1.5.5" make-error@^1.1.1: version "1.3.6" - resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== math-intrinsics@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + resolved "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz" integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== mimic-function@^5.0.0: version "5.0.1" - resolved "https://registry.yarnpkg.com/mimic-function/-/mimic-function-5.0.1.tgz#acbe2b3349f99b9deaca7fb70e48b83e94e67076" + resolved "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz" integrity sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA== minimatch@^9.0.4, minimatch@^9.0.5: version "9.0.5" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz" integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== dependencies: brace-expansion "^2.0.1" "minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: version "7.1.2" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + resolved "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== mlly@^1.7.4: version "1.8.0" - resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.8.0.tgz#e074612b938af8eba1eaf43299cbc89cb72d824e" + resolved "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz" integrity sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g== dependencies: acorn "^8.15.0" @@ -2119,7 +2119,7 @@ mlly@^1.7.4: mocha@^11.1.0: version "11.7.5" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-11.7.5.tgz#58f5bbfa5e0211ce7e5ee6128107cefc2515a627" + resolved "https://registry.npmjs.org/mocha/-/mocha-11.7.5.tgz" integrity sha512-mTT6RgopEYABzXWFx+GcJ+ZQ32kp4fMf0xvpZIIfSq9Z8lC/++MtcCnQ9t5FP2veYEP95FIYSvW+U9fV4xrlig== dependencies: browser-stdout "^1.3.1" @@ -2146,17 +2146,17 @@ mocha@^11.1.0: mock-socket@^9.3.1: version "9.3.1" - resolved "https://registry.yarnpkg.com/mock-socket/-/mock-socket-9.3.1.tgz#24fb00c2f573c84812aa4a24181bb025de80cc8e" + resolved "https://registry.npmjs.org/mock-socket/-/mock-socket-9.3.1.tgz" integrity sha512-qxBgB7Qa2sEQgHFjj0dSigq7fX4k6Saisd5Nelwp2q8mlbAFh5dHV9JTTlF8viYJLSSWgMCZFUom8PJcMNBoJw== ms@^2.1.3: version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== mz@^2.7.0: version "2.7.0" - resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" + resolved "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz" integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== dependencies: any-promise "^1.0.0" @@ -2165,7 +2165,7 @@ mz@^2.7.0: nock@^13.5.5: version "13.5.6" - resolved "https://registry.yarnpkg.com/nock/-/nock-13.5.6.tgz#5e693ec2300bbf603b61dae6df0225673e6c4997" + resolved "https://registry.npmjs.org/nock/-/nock-13.5.6.tgz" integrity sha512-o2zOYiCpzRqSzPj0Zt/dQ/DqZeYoaQ7TUonc/xUPjCGl9WeHpNbxgVvOquXYAaJzI0M9BXV3HTzG0p8IUAbBTQ== dependencies: debug "^4.1.0" @@ -2174,12 +2174,12 @@ nock@^13.5.5: node-domexception@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" + resolved "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz" integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== node-fetch@^3.3.2: version "3.3.2" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.2.tgz#d1e889bacdf733b4ff3b2b243eb7a12866a0b78b" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz" integrity sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA== dependencies: data-uri-to-buffer "^4.0.0" @@ -2188,7 +2188,7 @@ node-fetch@^3.3.2: normalize-package-data@^6.0.0: version "6.0.2" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-6.0.2.tgz#a7bc22167fe24025412bcff0a9651eb768b03506" + resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz" integrity sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g== dependencies: hosted-git-info "^7.0.0" @@ -2197,7 +2197,7 @@ normalize-package-data@^6.0.0: normalize-package-data@^8.0.0: version "8.0.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-8.0.0.tgz#bdce7ff2d6ba891b853e179e45a5337766e304a7" + resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-8.0.0.tgz" integrity sha512-RWk+PI433eESQ7ounYxIp67CYuVsS1uYSonX3kA6ps/3LWfjVQa/ptEg6Y3T6uAMq1mWpX9PQ+qx+QaHpsc7gQ== dependencies: hosted-git-info "^9.0.0" @@ -2206,7 +2206,7 @@ normalize-package-data@^8.0.0: npm-run-path@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-6.0.0.tgz#25cfdc4eae04976f3349c0b1afc089052c362537" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz" integrity sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA== dependencies: path-key "^4.0.0" @@ -2214,12 +2214,12 @@ npm-run-path@^6.0.0: object-assign@^4.0.1: version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== object-is@^1.1.5: version "1.1.6" - resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.6.tgz#1a6a53aed2dd8f7e6775ff870bea58545956ab07" + resolved "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz" integrity sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q== dependencies: call-bind "^1.0.7" @@ -2227,12 +2227,12 @@ object-is@^1.1.5: object-keys@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== object.assign@^4.1.4: version "4.1.7" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.7.tgz#8c14ca1a424c6a561b0bb2a22f66f5049a945d3d" + resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz" integrity sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw== dependencies: call-bind "^1.0.8" @@ -2244,14 +2244,14 @@ object.assign@^4.1.4: onetime@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-7.0.0.tgz#9f16c92d8c9ef5120e3acd9dd9957cceecc1ab60" + resolved "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz" integrity sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ== dependencies: mimic-function "^5.0.0" ora@^9.0.0: version "9.0.0" - resolved "https://registry.yarnpkg.com/ora/-/ora-9.0.0.tgz#945236f5ce78a024cf4c25df6c46ecd09ab6e685" + resolved "https://registry.npmjs.org/ora/-/ora-9.0.0.tgz" integrity sha512-m0pg2zscbYgWbqRR6ABga5c3sZdEon7bSgjnlXC64kxtxLOyjRcbbUkLj7HFyy/FTD+P2xdBWu8snGhYI0jc4A== dependencies: chalk "^5.6.2" @@ -2266,7 +2266,7 @@ ora@^9.0.0: ox@0.6.7: version "0.6.7" - resolved "https://registry.yarnpkg.com/ox/-/ox-0.6.7.tgz#afd53f2ecef68b8526660e9d29dee6e6b599a832" + resolved "https://registry.npmjs.org/ox/-/ox-0.6.7.tgz" integrity sha512-17Gk/eFsFRAZ80p5eKqv89a57uXjd3NgIf1CaXojATPBuujVc/fQSVhBeAU9JCRB+k7J50WQAyWTxK19T9GgbA== dependencies: "@adraffy/ens-normalize" "^1.10.1" @@ -2279,7 +2279,7 @@ ox@0.6.7: ox@0.9.6: version "0.9.6" - resolved "https://registry.yarnpkg.com/ox/-/ox-0.9.6.tgz#5cf02523b6db364c10ee7f293ff1e664e0e1eab7" + resolved "https://registry.npmjs.org/ox/-/ox-0.9.6.tgz" integrity sha512-8SuCbHPvv2eZLYXrNmC0EC12rdzXQLdhnOMlHDW2wiCPLxBrOOJwX5L5E61by+UjTPOryqQiRSnjIKCI+GykKg== dependencies: "@adraffy/ens-normalize" "^1.11.0" @@ -2293,26 +2293,26 @@ ox@0.9.6: p-limit@^3.0.2: version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== dependencies: yocto-queue "^0.1.0" p-locate@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz" integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== dependencies: p-limit "^3.0.2" package-json-from-dist@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" + resolved "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz" integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== parse-json@^8.0.0, parse-json@^8.3.0: version "8.3.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-8.3.0.tgz#88a195a2157025139a2317a4f2f9252b61304ed5" + resolved "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz" integrity sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ== dependencies: "@babel/code-frame" "^7.26.2" @@ -2321,27 +2321,27 @@ parse-json@^8.0.0, parse-json@^8.3.0: parse-ms@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-4.0.0.tgz#c0c058edd47c2a590151a718990533fd62803df4" + resolved "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz" integrity sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw== path-exists@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== path-key@^3.1.0: version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== path-key@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" + resolved "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz" integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== path-scurry@^1.11.1: version "1.11.1" - resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + resolved "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz" integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== dependencies: lru-cache "^10.2.0" @@ -2349,27 +2349,27 @@ path-scurry@^1.11.1: pathe@^2.0.1, pathe@^2.0.3: version "2.0.3" - resolved "https://registry.yarnpkg.com/pathe/-/pathe-2.0.3.tgz#3ecbec55421685b70a9da872b2cff3e1cbed1716" + resolved "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz" integrity sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w== picocolors@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz" integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== picomatch@^4.0.3: version "4.0.3" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz" integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q== pirates@^4.0.1: version "4.0.7" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.7.tgz#643b4a18c4257c8a65104b73f3049ce9a0a15e22" + resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz" integrity sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA== pkg-types@^1.3.1: version "1.3.1" - resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-1.3.1.tgz#bd7cc70881192777eef5326c19deb46e890917df" + resolved "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz" integrity sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ== dependencies: confbox "^0.1.8" @@ -2378,7 +2378,7 @@ pkg-types@^1.3.1: polkadot-api@^1.22.0: version "1.22.0" - resolved "https://registry.yarnpkg.com/polkadot-api/-/polkadot-api-1.22.0.tgz#fc042d9e86154ab5d3a27f335aab222eda2abb00" + resolved "https://registry.npmjs.org/polkadot-api/-/polkadot-api-1.22.0.tgz" integrity sha512-uREBLroPbnJxBBQ+qSkKLF493qukX4PAg32iThlELrZdxfNNgro6nvWRdVmBv73tFHvf+nyWWHKTx1c57nbixg== dependencies: "@polkadot-api/cli" "0.16.3" @@ -2403,48 +2403,48 @@ polkadot-api@^1.22.0: possible-typed-array-names@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz#93e3582bc0e5426586d9d07b79ee40fc841de4ae" + resolved "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz" integrity sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg== postcss-load-config@^6.0.1: version "6.0.1" - resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-6.0.1.tgz#6fd7dcd8ae89badcf1b2d644489cbabf83aa8096" + resolved "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz" integrity sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g== dependencies: lilconfig "^3.1.1" prettier@^3.3.3: version "3.7.4" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.7.4.tgz#d2f8335d4b1cec47e1c8098645411b0c9dff9c0f" + resolved "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz" integrity sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA== pretty-ms@^9.2.0: version "9.3.0" - resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-9.3.0.tgz#dd2524fcb3c326b4931b2272dfd1e1a8ed9a9f5a" + resolved "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.0.tgz" integrity sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ== dependencies: parse-ms "^4.0.0" propagate@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/propagate/-/propagate-2.0.1.tgz#40cdedab18085c792334e64f0ac17256d38f9a45" + resolved "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz" integrity sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag== punycode@^2.1.0: version "2.3.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== randombytes@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== dependencies: safe-buffer "^5.1.0" read-pkg@^10.0.0: version "10.0.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-10.0.0.tgz#06401f0331115e9fba9880cb3f2ae1efa3db00e4" + resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-10.0.0.tgz" integrity sha512-A70UlgfNdKI5NSvTTfHzLQj7NJRpJ4mT5tGafkllJ4wh71oYuGm/pzphHcmW4s35iox56KSK721AihodoXSc/A== dependencies: "@types/normalize-package-data" "^2.4.4" @@ -2455,7 +2455,7 @@ read-pkg@^10.0.0: read-pkg@^9.0.1: version "9.0.1" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-9.0.1.tgz#b1b81fb15104f5dbb121b6bbdee9bbc9739f569b" + resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz" integrity sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA== dependencies: "@types/normalize-package-data" "^2.4.3" @@ -2466,22 +2466,22 @@ read-pkg@^9.0.1: readdirp@^4.0.1: version "4.1.2" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.1.2.tgz#eb85801435fbf2a7ee58f19e0921b068fc69948d" + resolved "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz" integrity sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg== require-directory@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== resolve-from@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== restore-cursor@^5.0.0: version "5.1.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-5.1.0.tgz#0766d95699efacb14150993f55baf0953ea1ebe7" + resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz" integrity sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA== dependencies: onetime "^7.0.0" @@ -2489,7 +2489,7 @@ restore-cursor@^5.0.0: rollup@^4.34.8: version "4.53.3" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.53.3.tgz#dbc8cd8743b38710019fb8297e8d7a76e3faa406" + resolved "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz" integrity sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA== dependencies: "@types/estree" "1.0.8" @@ -2520,19 +2520,19 @@ rollup@^4.34.8: rxjs@^7.8.1, rxjs@^7.8.2: version "7.8.2" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.2.tgz#955bc473ed8af11a002a2be52071bf475638607b" + resolved "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz" integrity sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA== dependencies: tslib "^2.1.0" safe-buffer@^5.1.0: version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== safe-regex-test@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.1.0.tgz#7f87dfb67a3150782eaaf18583ff5d1711ac10c1" + resolved "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz" integrity sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw== dependencies: call-bound "^1.0.2" @@ -2541,24 +2541,24 @@ safe-regex-test@^1.1.0: scale-ts@^1.6.0, scale-ts@^1.6.1: version "1.6.1" - resolved "https://registry.yarnpkg.com/scale-ts/-/scale-ts-1.6.1.tgz#45151e156d6c04792223c39d8e7484ce926445f2" + resolved "https://registry.npmjs.org/scale-ts/-/scale-ts-1.6.1.tgz" integrity sha512-PBMc2AWc6wSEqJYBDPcyCLUj9/tMKnLX70jLOSndMtcUoLQucP/DM0vnQo1wJAYjTrQiq8iG9rD0q6wFzgjH7g== semver@^7.3.5: version "7.7.3" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946" + resolved "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz" integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q== serialize-javascript@^6.0.2: version "6.0.2" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" + resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz" integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== dependencies: randombytes "^2.1.0" set-function-length@^1.2.2: version "1.2.2" - resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + resolved "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz" integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== dependencies: define-data-property "^1.1.4" @@ -2570,19 +2570,19 @@ set-function-length@^1.2.2: shebang-command@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== dependencies: shebang-regex "^3.0.0" shebang-regex@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== signal-exit@^4.0.1, signal-exit@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== smoldot@2.0.26: @@ -2594,28 +2594,28 @@ smoldot@2.0.26: smoldot@2.0.39: version "2.0.39" - resolved "https://registry.yarnpkg.com/smoldot/-/smoldot-2.0.39.tgz#e99981d13476702bbd906b8d14d18a77ddef0c0d" + resolved "https://registry.npmjs.org/smoldot/-/smoldot-2.0.39.tgz" integrity sha512-yFMSzI6nkqWFTNao99lBA/TguUFU+bR3A5UGTDd/QqqB12jqzvZnmW/No6l2rKmagt8Qx/KybMNowV/E28znhA== dependencies: ws "^8.8.1" sort-keys@^5.0.0: version "5.1.0" - resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-5.1.0.tgz#50a3f3d1ad3c5a76d043e0aeeba7299241e9aa5c" + resolved "https://registry.npmjs.org/sort-keys/-/sort-keys-5.1.0.tgz" integrity sha512-aSbHV0DaBcr7u0PVHXzM6NbZNAtrr9sF6+Qfs9UUVG7Ll3jQ6hHi8F/xqIIcn2rvIVbr0v/2zyjSdwSV47AgLQ== dependencies: is-plain-obj "^4.0.0" source-map@0.8.0-beta.0: version "0.8.0-beta.0" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.8.0-beta.0.tgz#d4c1bb42c3f7ee925f005927ba10709e0d1d1f11" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz" integrity sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA== dependencies: whatwg-url "^7.0.0" spdx-correct@^3.0.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" + resolved "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz" integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== dependencies: spdx-expression-parse "^3.0.0" @@ -2623,12 +2623,12 @@ spdx-correct@^3.0.0: spdx-exceptions@^2.1.0: version "2.5.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz#5d607d27fc806f66d7b64a766650fa890f04ed66" + resolved "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz" integrity sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w== spdx-expression-parse@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + resolved "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz" integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== dependencies: spdx-exceptions "^2.1.0" @@ -2636,17 +2636,17 @@ spdx-expression-parse@^3.0.0: spdx-license-ids@^3.0.0: version "3.0.22" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz#abf5a08a6f5d7279559b669f47f0a43e8f3464ef" + resolved "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz" integrity sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ== stdin-discarder@^0.2.2: version "0.2.2" - resolved "https://registry.yarnpkg.com/stdin-discarder/-/stdin-discarder-0.2.2.tgz#390037f44c4ae1a1ae535c5fe38dc3aba8d997be" + resolved "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz" integrity sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ== "string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== dependencies: emoji-regex "^8.0.0" @@ -2655,7 +2655,7 @@ stdin-discarder@^0.2.2: string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== dependencies: emoji-regex "^8.0.0" @@ -2664,7 +2664,7 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + resolved "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz" integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== dependencies: eastasianwidth "^0.2.0" @@ -2673,7 +2673,7 @@ string-width@^5.0.1, string-width@^5.1.2: string-width@^8.1.0: version "8.1.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-8.1.0.tgz#9e9fb305174947cf45c30529414b5da916e9e8d1" + resolved "https://registry.npmjs.org/string-width/-/string-width-8.1.0.tgz" integrity sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg== dependencies: get-east-asian-width "^1.3.0" @@ -2681,38 +2681,38 @@ string-width@^8.1.0: "strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" strip-ansi@^7.0.1, strip-ansi@^7.1.0, strip-ansi@^7.1.2: version "7.1.2" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.2.tgz#132875abde678c7ea8d691533f2e7e22bb744dba" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz" integrity sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA== dependencies: ansi-regex "^6.0.1" strip-final-newline@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-4.0.0.tgz#35a369ec2ac43df356e3edd5dcebb6429aa1fa5c" + resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz" integrity sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw== strip-json-comments@^3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== sucrase@^3.35.0: version "3.35.1" - resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.35.1.tgz#4619ea50393fe8bd0ae5071c26abd9b2e346bfe1" + resolved "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz" integrity sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw== dependencies: "@jridgewell/gen-mapping" "^0.3.2" @@ -2725,45 +2725,45 @@ sucrase@^3.35.0: supports-color@^7.1.0: version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" supports-color@^8.1.1: version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== dependencies: has-flag "^4.0.0" tagged-tag@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/tagged-tag/-/tagged-tag-1.0.0.tgz#a0b5917c2864cba54841495abfa3f6b13edcf4d6" + resolved "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz" integrity sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng== thenify-all@^1.0.0: version "1.6.0" - resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" + resolved "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz" integrity sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA== dependencies: thenify ">= 3.1.0 < 4" "thenify@>= 3.1.0 < 4": version "3.3.1" - resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f" + resolved "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz" integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== dependencies: any-promise "^1.0.0" tinyexec@^0.3.2: version "0.3.2" - resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.2.tgz#941794e657a85e496577995c6eef66f53f42b3d2" + resolved "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz" integrity sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA== tinyglobby@^0.2.11: version "0.2.15" - resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2" + resolved "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz" integrity sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ== dependencies: fdir "^6.5.0" @@ -2771,24 +2771,24 @@ tinyglobby@^0.2.11: tr46@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" + resolved "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz" integrity sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA== dependencies: punycode "^2.1.0" tree-kill@^1.2.2: version "1.2.2" - resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" + resolved "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz" integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== ts-interface-checker@^0.1.9: version "0.1.13" - resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699" + resolved "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz" integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== ts-node@^10.9.2: version "10.9.2" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" + resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz" integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== dependencies: "@cspotcode/source-map-support" "^0.8.0" @@ -2807,22 +2807,22 @@ ts-node@^10.9.2: tsc-prog@^2.3.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/tsc-prog/-/tsc-prog-2.3.0.tgz#b14ffb4e9487cca5cf42185f2a94963978faf8ee" + resolved "https://registry.npmjs.org/tsc-prog/-/tsc-prog-2.3.0.tgz" integrity sha512-ycET2d75EgcX7y8EmG4KiZkLAwUzbY4xRhA6NU0uVbHkY4ZjrAAuzTMxXI85kOwATqPnBI5C/7y7rlpY0xdqHA== tslib@2.7.0: version "2.7.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz" integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA== tslib@^2.1.0, tslib@^2.7.0, tslib@^2.8.0, tslib@^2.8.1: version "2.8.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== tsup@8.5.0: version "8.5.0" - resolved "https://registry.yarnpkg.com/tsup/-/tsup-8.5.0.tgz#4b1e25b1a8f4e4f89b764207bf37cfe2d7411d31" + resolved "https://registry.npmjs.org/tsup/-/tsup-8.5.0.tgz" integrity sha512-VmBp77lWNQq6PfuMqCHD3xWl22vEoWsKajkF8t+yMBawlUS8JzEI+vOVMeuNZIuMML8qXRizFKi9oD5glKQVcQ== dependencies: bundle-require "^5.1.0" @@ -2845,54 +2845,54 @@ tsup@8.5.0: type-fest@^4.23.0, type-fest@^4.39.1, type-fest@^4.6.0: version "4.41.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.41.0.tgz#6ae1c8e5731273c2bf1f58ad39cbae2c91a46c58" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz" integrity sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA== type-fest@^5.2.0: version "5.3.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-5.3.0.tgz#9422125b3094b1087d8446ba151b72fb9f39411a" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-5.3.0.tgz" integrity sha512-d9CwU93nN0IA1QL+GSNDdwLAu1Ew5ZjTwupvedwg3WdfoH6pIDvYQ2hV0Uc2nKBLPq7NB5apCx57MLS5qlmO5g== dependencies: tagged-tag "^1.0.0" typescript@^5.7.2, typescript@^5.9.3: version "5.9.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f" + resolved "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz" integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== ufo@^1.6.1: version "1.6.1" - resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.6.1.tgz#ac2db1d54614d1b22c1d603e3aef44a85d8f146b" + resolved "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz" integrity sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA== undici-types@~6.19.2: version "6.19.8" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" + resolved "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz" integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== undici-types@~6.21.0: version "6.21.0" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" + resolved "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz" integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== undici-types@~7.16.0: version "7.16.0" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.16.0.tgz#ffccdff36aea4884cbfce9a750a0580224f58a46" + resolved "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz" integrity sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw== unicorn-magic@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/unicorn-magic/-/unicorn-magic-0.1.0.tgz#1bb9a51c823aaf9d73a8bfcd3d1a23dde94b0ce4" + resolved "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz" integrity sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ== unicorn-magic@^0.3.0: version "0.3.0" - resolved "https://registry.yarnpkg.com/unicorn-magic/-/unicorn-magic-0.3.0.tgz#4efd45c85a69e0dd576d25532fbfa22aa5c8a104" + resolved "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz" integrity sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA== util@^0.12.5: version "0.12.5" - resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc" + resolved "https://registry.npmjs.org/util/-/util-0.12.5.tgz" integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA== dependencies: inherits "^2.0.3" @@ -2903,12 +2903,12 @@ util@^0.12.5: v8-compile-cache-lib@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz" integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== validate-npm-package-license@^3.0.4: version "3.0.4" - resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz" integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== dependencies: spdx-correct "^3.0.0" @@ -2916,7 +2916,7 @@ validate-npm-package-license@^3.0.4: viem@2.23.4: version "2.23.4" - resolved "https://registry.yarnpkg.com/viem/-/viem-2.23.4.tgz#164279352d7b5df2603e3d338386b026dc355b80" + resolved "https://registry.npmjs.org/viem/-/viem-2.23.4.tgz" integrity sha512-UQquuolKlS1w5H5e0Fd1KKoUlIPJryIEBzY5AUhGyV1ka+9O6+3uYVhUzj6RbvGK0PtsMKn2ddwPZFwjNDVU/A== dependencies: "@noble/curves" "1.8.1" @@ -2930,7 +2930,7 @@ viem@2.23.4: viem@^2.37.9: version "2.41.2" - resolved "https://registry.yarnpkg.com/viem/-/viem-2.41.2.tgz#117eab182b6b5501e47462269bb63f8b365a802e" + resolved "https://registry.npmjs.org/viem/-/viem-2.41.2.tgz" integrity sha512-LYliajglBe1FU6+EH9mSWozp+gRA/QcHfxeD9Odf83AdH5fwUS7DroH4gHvlv6Sshqi1uXrYFA2B/EOczxd15g== dependencies: "@noble/curves" "1.9.1" @@ -2944,17 +2944,17 @@ viem@^2.37.9: web-streams-polyfill@^3.0.3: version "3.3.3" - resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz#2073b91a2fdb1fbfbd401e7de0ac9f8214cecb4b" + resolved "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz" integrity sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw== webidl-conversions@^4.0.2: version "4.0.2" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz" integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== whatwg-url@^7.0.0: version "7.1.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz" integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg== dependencies: lodash.sortby "^4.7.0" @@ -2963,7 +2963,7 @@ whatwg-url@^7.0.0: which-typed-array@^1.1.16, which-typed-array@^1.1.2: version "1.1.19" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.19.tgz#df03842e870b6b88e117524a4b364b6fc689f956" + resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz" integrity sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw== dependencies: available-typed-arrays "^1.0.7" @@ -2976,19 +2976,19 @@ which-typed-array@^1.1.16, which-typed-array@^1.1.2: which@^2.0.1: version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: isexe "^2.0.0" workerpool@^9.2.0: version "9.3.4" - resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-9.3.4.tgz#f6c92395b2141afd78e2a889e80cb338fe9fca41" + resolved "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz" integrity sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg== "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" @@ -2997,7 +2997,7 @@ workerpool@^9.2.0: wrap-ansi@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" @@ -3006,7 +3006,7 @@ wrap-ansi@^7.0.0: wrap-ansi@^8.1.0: version "8.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz" integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== dependencies: ansi-styles "^6.1.0" @@ -3015,7 +3015,7 @@ wrap-ansi@^8.1.0: write-file-atomic@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-5.0.1.tgz#68df4717c55c6fa4281a7860b4c2ba0a6d2b11e7" + resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz" integrity sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw== dependencies: imurmurhash "^0.1.4" @@ -3023,7 +3023,7 @@ write-file-atomic@^5.0.1: write-json-file@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/write-json-file/-/write-json-file-6.0.0.tgz#52f5d8178c5beb543ed14a2a24195b696b27e7cb" + resolved "https://registry.npmjs.org/write-json-file/-/write-json-file-6.0.0.tgz" integrity sha512-MNHcU3f9WxnNyR6MxsYSj64Jz0+dwIpisWKWq9gqLj/GwmA9INg3BZ3vt70/HB3GEwrnDQWr4RPrywnhNzmUFA== dependencies: detect-indent "^7.0.1" @@ -3033,7 +3033,7 @@ write-json-file@^6.0.0: write-package@^7.2.0: version "7.2.0" - resolved "https://registry.yarnpkg.com/write-package/-/write-package-7.2.0.tgz#d84e5a0dfe92cb7d17399adc8c083d38671cd871" + resolved "https://registry.npmjs.org/write-package/-/write-package-7.2.0.tgz" integrity sha512-uMQTubF/vcu+Wd0b5BGtDmiXePd/+44hUWQz2nZPbs92/BnxRo74tqs+hqDo12RLiEd+CXFKUwxvvIZvtt34Jw== dependencies: deepmerge-ts "^7.1.0" @@ -3044,32 +3044,32 @@ write-package@^7.2.0: ws@8.17.1: version "8.17.1" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b" + resolved "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz" integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== ws@8.18.0: version "8.18.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" + resolved "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz" integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== ws@8.18.3, ws@^8.18.0, ws@^8.18.2, ws@^8.18.3, ws@^8.8.1: version "8.18.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.3.tgz#b56b88abffde62791c639170400c93dcb0c95472" + resolved "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz" integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== y18n@^5.0.5: version "5.0.8" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== yargs-parser@^21.1.1: version "21.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== yargs-unparser@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" + resolved "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz" integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== dependencies: camelcase "^6.0.0" @@ -3079,7 +3079,7 @@ yargs-unparser@^2.0.0: yargs@^17.7.2: version "17.7.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + resolved "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== dependencies: cliui "^8.0.1" @@ -3092,15 +3092,15 @@ yargs@^17.7.2: yn@3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== yocto-queue@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== yoctocolors@^2.1.1: version "2.1.2" - resolved "https://registry.yarnpkg.com/yoctocolors/-/yoctocolors-2.1.2.tgz#d795f54d173494e7d8db93150cec0ed7f678c83a" + resolved "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz" integrity sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug== From 6723152ef3212b56b15b31e32f936c8068be8977 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 12 Jan 2026 08:29:23 -0500 Subject: [PATCH 125/240] Add clippy ignore for SafeInt division --- pallets/swap/src/pallet/balancer.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pallets/swap/src/pallet/balancer.rs b/pallets/swap/src/pallet/balancer.rs index 39648ebbf0..ddd0c4c406 100644 --- a/pallets/swap/src/pallet/balancer.rs +++ b/pallets/swap/src/pallet/balancer.rs @@ -364,7 +364,10 @@ impl Balancer { .unwrap_or(SafeInt::from(0)); // 0.5 scaled for rounding to the nearest integer + // Allow arithmetic side effects here: SafeInt doesn't panic + #[allow(clippy::arithmetic_side_effects)] let round_nearest_offset = (scale.clone() / SafeInt::from(2)).unwrap_or_default(); + #[allow(clippy::arithmetic_side_effects)] ((((exp_x * exp_y) / scale.clone()).unwrap_or_default() + round_nearest_offset) / scale) .unwrap_or_default() .to_u64() From 8d4880667b2589409993e79af72fb7b5c665de34 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 12 Jan 2026 10:51:49 -0500 Subject: [PATCH 126/240] Add tests for palswap initialization with price in migration --- pallets/subtensor/src/tests/coinbase.rs | 18 ++--- pallets/subtensor/src/tests/staking.rs | 6 +- pallets/swap-interface/src/lib.rs | 2 +- pallets/swap/src/benchmarking.rs | 2 +- pallets/swap/src/pallet/impls.rs | 47 +++++++++--- .../migrations/migrate_swapv3_to_balancer.rs | 11 ++- pallets/swap/src/pallet/tests.rs | 71 ++++++++++++++----- 7 files changed, 112 insertions(+), 45 deletions(-) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index b6648bf7b3..68b2ff0725 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -222,8 +222,8 @@ fn test_coinbase_tao_issuance_different_prices() { mock::setup_reserves(netuid2, initial_tao.into(), initial_alpha2.into()); // Force the swap to initialize - ::SwapInterface::init_swap(netuid1); - ::SwapInterface::init_swap(netuid2); + ::SwapInterface::init_swap(netuid1, None); + ::SwapInterface::init_swap(netuid2, None); // Make subnets dynamic. SubnetMechanism::::insert(netuid1, 1); @@ -592,8 +592,8 @@ fn test_coinbase_alpha_issuance_with_cap_trigger_and_block_emission() { SubnetTaoFlow::::insert(netuid2, 200_000_000_i64); // Force the swap to initialize - ::SwapInterface::init_swap(netuid1); - ::SwapInterface::init_swap(netuid2); + ::SwapInterface::init_swap(netuid1, None); + ::SwapInterface::init_swap(netuid2, None); // Get the prices before the run_coinbase let price_1_before = ::SwapInterface::current_alpha_price(netuid1); @@ -3349,7 +3349,7 @@ fn test_coinbase_subnet_terms_with_alpha_in_gt_alpha_emission() { AlphaCurrency::from(1_000_000_000_000_000), ); // Initialize swap v3 - Swap::maybe_initialize_palswap(netuid0); + Swap::maybe_initialize_palswap(netuid0, None); // Set netuid0 to have price tao_emission / price > alpha_emission let alpha_emission = U96F32::saturating_from_num( @@ -3430,7 +3430,7 @@ fn test_coinbase_subnet_terms_with_alpha_in_lte_alpha_emission() { AlphaCurrency::from(1_000_000_000_000_000), ); // Initialize swap v3 - Swap::maybe_initialize_palswap(netuid0); + Swap::maybe_initialize_palswap(netuid0, None); let alpha_emission = U96F32::saturating_from_num( SubtensorModule::get_block_emission_for_issuance( @@ -3493,7 +3493,7 @@ fn test_coinbase_inject_and_maybe_swap_does_not_skew_reserves() { AlphaCurrency::from(1_000_000_000_000_000), ); // Initialize swap v3 - Swap::maybe_initialize_palswap(netuid0); + Swap::maybe_initialize_palswap(netuid0, None); let tao_in = BTreeMap::from([(netuid0, U96F32::saturating_from_num(123))]); let alpha_in = BTreeMap::from([(netuid0, U96F32::saturating_from_num(456))]); @@ -3627,7 +3627,7 @@ fn test_coinbase_emit_to_subnets_with_no_root_sell() { AlphaCurrency::from(1_000_000_000_000_000), ); // Initialize swap v3 - Swap::maybe_initialize_palswap(netuid0); + Swap::maybe_initialize_palswap(netuid0, None); let tao_emission = U96F32::saturating_from_num(12345678); let subnet_emissions = BTreeMap::from([(netuid0, tao_emission)]); @@ -3718,7 +3718,7 @@ fn test_coinbase_emit_to_subnets_with_root_sell() { AlphaCurrency::from(1_000_000_000_000_000), ); // Initialize swap v3 - Swap::maybe_initialize_palswap(netuid0); + Swap::maybe_initialize_palswap(netuid0, None); let tao_emission = U96F32::saturating_from_num(12345678); let subnet_emissions = BTreeMap::from([(netuid0, tao_emission)]); diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index e61df01106..4437eaf699 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -573,7 +573,7 @@ fn test_add_stake_partial_below_min_stake_fails() { mock::setup_reserves(netuid, (amount * 10).into(), (amount * 10).into()); // Force the swap to initialize - ::SwapInterface::init_swap(netuid); + ::SwapInterface::init_swap(netuid, None); // Get the current price (should be 1.0) let current_price = @@ -2884,7 +2884,7 @@ fn test_max_amount_add_dynamic() { SubnetAlphaIn::::insert(netuid, alpha_in); // Force the swap to initialize - ::SwapInterface::init_swap(netuid); + ::SwapInterface::init_swap(netuid, None); if !alpha_in.is_zero() { let expected_price = U96F32::from_num(tao_in) / U96F32::from_num(alpha_in); @@ -5370,7 +5370,7 @@ fn test_large_swap() { SubnetAlphaIn::::insert(netuid, alpha); // Force the swap to initialize - ::SwapInterface::init_swap(netuid); + ::SwapInterface::init_swap(netuid, None); // TODO: Revise when user liquidity is available // setup_positions(netuid.into()); diff --git a/pallets/swap-interface/src/lib.rs b/pallets/swap-interface/src/lib.rs index 992192127f..0a86c960cf 100644 --- a/pallets/swap-interface/src/lib.rs +++ b/pallets/swap-interface/src/lib.rs @@ -51,7 +51,7 @@ pub trait SwapHandler { fn dissolve_all_liquidity_providers(netuid: NetUid) -> DispatchResult; fn toggle_user_liquidity(netuid: NetUid, enabled: bool); fn clear_protocol_liquidity(netuid: NetUid) -> DispatchResult; - fn init_swap(netuid: NetUid); + fn init_swap(netuid: NetUid, maybe_price: Option); } pub trait DefaultPriceLimit diff --git a/pallets/swap/src/benchmarking.rs b/pallets/swap/src/benchmarking.rs index 2082af4f19..4ad898a8f3 100644 --- a/pallets/swap/src/benchmarking.rs +++ b/pallets/swap/src/benchmarking.rs @@ -10,7 +10,7 @@ use crate::pallet::{Call, Config, Pallet}; #[allow(dead_code)] fn init_swap(netuid: NetUid) { - let _ = Pallet::::maybe_initialize_palswap(netuid); + let _ = Pallet::::maybe_initialize_palswap(netuid, None); } #[benchmarks(where T: Config)] diff --git a/pallets/swap/src/pallet/impls.rs b/pallets/swap/src/pallet/impls.rs index e2176babbd..1f900a47e9 100644 --- a/pallets/swap/src/pallet/impls.rs +++ b/pallets/swap/src/pallet/impls.rs @@ -60,21 +60,46 @@ impl Pallet { } // initializes pal-swap (balancer) for a subnet if needed - pub fn maybe_initialize_palswap(netuid: NetUid) -> Result<(), Error> { + pub fn maybe_initialize_palswap( + netuid: NetUid, + maybe_price: Option, + ) -> Result<(), Error> { if PalSwapInitialized::::get(netuid) { return Ok(()); } - // Insert 0.5 into SwapBalancer - let balancer = - Balancer::new(Perquintill::from_rational(1_u64, 2_u64)).map_err(|err| match err { - BalancerError::InvalidValue => Error::::ReservesOutOfBalance, - })?; + // Query reserves + let tao_reserve = T::TaoReserve::reserve(netuid.into()); + let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); + + // Create balancer based on price + let balancer = Balancer::new(if let Some(price) = maybe_price { + // Price is given, calculate weights: + // w_quote = y / (px + y) + let px_high = (price.saturating_to_num::() as u128) + .saturating_mul(u64::from(alpha_reserve) as u128); + let px_low = U64F64::saturating_from_num(alpha_reserve) + .saturating_mul(price.frac()) + .saturating_to_num::(); + let px_plus_y = px_high + .saturating_add(px_low) + .saturating_add(u64::from(tao_reserve) as u128); + + // If price is given and both reserves are zero, the swap doesn't initialize + if px_plus_y == 0u128 { + return Err(Error::::ReservesOutOfBalance); + } + Perquintill::from_rational(u64::from(tao_reserve) as u128, px_plus_y) + } else { + // No price = insert 0.5 into SwapBalancer + Perquintill::from_rational(1_u64, 2_u64) + }) + .map_err(|err| match err { + BalancerError::InvalidValue => Error::::ReservesOutOfBalance, + })?; SwapBalancer::::insert(netuid, balancer.clone()); // Insert current liquidity - let tao_reserve = T::TaoReserve::reserve(netuid.into()); - let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); let liquidity = balancer.calculate_current_liquidity(u64::from(tao_reserve), u64::from(alpha_reserve)); CurrentLiquidity::::insert(netuid, liquidity); @@ -230,7 +255,7 @@ impl Pallet { Error::::ReservesTooLow ); - Self::maybe_initialize_palswap(netuid)?; + Self::maybe_initialize_palswap(netuid, None)?; // Because user specifies the limit price, check that it is in fact beoynd the current one ensure!( @@ -715,7 +740,7 @@ impl SwapHandler for Pallet { fn clear_protocol_liquidity(netuid: NetUid) -> DispatchResult { Self::do_clear_protocol_liquidity(netuid) } - fn init_swap(netuid: NetUid) { - Self::maybe_initialize_palswap(netuid).unwrap_or_default(); + fn init_swap(netuid: NetUid, maybe_price: Option) { + Self::maybe_initialize_palswap(netuid, maybe_price).unwrap_or_default(); } } diff --git a/pallets/swap/src/pallet/migrations/migrate_swapv3_to_balancer.rs b/pallets/swap/src/pallet/migrations/migrate_swapv3_to_balancer.rs index 5a339ed2c6..ba5b7a580d 100644 --- a/pallets/swap/src/pallet/migrations/migrate_swapv3_to_balancer.rs +++ b/pallets/swap/src/pallet/migrations/migrate_swapv3_to_balancer.rs @@ -40,7 +40,14 @@ pub fn migrate_swapv3_to_balancer() -> Weight { ); // ------------------------------ - // Step 1: Clear Map entries + // Step 1: Initialize swaps with price before price removal + // ------------------------------ + for (netuid, price) in deprecated_swap_maps::AlphaSqrtPrice::::iter() { + crate::Pallet::::maybe_initialize_palswap(netuid, Some(price)).unwrap_or_default(); + } + + // ------------------------------ + // Step 2: Clear Map entries // ------------------------------ remove_prefix::("Swap", "AlphaSqrtPrice", &mut weight); remove_prefix::("Swap", "CurrentTick", &mut weight); @@ -56,7 +63,7 @@ pub fn migrate_swapv3_to_balancer() -> Weight { remove_prefix::("Swap", "Positions", &mut weight); // ------------------------------ - // Step 2: Mark Migration as Completed + // Step 3: Mark Migration as Completed // ------------------------------ HasMigrationRun::::insert(&migration_name, true); diff --git a/pallets/swap/src/pallet/tests.rs b/pallets/swap/src/pallet/tests.rs index 2c31915da0..f05cf2ceab 100644 --- a/pallets/swap/src/pallet/tests.rs +++ b/pallets/swap/src/pallet/tests.rs @@ -447,13 +447,13 @@ fn test_swap_initialization() { new_test_ext().execute_with(|| { let netuid = NetUid::from(1); - // Get reserves from the mock provider + // Setup reserves let tao = TaoCurrency::from(1_000_000_000u64); let alpha = AlphaCurrency::from(4_000_000_000u64); TaoReserve::set_mock_reserve(netuid, tao); AlphaReserve::set_mock_reserve(netuid, alpha); - assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + assert_ok!(Pallet::::maybe_initialize_palswap(netuid, None)); assert!(PalSwapInitialized::::get(netuid)); // Verify current price is set @@ -503,6 +503,35 @@ fn test_swap_initialization() { }); } +#[test] +fn test_swap_initialization_with_price() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1); + + // Setup reserves, tao / alpha = 0.25 + let tao = TaoCurrency::from(1_000_000_000u64); + let alpha = AlphaCurrency::from(4_000_000_000u64); + TaoReserve::set_mock_reserve(netuid, tao); + AlphaReserve::set_mock_reserve(netuid, alpha); + + // Initialize with 0.2 price + assert_ok!(Pallet::::maybe_initialize_palswap( + netuid, + Some(U64F64::from(1u16) / U64F64::from(5u16)) + )); + assert!(PalSwapInitialized::::get(netuid)); + + // Verify current price is set to 0.2 + let price = Pallet::::current_price(netuid); + let expected_price = U64F64::from_num(0.2_f64); + assert_abs_diff_eq!( + price.to_num::(), + expected_price.to_num::(), + epsilon = 0.000000001 + ); + }); +} + // TODO: Revise when user liquidity is available // Test adding liquidity on top of the existing protocol liquidity // #[test] @@ -1066,7 +1095,7 @@ fn test_swap_basic() { let initial_alpha_reserve = AlphaCurrency::from(4_000_000_000_u64); TaoReserve::set_mock_reserve(netuid, initial_tao_reserve); AlphaReserve::set_mock_reserve(netuid, initial_alpha_reserve); - assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + assert_ok!(Pallet::::maybe_initialize_palswap(netuid, None)); // Get current price let current_price_before = Pallet::::current_price(netuid); @@ -1300,7 +1329,7 @@ fn test_convert_deltas() { let netuid = NetUid::from(1); TaoReserve::set_mock_reserve(netuid, TaoCurrency::from(tao)); AlphaReserve::set_mock_reserve(netuid, AlphaCurrency::from(alpha)); - assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + assert_ok!(Pallet::::maybe_initialize_palswap(netuid, None)); let w_accuracy = 1_000_000_000_f64; let w_quote_pt = @@ -1786,7 +1815,7 @@ fn test_swap_subtoken_disabled() { let netuid = NetUid::from(SUBTOKEN_DISABLED_NETUID); // Use a netuid not used elsewhere let liquidity = 1_000_000_u64; - assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + assert_ok!(Pallet::::maybe_initialize_palswap(netuid, None)); assert_noop!( Pallet::::add_liquidity( @@ -2594,7 +2623,7 @@ fn test_clear_protocol_liquidity_green_path() { EnabledUserLiquidity::::insert(netuid, true); // Initialize swap state - assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); + assert_ok!(Pallet::::maybe_initialize_palswap(netuid, None)); assert!( PalSwapInitialized::::get(netuid), "Swap must be initialized" @@ -2668,28 +2697,34 @@ fn test_migrate_swapv3_to_balancer() { new_test_ext().execute_with(|| { let migration = crate::migrations::migrate_swapv3_to_balancer::migrate_swapv3_to_balancer::; + let netuid = NetUid::from(1); // Insert deprecated maps values - deprecated_swap_maps::AlphaSqrtPrice::::insert( - NetUid::from(1), - U64F64::from_num(1.23), - ); - deprecated_swap_maps::ScrapReservoirTao::::insert( - NetUid::from(1), - TaoCurrency::from(9876), - ); + deprecated_swap_maps::AlphaSqrtPrice::::insert(netuid, U64F64::from_num(1.23)); + deprecated_swap_maps::ScrapReservoirTao::::insert(netuid, TaoCurrency::from(9876)); deprecated_swap_maps::ScrapReservoirAlpha::::insert( - NetUid::from(1), + netuid, AlphaCurrency::from(9876), ); + // Insert reserves that do not match the 1.23 price + TaoReserve::set_mock_reserve(netuid, TaoCurrency::from(1_000_000_000)); + AlphaReserve::set_mock_reserve(netuid, AlphaCurrency::from(4_000_000_000)); + // Run migration migration(); // Test that values are removed from state assert!(!deprecated_swap_maps::AlphaSqrtPrice::::contains_key( - NetUid::from(1) - ),); - assert!(!deprecated_swap_maps::ScrapReservoirAlpha::::contains_key(NetUid::from(1)),); + netuid + )); + assert!(!deprecated_swap_maps::ScrapReservoirAlpha::::contains_key(netuid)); + + // Test that subnet price is still 1.23 + assert_abs_diff_eq!( + Swap::current_price(netuid).to_num::(), + 1.23, + epsilon = 0.1 + ); }); } From 55f9bbabbeacc06eb6db36f464e7ca01810e0fbb Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 12 Jan 2026 11:59:48 -0500 Subject: [PATCH 127/240] Fix price initialization --- .../swap/src/pallet/migrations/migrate_swapv3_to_balancer.rs | 3 ++- pallets/swap/src/pallet/tests.rs | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pallets/swap/src/pallet/migrations/migrate_swapv3_to_balancer.rs b/pallets/swap/src/pallet/migrations/migrate_swapv3_to_balancer.rs index ba5b7a580d..a504c6197c 100644 --- a/pallets/swap/src/pallet/migrations/migrate_swapv3_to_balancer.rs +++ b/pallets/swap/src/pallet/migrations/migrate_swapv3_to_balancer.rs @@ -42,7 +42,8 @@ pub fn migrate_swapv3_to_balancer() -> Weight { // ------------------------------ // Step 1: Initialize swaps with price before price removal // ------------------------------ - for (netuid, price) in deprecated_swap_maps::AlphaSqrtPrice::::iter() { + for (netuid, price_sqrt) in deprecated_swap_maps::AlphaSqrtPrice::::iter() { + let price = price_sqrt.saturating_mul(price_sqrt); crate::Pallet::::maybe_initialize_palswap(netuid, Some(price)).unwrap_or_default(); } diff --git a/pallets/swap/src/pallet/tests.rs b/pallets/swap/src/pallet/tests.rs index f05cf2ceab..3d73f7ed9e 100644 --- a/pallets/swap/src/pallet/tests.rs +++ b/pallets/swap/src/pallet/tests.rs @@ -2720,10 +2720,10 @@ fn test_migrate_swapv3_to_balancer() { )); assert!(!deprecated_swap_maps::ScrapReservoirAlpha::::contains_key(netuid)); - // Test that subnet price is still 1.23 + // Test that subnet price is still 1.23^2 assert_abs_diff_eq!( Swap::current_price(netuid).to_num::(), - 1.23, + 1.23 * 1.23, epsilon = 0.1 ); }); From 485949d80c8474e1dea95d65b35c4ff7b6ba0e56 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 12 Jan 2026 12:49:11 -0500 Subject: [PATCH 128/240] Fix remove_stake_full_limit benchmark --- pallets/subtensor/src/benchmarks.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index d289978020..430dbe237c 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -1350,7 +1350,7 @@ mod pallet_benchmarks { // by swapping 1 TAO let current_price = T::SwapInterface::current_alpha_price(netuid); let limit = current_price - .saturating_mul(U64F64::saturating_from_num(1_001_000_000)) + .saturating_mul(U64F64::saturating_from_num(1_500_000_000)) .saturating_to_num::(); let u64_staked_amt = 1_000_000_000; Subtensor::::add_balance_to_coldkey_account(&coldkey.clone(), u64_staked_amt); From b4ecbc3ba6db52bb42fa896dc6faac14a0ced2cd Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 12 Jan 2026 13:55:37 -0500 Subject: [PATCH 129/240] Fix remove_stake_full_limit benchmark --- pallets/subtensor/src/benchmarks.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index 23629abc46..fe17779dc8 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -1346,11 +1346,11 @@ mod pallet_benchmarks { hotkey.clone() )); - // Read current price and set limit price 50% higher, which is not getting hit + // Read current price and set limit price 50% lower, which is not getting hit // by swapping 1 TAO let current_price = T::SwapInterface::current_alpha_price(netuid); let limit = current_price - .saturating_mul(U64F64::saturating_from_num(1_500_000_000)) + .saturating_mul(U64F64::saturating_from_num(500_000_000)) .saturating_to_num::(); let u64_staked_amt = 1_000_000_000; Subtensor::::add_balance_to_coldkey_account(&coldkey.clone(), u64_staked_amt); From 860f9ebdb64bbdc01e91f157f9864fc8d7a7c2a6 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Thu, 15 Jan 2026 08:10:00 -0500 Subject: [PATCH 130/240] Remove unused function get_dynamic_tao_emission and the related test --- .../subtensor/src/coinbase/block_emission.rs | 76 ------------------- pallets/subtensor/src/tests/coinbase.rs | 30 -------- 2 files changed, 106 deletions(-) diff --git a/pallets/subtensor/src/coinbase/block_emission.rs b/pallets/subtensor/src/coinbase/block_emission.rs index 064bab4d2a..cee677a013 100644 --- a/pallets/subtensor/src/coinbase/block_emission.rs +++ b/pallets/subtensor/src/coinbase/block_emission.rs @@ -9,82 +9,6 @@ use subtensor_runtime_common::{NetUid, TaoCurrency}; use subtensor_swap_interface::SwapHandler; impl Pallet { - /// Calculates the dynamic TAO emission for a given subnet. - /// - /// This function determines the three terms tao_in, alpha_in, alpha_out - /// which are consecutively, 1) the amount of tao injected into the pool - /// 2) the amount of alpha injected into the pool, and 3) the amount of alpha - /// left to be distributed towards miners/validators/owners per block. - /// - /// # Arguments - /// * `netuid` - The unique identifier of the subnet. - /// * `tao_emission` - The amount of tao to distribute for this subnet. - /// * `alpha_block_emission` - The maximum alpha emission allowed for the block. - /// - /// # Returns - /// * `(u64, u64, u64)` - A tuple containing: - /// - `tao_in_emission`: The adjusted TAO emission always lower or equal to tao_emission - /// - `alpha_in_emission`: The adjusted alpha emission amount to be added into the pool. - /// - `alpha_out_emission`: The remaining alpha emission after adjustments to be distributed to miners/validators. - /// - /// The algorithm ensures that the pool injection of tao_in_emission, alpha_in_emission does not effect the pool price - /// It also ensures that the total amount of alpha_in_emission + alpha_out_emission sum to 2 * alpha_block_emission - /// It also ensures that 1 < alpha_out_emission < 2 * alpha_block_emission and 0 < alpha_in_emission < alpha_block_emission. - pub fn get_dynamic_tao_emission( - netuid: NetUid, - tao_emission: u64, - alpha_block_emission: u64, - ) -> (u64, u64, u64) { - // Init terms. - let mut tao_in_emission: U96F32 = U96F32::saturating_from_num(tao_emission); - let float_alpha_block_emission: U96F32 = U96F32::saturating_from_num(alpha_block_emission); - - // Get alpha price for subnet. - let alpha_price = T::SwapInterface::current_alpha_price(netuid.into()); - log::debug!("{netuid:?} - alpha_price: {alpha_price:?}"); - - // Get initial alpha_in - let mut alpha_in_emission: U96F32 = U96F32::saturating_from_num(tao_emission) - .checked_div(alpha_price) - .unwrap_or(float_alpha_block_emission); - - // Check if we are emitting too much alpha_in - if alpha_in_emission >= float_alpha_block_emission { - log::debug!( - "{netuid:?} - alpha_in_emission: {alpha_in_emission:?} > alpha_block_emission: {float_alpha_block_emission:?}" - ); - - // Scale down tao_in - // tao_in_emission = alpha_price.saturating_mul(float_alpha_block_emission); - - // Set to max alpha_block_emission - alpha_in_emission = float_alpha_block_emission; - } - - // Avoid rounding errors. - if tao_in_emission < U96F32::saturating_from_num(1) - || alpha_in_emission < U96F32::saturating_from_num(1) - { - alpha_in_emission = U96F32::saturating_from_num(0); - tao_in_emission = U96F32::saturating_from_num(0); - } - - // Set Alpha in emission. - let alpha_out_emission = float_alpha_block_emission; - - // Log results. - log::debug!("{netuid:?} - tao_in_emission: {tao_in_emission:?}"); - log::debug!("{netuid:?} - alpha_in_emission: {alpha_in_emission:?}"); - log::debug!("{netuid:?} - alpha_out_emission: {alpha_out_emission:?}"); - - // Return result. - ( - tao_in_emission.saturating_to_num::(), - alpha_in_emission.saturating_to_num::(), - alpha_out_emission.saturating_to_num::(), - ) - } - /// Calculates the block emission based on the total issuance. /// /// This function computes the block emission by applying a logarithmic function diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index a79f4b713a..093444e955 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -46,36 +46,6 @@ fn test_hotkey_take() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_dynamic_function_various_values --exact --show-output --nocapture -#[test] -fn test_dynamic_function_various_values() { - new_test_ext(1).execute_with(|| { - let price_values: [f64; 9] = [0.001, 0.1, 0.5, 1.0, 2.0, 10.0, 100.0, 200.0, 1000.0]; - let tao_in_values: [u64; 9] = [0, 1, 10, 100, 1_000, 1_000_000, 1_000_000_000, 1_000_000_000_000, 1_000_000_000_000_000 ]; - let alpha_emission_values: [u64; 9] = [0, 1, 10, 100, 1_000, 1_000_000, 1_000_000_000, 1_000_000_000_000, 1_000_000_000_000_000 ]; - - for &price in price_values.iter() { - for &tao_in in tao_in_values.iter() { - for &alpha_emission in alpha_emission_values.iter() { - // Set the price. - SubnetMechanism::::insert(NetUid::from(1), 1); - SubnetTAO::::insert(NetUid::from(1), TaoCurrency::from((price * 1_000_000_000.0) as u64)); - SubnetAlphaIn::::insert(NetUid::from(1), AlphaCurrency::from(1_000_000_000)); - let (tao_in_emission, alpha_in_emission, alpha_out_emission) = SubtensorModule::get_dynamic_tao_emission(1.into(), tao_in, alpha_emission); - assert!(tao_in_emission <= tao_in, "tao_in_emission is greater than tao_in"); - assert!(alpha_in_emission <= alpha_emission, "alpha_in_emission is greater than alpha_emission"); - assert!(alpha_out_emission <= 2 * alpha_emission, "alpha_out_emission is greater than 2 * alpha_emission"); - assert!((alpha_in_emission + alpha_out_emission) <= 2 * alpha_emission, "Sum of alpha_in_emission and alpha_out_emission is less than or equal to. 2 * alpha_emission"); - close( alpha_in_emission + alpha_out_emission, alpha_in_emission + alpha_emission, 10 ); - // if alpha_in_emission > 0 || tao_in_emission > 0 { - // assert!((tao_in_emission as f64 / alpha_in_emission as f64 - price).abs() < 1e-1, "Ratio of tao_in_emission to alpha_in_emission is not equal to price"); - // } - } - } - } - }); -} - // Test the base case of running coinbase with zero emission. // This test verifies that the coinbase mechanism can handle the edge case // of zero emission without errors or unexpected behavior. From dc47defe409a2bdfc2860a0f1a8fd5b539561896 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Thu, 15 Jan 2026 08:13:40 -0500 Subject: [PATCH 131/240] Cleanup unused imports --- pallets/subtensor/src/coinbase/block_emission.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/pallets/subtensor/src/coinbase/block_emission.rs b/pallets/subtensor/src/coinbase/block_emission.rs index cee677a013..3aefef927f 100644 --- a/pallets/subtensor/src/coinbase/block_emission.rs +++ b/pallets/subtensor/src/coinbase/block_emission.rs @@ -1,12 +1,8 @@ use super::*; use frame_support::traits::Get; use safe_math::*; -use substrate_fixed::{ - transcendental::log2, - types::{I96F32, U96F32}, -}; -use subtensor_runtime_common::{NetUid, TaoCurrency}; -use subtensor_swap_interface::SwapHandler; +use substrate_fixed::{transcendental::log2, types::I96F32}; +use subtensor_runtime_common::TaoCurrency; impl Pallet { /// Calculates the block emission based on the total issuance. From 023e0a8ba4ce924607c21a89957403358bfe9b63 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Thu, 15 Jan 2026 10:24:46 -0300 Subject: [PATCH 132/240] add migration to hook --- pallets/subtensor/src/macros/hooks.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pallets/subtensor/src/macros/hooks.rs b/pallets/subtensor/src/macros/hooks.rs index ed57d52c8b..899e8d32f2 100644 --- a/pallets/subtensor/src/macros/hooks.rs +++ b/pallets/subtensor/src/macros/hooks.rs @@ -164,7 +164,9 @@ mod hooks { // Remove unknown neuron axon, certificate prom .saturating_add(migrations::migrate_remove_unknown_neuron_axon_cert_prom::migrate_remove_unknown_neuron_axon_cert_prom::()) // Fix staking hot keys - .saturating_add(migrations::migrate_fix_staking_hot_keys::migrate_fix_staking_hot_keys::()); + .saturating_add(migrations::migrate_fix_staking_hot_keys::migrate_fix_staking_hot_keys::()) + // Migrate coldkey swap scheduled to announcements + .saturating_add(migrations::migrate_coldkey_swap_scheduled_to_announcements::migrate_coldkey_swap_scheduled_to_announcements::()); weight } From a3756d9b601b5a6522be330160c2bda62744d6d1 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Thu, 15 Jan 2026 09:10:31 -0500 Subject: [PATCH 133/240] Add R&D tests for exp_scaled --- pallets/swap/src/pallet/balancer.rs | 46 +++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/pallets/swap/src/pallet/balancer.rs b/pallets/swap/src/pallet/balancer.rs index ddd0c4c406..b2f15abfe8 100644 --- a/pallets/swap/src/pallet/balancer.rs +++ b/pallets/swap/src/pallet/balancer.rs @@ -1087,4 +1087,50 @@ mod tests { }); }); } + + // cargo test --package pallet-subtensor-swap --lib -- pallet::balancer::tests::test_exp_scaled --exact --nocapture + #[test] + fn test_exp_scaled() { + [ + // base_weight_numerator, base_weight_denominator, reserve, d_reserve, base_quote + (5_u64, 10_u64, 100000_u64, 100_u64, true, 0.999000999000999), + (1_u64, 4_u64, 500000_u64, 5000_u64, true, 0.970590147927644), + (3_u64, 4_u64, 200000_u64, 2000_u64, false, 0.970590147927644), + ( + 9_u64, + 10_u64, + 13513642_u64, + 1673_u64, + false, + 0.998886481979889, + ), + ( + 773_u64, + 1000_u64, + 7_000_000_000_u64, + 10_000_u64, + true, + 0.999999580484586, + ), + ] + .into_iter() + .map(|v| { + ( + Perquintill::from_rational(v.0, v.1), + v.2, + v.3, + v.4, + U64F64::from_num(v.5), + ) + }) + .for_each(|(quote_weight, reserve, d_reserve, base_quote, expected)| { + let balancer = Balancer::new(quote_weight).unwrap(); + let result = balancer.exp_scaled(reserve, d_reserve, base_quote); + assert_abs_diff_eq!( + result.to_num::(), + expected.to_num::(), + epsilon = 0.000000001 + ); + }); + } } From 06c2f71ed24e7f4cc3f1e8244edcb1bb421f9289 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Fri, 16 Jan 2026 09:43:28 -0500 Subject: [PATCH 134/240] Fix typo --- pallets/subtensor/src/coinbase/run_coinbase.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 19a074fa47..f40d4e704b 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -67,7 +67,7 @@ impl Pallet { let tao_to_swap_with: TaoCurrency = tou64!(excess_tao.get(netuid_i).unwrap_or(&asfloat!(0))).into(); - let (actal_injected_tao, actal_injected_alpha) = + let (actual_injected_tao, actual_injected_alpha) = T::SwapInterface::adjust_protocol_liquidity(*netuid_i, tao_in_i, alpha_in_i); if tao_to_swap_with > TaoCurrency::ZERO { @@ -89,7 +89,7 @@ impl Pallet { SubnetAlphaInEmission::::insert(*netuid_i, alpha_in_i); SubnetAlphaIn::::mutate(*netuid_i, |total| { // Reserves also received fees in addition to alpha_in_i - *total = total.saturating_add(actal_injected_alpha); + *total = total.saturating_add(actual_injected_alpha); }); // Inject TAO in. @@ -98,7 +98,7 @@ impl Pallet { SubnetTaoInEmission::::insert(*netuid_i, injected_tao); SubnetTAO::::mutate(*netuid_i, |total| { // Reserves also received fees in addition to injected_tao - *total = total.saturating_add(actal_injected_tao); + *total = total.saturating_add(actual_injected_tao); }); TotalStake::::mutate(|total| { *total = total.saturating_add(injected_tao); From c6b55edb09b939e76740d27e3259e2fa8700af00 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Mon, 19 Jan 2026 11:27:30 -0300 Subject: [PATCH 135/240] check migration ran --- pallets/subtensor/src/tests/migration.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index 0d0dbdfc80..beec7a3cba 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -2971,6 +2971,7 @@ fn test_migrate_remove_unknown_neuron_axon_cert_prom() { #[test] fn test_migrate_coldkey_swap_scheduled_to_announcements() { new_test_ext(1000).execute_with(|| { + const MIGRATION_NAME: &[u8] = b"migrate_coldkey_swap_scheduled_to_announcements"; use crate::migrations::migrate_coldkey_swap_scheduled_to_announcements::*; let now = frame_system::Pallet::::block_number(); @@ -3003,6 +3004,7 @@ fn test_migrate_coldkey_swap_scheduled_to_announcements() { let w = migrate_coldkey_swap_scheduled_to_announcements::(); assert!(!w.is_zero(), "weight must be non-zero"); + assert!(HasMigrationRun::::get(MIGRATION_NAME)); // Ensure the deprecated storage is cleared assert!(!deprecated::ColdkeySwapScheduleDuration::::exists()); From aec9bdd41234c3fc1524feceeae4a5ae5b3b10df Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Mon, 19 Jan 2026 11:27:45 -0300 Subject: [PATCH 136/240] ensure swap cost is charged correctly + fix tests --- pallets/subtensor/src/swap/swap_coldkey.rs | 11 +-- pallets/subtensor/src/tests/swap_coldkey.rs | 85 +++++++++++++++------ 2 files changed, 69 insertions(+), 27 deletions(-) diff --git a/pallets/subtensor/src/swap/swap_coldkey.rs b/pallets/subtensor/src/swap/swap_coldkey.rs index c7119fc066..54b07d9dbf 100644 --- a/pallets/subtensor/src/swap/swap_coldkey.rs +++ b/pallets/subtensor/src/swap/swap_coldkey.rs @@ -50,12 +50,13 @@ 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: TaoCurrency) -> DispatchResult { - ensure!( - Self::can_remove_balance_from_coldkey_account(coldkey, swap_cost.into()), - Error::::NotEnoughBalanceToPaySwapColdKey - ); + let burn_amount = Self::remove_balance_from_coldkey_account(coldkey, swap_cost.into()) + .map_err(|_| Error::::NotEnoughBalanceToPaySwapColdKey)?; + + if burn_amount < swap_cost { + return Err(Error::::NotEnoughBalanceToPaySwapColdKey.into()); + } - let burn_amount = Self::remove_balance_from_coldkey_account(coldkey, swap_cost.into())?; Self::recycle_tao(burn_amount); Ok(()) diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index 5437b9cc85..f99f4ecb64 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -185,8 +185,9 @@ fn test_announce_coldkey_swap_with_existing_announcement_not_past_delay_fails() assert_eq!(ColdkeySwapAnnouncements::::iter().count(), 0); - let swap_cost = SubtensorModule::get_key_swap_cost(); - SubtensorModule::add_balance_to_coldkey_account(&who, swap_cost.into()); + let swap_cost = SubtensorModule::get_key_swap_cost().to_u64(); + let ed = ExistentialDeposit::get(); + SubtensorModule::add_balance_to_coldkey_account(&who, swap_cost + ed); assert_ok!(SubtensorModule::announce_coldkey_swap( RuntimeOrigin::signed(who), @@ -229,6 +230,8 @@ fn test_swap_coldkey_announced_works() { let delay = ColdkeySwapAnnouncementDelay::::get() + 1; run_to_block(now + delay); + SubtensorModule::add_balance_to_coldkey_account(&who, left_over + stake1 + stake2 + stake3); + let ( netuid1, netuid2, @@ -244,7 +247,6 @@ fn test_swap_coldkey_announced_works() { who, new_coldkey, new_coldkey_hash, - left_over, stake1, stake2, stake3, @@ -274,7 +276,8 @@ fn test_swap_coldkey_announced_works() { hk2_alpha, hk3_alpha, total_ck_stake, - total_stake_before + total_stake_before, + 0_u64 // Charged on announcement ); }); } @@ -358,7 +361,8 @@ fn test_swap_coldkey_announced_with_already_associated_coldkey_fails() { let hotkey = U256::from(3); let swap_cost = SubtensorModule::get_key_swap_cost().to_u64(); - SubtensorModule::add_balance_to_coldkey_account(&who, swap_cost); + let ed = ExistentialDeposit::get(); + SubtensorModule::add_balance_to_coldkey_account(&who, swap_cost + ed); assert_ok!(SubtensorModule::announce_coldkey_swap( RuntimeOrigin::signed(who), @@ -418,13 +422,16 @@ fn test_swap_coldkey_works() { let hotkey2 = U256::from(1002); let hotkey3 = U256::from(1003); let left_over = 12345; + let swap_cost = SubtensorModule::get_key_swap_cost(); let min_stake = DefaultMinStake::::get().to_u64(); let stake1 = min_stake * 10; let stake2 = min_stake * 20; let stake3 = min_stake * 30; - let swap_cost = SubtensorModule::get_key_swap_cost(); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); + SubtensorModule::add_balance_to_coldkey_account( + &old_coldkey, + swap_cost.to_u64() + left_over + stake1 + stake2 + stake3, + ); let ( netuid1, @@ -441,7 +448,6 @@ fn test_swap_coldkey_works() { old_coldkey, new_coldkey, new_coldkey_hash, - left_over, stake1, stake2, stake3, @@ -473,7 +479,8 @@ fn test_swap_coldkey_works() { hk2_alpha, hk3_alpha, total_ck_stake, - total_stake_before + total_stake_before, + swap_cost.to_u64() ); }); } @@ -508,6 +515,39 @@ fn test_swap_coldkey_with_bad_origin_fails() { }); } +#[test] +fn test_swap_coldkey_with_not_enough_balance_to_pay_swap_cost_fails() { + new_test_ext(1).execute_with(|| { + let old_coldkey = U256::from(1); + let new_coldkey = U256::from(2); + let swap_cost = SubtensorModule::get_key_swap_cost(); + + // No balance to pay swap cost + assert_noop!( + SubtensorModule::swap_coldkey( + RuntimeOrigin::root(), + old_coldkey, + new_coldkey, + swap_cost + ), + Error::::NotEnoughBalanceToPaySwapColdKey + ); + + // Needs to preserve ED + let balance = SubtensorModule::get_key_swap_cost().to_u64() + ExistentialDeposit::get() - 1; + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, balance); + assert_noop!( + SubtensorModule::swap_coldkey( + RuntimeOrigin::root(), + old_coldkey, + new_coldkey, + swap_cost + ), + Error::::NotEnoughBalanceToPaySwapColdKey + ); + }); +} + #[test] fn test_do_swap_coldkey_preserves_new_coldkey_identity() { new_test_ext(1).execute_with(|| { @@ -541,6 +581,15 @@ fn test_announce_coldkey_swap_with_not_enough_balance_to_pay_swap_cost_fails() { let new_coldkey = U256::from(2); let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); + // No balance to pay swap cost + assert_noop!( + SubtensorModule::announce_coldkey_swap(RuntimeOrigin::signed(who), new_coldkey_hash), + Error::::NotEnoughBalanceToPaySwapColdKey + ); + + // Needs to preserve ED + let balance = SubtensorModule::get_key_swap_cost().to_u64() + ExistentialDeposit::get() - 1; + SubtensorModule::add_balance_to_coldkey_account(&who, balance); assert_noop!( SubtensorModule::announce_coldkey_swap(RuntimeOrigin::signed(who), new_coldkey_hash), Error::::NotEnoughBalanceToPaySwapColdKey @@ -1440,7 +1489,6 @@ macro_rules! comprehensive_setup { $who:expr, $new_coldkey:expr, $new_coldkey_hash:expr, - $left_over:expr, $stake1:expr, $stake2:expr, $stake3:expr, @@ -1448,11 +1496,6 @@ macro_rules! comprehensive_setup { $hotkey2:expr, $hotkey3:expr ) => {{ - SubtensorModule::add_balance_to_coldkey_account( - &$who, - $stake1 + $stake2 + $stake3 + $left_over, - ); - // Setup networks and subnet ownerships let netuid1 = NetUid::from(1); let netuid2 = NetUid::from(2); @@ -1561,17 +1604,15 @@ macro_rules! comprehensive_checks { $hk2_alpha:expr, $hk3_alpha:expr, $total_ck_stake:expr, - $total_stake_before:expr + $total_stake_before:expr, + $swap_cost:expr ) => { // Ensure the announcement has been consumed assert!(!ColdkeySwapAnnouncements::::contains_key($who)); - // // Ensure the cost has been withdrawn from the old coldkey and recycled - // let balance_after = SubtensorModule::get_coldkey_balance(&$who); - // assert_eq!( - // $balance_before - $swap_cost.to_u64(), - // balance_after + $left_over - // ); + // Ensure the cost has been withdrawn from the old coldkey and recycled + let balance_after = SubtensorModule::get_coldkey_balance(&$who); + assert_eq!($balance_before - $swap_cost, balance_after + $left_over); // Ensure the identity is correctly swapped assert!(IdentitiesV2::::get($who).is_none()); From 6c6b855393bed24f746e71c8ee3649820c9738d7 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Mon, 19 Jan 2026 12:02:52 -0300 Subject: [PATCH 137/240] fix naming left_over -> ed --- pallets/subtensor/src/tests/swap_coldkey.rs | 40 +++++++++------------ 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index f99f4ecb64..c7ce2a5f75 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -45,16 +45,13 @@ fn test_announce_coldkey_swap_works() { let who = U256::from(1); let new_coldkey = U256::from(2); let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); - let left_over = 12345; + let ed = ExistentialDeposit::get(); assert_eq!(ColdkeySwapAnnouncements::::iter().count(), 0); let swap_cost = SubtensorModule::get_key_swap_cost().to_u64(); - SubtensorModule::add_balance_to_coldkey_account(&who, swap_cost + left_over); - assert_eq!( - SubtensorModule::get_coldkey_balance(&who), - swap_cost + left_over - ); + SubtensorModule::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( RuntimeOrigin::signed(who), @@ -67,7 +64,7 @@ fn test_announce_coldkey_swap_works() { ColdkeySwapAnnouncements::::iter().collect::>(), vec![(who, (now + delay, new_coldkey_hash))] ); - assert_eq!(SubtensorModule::get_coldkey_balance(&who), left_over); + assert_eq!(SubtensorModule::get_coldkey_balance(&who), ed); assert_eq!( last_event(), RuntimeEvent::SubtensorModule(Event::ColdkeySwapAnnounced { @@ -128,20 +125,17 @@ fn test_announce_coldkey_swap_only_pays_swap_cost_if_no_announcement_exists() { let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); let new_coldkey_2 = U256::from(3); let new_coldkey_2_hash = ::Hashing::hash_of(&new_coldkey_2); - let left_over = 12345; + let ed = ExistentialDeposit::get(); let swap_cost = SubtensorModule::get_key_swap_cost().to_u64(); - SubtensorModule::add_balance_to_coldkey_account(&who, swap_cost + left_over); - assert_eq!( - SubtensorModule::get_coldkey_balance(&who), - swap_cost + left_over - ); + SubtensorModule::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( RuntimeOrigin::signed(who), new_coldkey_hash, )); - assert_eq!(SubtensorModule::get_coldkey_balance(&who), left_over); + assert_eq!(SubtensorModule::get_coldkey_balance(&who), ed); let now = System::block_number(); let base_delay = ColdkeySwapAnnouncementDelay::::get(); @@ -152,7 +146,7 @@ fn test_announce_coldkey_swap_only_pays_swap_cost_if_no_announcement_exists() { RuntimeOrigin::signed(who), new_coldkey_2_hash, )); - assert_eq!(SubtensorModule::get_coldkey_balance(&who), left_over); + assert_eq!(SubtensorModule::get_coldkey_balance(&who), ed); }); } @@ -217,7 +211,7 @@ fn test_swap_coldkey_announced_works() { let hotkey1 = U256::from(1001); let hotkey2 = U256::from(1002); let hotkey3 = U256::from(1003); - let left_over = 12345; + let ed = ExistentialDeposit::get(); let min_stake = DefaultMinStake::::get().to_u64(); let stake1 = min_stake * 10; let stake2 = min_stake * 20; @@ -230,7 +224,7 @@ fn test_swap_coldkey_announced_works() { let delay = ColdkeySwapAnnouncementDelay::::get() + 1; run_to_block(now + delay); - SubtensorModule::add_balance_to_coldkey_account(&who, left_over + stake1 + stake2 + stake3); + SubtensorModule::add_balance_to_coldkey_account(&who, stake1 + stake2 + stake3 + ed); let ( netuid1, @@ -268,7 +262,6 @@ fn test_swap_coldkey_announced_works() { hotkeys, new_coldkey, balance_before, - left_over, identity, netuid1, netuid2, @@ -421,7 +414,7 @@ fn test_swap_coldkey_works() { let hotkey1 = U256::from(1001); let hotkey2 = U256::from(1002); let hotkey3 = U256::from(1003); - let left_over = 12345; + let ed = ExistentialDeposit::get(); let swap_cost = SubtensorModule::get_key_swap_cost(); let min_stake = DefaultMinStake::::get().to_u64(); let stake1 = min_stake * 10; @@ -430,7 +423,7 @@ fn test_swap_coldkey_works() { SubtensorModule::add_balance_to_coldkey_account( &old_coldkey, - swap_cost.to_u64() + left_over + stake1 + stake2 + stake3, + swap_cost.to_u64() + stake1 + stake2 + stake3 + ed, ); let ( @@ -471,7 +464,6 @@ fn test_swap_coldkey_works() { hotkeys, new_coldkey, balance_before, - left_over, identity, netuid1, netuid2, @@ -1596,7 +1588,6 @@ macro_rules! comprehensive_checks { $hotkeys:expr, $new_coldkey:expr, $balance_before:expr, - $left_over:expr, $identity:expr, $netuid1:expr, $netuid2:expr, @@ -1612,7 +1603,8 @@ macro_rules! comprehensive_checks { // Ensure the cost has been withdrawn from the old coldkey and recycled let balance_after = SubtensorModule::get_coldkey_balance(&$who); - assert_eq!($balance_before - $swap_cost, balance_after + $left_over); + let ed = ExistentialDeposit::get(); + assert_eq!($balance_before - $swap_cost, balance_after + ed); // Ensure the identity is correctly swapped assert!(IdentitiesV2::::get($who).is_none()); @@ -1703,7 +1695,7 @@ macro_rules! comprehensive_checks { assert_eq!(SubtensorModule::get_coldkey_balance(&$who), 0); assert_eq!( SubtensorModule::get_coldkey_balance(&$new_coldkey), - $left_over + ExistentialDeposit::get() ); // Ensure total stake is unchanged From a5128752c5acd6752bb605f00d858abe7bd45bb0 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 19 Jan 2026 13:57:33 -0500 Subject: [PATCH 138/240] Fix extrinsic index --- pallets/admin-utils/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 9f2ccaee9a..0c736c47c8 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -2231,7 +2231,7 @@ pub mod pallet { } /// Sets the global maximum number of mechanisms in a subnet - #[pallet::call_index(85)] + #[pallet::call_index(86)] #[pallet::weight(Weight::from_parts(15_000_000, 0) .saturating_add(::DbWeight::get().reads(1_u64)) .saturating_add(::DbWeight::get().writes(1_u64)))] From 834f14cefa209f8a611bc06b58a748ebde2c4158 Mon Sep 17 00:00:00 2001 From: Pawel Polewicz Date: Mon, 19 Jan 2026 22:17:12 +0000 Subject: [PATCH 139/240] Reapply "Voting Power EMA" This reverts commit 8a1e018a87d9e296a57cbb10bff74b7166d7bfed. --- contract-tests/src/contracts/votingPower.ts | 104 +++ .../test/votingPower.precompile.test.ts | 226 ++++++ pallets/admin-utils/src/lib.rs | 2 + pallets/subtensor/src/epoch/run_epoch.rs | 6 + pallets/subtensor/src/lib.rs | 47 +- pallets/subtensor/src/macros/dispatches.rs | 91 +++ pallets/subtensor/src/macros/errors.rs | 4 + pallets/subtensor/src/macros/events.rs | 29 + pallets/subtensor/src/subnets/mechanism.rs | 7 +- pallets/subtensor/src/swap/swap_hotkey.rs | 5 + pallets/subtensor/src/tests/mod.rs | 1 + pallets/subtensor/src/tests/voting_power.rs | 760 ++++++++++++++++++ pallets/subtensor/src/utils/mod.rs | 1 + pallets/subtensor/src/utils/voting_power.rs | 280 +++++++ precompiles/src/lib.rs | 8 +- precompiles/src/voting_power.rs | 131 +++ 16 files changed, 1699 insertions(+), 3 deletions(-) create mode 100644 contract-tests/src/contracts/votingPower.ts create mode 100644 contract-tests/test/votingPower.precompile.test.ts create mode 100644 pallets/subtensor/src/tests/voting_power.rs create mode 100644 pallets/subtensor/src/utils/voting_power.rs create mode 100644 precompiles/src/voting_power.rs diff --git a/contract-tests/src/contracts/votingPower.ts b/contract-tests/src/contracts/votingPower.ts new file mode 100644 index 0000000000..bbcc3ca6e6 --- /dev/null +++ b/contract-tests/src/contracts/votingPower.ts @@ -0,0 +1,104 @@ +export const IVOTING_POWER_ADDRESS = "0x000000000000000000000000000000000000080d"; + +export const IVotingPowerABI = [ + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + } + ], + "name": "getVotingPower", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "isVotingPowerTrackingEnabled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getVotingPowerDisableAtBlock", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getVotingPowerEmaAlpha", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getTotalVotingPower", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/contract-tests/test/votingPower.precompile.test.ts b/contract-tests/test/votingPower.precompile.test.ts new file mode 100644 index 0000000000..26b29e257c --- /dev/null +++ b/contract-tests/test/votingPower.precompile.test.ts @@ -0,0 +1,226 @@ +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.1 * 10^18 = 100_000_000_000_000_000 + assert.strictEqual(alpha, BigInt("100000000000000000"), "Default alpha should be 0.1 (100_000_000_000_000_000)"); + }); + }); + + 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/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 2a33262073..5d7713df15 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -145,6 +145,8 @@ pub mod pallet { Leasing, /// Address mapping precompile AddressMapping, + /// Voting power precompile + VotingPower, } #[pallet::type_value] diff --git a/pallets/subtensor/src/epoch/run_epoch.rs b/pallets/subtensor/src/epoch/run_epoch.rs index f56b8a89a4..f90175ce0c 100644 --- a/pallets/subtensor/src/epoch/run_epoch.rs +++ b/pallets/subtensor/src/epoch/run_epoch.rs @@ -22,6 +22,7 @@ pub struct EpochTerms { pub validator_trust: u16, pub new_validator_permit: bool, pub bond: Vec<(u16, u16)>, + pub stake: u64, } pub struct EpochOutput(pub BTreeMap); @@ -988,6 +989,10 @@ impl Pallet { .iter() .map(|xi| fixed_proportion_to_u16(*xi)) .collect::>(); + let raw_stake: Vec = total_stake + .iter() + .map(|s| s.saturating_to_num::()) + .collect::>(); for (_hotkey, terms) in terms_map.iter_mut() { terms.dividend = cloned_dividends.get(terms.uid).copied().unwrap_or_default(); @@ -1012,6 +1017,7 @@ impl Pallet { .get(terms.uid) .copied() .unwrap_or_default(); + terms.stake = raw_stake.get(terms.uid).copied().unwrap_or_default(); let old_validator_permit = validator_permits .get(terms.uid) .copied() diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 6ae43ac384..9db212330e 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1,6 +1,7 @@ #![cfg_attr(not(feature = "std"), no_std)] #![recursion_limit = "512"] #![allow(clippy::too_many_arguments)] +#![allow(clippy::zero_prefixed_literal)] // Edit this file to define custom logic or remove it if it is not needed. // Learn more about FRAME and the core library of Substrate FRAME pallets: // @@ -1894,8 +1895,52 @@ pub mod pallet { pub type SubtokenEnabled = StorageMap<_, Identity, NetUid, bool, ValueQuery, DefaultFalse>; - /// Default value for burn keys limit + // ======================================= + // ==== VotingPower Storage ==== + // ======================================= + #[pallet::type_value] + /// Default VotingPower EMA alpha value (0.1 represented as u64 with 18 decimals) + /// alpha = 0.1 means slow response, 10% weight to new values per epoch + pub fn DefaultVotingPowerEmaAlpha() -> u64 { + 0_003_570_000_000_000_000 // 0.00357 * 10^18 = 2 weeks e-folding (time-constant) @ 361 + // blocks per tempo + // After 2 weeks -> EMA reaches 63.2% of a step change + // After ~4 weeks -> 86.5% + // After ~6 weeks -> 95% + } + + #[pallet::storage] + /// --- DMAP ( netuid, hotkey ) --> voting_power | EMA of stake for voting + /// This tracks stake EMA updated every epoch when VotingPowerTrackingEnabled is true. + /// Used by smart contracts to determine validator voting power for subnet governance. + pub type VotingPower = + StorageDoubleMap<_, Identity, NetUid, Blake2_128Concat, T::AccountId, u64, ValueQuery>; + + #[pallet::storage] + /// --- MAP ( netuid ) --> bool | Whether voting power tracking is enabled for this subnet. + /// When enabled, VotingPower EMA is updated every epoch. Default is false. + /// When disabled with disable_at_block set, tracking continues until that block. + pub type VotingPowerTrackingEnabled = + StorageMap<_, Identity, NetUid, bool, ValueQuery, DefaultFalse>; + + #[pallet::storage] + /// --- MAP ( netuid ) --> block_number | Block at which voting power tracking will be disabled. + /// When set (non-zero), tracking continues until this block, then automatically disables + /// and clears VotingPower entries for the subnet. Provides a 14-day grace period. + pub type VotingPowerDisableAtBlock = + StorageMap<_, Identity, NetUid, u64, ValueQuery>; + + #[pallet::storage] + /// --- MAP ( netuid ) --> u64 | EMA alpha value for voting power calculation. + /// Higher alpha = faster response to stake changes. + /// Stored as u64 with 18 decimal precision (1.0 = 10^18). + /// Only settable by sudo/root. + pub type VotingPowerEmaAlpha = + StorageMap<_, Identity, NetUid, u64, ValueQuery, DefaultVotingPowerEmaAlpha>; + + #[pallet::type_value] + /// Default value for burn keys limit pub fn DefaultImmuneOwnerUidsLimit() -> u16 { 1 } diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 2a362783ef..240864ae74 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2432,5 +2432,96 @@ mod dispatches { Ok(()) } + + /// Enables voting power tracking for a subnet. + /// + /// This function can be called by the subnet owner or root. + /// When enabled, voting power EMA is updated every epoch for all validators. + /// Voting power starts at 0 and increases over epochs. + /// + /// # Arguments: + /// * `origin` - The origin of the call, must be subnet owner or root. + /// * `netuid` - The subnet to enable voting power tracking for. + /// + /// # Errors: + /// * `SubnetNotExist` - If the subnet does not exist. + /// * `NotSubnetOwner` - If the caller is not the subnet owner or root. + #[pallet::call_index(125)] + #[pallet::weight(( + Weight::from_parts(10_000, 0) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)), + DispatchClass::Operational, + Pays::Yes + ))] + pub fn enable_voting_power_tracking( + origin: OriginFor, + netuid: NetUid, + ) -> DispatchResult { + Self::ensure_subnet_owner_or_root(origin, netuid)?; + Self::do_enable_voting_power_tracking(netuid) + } + + /// Schedules disabling of voting power tracking for a subnet. + /// + /// This function can be called by the subnet owner or root. + /// Voting power tracking will continue for 14 days (grace period) after this call, + /// then automatically disable and clear all VotingPower entries for the subnet. + /// + /// # Arguments: + /// * `origin` - The origin of the call, must be subnet owner or root. + /// * `netuid` - The subnet to schedule disabling voting power tracking for. + /// + /// # Errors: + /// * `SubnetNotExist` - If the subnet does not exist. + /// * `NotSubnetOwner` - If the caller is not the subnet owner or root. + /// * `VotingPowerTrackingNotEnabled` - If voting power tracking is not enabled. + #[pallet::call_index(126)] + #[pallet::weight(( + Weight::from_parts(10_000, 0) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)), + DispatchClass::Operational, + Pays::Yes + ))] + pub fn disable_voting_power_tracking( + origin: OriginFor, + netuid: NetUid, + ) -> DispatchResult { + Self::ensure_subnet_owner_or_root(origin, netuid)?; + Self::do_disable_voting_power_tracking(netuid) + } + + /// Sets the EMA alpha value for voting power calculation on a subnet. + /// + /// This function can only be called by root (sudo). + /// Higher alpha = faster response to stake changes. + /// Alpha is stored as u64 with 18 decimal precision (1.0 = 10^18). + /// + /// # Arguments: + /// * `origin` - The origin of the call, must be root. + /// * `netuid` - The subnet to set the alpha for. + /// * `alpha` - The new alpha value (u64 with 18 decimal precision). + /// + /// # Errors: + /// * `BadOrigin` - If the origin is not root. + /// * `SubnetNotExist` - If the subnet does not exist. + /// * `InvalidVotingPowerEmaAlpha` - If alpha is greater than 10^18 (1.0). + #[pallet::call_index(127)] + #[pallet::weight(( + Weight::from_parts(6_000, 0) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)), + DispatchClass::Operational, + Pays::Yes + ))] + pub fn sudo_set_voting_power_ema_alpha( + origin: OriginFor, + netuid: NetUid, + alpha: u64, + ) -> DispatchResult { + ensure_root(origin)?; + Self::do_set_voting_power_ema_alpha(netuid, alpha) + } } } diff --git a/pallets/subtensor/src/macros/errors.rs b/pallets/subtensor/src/macros/errors.rs index 6c3d7a35df..995c9b5a31 100644 --- a/pallets/subtensor/src/macros/errors.rs +++ b/pallets/subtensor/src/macros/errors.rs @@ -266,6 +266,10 @@ mod errors { InvalidRootClaimThreshold, /// Exceeded subnet limit number or zero. InvalidSubnetNumber, + /// Voting power tracking is not enabled for this subnet. + VotingPowerTrackingNotEnabled, + /// Invalid voting power EMA alpha value (must be <= 10^18). + InvalidVotingPowerEmaAlpha, /// Unintended precision loss when unstaking alpha PrecisionLoss, } diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index c86cc1a1e5..457eacd7b1 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -472,6 +472,35 @@ mod events { root_claim_type: RootClaimTypeEnum, }, + /// Voting power tracking has been enabled for a subnet. + VotingPowerTrackingEnabled { + /// The subnet ID + netuid: NetUid, + }, + + /// Voting power tracking has been scheduled for disabling. + /// Tracking will continue until disable_at_block, then stop and clear entries. + VotingPowerTrackingDisableScheduled { + /// The subnet ID + netuid: NetUid, + /// Block at which tracking will be disabled + disable_at_block: u64, + }, + + /// Voting power tracking has been fully disabled and entries cleared. + VotingPowerTrackingDisabled { + /// The subnet ID + netuid: NetUid, + }, + + /// Voting power EMA alpha has been set for a subnet. + VotingPowerEmaAlphaSet { + /// The subnet ID + netuid: NetUid, + /// The new alpha value (u64 with 18 decimal precision) + alpha: u64, + }, + /// Subnet lease dividends have been distributed. SubnetLeaseDividendsDistributed { /// The lease ID diff --git a/pallets/subtensor/src/subnets/mechanism.rs b/pallets/subtensor/src/subnets/mechanism.rs index 481974ef05..b5ed928930 100644 --- a/pallets/subtensor/src/subnets/mechanism.rs +++ b/pallets/subtensor/src/subnets/mechanism.rs @@ -322,6 +322,7 @@ impl Pallet { sub_weight, ); acc_terms.new_validator_permit |= terms.new_validator_permit; + acc_terms.stake = acc_terms.stake.saturating_add(terms.stake); }) .or_insert_with(|| { // weighted insert for the first sub-subnet seen for this hotkey @@ -349,7 +350,8 @@ impl Pallet { sub_weight, ), new_validator_permit: terms.new_validator_permit, - bond: Vec::new(), // aggregated map doesn’t use bonds; keep empty + bond: Vec::new(), // aggregated map doesn't use bonds; keep empty + stake: terms.stake, } }); acc @@ -358,6 +360,9 @@ impl Pallet { // State updates from epoch function Self::persist_netuid_epoch_terms(netuid, &aggregated); + // Update voting power EMA for all validators on this subnet + Self::update_voting_power_for_subnet(netuid, &aggregated); + // Remap BTreeMap back to Vec<(T::AccountId, AlphaCurrency, AlphaCurrency)> format // for processing emissions in run_coinbase // Emission tuples ( hotkeys, server_emission, validator_emission ) diff --git a/pallets/subtensor/src/swap/swap_hotkey.rs b/pallets/subtensor/src/swap/swap_hotkey.rs index 4fdf87fb7b..a54a02a750 100644 --- a/pallets/subtensor/src/swap/swap_hotkey.rs +++ b/pallets/subtensor/src/swap/swap_hotkey.rs @@ -493,6 +493,11 @@ impl Pallet { // 8.3 Swap TaoDividendsPerSubnet // Tao dividends were removed + // 8.4 Swap VotingPower + // VotingPower( netuid, hotkey ) --> u64 -- the voting power EMA for the hotkey. + Self::swap_voting_power_for_hotkey(old_hotkey, new_hotkey, netuid); + weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2)); + // 9. Swap Alpha // Alpha( hotkey, coldkey, netuid ) -> alpha let old_alpha_values: Vec<((T::AccountId, NetUid), U64F64)> = diff --git a/pallets/subtensor/src/tests/mod.rs b/pallets/subtensor/src/tests/mod.rs index bbaf25af58..6ce0639516 100644 --- a/pallets/subtensor/src/tests/mod.rs +++ b/pallets/subtensor/src/tests/mod.rs @@ -30,4 +30,5 @@ mod swap_coldkey; mod swap_hotkey; mod swap_hotkey_with_subnet; mod uids; +mod voting_power; mod weights; diff --git a/pallets/subtensor/src/tests/voting_power.rs b/pallets/subtensor/src/tests/voting_power.rs new file mode 100644 index 0000000000..7edc06fbab --- /dev/null +++ b/pallets/subtensor/src/tests/voting_power.rs @@ -0,0 +1,760 @@ +#![allow(unused, clippy::indexing_slicing, clippy::panic, clippy::unwrap_used)] + +use alloc::collections::BTreeMap; +use frame_support::weights::Weight; +use frame_support::{assert_err, assert_noop, assert_ok}; +use frame_system::RawOrigin; +use sp_core::U256; +use subtensor_runtime_common::NetUid; + +use super::mock; +use super::mock::*; +use crate::epoch::run_epoch::EpochTerms; +use crate::utils::voting_power::{ + MAX_VOTING_POWER_EMA_ALPHA, VOTING_POWER_DISABLE_GRACE_PERIOD_BLOCKS, +}; +use crate::*; + +// ============================================ +// === Test Helpers === +// ============================================ + +const DEFAULT_STAKE_AMOUNT: u64 = 1_000_000_000_000; // 1 million RAO + +/// Build epoch output from current state for testing voting power updates. +fn build_mock_epoch_output(netuid: NetUid) -> BTreeMap { + let n = SubtensorModule::get_subnetwork_n(netuid); + let validator_permits = ValidatorPermit::::get(netuid); + + let mut output = BTreeMap::new(); + for uid in 0..n { + if let Ok(hotkey) = SubtensorModule::get_hotkey_for_net_and_uid(netuid, uid) { + let has_permit = validator_permits + .get(uid as usize) + .copied() + .unwrap_or(false); + let stake = SubtensorModule::get_stake_for_hotkey_on_subnet(&hotkey, netuid).to_u64(); + output.insert( + hotkey, + EpochTerms { + uid: uid as usize, + new_validator_permit: has_permit, + stake, + ..Default::default() + }, + ); + } + } + output +} + +/// Test fixture containing common test setup data +struct VotingPowerTestFixture { + hotkey: U256, + coldkey: U256, + netuid: NetUid, +} + +impl VotingPowerTestFixture { + /// Create a basic fixture with a dynamic network + fn new() -> Self { + let hotkey = U256::from(1); + let coldkey = U256::from(2); + let netuid = add_dynamic_network(&hotkey, &coldkey); + Self { + hotkey, + coldkey, + netuid, + } + } + + /// Setup reserves and add balance to coldkey for staking + fn setup_for_staking(&self) { + self.setup_for_staking_with_amount(DEFAULT_STAKE_AMOUNT); + } + + /// Setup reserves and add balance with custom amount + #[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); + } + + /// Enable voting power tracking for the subnet + fn enable_tracking(&self) { + assert_ok!(SubtensorModule::enable_voting_power_tracking( + RuntimeOrigin::signed(self.coldkey), + self.netuid + )); + } + + /// Add stake from coldkey to hotkey + fn add_stake(&self, amount: u64) { + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(self.coldkey), + self.hotkey, + self.netuid, + amount.into() + )); + } + + /// Set validator permit for the hotkey (uid 0) + fn set_validator_permit(&self, has_permit: bool) { + ValidatorPermit::::insert(self.netuid, vec![has_permit]); + } + + /// Run voting power update for N epochs + fn run_epochs(&self, n: u32) { + for _ in 0..n { + let epoch_output = build_mock_epoch_output(self.netuid); + SubtensorModule::update_voting_power_for_subnet(self.netuid, &epoch_output); + } + } + + /// Get current voting power for the hotkey + fn get_voting_power(&self) -> u64 { + SubtensorModule::get_voting_power(self.netuid, &self.hotkey) + } + + /// Full setup: reserves, balance, tracking enabled, stake added, validator permit + fn setup_full(&self) { + self.setup_for_staking(); + self.enable_tracking(); + self.add_stake(DEFAULT_STAKE_AMOUNT); + self.set_validator_permit(true); + } +} + +// ============================================ +// === Test Enable/Disable Voting Power === +// ============================================ + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::voting_power::test_enable_voting_power_tracking --exact --nocapture +#[test] +fn test_enable_voting_power_tracking() { + new_test_ext(1).execute_with(|| { + let f = VotingPowerTestFixture::new(); + + // Initially disabled + assert!(!SubtensorModule::get_voting_power_tracking_enabled( + f.netuid + )); + + // Enable tracking (subnet owner can do this) + f.enable_tracking(); + + // Now enabled + assert!(SubtensorModule::get_voting_power_tracking_enabled(f.netuid)); + assert_eq!( + SubtensorModule::get_voting_power_disable_at_block(f.netuid), + 0 + ); + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::voting_power::test_enable_voting_power_tracking_root_can_enable --exact --nocapture +#[test] +fn test_enable_voting_power_tracking_root_can_enable() { + new_test_ext(1).execute_with(|| { + let f = VotingPowerTestFixture::new(); + + // Root can enable + assert_ok!(SubtensorModule::enable_voting_power_tracking( + RuntimeOrigin::root(), + f.netuid + )); + + assert!(SubtensorModule::get_voting_power_tracking_enabled(f.netuid)); + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::voting_power::test_disable_voting_power_tracking_schedules_disable --exact --nocapture +#[test] +fn test_disable_voting_power_tracking_schedules_disable() { + new_test_ext(1).execute_with(|| { + let f = VotingPowerTestFixture::new(); + f.enable_tracking(); + + let current_block = SubtensorModule::get_current_block_as_u64(); + + // Schedule disable + assert_ok!(SubtensorModule::disable_voting_power_tracking( + RuntimeOrigin::signed(f.coldkey), + f.netuid + )); + + // Still enabled, but scheduled for disable + assert!(SubtensorModule::get_voting_power_tracking_enabled(f.netuid)); + let disable_at = SubtensorModule::get_voting_power_disable_at_block(f.netuid); + assert_eq!( + disable_at, + current_block + VOTING_POWER_DISABLE_GRACE_PERIOD_BLOCKS + ); + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::voting_power::test_disable_voting_power_tracking_fails_when_not_enabled --exact --nocapture +#[test] +fn test_disable_voting_power_tracking_fails_when_not_enabled() { + new_test_ext(1).execute_with(|| { + let f = VotingPowerTestFixture::new(); + + // Try to disable when not enabled + assert_noop!( + SubtensorModule::disable_voting_power_tracking( + RuntimeOrigin::signed(f.coldkey), + f.netuid + ), + Error::::VotingPowerTrackingNotEnabled + ); + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::voting_power::test_enable_voting_power_tracking_non_owner_fails --exact --nocapture +#[test] +fn test_enable_voting_power_tracking_non_owner_fails() { + new_test_ext(1).execute_with(|| { + let f = VotingPowerTestFixture::new(); + let random_account = U256::from(999); + + // Non-owner cannot enable (returns BadOrigin) + assert_noop!( + SubtensorModule::enable_voting_power_tracking( + RuntimeOrigin::signed(random_account), + f.netuid + ), + sp_runtime::DispatchError::BadOrigin + ); + + // Should still be disabled + assert!(!SubtensorModule::get_voting_power_tracking_enabled( + f.netuid + )); + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::voting_power::test_disable_voting_power_tracking_non_owner_fails --exact --nocapture +#[test] +fn test_disable_voting_power_tracking_non_owner_fails() { + new_test_ext(1).execute_with(|| { + let f = VotingPowerTestFixture::new(); + let random_account = U256::from(999); + f.enable_tracking(); + + // Non-owner cannot disable (returns BadOrigin) + assert_noop!( + SubtensorModule::disable_voting_power_tracking( + RuntimeOrigin::signed(random_account), + f.netuid + ), + sp_runtime::DispatchError::BadOrigin + ); + + // Should still be enabled with no disable scheduled + assert!(SubtensorModule::get_voting_power_tracking_enabled(f.netuid)); + assert_eq!( + SubtensorModule::get_voting_power_disable_at_block(f.netuid), + 0 + ); + }); +} + +// ============================================ +// === Test EMA Alpha === +// ============================================ + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::voting_power::test_set_voting_power_ema_alpha --exact --nocapture +#[test] +fn test_set_voting_power_ema_alpha() { + new_test_ext(1).execute_with(|| { + let f = VotingPowerTestFixture::new(); + + // Get default alpha + let default_alpha = SubtensorModule::get_voting_power_ema_alpha(f.netuid); + assert_eq!(default_alpha, 3_570_000_000_000_000); // 0.00357 * 10^18 = 2 weeks e-folding + + // Set new alpha (only root can do this) + let new_alpha: u64 = 500_000_000_000_000_000; // 0.5 * 10^18 + assert_ok!(SubtensorModule::sudo_set_voting_power_ema_alpha( + RuntimeOrigin::root(), + f.netuid, + new_alpha + )); + + assert_eq!( + SubtensorModule::get_voting_power_ema_alpha(f.netuid), + new_alpha + ); + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::voting_power::test_set_voting_power_ema_alpha_fails_above_one --exact --nocapture +#[test] +fn test_set_voting_power_ema_alpha_fails_above_one() { + new_test_ext(1).execute_with(|| { + let f = VotingPowerTestFixture::new(); + + // Try to set alpha > 1.0 (> 10^18) + let invalid_alpha: u64 = MAX_VOTING_POWER_EMA_ALPHA + 1; + assert_noop!( + SubtensorModule::sudo_set_voting_power_ema_alpha( + RuntimeOrigin::root(), + f.netuid, + invalid_alpha + ), + Error::::InvalidVotingPowerEmaAlpha + ); + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::voting_power::test_set_voting_power_ema_alpha_non_root_fails --exact --nocapture +#[test] +fn test_set_voting_power_ema_alpha_non_root_fails() { + new_test_ext(1).execute_with(|| { + let f = VotingPowerTestFixture::new(); + + // Non-root cannot set alpha + assert_noop!( + SubtensorModule::sudo_set_voting_power_ema_alpha( + RuntimeOrigin::signed(f.coldkey), + f.netuid, + 500_000_000_000_000_000 + ), + sp_runtime::DispatchError::BadOrigin + ); + }); +} + +// ============================================ +// === Test EMA Calculation === +// ============================================ + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::voting_power::test_voting_power_ema_calculation --exact --nocapture +#[test] +fn test_voting_power_ema_calculation() { + new_test_ext(1).execute_with(|| { + let f = VotingPowerTestFixture::new(); + f.setup_full(); + + // Initially voting power is 0 + assert_eq!(f.get_voting_power(), 0); + + // Run epoch to update voting power + f.run_epochs(1); + + // Voting power should now be > 0 (but less than full stake due to EMA starting from 0) + let voting_power_after_first_epoch = f.get_voting_power(); + assert!(voting_power_after_first_epoch > 0); + + // Run more epochs - voting power should increase towards stake + f.run_epochs(10); + + let voting_power_after_many_epochs = f.get_voting_power(); + assert!(voting_power_after_many_epochs > voting_power_after_first_epoch); + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::voting_power::test_voting_power_cleared_when_deregistered --exact --nocapture +#[test] +fn test_voting_power_cleared_when_deregistered() { + new_test_ext(1).execute_with(|| { + let f = VotingPowerTestFixture::new(); + f.setup_full(); + + // Run epochs to build up voting power + f.run_epochs(10); + + let voting_power_before = f.get_voting_power(); + assert!(voting_power_before > 0, "Voting power should be built up"); + + // Deregister the hotkey (simulate by removing from IsNetworkMember) + IsNetworkMember::::remove(f.hotkey, f.netuid); + + // Run epoch - voting power should be cleared for deregistered hotkey + f.run_epochs(1); + + // Should be removed from storage immediately when deregistered + assert_eq!(f.get_voting_power(), 0); + assert!( + !VotingPower::::contains_key(f.netuid, f.hotkey), + "Entry should be removed when hotkey is deregistered" + ); + }); +} + +// ============================================ +// === Test Validators Only === +// ============================================ + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::voting_power::test_only_validators_get_voting_power --exact --nocapture +#[test] +fn test_only_validators_get_voting_power() { + new_test_ext(1).execute_with(|| { + let validator_hotkey = U256::from(1); + let miner_hotkey = U256::from(2); + let coldkey = U256::from(3); + + let netuid = add_dynamic_network(&validator_hotkey, &coldkey); + + mock::setup_reserves( + netuid, + (DEFAULT_STAKE_AMOUNT * 100).into(), + (DEFAULT_STAKE_AMOUNT * 100).into(), + ); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, DEFAULT_STAKE_AMOUNT * 20); + + // Register miner + register_ok_neuron(netuid, miner_hotkey, coldkey, 0); + + // Enable voting power tracking + assert_ok!(SubtensorModule::enable_voting_power_tracking( + RuntimeOrigin::signed(coldkey), + netuid + )); + + // Add stake to both + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(coldkey), + validator_hotkey, + netuid, + DEFAULT_STAKE_AMOUNT.into() + )); + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(coldkey), + miner_hotkey, + netuid, + DEFAULT_STAKE_AMOUNT.into() + )); + + // Set validator permit: uid 0 (validator) has permit, uid 1 (miner) does not + ValidatorPermit::::insert(netuid, vec![true, false]); + + // Run epoch + let epoch_output = build_mock_epoch_output(netuid); + SubtensorModule::update_voting_power_for_subnet(netuid, &epoch_output); + + // Only validator should have voting power + assert!(SubtensorModule::get_voting_power(netuid, &validator_hotkey) > 0); + assert_eq!(SubtensorModule::get_voting_power(netuid, &miner_hotkey), 0); + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::voting_power::test_miner_voting_power_removed_when_loses_vpermit --exact --nocapture +#[test] +fn test_miner_voting_power_removed_when_loses_vpermit() { + new_test_ext(1).execute_with(|| { + let f = VotingPowerTestFixture::new(); + f.setup_full(); + + // Run epochs to build voting power + f.run_epochs(10); + + let voting_power_before = f.get_voting_power(); + assert!(voting_power_before > 0); + + // Remove validator permit (now they're a miner) + f.set_validator_permit(false); + + // Run epoch - voting power should be removed + f.run_epochs(1); + + assert_eq!(f.get_voting_power(), 0); + }); +} + +// ============================================ +// === Test Hotkey Swap === +// ============================================ + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::voting_power::test_voting_power_transfers_on_hotkey_swap --exact --nocapture +#[test] +fn test_voting_power_transfers_on_hotkey_swap() { + new_test_ext(1).execute_with(|| { + let f = VotingPowerTestFixture::new(); + let new_hotkey = U256::from(99); + let voting_power_value = 5_000_000_000_000_u64; + + // Set some voting power for the old hotkey + VotingPower::::insert(f.netuid, f.hotkey, voting_power_value); + + // Verify old hotkey has voting power + assert_eq!(f.get_voting_power(), voting_power_value); + assert_eq!(SubtensorModule::get_voting_power(f.netuid, &new_hotkey), 0); + + // Perform hotkey swap for this subnet + SubtensorModule::swap_voting_power_for_hotkey(&f.hotkey, &new_hotkey, f.netuid); + + // Old hotkey should have 0, new hotkey should have the voting power + assert_eq!(f.get_voting_power(), 0); + assert_eq!( + SubtensorModule::get_voting_power(f.netuid, &new_hotkey), + voting_power_value + ); + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::voting_power::test_voting_power_swap_adds_to_existing --exact --nocapture +#[test] +fn test_voting_power_swap_adds_to_existing() { + new_test_ext(1).execute_with(|| { + let f = VotingPowerTestFixture::new(); + let new_hotkey = U256::from(99); + let old_voting_power = 5_000_000_000_000_u64; + let new_existing_voting_power = 2_000_000_000_000_u64; + + // Set voting power for both hotkeys + VotingPower::::insert(f.netuid, f.hotkey, old_voting_power); + VotingPower::::insert(f.netuid, new_hotkey, new_existing_voting_power); + + // Perform swap + SubtensorModule::swap_voting_power_for_hotkey(&f.hotkey, &new_hotkey, f.netuid); + + // New hotkey should have combined voting power + assert_eq!(f.get_voting_power(), 0); + assert_eq!( + SubtensorModule::get_voting_power(f.netuid, &new_hotkey), + old_voting_power + new_existing_voting_power + ); + }); +} + +// ============================================ +// === Test Threshold Logic === +// ============================================ +// Tests the rule: Only remove voting power entry if it decayed FROM above threshold TO below. +// New validators building up from 0 should NOT be removed even if below threshold. + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::voting_power::test_voting_power_not_removed_if_never_above_threshold --exact --nocapture +#[test] +fn test_voting_power_not_removed_if_never_above_threshold() { + new_test_ext(1).execute_with(|| { + let f = VotingPowerTestFixture::new(); + f.setup_full(); + + // Get the threshold + let min_stake = SubtensorModule::get_stake_threshold(); + + // Set voting power directly to a value below threshold (simulating building up) + // This is below threshold but was never above it + let below_threshold = min_stake.saturating_sub(1); + VotingPower::::insert(f.netuid, f.hotkey, below_threshold); + + // Run epoch + f.run_epochs(1); + + // Key assertion: Entry should NOT be removed because previous_ema was below threshold + // The removal rule only triggers when previous_ema >= threshold and new_ema < threshold + let voting_power = f.get_voting_power(); + assert!( + voting_power > 0, + "Voting power should still exist - it was never above threshold" + ); + assert!( + VotingPower::::contains_key(f.netuid, f.hotkey), + "Entry should exist - it was never above threshold so shouldn't be removed" + ); + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::voting_power::test_voting_power_not_removed_with_small_dip_below_threshold --exact --nocapture +#[test] +fn test_voting_power_not_removed_with_small_dip_below_threshold() { + new_test_ext(1).execute_with(|| { + let f = VotingPowerTestFixture::new(); + f.setup_for_staking(); + f.enable_tracking(); + f.set_validator_permit(true); + + let min_stake = SubtensorModule::get_stake_threshold(); + + // Set voting power above threshold (validator was established) + let above_threshold = min_stake + 100; + VotingPower::::insert(f.netuid, f.hotkey, above_threshold); + + // Simulate a small dip: new EMA drops to 95% of threshold (within 10% buffer) + // This is above the removal threshold (90%) so should NOT be removed + let small_dip = min_stake * 95 / 100; + VotingPower::::insert(f.netuid, f.hotkey, small_dip); + + // Manually trigger the removal check by setting previous to above threshold + // and running with stake that would produce EMA in the buffer zone + VotingPower::::insert(f.netuid, f.hotkey, above_threshold); + + // Build epoch output with stake that will produce EMA around 95% of threshold + let mut epoch_output = build_mock_epoch_output(f.netuid); + if let Some(terms) = epoch_output.get_mut(&f.hotkey) { + terms.stake = small_dip; // Stake drops but stays in buffer zone + } + + SubtensorModule::update_voting_power_for_subnet(f.netuid, &epoch_output); + + // Should NOT be removed - dip is within hysteresis buffer + assert!( + VotingPower::::contains_key(f.netuid, f.hotkey), + "Entry should exist - small dip within 10% buffer should not trigger removal" + ); + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::voting_power::test_voting_power_removed_with_significant_drop_below_threshold --exact --nocapture +#[test] +fn test_voting_power_removed_with_significant_drop_below_threshold() { + new_test_ext(1).execute_with(|| { + let f = VotingPowerTestFixture::new(); + f.enable_tracking(); + + // Use explicit values since get_stake_threshold() may return 0 in tests + let min_stake: u64 = 1_000_000_000; + StakeThreshold::::put(min_stake); + + // Set voting power above threshold (validator was established) + VotingPower::::insert(f.netuid, f.hotkey, min_stake); + + // Set alpha to 100% so new_ema = current_stake directly (for testing removal) + VotingPowerEmaAlpha::::insert(f.netuid, MAX_VOTING_POWER_EMA_ALPHA); + + // Build epoch output manually with stake = 0 and validator permit = true + let mut epoch_output = BTreeMap::new(); + epoch_output.insert( + f.hotkey, + EpochTerms { + uid: 0, + new_validator_permit: true, + stake: 0, // Complete unstake + ..Default::default() + }, + ); + + // With alpha = 1.0: new_ema = 1.0 * 0 + 0 * previous = 0 + // 0 < removal_threshold (90% of min_stake = 900M) AND previous (1B) >= min_stake (1B) + // Should trigger removal + SubtensorModule::update_voting_power_for_subnet(f.netuid, &epoch_output); + + assert!( + !VotingPower::::contains_key(f.netuid, f.hotkey), + "Entry should be removed - stake dropped to 0 with alpha=1.0" + ); + }); +} + +// ============================================ +// === Test Tracking Not Active === +// ============================================ + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::voting_power::test_voting_power_not_updated_when_disabled --exact --nocapture +#[test] +fn test_voting_power_not_updated_when_disabled() { + new_test_ext(1).execute_with(|| { + let f = VotingPowerTestFixture::new(); + f.setup_for_staking(); + // DON'T enable voting power tracking + f.add_stake(DEFAULT_STAKE_AMOUNT); + f.set_validator_permit(true); + + // Run epoch + f.run_epochs(1); + + // Voting power should still be 0 since tracking is disabled + assert_eq!(f.get_voting_power(), 0); + }); +} + +// ============================================ +// === Test Re-enable After Disable === +// ============================================ + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::voting_power::test_reenable_voting_power_clears_disable_schedule --exact --nocapture +#[test] +fn test_reenable_voting_power_clears_disable_schedule() { + new_test_ext(1).execute_with(|| { + let f = VotingPowerTestFixture::new(); + f.enable_tracking(); + + // Schedule disable + assert_ok!(SubtensorModule::disable_voting_power_tracking( + RuntimeOrigin::signed(f.coldkey), + f.netuid + )); + + assert!(SubtensorModule::get_voting_power_disable_at_block(f.netuid) > 0); + + // Re-enable should clear the disable schedule + f.enable_tracking(); + + assert!(SubtensorModule::get_voting_power_tracking_enabled(f.netuid)); + assert_eq!( + SubtensorModule::get_voting_power_disable_at_block(f.netuid), + 0 + ); + }); +} + +// ============================================ +// === Test Grace Period Finalization === +// ============================================ + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::voting_power::test_voting_power_finalized_after_grace_period --exact --nocapture +#[test] +fn test_voting_power_finalized_after_grace_period() { + new_test_ext(1).execute_with(|| { + let f = VotingPowerTestFixture::new(); + f.setup_full(); + + // Build up voting power + f.run_epochs(10); + + let voting_power_before = f.get_voting_power(); + assert!(voting_power_before > 0); + + // Schedule disable + assert_ok!(SubtensorModule::disable_voting_power_tracking( + RuntimeOrigin::signed(f.coldkey), + f.netuid + )); + + let disable_at = SubtensorModule::get_voting_power_disable_at_block(f.netuid); + + // Advance block past grace period (time travel!) + System::set_block_number(disable_at + 1); + + // Run epoch - should finalize disable + f.run_epochs(1); + + // Tracking should be disabled and all entries cleared + assert!(!SubtensorModule::get_voting_power_tracking_enabled( + f.netuid + )); + assert_eq!( + SubtensorModule::get_voting_power_disable_at_block(f.netuid), + 0 + ); + assert_eq!(f.get_voting_power(), 0); + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::voting_power::test_voting_power_continues_during_grace_period --exact --nocapture +#[test] +fn test_voting_power_continues_during_grace_period() { + new_test_ext(1).execute_with(|| { + let f = VotingPowerTestFixture::new(); + f.setup_full(); + + // Schedule disable + assert_ok!(SubtensorModule::disable_voting_power_tracking( + RuntimeOrigin::signed(f.coldkey), + f.netuid + )); + + let disable_at = SubtensorModule::get_voting_power_disable_at_block(f.netuid); + + // Set block to middle of grace period (time travel!) + System::set_block_number(disable_at - 1000); + + // Run epoch - should still update voting power during grace period + f.run_epochs(1); + + // Tracking should still be enabled and voting power should exist + assert!(SubtensorModule::get_voting_power_tracking_enabled(f.netuid)); + assert!(f.get_voting_power() > 0); + }); +} diff --git a/pallets/subtensor/src/utils/mod.rs b/pallets/subtensor/src/utils/mod.rs index 3eb8439959..a91875da59 100644 --- a/pallets/subtensor/src/utils/mod.rs +++ b/pallets/subtensor/src/utils/mod.rs @@ -5,3 +5,4 @@ pub mod misc; pub mod rate_limiting; #[cfg(feature = "try-runtime")] pub mod try_state; +pub mod voting_power; diff --git a/pallets/subtensor/src/utils/voting_power.rs b/pallets/subtensor/src/utils/voting_power.rs new file mode 100644 index 0000000000..380861bbf5 --- /dev/null +++ b/pallets/subtensor/src/utils/voting_power.rs @@ -0,0 +1,280 @@ +use super::*; +use crate::epoch::run_epoch::EpochTerms; +use alloc::collections::BTreeMap; +use subtensor_runtime_common::NetUid; + +/// 14 days in blocks (assuming ~12 second blocks) +/// 14 * 24 * 60 * 60 / 12 = 100800 blocks +pub const VOTING_POWER_DISABLE_GRACE_PERIOD_BLOCKS: u64 = 100800; + +/// Maximum alpha value (1.0 represented as u64 with 18 decimals) +pub const MAX_VOTING_POWER_EMA_ALPHA: u64 = 1_000_000_000_000_000_000; + +impl Pallet { + // ======================== + // === Getters === + // ======================== + + /// Get voting power for a hotkey on a subnet. + /// Returns 0 if not found or tracking disabled. + pub fn get_voting_power(netuid: NetUid, hotkey: &T::AccountId) -> u64 { + VotingPower::::get(netuid, hotkey) + } + + /// Check if voting power tracking is enabled for a subnet. + pub fn get_voting_power_tracking_enabled(netuid: NetUid) -> bool { + VotingPowerTrackingEnabled::::get(netuid) + } + + /// Get the block at which voting power tracking will be disabled. + /// Returns 0 if not scheduled for disabling. + pub fn get_voting_power_disable_at_block(netuid: NetUid) -> u64 { + VotingPowerDisableAtBlock::::get(netuid) + } + + /// Get the EMA alpha value for voting power calculation on a subnet. + pub fn get_voting_power_ema_alpha(netuid: NetUid) -> u64 { + VotingPowerEmaAlpha::::get(netuid) + } + + // ======================== + // === Extrinsic Handlers === + // ======================== + + /// Enable voting power tracking for a subnet. + pub fn do_enable_voting_power_tracking(netuid: NetUid) -> DispatchResult { + // Enable tracking + VotingPowerTrackingEnabled::::insert(netuid, true); + + // Clear any scheduled disable + VotingPowerDisableAtBlock::::remove(netuid); + + // Emit event + Self::deposit_event(Event::VotingPowerTrackingEnabled { netuid }); + + log::info!("VotingPower tracking enabled for netuid {netuid:?}"); + + Ok(()) + } + + /// Schedule disabling of voting power tracking for a subnet. + /// Tracking will continue for 14 days, then automatically disable. + pub fn do_disable_voting_power_tracking(netuid: NetUid) -> DispatchResult { + // Check if tracking is enabled + ensure!( + Self::get_voting_power_tracking_enabled(netuid), + Error::::VotingPowerTrackingNotEnabled + ); + + // Calculate the block at which tracking will be disabled + let current_block = Self::get_current_block_as_u64(); + let disable_at_block = + current_block.saturating_add(VOTING_POWER_DISABLE_GRACE_PERIOD_BLOCKS); + + // Schedule disable + VotingPowerDisableAtBlock::::insert(netuid, disable_at_block); + + // Emit event + Self::deposit_event(Event::VotingPowerTrackingDisableScheduled { + netuid, + disable_at_block, + }); + + log::info!( + "VotingPower tracking scheduled to disable at block {disable_at_block:?} for netuid {netuid:?}" + ); + + Ok(()) + } + + /// Set the EMA alpha value for voting power calculation on a subnet. + pub fn do_set_voting_power_ema_alpha(netuid: NetUid, alpha: u64) -> DispatchResult { + // Validate alpha (must be <= 1.0, represented as 10^18) + ensure!( + alpha <= MAX_VOTING_POWER_EMA_ALPHA, + Error::::InvalidVotingPowerEmaAlpha + ); + + // Set the alpha + VotingPowerEmaAlpha::::insert(netuid, alpha); + + // Emit event + Self::deposit_event(Event::VotingPowerEmaAlphaSet { netuid, alpha }); + + log::info!("VotingPower EMA alpha set to {alpha:?} for netuid {netuid:?}"); + + Ok(()) + } + + // ======================== + // === Epoch Processing === + // ======================== + + /// Update voting power for all validators on a subnet using pre-calculated epoch terms. + pub fn update_voting_power_for_subnet( + netuid: NetUid, + epoch_output: &BTreeMap, + ) { + // Early exit if tracking not enabled + if !Self::get_voting_power_tracking_enabled(netuid) { + return; + } + + // Check if past grace period and should finalize disable + let disable_at = Self::get_voting_power_disable_at_block(netuid); + if disable_at > 0 { + let current_block = Self::get_current_block_as_u64(); + if current_block >= disable_at { + Self::finalize_voting_power_disable(netuid); + return; + } + // Still in grace period - continue updating + } + + // Get the EMA alpha value for this subnet + let alpha = Self::get_voting_power_ema_alpha(netuid); + + // Get minimum stake threshold for validator permit + let min_stake = Self::get_stake_threshold(); + + // Iterate over epoch output using pre-calculated values + for (hotkey, terms) in epoch_output.iter() { + // Only validators (with vpermit) get voting power, not miners + if terms.new_validator_permit { + // Use the subnet-specific stake from epoch calculation + Self::update_voting_power_for_hotkey(netuid, hotkey, terms.stake, alpha, min_stake); + } else { + // Miner without vpermit - remove any existing voting power + VotingPower::::remove(netuid, hotkey); + } + } + + // Remove voting power for any hotkeys that are no longer registered on this subnet + Self::clear_voting_power_for_deregistered_hotkeys(netuid); + + log::trace!("VotingPower updated for validators on netuid {netuid:?}"); + } + + /// Clear voting power for hotkeys that are no longer registered on the subnet. + fn clear_voting_power_for_deregistered_hotkeys(netuid: NetUid) { + // Collect hotkeys to remove (can't mutate while iterating) + let hotkeys_to_remove: Vec = VotingPower::::iter_prefix(netuid) + .filter_map(|(hotkey, _)| { + // If the hotkey is not a network member, it's deregistered + if !IsNetworkMember::::get(&hotkey, netuid) { + Some(hotkey) + } else { + None + } + }) + .collect(); + + // Remove voting power for deregistered hotkeys + for hotkey in hotkeys_to_remove { + VotingPower::::remove(netuid, &hotkey); + log::trace!( + "VotingPower removed for deregistered hotkey {hotkey:?} on netuid {netuid:?}" + ); + } + } + + /// Update voting power EMA for a single hotkey using subnet-specific stake. + fn update_voting_power_for_hotkey( + netuid: NetUid, + hotkey: &T::AccountId, + current_stake: u64, + alpha: u64, + min_stake: u64, + ) { + // Get previous EMA value + let previous_ema = VotingPower::::get(netuid, hotkey); + + // Calculate new EMA value + // new_ema = alpha * current_stake + (1 - alpha) * previous_ema + // All values use 18 decimal precision for alpha (alpha is in range [0, 10^18]) + let new_ema = Self::calculate_voting_power_ema(current_stake, previous_ema, alpha); + + // Only remove if they previously had voting power ABOVE threshold and decayed below. + // This allows new validators to build up voting power from 0 without being removed. + if new_ema < min_stake && previous_ema >= min_stake { + // Was above threshold, now decayed below - remove + VotingPower::::remove(netuid, hotkey); + log::trace!( + "VotingPower removed for hotkey {hotkey:?} on netuid {netuid:?} (decayed below removal threshold: {new_ema:?} < {min_stake:?})" + ); + } else if new_ema > 0 { + // Update voting power (building up or maintaining) + VotingPower::::insert(netuid, hotkey, new_ema); + log::trace!( + "VotingPower updated for hotkey {hotkey:?} on netuid {netuid:?}: {previous_ema:?} -> {new_ema:?}" + ); + } + // If new_ema == 0 do nothing + } + + /// Calculate EMA for voting power. + /// new_ema = alpha * current_stake + (1 - alpha) * previous_ema + /// Alpha is in 18 decimal precision (10^18 = 1.0) + fn calculate_voting_power_ema(current_stake: u64, previous_ema: u64, alpha: u64) -> u64 { + // Use u128 for intermediate calculations to avoid overflow + let alpha_128 = alpha as u128; + let one_minus_alpha = (MAX_VOTING_POWER_EMA_ALPHA as u128).saturating_sub(alpha_128); + let current_128 = current_stake as u128; + let previous_128 = previous_ema as u128; + + // new_ema = (alpha * current_stake + (1 - alpha) * previous_ema) / 10^18 + let numerator = alpha_128 + .saturating_mul(current_128) + .saturating_add(one_minus_alpha.saturating_mul(previous_128)); + + let result = numerator + .checked_div(MAX_VOTING_POWER_EMA_ALPHA as u128) + .unwrap_or(0); + + // Safely convert back to u64, saturating at u64::MAX + result.min(u64::MAX as u128) as u64 + } + + /// Finalize the disabling of voting power tracking. + /// Clears all VotingPower entries for the subnet. + fn finalize_voting_power_disable(netuid: NetUid) { + // Clear all VotingPower entries for this subnet + let _ = VotingPower::::clear_prefix(netuid, u32::MAX, None); + + // Disable tracking + VotingPowerTrackingEnabled::::insert(netuid, false); + + // Clear disable schedule + VotingPowerDisableAtBlock::::remove(netuid); + + // Emit event + Self::deposit_event(Event::VotingPowerTrackingDisabled { netuid }); + + log::info!("VotingPower tracking disabled and entries cleared for netuid {netuid:?}"); + } + + // ======================== + // === Hotkey Swap === + // ======================== + + /// Transfer voting power from old hotkey to new hotkey during swap. + pub fn swap_voting_power_for_hotkey( + old_hotkey: &T::AccountId, + new_hotkey: &T::AccountId, + netuid: NetUid, + ) { + // Get voting power from old hotkey + let voting_power = VotingPower::::take(netuid, old_hotkey); + + // Transfer to new hotkey if non-zero + if voting_power > 0 { + // Add to any existing voting power on new hotkey (in case new hotkey already has some) + let existing = VotingPower::::get(netuid, new_hotkey); + VotingPower::::insert(netuid, new_hotkey, voting_power.saturating_add(existing)); + + log::trace!( + "VotingPower transferred from {old_hotkey:?} to {new_hotkey:?} on netuid {netuid:?}: {voting_power:?}" + ); + } + } +} diff --git a/precompiles/src/lib.rs b/precompiles/src/lib.rs index 864119d89f..abc5e744b2 100644 --- a/precompiles/src/lib.rs +++ b/precompiles/src/lib.rs @@ -41,6 +41,7 @@ use crate::staking::*; use crate::storage_query::*; use crate::subnet::*; use crate::uid_lookup::*; +use crate::voting_power::*; mod address_mapping; mod alpha; @@ -57,6 +58,7 @@ mod staking; mod storage_query; mod subnet; mod uid_lookup; +mod voting_power; pub struct Precompiles(PhantomData); @@ -125,7 +127,7 @@ where Self(Default::default()) } - pub fn used_addresses() -> [H160; 26] { + pub fn used_addresses() -> [H160; 27] { [ hash(1), hash(2), @@ -151,6 +153,7 @@ where hash(AlphaPrecompile::::INDEX), hash(CrowdloanPrecompile::::INDEX), hash(LeasingPrecompile::::INDEX), + hash(VotingPowerPrecompile::::INDEX), hash(ProxyPrecompile::::INDEX), hash(AddressMappingPrecompile::::INDEX), ] @@ -245,6 +248,9 @@ where a if a == hash(LeasingPrecompile::::INDEX) => { LeasingPrecompile::::try_execute::(handle, PrecompileEnum::Leasing) } + a if a == hash(VotingPowerPrecompile::::INDEX) => { + VotingPowerPrecompile::::try_execute::(handle, PrecompileEnum::VotingPower) + } a if a == hash(ProxyPrecompile::::INDEX) => { ProxyPrecompile::::try_execute::(handle, PrecompileEnum::Proxy) } diff --git a/precompiles/src/voting_power.rs b/precompiles/src/voting_power.rs new file mode 100644 index 0000000000..74e1731b6e --- /dev/null +++ b/precompiles/src/voting_power.rs @@ -0,0 +1,131 @@ +use core::marker::PhantomData; + +use fp_evm::PrecompileHandle; +use precompile_utils::EvmResult; +use sp_core::{ByteArray, H256, U256}; +use subtensor_runtime_common::NetUid; + +use crate::PrecompileExt; + +/// VotingPower precompile for smart contract access to validator voting power. +/// +/// This precompile allows smart contracts to query voting power for validators, +/// enabling on-chain governance decisions like slashing and spending. +pub struct VotingPowerPrecompile(PhantomData); + +impl PrecompileExt for VotingPowerPrecompile +where + R: frame_system::Config + pallet_subtensor::Config, + R::AccountId: From<[u8; 32]> + ByteArray, +{ + const INDEX: u64 = 2061; +} + +#[precompile_utils::precompile] +impl VotingPowerPrecompile +where + R: frame_system::Config + pallet_subtensor::Config, + R::AccountId: From<[u8; 32]>, +{ + /// Get voting power for a hotkey on a subnet. + /// + /// Returns the EMA of stake for the hotkey, which represents its voting power. + /// Returns 0 if: + /// - The hotkey has no voting power entry + /// - Voting power tracking is not enabled for the subnet + /// - The hotkey is not registered on the subnet + /// + /// # Arguments + /// * `netuid` - The subnet identifier (u16) + /// * `hotkey` - The hotkey account ID (bytes32) + /// + /// # Returns + /// * `u256` - The voting power value (in RAO, same precision as stake) + #[precompile::public("getVotingPower(uint16,bytes32)")] + #[precompile::view] + fn get_voting_power( + _: &mut impl PrecompileHandle, + netuid: u16, + hotkey: H256, + ) -> EvmResult { + let hotkey = R::AccountId::from(hotkey.0); + let voting_power = pallet_subtensor::VotingPower::::get(NetUid::from(netuid), &hotkey); + Ok(U256::from(voting_power)) + } + + /// Check if voting power tracking is enabled for a subnet. + /// + /// # Arguments + /// * `netuid` - The subnet identifier (u16) + /// + /// # Returns + /// * `bool` - True if voting power tracking is enabled + #[precompile::public("isVotingPowerTrackingEnabled(uint16)")] + #[precompile::view] + fn is_voting_power_tracking_enabled( + _: &mut impl PrecompileHandle, + netuid: u16, + ) -> EvmResult { + Ok(pallet_subtensor::VotingPowerTrackingEnabled::::get( + NetUid::from(netuid), + )) + } + + /// Get the block at which voting power tracking will be disabled. + /// + /// Returns 0 if not scheduled for disabling. + /// When non-zero, tracking continues until this block, then stops. + /// + /// # Arguments + /// * `netuid` - The subnet identifier (u16) + /// + /// # Returns + /// * `u64` - The block number at which tracking will be disabled (0 if not scheduled) + #[precompile::public("getVotingPowerDisableAtBlock(uint16)")] + #[precompile::view] + fn get_voting_power_disable_at_block( + _: &mut impl PrecompileHandle, + netuid: u16, + ) -> EvmResult { + Ok(pallet_subtensor::VotingPowerDisableAtBlock::::get( + NetUid::from(netuid), + )) + } + + /// Get the EMA alpha value for voting power calculation on a subnet. + /// + /// Alpha is stored with 18 decimal precision (1.0 = 10^18). + /// Higher alpha = faster response to stake changes. + /// + /// # Arguments + /// * `netuid` - The subnet identifier (u16) + /// + /// # Returns + /// * `u64` - The alpha value (with 18 decimal precision) + #[precompile::public("getVotingPowerEmaAlpha(uint16)")] + #[precompile::view] + fn get_voting_power_ema_alpha(_: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + Ok(pallet_subtensor::VotingPowerEmaAlpha::::get( + NetUid::from(netuid), + )) + } + + /// Get total voting power for a subnet. + /// + /// Returns the sum of all voting power for all validators on the subnet. + /// Useful for calculating voting thresholds (e.g., 51% quorum). + /// + /// # Arguments + /// * `netuid` - The subnet identifier (u16) + /// + /// # Returns + /// * `u256` - The total voting power across all validators + #[precompile::public("getTotalVotingPower(uint16)")] + #[precompile::view] + fn get_total_voting_power(_: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + let total: u64 = pallet_subtensor::VotingPower::::iter_prefix(NetUid::from(netuid)) + .map(|(_, voting_power)| voting_power) + .fold(0u64, |acc, vp| acc.saturating_add(vp)); + Ok(U256::from(total)) + } +} From d5d985f2d61745243585c31cb0294befe1892dd7 Mon Sep 17 00:00:00 2001 From: Pawel Polewicz Date: Mon, 19 Jan 2026 22:04:02 +0000 Subject: [PATCH 140/240] Use AlphaCurrency type for stake in EpochTerms Changed stake field from u64 to AlphaCurrency for type safety. Updated all conversions in epoch processing and voting power calculations. Fixed tests to use .into() for type conversions. Addresses PR review comment about using type-safe currency types. Co-Authored-By: Claude Sonnet 4.5 --- pallets/subtensor/src/epoch/run_epoch.rs | 8 ++++++-- pallets/subtensor/src/tests/voting_power.rs | 6 +++--- pallets/subtensor/src/utils/voting_power.rs | 6 +++--- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/pallets/subtensor/src/epoch/run_epoch.rs b/pallets/subtensor/src/epoch/run_epoch.rs index f90175ce0c..d9eeafeb3b 100644 --- a/pallets/subtensor/src/epoch/run_epoch.rs +++ b/pallets/subtensor/src/epoch/run_epoch.rs @@ -22,7 +22,7 @@ pub struct EpochTerms { pub validator_trust: u16, pub new_validator_permit: bool, pub bond: Vec<(u16, u16)>, - pub stake: u64, + pub stake: AlphaCurrency, } pub struct EpochOutput(pub BTreeMap); @@ -1017,7 +1017,11 @@ impl Pallet { .get(terms.uid) .copied() .unwrap_or_default(); - terms.stake = raw_stake.get(terms.uid).copied().unwrap_or_default(); + terms.stake = raw_stake + .get(terms.uid) + .copied() + .unwrap_or_default() + .into(); let old_validator_permit = validator_permits .get(terms.uid) .copied() diff --git a/pallets/subtensor/src/tests/voting_power.rs b/pallets/subtensor/src/tests/voting_power.rs index 7edc06fbab..63418fb6a9 100644 --- a/pallets/subtensor/src/tests/voting_power.rs +++ b/pallets/subtensor/src/tests/voting_power.rs @@ -39,7 +39,7 @@ fn build_mock_epoch_output(netuid: NetUid) -> BTreeMap { EpochTerms { uid: uid as usize, new_validator_permit: has_permit, - stake, + stake: stake.into(), ..Default::default() }, ); @@ -583,7 +583,7 @@ fn test_voting_power_not_removed_with_small_dip_below_threshold() { // Build epoch output with stake that will produce EMA around 95% of threshold let mut epoch_output = build_mock_epoch_output(f.netuid); if let Some(terms) = epoch_output.get_mut(&f.hotkey) { - terms.stake = small_dip; // Stake drops but stays in buffer zone + terms.stake = small_dip.into(); // Stake drops but stays in buffer zone } SubtensorModule::update_voting_power_for_subnet(f.netuid, &epoch_output); @@ -620,7 +620,7 @@ fn test_voting_power_removed_with_significant_drop_below_threshold() { EpochTerms { uid: 0, new_validator_permit: true, - stake: 0, // Complete unstake + stake: 0.into(), // Complete unstake ..Default::default() }, ); diff --git a/pallets/subtensor/src/utils/voting_power.rs b/pallets/subtensor/src/utils/voting_power.rs index 380861bbf5..55437f8885 100644 --- a/pallets/subtensor/src/utils/voting_power.rs +++ b/pallets/subtensor/src/utils/voting_power.rs @@ -1,7 +1,7 @@ use super::*; use crate::epoch::run_epoch::EpochTerms; use alloc::collections::BTreeMap; -use subtensor_runtime_common::NetUid; +use subtensor_runtime_common::{AlphaCurrency, NetUid}; /// 14 days in blocks (assuming ~12 second blocks) /// 14 * 24 * 60 * 60 / 12 = 100800 blocks @@ -182,7 +182,7 @@ impl Pallet { fn update_voting_power_for_hotkey( netuid: NetUid, hotkey: &T::AccountId, - current_stake: u64, + current_stake: AlphaCurrency, alpha: u64, min_stake: u64, ) { @@ -192,7 +192,7 @@ impl Pallet { // Calculate new EMA value // new_ema = alpha * current_stake + (1 - alpha) * previous_ema // All values use 18 decimal precision for alpha (alpha is in range [0, 10^18]) - let new_ema = Self::calculate_voting_power_ema(current_stake, previous_ema, alpha); + let new_ema = Self::calculate_voting_power_ema(current_stake.to_u64(), previous_ema, alpha); // Only remove if they previously had voting power ABOVE threshold and decayed below. // This allows new validators to build up voting power from 0 without being removed. From cb74680af3be78b2b5e279245b0fe27481657269 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 20 Jan 2026 01:13:10 +0000 Subject: [PATCH 141/240] auto-update benchmark weights --- pallets/subtensor/src/macros/dispatches.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 240864ae74..7b2677d151 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -1055,7 +1055,7 @@ mod dispatches { /// The extrinsic for user to change its hotkey in subnet or all subnets. #[pallet::call_index(70)] #[pallet::weight((Weight::from_parts(275_300_000, 0) - .saturating_add(T::DbWeight::get().reads(50_u64)) + .saturating_add(T::DbWeight::get().reads(52_u64)) .saturating_add(T::DbWeight::get().writes(35_u64)), DispatchClass::Normal, Pays::No))] pub fn swap_hotkey( origin: OriginFor, @@ -1517,7 +1517,7 @@ mod dispatches { /// User register a new subnetwork #[pallet::call_index(79)] - #[pallet::weight((Weight::from_parts(234_200_000, 0) + #[pallet::weight((Weight::from_parts(396_000_000, 0) .saturating_add(T::DbWeight::get().reads(35_u64)) .saturating_add(T::DbWeight::get().writes(51_u64)), DispatchClass::Normal, Pays::Yes))] pub fn register_network_with_identity( From 550ce8d4c94c8e4ec900d83fa110b1dcc69c1f32 Mon Sep 17 00:00:00 2001 From: John Reed <87283488+JohnReedV@users.noreply.github.com> Date: Mon, 19 Jan 2026 17:13:47 -0800 Subject: [PATCH 142/240] fmt --- pallets/subtensor/src/epoch/run_epoch.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/pallets/subtensor/src/epoch/run_epoch.rs b/pallets/subtensor/src/epoch/run_epoch.rs index d9eeafeb3b..2290b49b8d 100644 --- a/pallets/subtensor/src/epoch/run_epoch.rs +++ b/pallets/subtensor/src/epoch/run_epoch.rs @@ -1017,11 +1017,7 @@ impl Pallet { .get(terms.uid) .copied() .unwrap_or_default(); - terms.stake = raw_stake - .get(terms.uid) - .copied() - .unwrap_or_default() - .into(); + terms.stake = raw_stake.get(terms.uid).copied().unwrap_or_default().into(); let old_validator_permit = validator_permits .get(terms.uid) .copied() From 7f8fdd514c54f0ab17d2ba2f05872d2c84a4b313 Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 20 Jan 2026 14:09:32 +0800 Subject: [PATCH 143/240] fix voting power ema alpha check --- contract-tests/test/votingPower.precompile.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contract-tests/test/votingPower.precompile.test.ts b/contract-tests/test/votingPower.precompile.test.ts index 26b29e257c..f98edd5fc1 100644 --- a/contract-tests/test/votingPower.precompile.test.ts +++ b/contract-tests/test/votingPower.precompile.test.ts @@ -76,8 +76,8 @@ describe("Test VotingPower Precompile", () => { assert.ok(alpha !== undefined, "getVotingPowerEmaAlpha should return a value"); assert.strictEqual(typeof alpha, 'bigint', "getVotingPowerEmaAlpha should return a bigint"); - // Default alpha is 0.1 * 10^18 = 100_000_000_000_000_000 - assert.strictEqual(alpha, BigInt("100000000000000000"), "Default alpha should be 0.1 (100_000_000_000_000_000)"); + // 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)"); }); }); From 4864909bf392c21a6fd175446edfa87b93f26895 Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 20 Jan 2026 17:10:58 +0800 Subject: [PATCH 144/240] fix high network registration fee --- contract-tests/src/subtensor.ts | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/contract-tests/src/subtensor.ts b/contract-tests/src/subtensor.ts index fab9e8cc10..21cab2e1e1 100644 --- a/contract-tests/src/subtensor.ts +++ b/contract-tests/src/subtensor.ts @@ -1,11 +1,12 @@ import * as assert from "assert"; import { devnet, MultiAddress } from '@polkadot-api/descriptors'; -import { TypedApi, TxCallData, Binary, Enum } from 'polkadot-api'; +import { TypedApi, TxCallData, Binary, Enum, getTypedCodecs } from 'polkadot-api'; import { KeyPair } from "@polkadot-labs/hdkd-helpers" import { getAliceSigner, waitForTransactionCompletion, getSignerFromKeypair, waitForTransactionWithRetry } from './substrate' import { convertH160ToSS58, convertPublicKeyToSs58, ethAddressToH160 } from './address-utils' import { tao } from './balance-math' import internal from "stream"; +import { createCodec } from "scale-ts"; // create a new subnet and return netuid export async function addNewSubnetwork(api: TypedApi, hotkey: KeyPair, coldkey: KeyPair) { @@ -26,6 +27,9 @@ export async function addNewSubnetwork(api: TypedApi, hotkey: Key const newTotalNetworks = await api.query.SubtensorModule.TotalNetworks.getValue() // could create multiple subnetworks during retry, just return the first created one assert.ok(newTotalNetworks > totalNetworks) + + // rewrite network last lock cost to 0, to avoid the lock cost calculation error + await setNetworkLastLockCost(api) return totalNetworks } @@ -398,4 +402,19 @@ export async function sendWasmContractExtrinsic(api: TypedApi, co storage_deposit_limit: BigInt(1000000000) }) await waitForTransactionWithRetry(api, tx, signer) -} \ No newline at end of file +} + +export async function setNetworkLastLockCost(api: TypedApi) { + const alice = getAliceSigner() + const key = await api.query.SubtensorModule.NetworkLastLockCost.getKey() + const codec = await getTypedCodecs(devnet); + const value = codec.query.SubtensorModule.NetworkLastLockCost.value.enc(BigInt(0)) + const internalCall = api.tx.System.set_storage({ + items: [[Binary.fromHex(key), Binary.fromBytes(value)]] + }) + const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }) + await waitForTransactionWithRetry(api, tx, alice) + + const valueOnChain = await api.query.SubtensorModule.NetworkLastLockCost.getValue() + assert.equal(BigInt(0), valueOnChain) +} \ No newline at end of file From b2869b6bfb8b832b1b563c662f2710f205fe4cf2 Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 20 Jan 2026 19:40:49 +0800 Subject: [PATCH 145/240] reset as default value --- contract-tests/src/subtensor.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/contract-tests/src/subtensor.ts b/contract-tests/src/subtensor.ts index 21cab2e1e1..1b05ad8d5a 100644 --- a/contract-tests/src/subtensor.ts +++ b/contract-tests/src/subtensor.ts @@ -13,6 +13,8 @@ export async function addNewSubnetwork(api: TypedApi, hotkey: Key const alice = getAliceSigner() const totalNetworks = await api.query.SubtensorModule.TotalNetworks.getValue() + const defaultNetworkLastLockCost = await api.query.SubtensorModule.NetworkLastLockCost.getValue() + const rateLimit = await api.query.SubtensorModule.NetworkRateLimit.getValue() if (rateLimit !== BigInt(0)) { const internalCall = api.tx.AdminUtils.sudo_set_network_rate_limit({ rate_limit: BigInt(0) }) @@ -28,8 +30,8 @@ export async function addNewSubnetwork(api: TypedApi, hotkey: Key // could create multiple subnetworks during retry, just return the first created one assert.ok(newTotalNetworks > totalNetworks) - // rewrite network last lock cost to 0, to avoid the lock cost calculation error - await setNetworkLastLockCost(api) + // reset network last lock cost to 0, to avoid the lock cost calculation error + await setNetworkLastLockCost(api, defaultNetworkLastLockCost) return totalNetworks } @@ -404,11 +406,11 @@ export async function sendWasmContractExtrinsic(api: TypedApi, co await waitForTransactionWithRetry(api, tx, signer) } -export async function setNetworkLastLockCost(api: TypedApi) { +export async function setNetworkLastLockCost(api: TypedApi, defaultNetworkLastLockCost: bigint) { const alice = getAliceSigner() const key = await api.query.SubtensorModule.NetworkLastLockCost.getKey() const codec = await getTypedCodecs(devnet); - const value = codec.query.SubtensorModule.NetworkLastLockCost.value.enc(BigInt(0)) + const value = codec.query.SubtensorModule.NetworkLastLockCost.value.enc(defaultNetworkLastLockCost) const internalCall = api.tx.System.set_storage({ items: [[Binary.fromHex(key), Binary.fromBytes(value)]] }) From 6cf5808ff7609f4963e4c2154da6bbac98c9bdf5 Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 20 Jan 2026 19:46:23 +0800 Subject: [PATCH 146/240] set as default value for last lock cost --- contract-tests/src/subtensor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contract-tests/src/subtensor.ts b/contract-tests/src/subtensor.ts index 1b05ad8d5a..f5829c76aa 100644 --- a/contract-tests/src/subtensor.ts +++ b/contract-tests/src/subtensor.ts @@ -418,5 +418,5 @@ export async function setNetworkLastLockCost(api: TypedApi, defau await waitForTransactionWithRetry(api, tx, alice) const valueOnChain = await api.query.SubtensorModule.NetworkLastLockCost.getValue() - assert.equal(BigInt(0), valueOnChain) + assert.equal(defaultNetworkLastLockCost, valueOnChain) } \ No newline at end of file From 0dece67374e384e5ad20c545fad4451befc745e8 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Tue, 20 Jan 2026 11:36:28 -0300 Subject: [PATCH 147/240] remove reannouncement delay + fix tests --- chain-extensions/src/mock.rs | 2 - pallets/admin-utils/src/lib.rs | 19 --- pallets/admin-utils/src/tests/mod.rs | 36 ------ pallets/subtensor/src/lib.rs | 11 -- pallets/subtensor/src/macros/config.rs | 3 - pallets/subtensor/src/macros/dispatches.rs | 11 +- pallets/subtensor/src/macros/errors.rs | 2 - pallets/subtensor/src/macros/events.rs | 2 - pallets/subtensor/src/tests/mock.rs | 2 - pallets/subtensor/src/tests/swap_coldkey.rs | 128 ++++---------------- pallets/transaction-fee/src/tests/mock.rs | 2 - 11 files changed, 28 insertions(+), 190 deletions(-) diff --git a/chain-extensions/src/mock.rs b/chain-extensions/src/mock.rs index 9902d813c7..2c96ca9d02 100644 --- a/chain-extensions/src/mock.rs +++ b/chain-extensions/src/mock.rs @@ -326,7 +326,6 @@ parameter_types! { pub const InitialLiquidAlphaOn: bool = false; // Default value for LiquidAlphaOn pub const InitialYuma3On: bool = false; // Default value for Yuma3On pub const InitialColdkeySwapAnnouncementDelay: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days - pub const InitialColdkeySwapReannouncementDelay: u64 = 24 * 60 * 60 / 12; // Default as 1 day pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days pub const InitialTaoWeight: u64 = 0; // 100% global weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks @@ -397,7 +396,6 @@ impl pallet_subtensor::Config for Test { type Yuma3On = InitialYuma3On; type Preimages = Preimage; type InitialColdkeySwapAnnouncementDelay = InitialColdkeySwapAnnouncementDelay; - type InitialColdkeySwapReannouncementDelay = InitialColdkeySwapReannouncementDelay; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialTaoWeight = InitialTaoWeight; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 91a09866a8..7307d34c42 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -2238,25 +2238,6 @@ pub mod pallet { log::trace!("ColdkeySwapAnnouncementDelaySet( duration: {duration:?} )"); Ok(()) } - - /// Sets the reannouncement delay for coldkey swap. - #[pallet::call_index(86)] - #[pallet::weight(( - Weight::from_parts(5_000_000, 0) - .saturating_add(T::DbWeight::get().reads(0_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)), - DispatchClass::Operational, - Pays::Yes - ))] - pub fn sudo_set_coldkey_swap_reannouncement_delay( - origin: OriginFor, - duration: BlockNumberFor, - ) -> DispatchResult { - ensure_root(origin)?; - pallet_subtensor::Pallet::::set_coldkey_swap_reannouncement_delay(duration); - log::trace!("ColdkeySwapReannouncementDelaySet( duration: {duration:?} )"); - Ok(()) - } } } diff --git a/pallets/admin-utils/src/tests/mod.rs b/pallets/admin-utils/src/tests/mod.rs index 00ce3d19f4..c707c48975 100644 --- a/pallets/admin-utils/src/tests/mod.rs +++ b/pallets/admin-utils/src/tests/mod.rs @@ -1417,42 +1417,6 @@ fn test_sudo_set_coldkey_swap_announcement_delay() { }); } -#[test] -fn test_sudo_set_coldkey_swap_reannouncement_delay() { - new_test_ext().execute_with(|| { - // Arrange - let root = RuntimeOrigin::root(); - let non_root = RuntimeOrigin::signed(U256::from(1)); - let new_delay = 100u32.into(); - - // Act & Assert: Non-root account should fail - assert_noop!( - AdminUtils::sudo_set_coldkey_swap_reannouncement_delay(non_root, new_delay), - DispatchError::BadOrigin - ); - - // Act: Root account should succeed - assert_ok!(AdminUtils::sudo_set_coldkey_swap_reannouncement_delay( - root.clone(), - new_delay - )); - - // Assert: Check if the delay was actually set - assert_eq!( - pallet_subtensor::ColdkeySwapReannouncementDelay::::get(), - new_delay - ); - - // Act & Assert: Setting the same value again should succeed (idempotent operation) - assert_ok!(AdminUtils::sudo_set_coldkey_swap_reannouncement_delay( - root, new_delay - )); - - // You might want to check for events here if your pallet emits them - System::assert_last_event(Event::ColdkeySwapReannouncementDelaySet(new_delay).into()); - }); -} - #[test] fn test_sudo_set_dissolve_network_schedule_duration() { new_test_ext().execute_with(|| { diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index e9f32628d0..e8577e8f9a 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -950,12 +950,6 @@ pub mod pallet { T::InitialColdkeySwapAnnouncementDelay::get() } - /// Default value for coldkey swap reannouncement delay. - #[pallet::type_value] - pub fn DefaultColdkeySwapReannouncementDelay() -> BlockNumberFor { - T::InitialColdkeySwapReannouncementDelay::get() - } - /// Default value for applying pending items (e.g. childkeys). #[pallet::type_value] pub fn DefaultPendingCooldown() -> u64 { @@ -1360,11 +1354,6 @@ pub mod pallet { pub type ColdkeySwapAnnouncementDelay = StorageValue<_, BlockNumberFor, ValueQuery, DefaultColdkeySwapAnnouncementDelay>; - /// The delay after a reannouncement before a coldkey swap can be performed. - #[pallet::storage] - pub type ColdkeySwapReannouncementDelay = - StorageValue<_, BlockNumberFor, ValueQuery, DefaultColdkeySwapReannouncementDelay>; - /// A map of the coldkey swap announcements from a coldkey /// to the block number the coldkey swap can be performed. #[pallet::storage] diff --git a/pallets/subtensor/src/macros/config.rs b/pallets/subtensor/src/macros/config.rs index 15a8fe90c8..828492099d 100644 --- a/pallets/subtensor/src/macros/config.rs +++ b/pallets/subtensor/src/macros/config.rs @@ -219,9 +219,6 @@ mod config { /// Coldkey swap announcement delay. #[pallet::constant] type InitialColdkeySwapAnnouncementDelay: Get>; - /// Coldkey swap reannouncement delay. - #[pallet::constant] - type InitialColdkeySwapReannouncementDelay: Get>; /// Dissolve network schedule duration #[pallet::constant] type InitialDissolveNetworkScheduleDuration: Get>; diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index a0f70844be..a3e40287a5 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2354,8 +2354,7 @@ mod dispatches { /// This is required before the coldkey swap can be performed /// after the delay period. /// - /// It can be reannounced after a delay of `ColdkeySwapReannouncementDelay` following - /// the first valid execution block of the original announcement. + /// It can be reannounced directly without a delay. /// /// The dispatch origin of this call must be the original coldkey that made the announcement. /// @@ -2376,12 +2375,8 @@ mod dispatches { let who = ensure_signed(origin)?; let now = >::block_number(); - if let Some((when, _)) = ColdkeySwapAnnouncements::::get(who.clone()) { - let reannouncement_delay = ColdkeySwapReannouncementDelay::::get(); - let new_when = when.saturating_add(reannouncement_delay); - ensure!(now >= new_when, Error::::ColdkeySwapReannouncedTooEarly); - } else { - // Only charge the swap cost on the first announcement + // Only charge the swap cost on the first announcement + if !ColdkeySwapAnnouncements::::contains_key(who.clone()) { let swap_cost = Self::get_key_swap_cost(); Self::charge_swap_cost(&who, swap_cost)?; } diff --git a/pallets/subtensor/src/macros/errors.rs b/pallets/subtensor/src/macros/errors.rs index 0f9174275a..0b4793a932 100644 --- a/pallets/subtensor/src/macros/errors.rs +++ b/pallets/subtensor/src/macros/errors.rs @@ -152,8 +152,6 @@ mod errors { ColdkeySwapAnnouncementNotFound, /// Coldkey swap too early. ColdkeySwapTooEarly, - /// Coldkey swap reannounced too early. - ColdkeySwapReannouncedTooEarly, /// The announced coldkey hash does not match the new coldkey hash. AnnouncedColdkeyHashDoesNotMatch, /// New coldkey is hotkey diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index e8a12ab45b..c6efc24549 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -232,8 +232,6 @@ mod events { }, /// The coldkey swap announcement delay has been set. ColdkeySwapAnnouncementDelaySet(BlockNumberFor), - /// The coldkey swap reannouncement delay has been set. - ColdkeySwapReannouncementDelaySet(BlockNumberFor), /// The duration of dissolve network has been set DissolveNetworkScheduleDurationSet(BlockNumberFor), /// Commit-reveal v3 weights have been successfully committed. diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index f0dfd89bdf..23eee220f3 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -217,7 +217,6 @@ parameter_types! { 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 @@ -288,7 +287,6 @@ impl crate::Config for Test { type Yuma3On = InitialYuma3On; type Preimages = Preimage; type InitialColdkeySwapAnnouncementDelay = InitialColdkeySwapAnnouncementDelay; - type InitialColdkeySwapReannouncementDelay = InitialColdkeySwapReannouncementDelay; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialTaoWeight = InitialTaoWeight; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index c7ce2a5f75..bfae709eda 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -53,18 +53,21 @@ fn test_announce_coldkey_swap_works() { SubtensorModule::add_balance_to_coldkey_account(&who, swap_cost + ed); assert_eq!(SubtensorModule::get_coldkey_balance(&who), swap_cost + ed); + // First announcement assert_ok!(SubtensorModule::announce_coldkey_swap( RuntimeOrigin::signed(who), new_coldkey_hash, )); - let now = System::block_number(); + // Only charged on first announcement + assert_eq!(SubtensorModule::get_coldkey_balance(&who), ed); + let delay = ColdkeySwapAnnouncementDelay::::get(); + let now = System::block_number(); assert_eq!( ColdkeySwapAnnouncements::::iter().collect::>(), vec![(who, (now + delay, new_coldkey_hash))] ); - assert_eq!(SubtensorModule::get_coldkey_balance(&who), ed); assert_eq!( last_event(), RuntimeEvent::SubtensorModule(Event::ColdkeySwapAnnounced { @@ -72,84 +75,25 @@ fn test_announce_coldkey_swap_works() { new_coldkey_hash, }) ); - }); -} - -#[test] -fn test_announce_coldkey_swap_with_existing_announcement_past_delay_works() { - new_test_ext(1).execute_with(|| { - let who = U256::from(1); - let new_coldkey = U256::from(2); - let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); - let new_coldkey_2 = U256::from(3); - let new_coldkey_2_hash = ::Hashing::hash_of(&new_coldkey_2); - assert_eq!(ColdkeySwapAnnouncements::::iter().count(), 0); - - let swap_cost = SubtensorModule::get_key_swap_cost().to_u64(); - SubtensorModule::add_balance_to_coldkey_account(&who, 2 * swap_cost); - - assert_ok!(SubtensorModule::announce_coldkey_swap( - RuntimeOrigin::signed(who), - new_coldkey_hash, - )); + run_to_block(now + delay / 2); + // We can reannounce with no delay let now = System::block_number(); - let delay = ColdkeySwapAnnouncementDelay::::get(); - assert_eq!( - ColdkeySwapAnnouncements::::iter().collect::>(), - vec![(who, (now + delay, new_coldkey_hash))] - ); - - let reannouncement_delay = ColdkeySwapReannouncementDelay::::get(); - run_to_block(now + delay + reannouncement_delay); - assert_ok!(SubtensorModule::announce_coldkey_swap( RuntimeOrigin::signed(who), - new_coldkey_2_hash, + new_coldkey_hash, )); - - let now = System::block_number(); assert_eq!( - ColdkeySwapAnnouncements::::iter().collect::>(), - vec![(who, (now + delay, new_coldkey_2_hash))] + last_event(), + RuntimeEvent::SubtensorModule(Event::ColdkeySwapAnnounced { + who, + new_coldkey_hash, + }) ); }); } -#[test] -fn test_announce_coldkey_swap_only_pays_swap_cost_if_no_announcement_exists() { - new_test_ext(1).execute_with(|| { - let who = U256::from(1); - let new_coldkey = U256::from(2); - let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); - let new_coldkey_2 = U256::from(3); - let new_coldkey_2_hash = ::Hashing::hash_of(&new_coldkey_2); - let ed = ExistentialDeposit::get(); - - let swap_cost = SubtensorModule::get_key_swap_cost().to_u64(); - SubtensorModule::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( - RuntimeOrigin::signed(who), - new_coldkey_hash, - )); - assert_eq!(SubtensorModule::get_coldkey_balance(&who), ed); - - let now = System::block_number(); - let base_delay = ColdkeySwapAnnouncementDelay::::get(); - let reannouncement_delay = ColdkeySwapReannouncementDelay::::get(); - run_to_block(now + base_delay + reannouncement_delay); - - assert_ok!(SubtensorModule::announce_coldkey_swap( - RuntimeOrigin::signed(who), - new_coldkey_2_hash, - )); - assert_eq!(SubtensorModule::get_coldkey_balance(&who), ed); - }); -} - #[test] fn test_announce_coldkey_swap_with_bad_origin_fails() { new_test_ext(1).execute_with(|| { @@ -168,40 +112,6 @@ fn test_announce_coldkey_swap_with_bad_origin_fails() { }); } -#[test] -fn test_announce_coldkey_swap_with_existing_announcement_not_past_delay_fails() { - new_test_ext(1).execute_with(|| { - let who = U256::from(1); - let new_coldkey = U256::from(2); - let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); - let new_coldkey_2 = U256::from(3); - let new_coldkey_2_hash = ::Hashing::hash_of(&new_coldkey_2); - - assert_eq!(ColdkeySwapAnnouncements::::iter().count(), 0); - - let swap_cost = SubtensorModule::get_key_swap_cost().to_u64(); - let ed = ExistentialDeposit::get(); - SubtensorModule::add_balance_to_coldkey_account(&who, swap_cost + ed); - - assert_ok!(SubtensorModule::announce_coldkey_swap( - RuntimeOrigin::signed(who), - new_coldkey_hash, - )); - - let now = System::block_number(); - let delay = ColdkeySwapAnnouncementDelay::::get(); - assert_eq!( - ColdkeySwapAnnouncements::::iter().collect::>(), - vec![(who, (now + delay, new_coldkey_hash))] - ); - - assert_noop!( - SubtensorModule::announce_coldkey_swap(RuntimeOrigin::signed(who), new_coldkey_2_hash,), - Error::::ColdkeySwapReannouncedTooEarly - ); - }); -} - #[test] fn test_swap_coldkey_announced_works() { new_test_ext(1).execute_with(|| { @@ -331,6 +241,7 @@ fn test_swap_coldkey_announced_too_early_fails() { let new_coldkey = U256::from(2); let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); + // Now case let now = System::block_number(); let delay = ColdkeySwapAnnouncementDelay::::get(); ColdkeySwapAnnouncements::::insert(who, (now + delay, new_coldkey_hash)); @@ -342,6 +253,17 @@ fn test_swap_coldkey_announced_too_early_fails() { ), Error::::ColdkeySwapTooEarly ); + + // Now + delay - 1 case + run_to_block(now + delay - 1); + + assert_noop!( + SubtensorModule::swap_coldkey_announced( + ::RuntimeOrigin::signed(who), + new_coldkey + ), + Error::::ColdkeySwapTooEarly + ); }) } diff --git a/pallets/transaction-fee/src/tests/mock.rs b/pallets/transaction-fee/src/tests/mock.rs index 3a20bd945d..d194c7d004 100644 --- a/pallets/transaction-fee/src/tests/mock.rs +++ b/pallets/transaction-fee/src/tests/mock.rs @@ -203,7 +203,6 @@ parameter_types! { pub const InitialLiquidAlphaOn: bool = false; // Default value for LiquidAlphaOn pub const InitialYuma3On: bool = false; // Default value for Yuma3On pub const InitialColdkeySwapAnnouncementDelay: u64 = 5 * 24 * 60 * 60 / 12; // 5 days - pub const InitialColdkeySwapReannouncementDelay: u64 = 24 * 60 * 60 / 12; // 1 day pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // 5 days pub const InitialTaoWeight: u64 = u64::MAX/10; // 10% global weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks @@ -273,7 +272,6 @@ impl pallet_subtensor::Config for Test { type Yuma3On = InitialYuma3On; type Preimages = (); type InitialColdkeySwapAnnouncementDelay = InitialColdkeySwapAnnouncementDelay; - type InitialColdkeySwapReannouncementDelay = InitialColdkeySwapReannouncementDelay; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialTaoWeight = InitialTaoWeight; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; From 09d4dbd2345b7ced12764b42571d34cffde2b8b3 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Tue, 20 Jan 2026 11:48:43 -0300 Subject: [PATCH 148/240] remove deadcode --- pallets/subtensor/src/utils/misc.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pallets/subtensor/src/utils/misc.rs b/pallets/subtensor/src/utils/misc.rs index 609e43cf63..c4c81e050c 100644 --- a/pallets/subtensor/src/utils/misc.rs +++ b/pallets/subtensor/src/utils/misc.rs @@ -813,11 +813,6 @@ impl Pallet { Self::deposit_event(Event::ColdkeySwapAnnouncementDelaySet(duration)); } - pub fn set_coldkey_swap_reannouncement_delay(duration: BlockNumberFor) { - ColdkeySwapReannouncementDelay::::set(duration); - Self::deposit_event(Event::ColdkeySwapReannouncementDelaySet(duration)); - } - /// Set the duration for dissolve network /// /// # Arguments From f7c7731cd815fe089b693c8dd996357c7cf1553d Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Tue, 20 Jan 2026 11:53:25 -0300 Subject: [PATCH 149/240] added test for no cost root swap --- pallets/subtensor/src/tests/swap_coldkey.rs | 72 +++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index bfae709eda..bac4b07012 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -399,6 +399,78 @@ fn test_swap_coldkey_works() { }); } +#[test] +fn test_swap_coldkey_works_with_zero_cost() { + new_test_ext(1).execute_with(|| { + let old_coldkey = U256::from(1); + let new_coldkey = U256::from(2); + let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); + let hotkey1 = U256::from(1001); + let hotkey2 = U256::from(1002); + let hotkey3 = U256::from(1003); + let ed = ExistentialDeposit::get(); + let swap_cost = 0u64; + let min_stake = DefaultMinStake::::get().to_u64(); + let stake1 = min_stake * 10; + let stake2 = min_stake * 20; + let stake3 = min_stake * 30; + + SubtensorModule::add_balance_to_coldkey_account( + &old_coldkey, + stake1 + stake2 + stake3 + ed, + ); + + let ( + netuid1, + netuid2, + hotkeys, + hk1_alpha, + hk2_alpha, + hk3_alpha, + total_ck_stake, + identity, + balance_before, + total_stake_before, + ) = comprehensive_setup!( + old_coldkey, + new_coldkey, + new_coldkey_hash, + stake1, + stake2, + stake3, + hotkey1, + hotkey2, + hotkey3 + ); + + assert_ok!(SubtensorModule::swap_coldkey( + ::RuntimeOrigin::root(), + old_coldkey, + new_coldkey, + swap_cost.into(), + )); + + comprehensive_checks!( + old_coldkey, + hotkey1, + hotkey2, + hotkey3, + hotkeys, + new_coldkey, + balance_before, + identity, + netuid1, + netuid2, + hk1_alpha, + hk2_alpha, + hk3_alpha, + total_ck_stake, + total_stake_before, + swap_cost + ); + }); +} + #[test] fn test_swap_coldkey_with_bad_origin_fails() { new_test_ext(1).execute_with(|| { From 340de89c898efce9f9dde9594afa2c4040917fcf Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Tue, 20 Jan 2026 11:53:58 -0300 Subject: [PATCH 150/240] fix call index --- pallets/admin-utils/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 7307d34c42..047db1cbe3 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -2221,7 +2221,7 @@ pub mod pallet { } /// Sets the announcement delay for coldkey swap. - #[pallet::call_index(85)] + #[pallet::call_index(86)] #[pallet::weight(( Weight::from_parts(5_000_000, 0) .saturating_add(T::DbWeight::get().reads(0_u64)) From ae8b5f23e1f6771df43771947fe5e780f3c9b576 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Tue, 20 Jan 2026 11:56:36 -0300 Subject: [PATCH 151/240] remove unused param --- runtime/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 0976e69a30..ed8d163fcc 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -1051,7 +1051,6 @@ parameter_types! { pub const InitialLiquidAlphaOn: bool = false; // Default value for LiquidAlphaOn pub const InitialYuma3On: bool = false; // Default value for Yuma3On pub const InitialColdkeySwapAnnouncementDelay: BlockNumber = prod_or_fast!(5 * 24 * 60 * 60 / 12, 50); // 5 days - pub const InitialColdkeySwapReannouncementDelay: BlockNumber = prod_or_fast!(24 * 60 * 60 / 12, 10); // 1 day pub const InitialDissolveNetworkScheduleDuration: BlockNumber = 5 * 24 * 60 * 60 / 12; // 5 days pub const SubtensorInitialTaoWeight: u64 = 971_718_665_099_567_868; // 0.05267697438728329% tao weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks @@ -1123,7 +1122,6 @@ impl pallet_subtensor::Config for Runtime { type InitialTaoWeight = SubtensorInitialTaoWeight; type Preimages = Preimage; type InitialColdkeySwapAnnouncementDelay = InitialColdkeySwapAnnouncementDelay; - type InitialColdkeySwapReannouncementDelay = InitialColdkeySwapReannouncementDelay; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; type InitialStartCallDelay = InitialStartCallDelay; From cafad6b4023d1ed859357f8e357bb5cf96c3c3b9 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Tue, 20 Jan 2026 12:02:54 -0300 Subject: [PATCH 152/240] check old ck is cleared by root call --- pallets/subtensor/src/tests/swap_coldkey.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index bac4b07012..4c19af1374 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -329,7 +329,7 @@ fn test_swap_coldkey_announced_with_hotkey_fails() { #[test] fn test_swap_coldkey_works() { - new_test_ext(1).execute_with(|| { + new_test_ext(1000).execute_with(|| { let old_coldkey = U256::from(1); let new_coldkey = U256::from(2); let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); @@ -347,6 +347,10 @@ fn test_swap_coldkey_works() { &old_coldkey, swap_cost.to_u64() + stake1 + stake2 + stake3 + ed, ); + + // Some old announcement that will be cleared + let now = System::block_number() - 100; + ColdkeySwapAnnouncements::::insert(old_coldkey, (now, new_coldkey_hash)); let ( netuid1, @@ -396,6 +400,9 @@ fn test_swap_coldkey_works() { total_stake_before, swap_cost.to_u64() ); + + // Check that the old announcement is cleared + assert!(!ColdkeySwapAnnouncements::::contains_key(&old_coldkey)); }); } From fb5f1b9f06c42471977f6452a69547ed403e1b75 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Tue, 20 Jan 2026 12:07:27 -0300 Subject: [PATCH 153/240] fix benchmark import --- pallets/shield/src/benchmarking.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/shield/src/benchmarking.rs b/pallets/shield/src/benchmarking.rs index 8d08185e95..ef5ae8719f 100644 --- a/pallets/shield/src/benchmarking.rs +++ b/pallets/shield/src/benchmarking.rs @@ -4,7 +4,7 @@ use frame_benchmarking::v2::*; use frame_support::{BoundedVec, pallet_prelude::ConstU32}; use frame_system::{RawOrigin, pallet_prelude::BlockNumberFor}; use sp_core::sr25519; -use sp_runtime::traits::Hash as HashT; +use sp_runtime::{traits::Hash as HashT, AccountId32}; use sp_std::vec; // /// Helper to build bounded bytes (public key) of a given length. From 09ae2f18b55fbac982c41a0d845c1160df76ca0f Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Tue, 20 Jan 2026 12:09:30 -0300 Subject: [PATCH 154/240] clippy --- pallets/admin-utils/src/tests/mock.rs | 2 -- pallets/subtensor/src/tests/swap_coldkey.rs | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/pallets/admin-utils/src/tests/mock.rs b/pallets/admin-utils/src/tests/mock.rs index fa3f586cae..8bf6d677a8 100644 --- a/pallets/admin-utils/src/tests/mock.rs +++ b/pallets/admin-utils/src/tests/mock.rs @@ -138,7 +138,6 @@ parameter_types! { pub const InitialLiquidAlphaOn: bool = false; // Default value for LiquidAlphaOn pub const InitialYuma3On: bool = false; // Default value for Yuma3On pub const InitialColdkeySwapAnnouncementDelay: u64 = 5 * 24 * 60 * 60 / 12; // 5 days - pub const InitialColdkeySwapReannouncementDelay: u64 = 24 * 60 * 60 / 12; // 1 day pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // 5 days pub const InitialTaoWeight: u64 = u64::MAX/10; // 10% global weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks @@ -208,7 +207,6 @@ impl pallet_subtensor::Config for Test { type Yuma3On = InitialYuma3On; type Preimages = (); type InitialColdkeySwapAnnouncementDelay = InitialColdkeySwapAnnouncementDelay; - type InitialColdkeySwapReannouncementDelay = InitialColdkeySwapReannouncementDelay; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialTaoWeight = InitialTaoWeight; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index 4c19af1374..688632527b 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -402,7 +402,7 @@ fn test_swap_coldkey_works() { ); // Check that the old announcement is cleared - assert!(!ColdkeySwapAnnouncements::::contains_key(&old_coldkey)); + assert!(!ColdkeySwapAnnouncements::::contains_key(old_coldkey)); }); } From 6a1d7c298b61b036dd3025a66f36341de4d437ca Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Tue, 20 Jan 2026 12:13:23 -0300 Subject: [PATCH 155/240] cargo fmt --- pallets/shield/src/benchmarking.rs | 2 +- pallets/subtensor/src/tests/swap_coldkey.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pallets/shield/src/benchmarking.rs b/pallets/shield/src/benchmarking.rs index ef5ae8719f..e3890bae76 100644 --- a/pallets/shield/src/benchmarking.rs +++ b/pallets/shield/src/benchmarking.rs @@ -4,7 +4,7 @@ use frame_benchmarking::v2::*; use frame_support::{BoundedVec, pallet_prelude::ConstU32}; use frame_system::{RawOrigin, pallet_prelude::BlockNumberFor}; use sp_core::sr25519; -use sp_runtime::{traits::Hash as HashT, AccountId32}; +use sp_runtime::{AccountId32, traits::Hash as HashT}; use sp_std::vec; // /// Helper to build bounded bytes (public key) of a given length. diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index 688632527b..85f8a7da31 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -347,7 +347,7 @@ fn test_swap_coldkey_works() { &old_coldkey, swap_cost.to_u64() + stake1 + stake2 + stake3 + ed, ); - + // Some old announcement that will be cleared let now = System::block_number() - 100; ColdkeySwapAnnouncements::::insert(old_coldkey, (now, new_coldkey_hash)); @@ -400,7 +400,7 @@ fn test_swap_coldkey_works() { total_stake_before, swap_cost.to_u64() ); - + // Check that the old announcement is cleared assert!(!ColdkeySwapAnnouncements::::contains_key(old_coldkey)); }); From 138e8924195640f4916d83534b79313cbc1a3be6 Mon Sep 17 00:00:00 2001 From: Shamil Gadelshin Date: Tue, 20 Jan 2026 18:44:07 +0300 Subject: [PATCH 156/240] Update base call filter --- runtime/src/base_call_filter.rs | 96 +++++++++++++++++++++++++++++++++ runtime/src/lib.rs | 50 ++--------------- 2 files changed, 101 insertions(+), 45 deletions(-) create mode 100644 runtime/src/base_call_filter.rs diff --git a/runtime/src/base_call_filter.rs b/runtime/src/base_call_filter.rs new file mode 100644 index 0000000000..8d76c422c1 --- /dev/null +++ b/runtime/src/base_call_filter.rs @@ -0,0 +1,96 @@ +use crate::RuntimeCall; +use crate::Vec; +use crate::pallet_proxy; +use crate::pallet_utility; +use frame_support::traits::Contains; +use sp_std::boxed::Box; +use sp_std::vec; +pub struct NoNestingCallFilter; + +impl Contains for NoNestingCallFilter { + fn contains(call: &RuntimeCall) -> bool { + let calls = match call { + RuntimeCall::Utility(inner) => { + let calls = match inner { + pallet_utility::Call::force_batch { calls } => calls, + pallet_utility::Call::batch { calls } => calls, + pallet_utility::Call::batch_all { calls } => calls, + _ => return true, + }; + + calls + .iter() + .map(|call| Box::new(call.clone())) + .collect::>() + } + RuntimeCall::Proxy(inner) => { + let call = match inner { + pallet_proxy::Call::proxy { call, .. } => call, + pallet_proxy::Call::proxy_announced { call, .. } => call, + _ => return true, + }; + + vec![call.clone()] + } + RuntimeCall::Multisig(inner) => { + let call = match inner { + pallet_multisig::Call::as_multi { call, .. } => call, + pallet_multisig::Call::as_multi_threshold_1 { call, .. } => call, + _ => return true, + }; + + vec![call.clone()] + } + RuntimeCall::Crowdloan(inner) => { + let call = match inner { + pallet_crowdloan::Call::create { + call: Some(call), .. + } => call, + _ => return true, + }; + + vec![call.clone()] + } + RuntimeCall::Scheduler(inner) => { + let call = match inner { + pallet_scheduler::Call::schedule { call, .. } => call, + pallet_scheduler::Call::schedule_after { call, .. } => call, + pallet_scheduler::Call::schedule_named { call, .. } => call, + pallet_scheduler::Call::schedule_named_after { call, .. } => call, + _ => return true, + }; + + vec![call.clone()] + } + _ => return true, + }; + + !calls.iter().any(|call| { + matches!(&**call, RuntimeCall::Utility(inner) if matches!(inner, pallet_utility::Call::force_batch { .. } | pallet_utility::Call::batch_all { .. } | pallet_utility::Call::batch { .. })) || + matches!(&**call, RuntimeCall::Proxy(inner) if matches!(inner, pallet_proxy::Call::proxy { .. } | pallet_proxy::Call::proxy_announced { .. })) || + matches!(&**call, RuntimeCall::Multisig(inner) if matches!(inner, pallet_multisig::Call::as_multi { .. } | pallet_multisig::Call::as_multi_threshold_1 { .. })) || + matches!(&**call, RuntimeCall::Crowdloan(inner) if matches!(inner, pallet_crowdloan::Call::create { .. } )) || + matches!(&**call, RuntimeCall::Scheduler(inner) if matches!(inner, pallet_scheduler::Call::schedule {..} | pallet_scheduler::Call::schedule_after { .. } | pallet_scheduler::Call::schedule_named {.. } | pallet_scheduler::Call::schedule_named_after { .. } )) || + matches!(&**call, RuntimeCall::Sudo(inner) if matches!(inner, pallet_sudo::Call::sudo {..} | pallet_sudo::Call::sudo_as { .. } | pallet_sudo::Call::sudo_unchecked_weight { .. } )) + }) + } +} + +pub struct SafeModeWhitelistedCalls; +impl Contains for SafeModeWhitelistedCalls { + fn contains(call: &RuntimeCall) -> bool { + matches!( + call, + RuntimeCall::Sudo(_) + | RuntimeCall::Multisig(_) + | RuntimeCall::System(_) + | RuntimeCall::SafeMode(_) + | RuntimeCall::Timestamp(_) + | RuntimeCall::SubtensorModule( + pallet_subtensor::Call::set_weights { .. } + | pallet_subtensor::Call::serve_axon { .. } + ) + | RuntimeCall::Commitments(pallet_commitments::Call::set_commitment { .. }) + ) + } +} diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index e5491e1eea..9324e2c51a 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -10,6 +10,7 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); use core::num::NonZeroU64; +mod base_call_filter; pub mod check_nonce; mod migrations; pub mod sudo_wrapper; @@ -75,6 +76,9 @@ use subtensor_runtime_common::{AlphaCurrency, TaoCurrency, time::*, *}; use subtensor_swap_interface::{Order, SwapHandler}; // A few exports that help ease life for downstream crates. +use crate::base_call_filter::NoNestingCallFilter; +use crate::base_call_filter::SafeModeWhitelistedCalls; +use core::marker::PhantomData; pub use frame_support::{ StorageValue, construct_runtime, parameter_types, traits::{ @@ -94,15 +98,12 @@ pub use pallet_balances::Call as BalancesCall; use pallet_commitments::GetCommitments; pub use pallet_timestamp::Call as TimestampCall; use pallet_transaction_payment::{ConstFeeMultiplier, Multiplier}; +use scale_info::TypeInfo; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; pub use sp_runtime::{Perbill, Permill}; use subtensor_transaction_fee::{SubtensorTxFeeHandler, TransactionFeeHandler}; -use core::marker::PhantomData; - -use scale_info::TypeInfo; - // Frontier use fp_rpc::TransactionStatus; use pallet_ethereum::{Call::transact, PostLogContent, Transaction as EthereumTransaction}; @@ -276,28 +277,6 @@ parameter_types! { pub const SS58Prefix: u8 = 42; } -pub struct NoNestingCallFilter; - -impl Contains for NoNestingCallFilter { - fn contains(call: &RuntimeCall) -> bool { - match call { - RuntimeCall::Utility(inner) => { - let calls = match inner { - pallet_utility::Call::force_batch { calls } => calls, - pallet_utility::Call::batch { calls } => calls, - pallet_utility::Call::batch_all { calls } => calls, - _ => &Vec::new(), - }; - - !calls.iter().any(|call| { - matches!(call, RuntimeCall::Utility(inner) if matches!(inner, pallet_utility::Call::force_batch { .. } | pallet_utility::Call::batch_all { .. } | pallet_utility::Call::batch { .. })) - }) - } - _ => true, - } - } -} - // Configure FRAME pallets to include in runtime. impl frame_system::Config for Runtime { @@ -431,25 +410,6 @@ parameter_types! { pub const DisallowPermissionlessRelease: Option = None; } -pub struct SafeModeWhitelistedCalls; -impl Contains for SafeModeWhitelistedCalls { - fn contains(call: &RuntimeCall) -> bool { - matches!( - call, - RuntimeCall::Sudo(_) - | RuntimeCall::Multisig(_) - | RuntimeCall::System(_) - | RuntimeCall::SafeMode(_) - | RuntimeCall::Timestamp(_) - | RuntimeCall::SubtensorModule( - pallet_subtensor::Call::set_weights { .. } - | pallet_subtensor::Call::serve_axon { .. } - ) - | RuntimeCall::Commitments(pallet_commitments::Call::set_commitment { .. }) - ) - } -} - impl pallet_safe_mode::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; From 703245a71d915cda257f32c4c10fe242de4f075f Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Tue, 20 Jan 2026 12:54:25 -0300 Subject: [PATCH 157/240] bump spec version --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index ed8d163fcc..5de7ffaf80 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -241,7 +241,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: 370, + spec_version: 371, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 3a3379f99e2be6f3166a03c2427142564b6eb08d Mon Sep 17 00:00:00 2001 From: John Reed <87283488+JohnReedV@users.noreply.github.com> Date: Tue, 20 Jan 2026 10:38:34 -0800 Subject: [PATCH 158/240] bump spec --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index e5491e1eea..225c2e2706 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -241,7 +241,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: 370, + spec_version: 371, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 3455b1ba3fb11382a71df48fbfbf1d85f9ef439c Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Tue, 20 Jan 2026 16:44:02 -0300 Subject: [PATCH 159/240] fix benchmarks --- pallets/subtensor/src/benchmarks.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index cf47c79576..5f71ad7293 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -18,7 +18,11 @@ use sp_std::collections::btree_set::BTreeSet; use sp_std::vec; use subtensor_runtime_common::{AlphaCurrency, NetUid, TaoCurrency}; -#[frame_benchmarking::v2::benchmarks] +#[benchmarks( + where + T: pallet_balances::Config, + ::ExistentialDeposit: Get, +)] mod pallet_benchmarks { use super::*; @@ -413,8 +417,9 @@ mod pallet_benchmarks { let new_coldkey: T::AccountId = account("new_coldkey", 0, 0); let new_coldkey_hash: T::Hash = ::Hashing::hash_of(&new_coldkey); + let ed = ::ExistentialDeposit::get(); let swap_cost = Subtensor::::get_key_swap_cost(); - Subtensor::::add_balance_to_coldkey_account(&coldkey, swap_cost.into()); + Subtensor::::add_balance_to_coldkey_account(&coldkey, swap_cost.to_u64() + ed); #[extrinsic_call] _(RawOrigin::Signed(coldkey), new_coldkey_hash); @@ -460,8 +465,9 @@ mod pallet_benchmarks { let new_coldkey: T::AccountId = account("new_coldkey", 0, 0); let hotkey1: T::AccountId = account("hotkey1", 0, 0); + let ed = ::ExistentialDeposit::get(); let swap_cost = Subtensor::::get_key_swap_cost(); - Subtensor::::add_balance_to_coldkey_account(&old_coldkey, swap_cost.into()); + Subtensor::::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64() + ed); let netuid = NetUid::from(1); Subtensor::::init_new_network(netuid, 1); From 9426cc5e7dcea9b065e462fd56de7afcf42f28c5 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Wed, 21 Jan 2026 18:20:10 -0300 Subject: [PATCH 160/240] restore reannouncement logic --- chain-extensions/src/mock.rs | 4 +- pallets/admin-utils/src/benchmarking.rs | 6 ++ pallets/admin-utils/src/lib.rs | 19 ++++ pallets/admin-utils/src/tests/mock.rs | 4 +- pallets/admin-utils/src/tests/mod.rs | 36 +++++++ pallets/subtensor/src/lib.rs | 11 ++ pallets/subtensor/src/macros/config.rs | 3 + pallets/subtensor/src/macros/dispatches.rs | 11 +- pallets/subtensor/src/macros/errors.rs | 2 + pallets/subtensor/src/macros/events.rs | 2 + pallets/subtensor/src/tests/mock.rs | 2 + pallets/subtensor/src/tests/swap_coldkey.rs | 114 +++++++++++++++++--- pallets/subtensor/src/utils/misc.rs | 5 + pallets/transaction-fee/src/tests/mock.rs | 4 +- runtime/src/lib.rs | 2 + 15 files changed, 207 insertions(+), 18 deletions(-) diff --git a/chain-extensions/src/mock.rs b/chain-extensions/src/mock.rs index 2c96ca9d02..27ac5bc06e 100644 --- a/chain-extensions/src/mock.rs +++ b/chain-extensions/src/mock.rs @@ -325,7 +325,8 @@ parameter_types! { 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 = 5 * 24 * 60 * 60 / 12; // Default as 5 days + 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 @@ -396,6 +397,7 @@ impl pallet_subtensor::Config for Test { type Yuma3On = InitialYuma3On; type Preimages = Preimage; type InitialColdkeySwapAnnouncementDelay = InitialColdkeySwapAnnouncementDelay; + type InitialColdkeySwapReannouncementDelay = InitialColdkeySwapReannouncementDelay; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialTaoWeight = InitialTaoWeight; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; diff --git a/pallets/admin-utils/src/benchmarking.rs b/pallets/admin-utils/src/benchmarking.rs index e9f29e184b..e676c40752 100644 --- a/pallets/admin-utils/src/benchmarking.rs +++ b/pallets/admin-utils/src/benchmarking.rs @@ -485,6 +485,12 @@ mod benchmarks { _(RawOrigin::Root, 100u32.into()); } + #[benchmark] + fn sudo_set_coldkey_swap_reannouncement_delay() { + #[extrinsic_call] + _(RawOrigin::Root, 100u32.into()); + } + #[benchmark] fn sudo_set_dissolve_network_schedule_duration() { #[extrinsic_call] diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 047db1cbe3..ab02445c79 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -2238,6 +2238,25 @@ pub mod pallet { log::trace!("ColdkeySwapAnnouncementDelaySet( duration: {duration:?} )"); Ok(()) } + + /// Sets the coldkey swap reannouncement delay. + #[pallet::call_index(87)] + #[pallet::weight(( + Weight::from_parts(5_000_000, 0) + .saturating_add(T::DbWeight::get().reads(0_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)), + DispatchClass::Operational, + Pays::Yes + ))] + pub fn sudo_set_coldkey_swap_reannouncement_delay( + origin: OriginFor, + duration: BlockNumberFor, + ) -> DispatchResult { + ensure_root(origin)?; + pallet_subtensor::Pallet::::set_coldkey_swap_reannouncement_delay(duration); + log::trace!("ColdkeySwapReannouncementDelaySet( duration: {duration:?} )"); + Ok(()) + } } } diff --git a/pallets/admin-utils/src/tests/mock.rs b/pallets/admin-utils/src/tests/mock.rs index 8bf6d677a8..f97e678034 100644 --- a/pallets/admin-utils/src/tests/mock.rs +++ b/pallets/admin-utils/src/tests/mock.rs @@ -137,7 +137,8 @@ parameter_types! { 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 = 5 * 24 * 60 * 60 / 12; // 5 days + pub const InitialColdkeySwapAnnouncementDelay: u64 = 50; + pub const InitialColdkeySwapReannouncementDelay: u64 = 10; pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // 5 days pub const InitialTaoWeight: u64 = u64::MAX/10; // 10% global weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks @@ -207,6 +208,7 @@ impl pallet_subtensor::Config for Test { type Yuma3On = InitialYuma3On; type Preimages = (); type InitialColdkeySwapAnnouncementDelay = InitialColdkeySwapAnnouncementDelay; + type InitialColdkeySwapReannouncementDelay = InitialColdkeySwapReannouncementDelay; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialTaoWeight = InitialTaoWeight; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; diff --git a/pallets/admin-utils/src/tests/mod.rs b/pallets/admin-utils/src/tests/mod.rs index c707c48975..00ce3d19f4 100644 --- a/pallets/admin-utils/src/tests/mod.rs +++ b/pallets/admin-utils/src/tests/mod.rs @@ -1417,6 +1417,42 @@ fn test_sudo_set_coldkey_swap_announcement_delay() { }); } +#[test] +fn test_sudo_set_coldkey_swap_reannouncement_delay() { + new_test_ext().execute_with(|| { + // Arrange + let root = RuntimeOrigin::root(); + let non_root = RuntimeOrigin::signed(U256::from(1)); + let new_delay = 100u32.into(); + + // Act & Assert: Non-root account should fail + assert_noop!( + AdminUtils::sudo_set_coldkey_swap_reannouncement_delay(non_root, new_delay), + DispatchError::BadOrigin + ); + + // Act: Root account should succeed + assert_ok!(AdminUtils::sudo_set_coldkey_swap_reannouncement_delay( + root.clone(), + new_delay + )); + + // Assert: Check if the delay was actually set + assert_eq!( + pallet_subtensor::ColdkeySwapReannouncementDelay::::get(), + new_delay + ); + + // Act & Assert: Setting the same value again should succeed (idempotent operation) + assert_ok!(AdminUtils::sudo_set_coldkey_swap_reannouncement_delay( + root, new_delay + )); + + // You might want to check for events here if your pallet emits them + System::assert_last_event(Event::ColdkeySwapReannouncementDelaySet(new_delay).into()); + }); +} + #[test] fn test_sudo_set_dissolve_network_schedule_duration() { new_test_ext().execute_with(|| { diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index e8577e8f9a..85d369ab31 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -950,6 +950,12 @@ pub mod pallet { T::InitialColdkeySwapAnnouncementDelay::get() } + /// Default value for coldkey swap reannouncement delay. + #[pallet::type_value] + pub fn DefaultColdkeySwapReannouncementDelay() -> BlockNumberFor { + T::InitialColdkeySwapReannouncementDelay::get() + } + /// Default value for applying pending items (e.g. childkeys). #[pallet::type_value] pub fn DefaultPendingCooldown() -> u64 { @@ -1354,6 +1360,11 @@ pub mod pallet { pub type ColdkeySwapAnnouncementDelay = StorageValue<_, BlockNumberFor, ValueQuery, DefaultColdkeySwapAnnouncementDelay>; + /// The delay after the initial delay has passed before a new announcement can be made. + #[pallet::storage] + pub type ColdkeySwapReannouncementDelay = + StorageValue<_, BlockNumberFor, ValueQuery, DefaultColdkeySwapReannouncementDelay>; + /// A map of the coldkey swap announcements from a coldkey /// to the block number the coldkey swap can be performed. #[pallet::storage] diff --git a/pallets/subtensor/src/macros/config.rs b/pallets/subtensor/src/macros/config.rs index 828492099d..15a8fe90c8 100644 --- a/pallets/subtensor/src/macros/config.rs +++ b/pallets/subtensor/src/macros/config.rs @@ -219,6 +219,9 @@ mod config { /// Coldkey swap announcement delay. #[pallet::constant] type InitialColdkeySwapAnnouncementDelay: Get>; + /// Coldkey swap reannouncement delay. + #[pallet::constant] + type InitialColdkeySwapReannouncementDelay: Get>; /// Dissolve network schedule duration #[pallet::constant] type InitialDissolveNetworkScheduleDuration: Get>; diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index a3e40287a5..a0f70844be 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2354,7 +2354,8 @@ mod dispatches { /// This is required before the coldkey swap can be performed /// after the delay period. /// - /// It can be reannounced directly without a delay. + /// It can be reannounced after a delay of `ColdkeySwapReannouncementDelay` following + /// the first valid execution block of the original announcement. /// /// The dispatch origin of this call must be the original coldkey that made the announcement. /// @@ -2375,8 +2376,12 @@ mod dispatches { let who = ensure_signed(origin)?; let now = >::block_number(); - // Only charge the swap cost on the first announcement - if !ColdkeySwapAnnouncements::::contains_key(who.clone()) { + if let Some((when, _)) = ColdkeySwapAnnouncements::::get(who.clone()) { + let reannouncement_delay = ColdkeySwapReannouncementDelay::::get(); + let new_when = when.saturating_add(reannouncement_delay); + ensure!(now >= new_when, Error::::ColdkeySwapReannouncedTooEarly); + } else { + // Only charge the swap cost on the first announcement let swap_cost = Self::get_key_swap_cost(); Self::charge_swap_cost(&who, swap_cost)?; } diff --git a/pallets/subtensor/src/macros/errors.rs b/pallets/subtensor/src/macros/errors.rs index 0b4793a932..0f9174275a 100644 --- a/pallets/subtensor/src/macros/errors.rs +++ b/pallets/subtensor/src/macros/errors.rs @@ -152,6 +152,8 @@ mod errors { ColdkeySwapAnnouncementNotFound, /// Coldkey swap too early. ColdkeySwapTooEarly, + /// Coldkey swap reannounced too early. + ColdkeySwapReannouncedTooEarly, /// The announced coldkey hash does not match the new coldkey hash. AnnouncedColdkeyHashDoesNotMatch, /// New coldkey is hotkey diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index c6efc24549..e8a12ab45b 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -232,6 +232,8 @@ mod events { }, /// The coldkey swap announcement delay has been set. ColdkeySwapAnnouncementDelaySet(BlockNumberFor), + /// The coldkey swap reannouncement delay has been set. + ColdkeySwapReannouncementDelaySet(BlockNumberFor), /// The duration of dissolve network has been set DissolveNetworkScheduleDurationSet(BlockNumberFor), /// Commit-reveal v3 weights have been successfully committed. diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index 23eee220f3..f0dfd89bdf 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -217,6 +217,7 @@ parameter_types! { 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 @@ -287,6 +288,7 @@ impl crate::Config for Test { type Yuma3On = InitialYuma3On; type Preimages = Preimage; type InitialColdkeySwapAnnouncementDelay = InitialColdkeySwapAnnouncementDelay; + type InitialColdkeySwapReannouncementDelay = InitialColdkeySwapReannouncementDelay; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialTaoWeight = InitialTaoWeight; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index 85f8a7da31..5f899aa082 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -53,21 +53,18 @@ fn test_announce_coldkey_swap_works() { SubtensorModule::add_balance_to_coldkey_account(&who, swap_cost + ed); assert_eq!(SubtensorModule::get_coldkey_balance(&who), swap_cost + ed); - // First announcement assert_ok!(SubtensorModule::announce_coldkey_swap( RuntimeOrigin::signed(who), new_coldkey_hash, )); - // Only charged on first announcement - assert_eq!(SubtensorModule::get_coldkey_balance(&who), ed); - let delay = ColdkeySwapAnnouncementDelay::::get(); let now = System::block_number(); assert_eq!( ColdkeySwapAnnouncements::::iter().collect::>(), vec![(who, (now + delay, new_coldkey_hash))] ); + assert_eq!(SubtensorModule::get_coldkey_balance(&who), ed); assert_eq!( last_event(), RuntimeEvent::SubtensorModule(Event::ColdkeySwapAnnounced { @@ -75,25 +72,84 @@ fn test_announce_coldkey_swap_works() { new_coldkey_hash, }) ); + }); +} - run_to_block(now + delay / 2); +#[test] +fn test_announce_coldkey_swap_with_existing_announcement_past_delay_works() { + new_test_ext(1).execute_with(|| { + let who = U256::from(1); + let new_coldkey = U256::from(2); + let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); + let new_coldkey_2 = U256::from(3); + let new_coldkey_2_hash = ::Hashing::hash_of(&new_coldkey_2); + + assert_eq!(ColdkeySwapAnnouncements::::iter().count(), 0); + + let swap_cost = SubtensorModule::get_key_swap_cost().to_u64(); + SubtensorModule::add_balance_to_coldkey_account(&who, 2 * swap_cost); - // We can reannounce with no delay - let now = System::block_number(); assert_ok!(SubtensorModule::announce_coldkey_swap( RuntimeOrigin::signed(who), new_coldkey_hash, )); + + let now = System::block_number(); + let delay = ColdkeySwapAnnouncementDelay::::get(); assert_eq!( - last_event(), - RuntimeEvent::SubtensorModule(Event::ColdkeySwapAnnounced { - who, - new_coldkey_hash, - }) + ColdkeySwapAnnouncements::::iter().collect::>(), + vec![(who, (now + delay, new_coldkey_hash))] + ); + + let reannouncement_delay = ColdkeySwapReannouncementDelay::::get(); + run_to_block(now + delay + reannouncement_delay); + + assert_ok!(SubtensorModule::announce_coldkey_swap( + RuntimeOrigin::signed(who), + new_coldkey_2_hash, + )); + + let now = System::block_number(); + assert_eq!( + ColdkeySwapAnnouncements::::iter().collect::>(), + vec![(who, (now + delay, new_coldkey_2_hash))] ); }); } +#[test] +fn test_announce_coldkey_swap_only_pays_swap_cost_if_no_announcement_exists() { + new_test_ext(1).execute_with(|| { + let who = U256::from(1); + let new_coldkey = U256::from(2); + let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); + let new_coldkey_2 = U256::from(3); + let new_coldkey_2_hash = ::Hashing::hash_of(&new_coldkey_2); + let ed = ExistentialDeposit::get(); + + let swap_cost = SubtensorModule::get_key_swap_cost().to_u64(); + SubtensorModule::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( + RuntimeOrigin::signed(who), + new_coldkey_hash, + )); + assert_eq!(SubtensorModule::get_coldkey_balance(&who), ed); + + let now = System::block_number(); + let base_delay = ColdkeySwapAnnouncementDelay::::get(); + let reannouncement_delay = ColdkeySwapReannouncementDelay::::get(); + run_to_block(now + base_delay + reannouncement_delay); + + assert_ok!(SubtensorModule::announce_coldkey_swap( + RuntimeOrigin::signed(who), + new_coldkey_2_hash, + )); + assert_eq!(SubtensorModule::get_coldkey_balance(&who), ed); + }); +} + #[test] fn test_announce_coldkey_swap_with_bad_origin_fails() { new_test_ext(1).execute_with(|| { @@ -112,6 +168,40 @@ fn test_announce_coldkey_swap_with_bad_origin_fails() { }); } +#[test] +fn test_announce_coldkey_swap_with_existing_announcement_not_past_delay_fails() { + new_test_ext(1).execute_with(|| { + let who = U256::from(1); + let new_coldkey = U256::from(2); + let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); + let new_coldkey_2 = U256::from(3); + let new_coldkey_2_hash = ::Hashing::hash_of(&new_coldkey_2); + + assert_eq!(ColdkeySwapAnnouncements::::iter().count(), 0); + + let swap_cost = SubtensorModule::get_key_swap_cost().to_u64(); + let ed = ExistentialDeposit::get(); + SubtensorModule::add_balance_to_coldkey_account(&who, swap_cost + ed); + + assert_ok!(SubtensorModule::announce_coldkey_swap( + RuntimeOrigin::signed(who), + new_coldkey_hash, + )); + + let now = System::block_number(); + let delay = ColdkeySwapAnnouncementDelay::::get(); + assert_eq!( + ColdkeySwapAnnouncements::::iter().collect::>(), + vec![(who, (now + delay, new_coldkey_hash))] + ); + + assert_noop!( + SubtensorModule::announce_coldkey_swap(RuntimeOrigin::signed(who), new_coldkey_2_hash,), + Error::::ColdkeySwapReannouncedTooEarly + ); + }); +} + #[test] fn test_swap_coldkey_announced_works() { new_test_ext(1).execute_with(|| { diff --git a/pallets/subtensor/src/utils/misc.rs b/pallets/subtensor/src/utils/misc.rs index c4c81e050c..609e43cf63 100644 --- a/pallets/subtensor/src/utils/misc.rs +++ b/pallets/subtensor/src/utils/misc.rs @@ -813,6 +813,11 @@ impl Pallet { Self::deposit_event(Event::ColdkeySwapAnnouncementDelaySet(duration)); } + pub fn set_coldkey_swap_reannouncement_delay(duration: BlockNumberFor) { + ColdkeySwapReannouncementDelay::::set(duration); + Self::deposit_event(Event::ColdkeySwapReannouncementDelaySet(duration)); + } + /// Set the duration for dissolve network /// /// # Arguments diff --git a/pallets/transaction-fee/src/tests/mock.rs b/pallets/transaction-fee/src/tests/mock.rs index d194c7d004..7a54f98c5d 100644 --- a/pallets/transaction-fee/src/tests/mock.rs +++ b/pallets/transaction-fee/src/tests/mock.rs @@ -202,7 +202,8 @@ parameter_types! { 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 = 5 * 24 * 60 * 60 / 12; // 5 days + pub const InitialColdkeySwapAnnouncementDelay: u64 = 50; + pub const InitialColdkeySwapReannouncementDelay: u64 = 10; pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // 5 days pub const InitialTaoWeight: u64 = u64::MAX/10; // 10% global weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks @@ -272,6 +273,7 @@ impl pallet_subtensor::Config for Test { type Yuma3On = InitialYuma3On; type Preimages = (); type InitialColdkeySwapAnnouncementDelay = InitialColdkeySwapAnnouncementDelay; + type InitialColdkeySwapReannouncementDelay = InitialColdkeySwapReannouncementDelay; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialTaoWeight = InitialTaoWeight; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 5de7ffaf80..9ff792dda1 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -1051,6 +1051,7 @@ parameter_types! { pub const InitialLiquidAlphaOn: bool = false; // Default value for LiquidAlphaOn pub const InitialYuma3On: bool = false; // Default value for Yuma3On pub const InitialColdkeySwapAnnouncementDelay: BlockNumber = prod_or_fast!(5 * 24 * 60 * 60 / 12, 50); // 5 days + pub const InitialColdkeySwapReannouncementDelay: BlockNumber = prod_or_fast!(24 * 60 * 60 / 12, 10); // 1 day pub const InitialDissolveNetworkScheduleDuration: BlockNumber = 5 * 24 * 60 * 60 / 12; // 5 days pub const SubtensorInitialTaoWeight: u64 = 971_718_665_099_567_868; // 0.05267697438728329% tao weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks @@ -1122,6 +1123,7 @@ impl pallet_subtensor::Config for Runtime { type InitialTaoWeight = SubtensorInitialTaoWeight; type Preimages = Preimage; type InitialColdkeySwapAnnouncementDelay = InitialColdkeySwapAnnouncementDelay; + type InitialColdkeySwapReannouncementDelay = InitialColdkeySwapReannouncementDelay; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; type InitialStartCallDelay = InitialStartCallDelay; From 566efcd834a30b1ebd9557e14fb9470a7208010a Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Wed, 21 Jan 2026 20:42:27 -0300 Subject: [PATCH 161/240] add coldkey swap dispute logic and tests --- pallets/subtensor/src/benchmarks.rs | 15 +- pallets/subtensor/src/lib.rs | 8 ++ pallets/subtensor/src/macros/dispatches.rs | 51 +++++-- pallets/subtensor/src/macros/errors.rs | 2 + pallets/subtensor/src/macros/events.rs | 5 + pallets/subtensor/src/tests/swap_coldkey.rs | 135 ++++++++++++++++-- .../subtensor/src/transaction_extension.rs | 8 +- 7 files changed, 199 insertions(+), 25 deletions(-) diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index 5f71ad7293..ce3238e2e6 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -497,13 +497,26 @@ mod pallet_benchmarks { } #[benchmark] - fn remove_coldkey_swap_announcement() { + fn dispute_coldkey_swap() { let coldkey: T::AccountId = account("old_coldkey", 0, 0); let coldkey_hash: T::Hash = ::Hashing::hash_of(&coldkey); let now = frame_system::Pallet::::block_number(); ColdkeySwapAnnouncements::::insert(&coldkey, (now, coldkey_hash)); + #[extrinsic_call] + _(RawOrigin::Signed(coldkey)); + } + + #[benchmark] + fn reset_coldkey_swap() { + let coldkey: T::AccountId = account("old_coldkey", 0, 0); + let coldkey_hash: T::Hash = ::Hashing::hash_of(&coldkey); + let now = frame_system::Pallet::::block_number(); + + ColdkeySwapAnnouncements::::insert(&coldkey, (now, coldkey_hash)); + ColdkeySwapDisputes::::insert(&coldkey, now); + #[extrinsic_call] _(RawOrigin::Root, coldkey); } diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 85d369ab31..8c77c23397 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1371,6 +1371,12 @@ pub mod pallet { pub type ColdkeySwapAnnouncements = StorageMap<_, Twox64Concat, T::AccountId, (BlockNumberFor, T::Hash), OptionQuery>; + /// A map of the coldkey swap disputes from a coldkey to the + /// block number the coldkey swap was disputed. + #[pallet::storage] + pub type ColdkeySwapDisputes = + StorageMap<_, Twox64Concat, T::AccountId, BlockNumberFor, OptionQuery>; + /// --- DMAP ( hot, netuid ) --> alpha | Returns the total amount of alpha a hotkey owns. #[pallet::storage] pub type TotalHotkeyAlpha = StorageDoubleMap< @@ -2449,6 +2455,7 @@ pub enum CustomTransactionError { InputLengthsUnequal, UidNotFound, EvmKeyAssociateRateLimitExceeded, + ColdkeySwapDisputed, } impl From for u8 { @@ -2476,6 +2483,7 @@ impl From for u8 { CustomTransactionError::InputLengthsUnequal => 18, CustomTransactionError::UidNotFound => 19, CustomTransactionError::EvmKeyAssociateRateLimitExceeded => 20, + CustomTransactionError::ColdkeySwapDisputed => 21, } } } diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index a0f70844be..1f68bb77d3 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -1087,8 +1087,9 @@ mod dispatches { } Self::do_swap_coldkey(&old_coldkey, &new_coldkey)?; - // We also remove any announcement for security reasons - ColdkeySwapAnnouncements::::remove(old_coldkey); + // We also clear any announcement or dispute for security reasons + ColdkeySwapAnnouncements::::remove(&old_coldkey); + ColdkeySwapDisputes::::remove(old_coldkey); Ok(()) } @@ -2433,11 +2434,12 @@ mod dispatches { Ok(()) } - /// Removes a coldkey swap announcement for a coldkey. + /// Dispute a coldkey swap. /// - /// The dispatch origin of this call must be root. + /// This will prevent any further actions on the coldkey swap + /// until triumvirate step in to resolve the issue. /// - /// - `coldkey`: The coldkey to remove the swap announcement for. + /// - `coldkey`: The coldkey to dispute the swap for. /// #[pallet::call_index(127)] #[pallet::weight( @@ -2445,12 +2447,41 @@ mod dispatches { .saturating_add(T::DbWeight::get().reads(0_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) )] - pub fn remove_coldkey_swap_announcement( - origin: OriginFor, - coldkey: T::AccountId, - ) -> DispatchResult { + pub fn dispute_coldkey_swap(origin: OriginFor) -> DispatchResult { + let coldkey = ensure_signed(origin)?; + + ensure!( + ColdkeySwapAnnouncements::::contains_key(&coldkey), + Error::::ColdkeySwapAnnouncementNotFound + ); + ensure!( + !ColdkeySwapDisputes::::contains_key(&coldkey), + Error::::ColdkeySwapAlreadyDisputed + ); + + let now = >::block_number(); + ColdkeySwapDisputes::::insert(&coldkey, now); + + Self::deposit_event(Event::ColdkeySwapDisputed { coldkey }); + Ok(()) + } + + /// Reset a coldkey swap by clearing the announcement and dispute status. + /// + /// The dispatch origin of this call must be root. + /// + /// - `coldkey`: The coldkey to reset the swap for. + /// + #[pallet::call_index(128)] + #[pallet::weight( + Weight::from_parts(4_609_000, 0) + .saturating_add(T::DbWeight::get().reads(0_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + )] + pub fn reset_coldkey_swap(origin: OriginFor, coldkey: T::AccountId) -> DispatchResult { ensure_root(origin)?; - ColdkeySwapAnnouncements::::remove(coldkey); + ColdkeySwapAnnouncements::::remove(&coldkey); + ColdkeySwapDisputes::::remove(coldkey); Ok(()) } } diff --git a/pallets/subtensor/src/macros/errors.rs b/pallets/subtensor/src/macros/errors.rs index 0f9174275a..da9f238963 100644 --- a/pallets/subtensor/src/macros/errors.rs +++ b/pallets/subtensor/src/macros/errors.rs @@ -156,6 +156,8 @@ mod errors { ColdkeySwapReannouncedTooEarly, /// The announced coldkey hash does not match the new coldkey hash. AnnouncedColdkeyHashDoesNotMatch, + /// Coldkey swap already disputed + ColdkeySwapAlreadyDisputed, /// New coldkey is hotkey NewColdKeyIsHotkey, /// Childkey take is invalid. diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index e8a12ab45b..d213322f36 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -191,6 +191,11 @@ mod events { /// The account ID of new coldkey. new_coldkey: T::AccountId, }, + /// A coldkey swap has been disputed. + ColdkeySwapDisputed { + /// The account ID of the coldkey that was disputed. + coldkey: T::AccountId, + }, /// All balance of a hotkey has been unstaked and transferred to a new coldkey AllBalanceUnstakedAndTransferredToNewColdkey { /// The account ID of the current coldkey diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index 5f899aa082..5ce44dfa1d 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -438,9 +438,10 @@ fn test_swap_coldkey_works() { swap_cost.to_u64() + stake1 + stake2 + stake3 + ed, ); - // Some old announcement that will be cleared + // Some old announcement and dispute that will be cleared let now = System::block_number() - 100; ColdkeySwapAnnouncements::::insert(old_coldkey, (now, new_coldkey_hash)); + ColdkeySwapDisputes::::insert(old_coldkey, now); let ( netuid1, @@ -491,8 +492,9 @@ fn test_swap_coldkey_works() { swap_cost.to_u64() ); - // Check that the old announcement is cleared + // Check that the old announcement and dispute are cleared assert!(!ColdkeySwapAnnouncements::::contains_key(old_coldkey)); + assert!(!ColdkeySwapDisputes::::contains_key(old_coldkey)); }); } @@ -1376,7 +1378,7 @@ fn test_do_swap_coldkey_effect_on_delegations() { } #[test] -fn test_remove_coldkey_swap_announcement_works() { +fn test_dispute_coldkey_swap_works() { new_test_ext(1).execute_with(|| { let who = U256::from(1); let new_coldkey = U256::from(2); @@ -1385,35 +1387,114 @@ fn test_remove_coldkey_swap_announcement_works() { ColdkeySwapAnnouncements::::insert(who, (now, new_coldkey_hash)); - assert_ok!(SubtensorModule::remove_coldkey_swap_announcement( + assert_ok!(SubtensorModule::dispute_coldkey_swap( + RuntimeOrigin::signed(who) + )); + + assert_eq!(ColdkeySwapDisputes::::get(who), Some(now)); + assert!(matches!( + last_event(), + RuntimeEvent::SubtensorModule(Event::ColdkeySwapDisputed { coldkey: _ }) + )); + }); +} + +#[test] +fn test_dispute_coldkey_swap_with_bad_origin_fails() { + new_test_ext(1).execute_with(|| { + let who = U256::from(1); + let new_coldkey = U256::from(2); + let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); + let now = System::block_number(); + + ColdkeySwapAnnouncements::::insert(who, (now, new_coldkey_hash)); + + assert_noop!( + SubtensorModule::dispute_coldkey_swap(RuntimeOrigin::root()), + BadOrigin + ); + + assert_noop!( + SubtensorModule::dispute_coldkey_swap(RuntimeOrigin::none()), + BadOrigin + ); + }); +} + +#[test] +fn test_dispute_coldkey_swap_without_announcement_fails() { + new_test_ext(1).execute_with(|| { + let who = U256::from(1); + let new_coldkey = U256::from(2); + let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); + let now = System::block_number(); + + assert_noop!( + SubtensorModule::dispute_coldkey_swap(RuntimeOrigin::signed(who)), + Error::::ColdkeySwapAnnouncementNotFound + ); + }); +} + +#[test] +fn test_dispute_coldkey_swap_already_disputed_fails() { + new_test_ext(1).execute_with(|| { + let who = U256::from(1); + let new_coldkey = U256::from(2); + let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); + let now = System::block_number(); + + ColdkeySwapAnnouncements::::insert(who, (now, new_coldkey_hash)); + ColdkeySwapDisputes::::insert(who, now); + + assert_noop!( + SubtensorModule::dispute_coldkey_swap(RuntimeOrigin::signed(who)), + Error::::ColdkeySwapAlreadyDisputed + ); + }); +} + +#[test] +fn test_reset_coldkey_swap_works() { + new_test_ext(1).execute_with(|| { + let who = U256::from(1); + let new_coldkey = U256::from(2); + let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); + let now = System::block_number(); + + ColdkeySwapAnnouncements::::insert(who, (now, new_coldkey_hash)); + ColdkeySwapDisputes::::insert(who, now); + + assert_ok!(SubtensorModule::reset_coldkey_swap( RuntimeOrigin::root(), who, )); assert!(!ColdkeySwapAnnouncements::::contains_key(who)); + assert!(!ColdkeySwapDisputes::::contains_key(who)); }); } #[test] -fn test_remove_coldkey_swap_announcement_with_bad_origin_fails() { +fn test_reset_coldkey_swap_with_bad_origin_fails() { new_test_ext(1).execute_with(|| { let who = U256::from(1); let coldkey = U256::from(2); assert_noop!( - SubtensorModule::remove_coldkey_swap_announcement(RuntimeOrigin::signed(who), coldkey), + SubtensorModule::reset_coldkey_swap(RuntimeOrigin::signed(who), coldkey), BadOrigin ); assert_noop!( - SubtensorModule::remove_coldkey_swap_announcement(RuntimeOrigin::none(), coldkey), + SubtensorModule::reset_coldkey_swap(RuntimeOrigin::none(), coldkey), BadOrigin ); }); } #[test] -fn test_subtensor_extension_rejects_any_call_that_is_not_announce_or_swap() { +fn test_subtensor_extension_rejects_calls_when_announced_or_disputed() { new_test_ext(0).execute_with(|| { let netuid = NetUid::from(1); let who = U256::from(0); @@ -1515,11 +1596,17 @@ fn test_subtensor_extension_rejects_any_call_that_is_not_announce_or_swap() { }), ]; - for call in forbidden_calls { + for call in &forbidden_calls { let info = call.get_dispatch_info(); let ext = SubtensorTransactionExtension::::new(); assert_noop!( - ext.dispatch_transaction(RuntimeOrigin::signed(who).into(), call, &info, 0, 0), + ext.dispatch_transaction( + RuntimeOrigin::signed(who).into(), + call.clone(), + &info, + 0, + 0 + ), CustomTransactionError::ColdkeySwapAnnounced ); } @@ -1535,17 +1622,41 @@ fn test_subtensor_extension_rejects_any_call_that_is_not_announce_or_swap() { }), ]; - for call in authorized_calls { + for call in &authorized_calls { let info = call.get_dispatch_info(); let ext = SubtensorTransactionExtension::::new(); assert_ok!(ext.dispatch_transaction( RuntimeOrigin::signed(who).into(), - call, + call.clone(), &info, 0, 0 )); } + + // Dispute the coldkey swap + assert_ok!(SubtensorModule::dispute_coldkey_swap( + ::RuntimeOrigin::signed(who), + )); + assert!(ColdkeySwapDisputes::::contains_key(who)); + + // All calls should fail when the coldkey swap is disputed + let all_calls = authorized_calls.iter().chain(forbidden_calls.iter()); + + for call in all_calls { + let info = call.get_dispatch_info(); + let ext = SubtensorTransactionExtension::::new(); + assert_noop!( + ext.dispatch_transaction( + RuntimeOrigin::signed(who).into(), + call.clone(), + &info, + 0, + 0 + ), + CustomTransactionError::ColdkeySwapDisputed + ); + } }); } diff --git a/pallets/subtensor/src/transaction_extension.rs b/pallets/subtensor/src/transaction_extension.rs index e842a9b6ce..026a2580f4 100644 --- a/pallets/subtensor/src/transaction_extension.rs +++ b/pallets/subtensor/src/transaction_extension.rs @@ -1,6 +1,6 @@ use crate::{ - BalancesCall, Call, ColdkeySwapAnnouncements, Config, CustomTransactionError, Error, Pallet, - TransactionType, + BalancesCall, Call, ColdkeySwapAnnouncements, ColdkeySwapDisputes, Config, + CustomTransactionError, Error, Pallet, TransactionType, }; use codec::{Decode, DecodeWithMemTracking, Encode}; use frame_support::dispatch::{DispatchInfo, PostDispatchInfo}; @@ -117,6 +117,10 @@ where }; if ColdkeySwapAnnouncements::::contains_key(who) { + if ColdkeySwapDisputes::::contains_key(who) { + return Err(CustomTransactionError::ColdkeySwapDisputed.into()); + } + let is_allowed_direct = matches!( call.is_sub_type(), Some(Call::announce_coldkey_swap { .. }) From 2f8b1e8a6803b99fdba2a0e4b69d57bbdf45b069 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Wed, 21 Jan 2026 20:44:19 -0300 Subject: [PATCH 162/240] restore contract-tests package json --- contract-tests/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contract-tests/package.json b/contract-tests/package.json index 0bf4adac9d..5e2413d766 100644 --- a/contract-tests/package.json +++ b/contract-tests/package.json @@ -1,6 +1,6 @@ { "scripts": { - "test": "TS_NODE_PREFER_TS_EXTS=1 TS_NODE_TRANSPILE_ONLY=1 mocha --timeout 999999 --retries 3 --file src/setup.ts --require ts-node/register --extension ts \"test/subnet.precompile.hyperparameter.test.ts\"" + "test": "TS_NODE_PREFER_TS_EXTS=1 TS_NODE_TRANSPILE_ONLY=1 mocha --timeout 999999 --retries 3 --file src/setup.ts --require ts-node/register --extension ts \"test/**/*.ts\"" }, "keywords": [], "author": "", From 8b7c1ad984cf9274f016160bee69d2d6b1e024d0 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Wed, 21 Jan 2026 21:21:25 -0300 Subject: [PATCH 163/240] bump spec version --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 9ff792dda1..511066a87c 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -241,7 +241,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: 371, + spec_version: 372, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 89c6e155a68c789af5269916e329f321d56e985b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 22 Jan 2026 02:33:37 +0000 Subject: [PATCH 164/240] auto-update benchmark weights --- pallets/subtensor/src/macros/dispatches.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 1f68bb77d3..f46cb42787 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -1072,7 +1072,7 @@ mod dispatches { #[pallet::weight( Weight::from_parts(183_600_000, 0) .saturating_add(T::DbWeight::get().reads(17_u64)) - .saturating_add(T::DbWeight::get().writes(9_u64)) + .saturating_add(T::DbWeight::get().writes(10_u64)) )] pub fn swap_coldkey( origin: OriginFor, @@ -2443,8 +2443,8 @@ mod dispatches { /// #[pallet::call_index(127)] #[pallet::weight( - Weight::from_parts(4_609_000, 0) - .saturating_add(T::DbWeight::get().reads(0_u64)) + Weight::from_parts(20_750_000, 0) + .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) )] pub fn dispute_coldkey_swap(origin: OriginFor) -> DispatchResult { @@ -2476,7 +2476,7 @@ mod dispatches { #[pallet::weight( Weight::from_parts(4_609_000, 0) .saturating_add(T::DbWeight::get().reads(0_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) )] pub fn reset_coldkey_swap(origin: OriginFor, coldkey: T::AccountId) -> DispatchResult { ensure_root(origin)?; From 1af61f8cfa4f2d24a58078d973fb71855d9ce1c9 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Thu, 22 Jan 2026 11:35:19 -0300 Subject: [PATCH 165/240] fix event emitting for reset_coldkey_swap --- pallets/subtensor/src/macros/dispatches.rs | 5 ++++- pallets/subtensor/src/macros/events.rs | 6 +++--- pallets/subtensor/src/tests/swap_coldkey.rs | 10 +++++++--- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index f46cb42787..7526b23a13 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2480,8 +2480,11 @@ mod dispatches { )] pub fn reset_coldkey_swap(origin: OriginFor, coldkey: T::AccountId) -> DispatchResult { ensure_root(origin)?; + ColdkeySwapAnnouncements::::remove(&coldkey); - ColdkeySwapDisputes::::remove(coldkey); + ColdkeySwapDisputes::::remove(&coldkey); + + Self::deposit_event(Event::ColdkeySwapReset { who: coldkey }); Ok(()) } } diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index d213322f36..4fa69b100a 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -179,9 +179,9 @@ mod events { /// The hash of the new coldkey. new_coldkey_hash: T::Hash, }, - /// A coldkey swap announcement has been removed. - ColdkeySwapAnnouncementRemoved { - /// The account ID of the coldkey that made the announcement. + /// A coldkey swap has been reset. + ColdkeySwapReset { + /// The account ID of the coldkey for which the swap has been reset. who: T::AccountId, }, /// A coldkey has been swapped. diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index 5ce44dfa1d..28682d7b94 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -1467,11 +1467,15 @@ fn test_reset_coldkey_swap_works() { assert_ok!(SubtensorModule::reset_coldkey_swap( RuntimeOrigin::root(), - who, + who.clone(), )); - assert!(!ColdkeySwapAnnouncements::::contains_key(who)); - assert!(!ColdkeySwapDisputes::::contains_key(who)); + assert!(!ColdkeySwapAnnouncements::::contains_key(&who)); + assert!(!ColdkeySwapDisputes::::contains_key(&who)); + assert!(matches!( + last_event(), + RuntimeEvent::SubtensorModule(Event::ColdkeySwapReset { who }) + )); }); } From 9c11f2e2000118a615511505137999ad8fe23186 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Thu, 22 Jan 2026 12:00:00 -0300 Subject: [PATCH 166/240] cargo clippy --- pallets/subtensor/src/tests/swap_coldkey.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index 28682d7b94..fea013fe0a 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -1467,11 +1467,11 @@ fn test_reset_coldkey_swap_works() { assert_ok!(SubtensorModule::reset_coldkey_swap( RuntimeOrigin::root(), - who.clone(), + who, )); - assert!(!ColdkeySwapAnnouncements::::contains_key(&who)); - assert!(!ColdkeySwapDisputes::::contains_key(&who)); + assert!(!ColdkeySwapAnnouncements::::contains_key(who)); + assert!(!ColdkeySwapDisputes::::contains_key(who)); assert!(matches!( last_event(), RuntimeEvent::SubtensorModule(Event::ColdkeySwapReset { who }) From def22e2cf77f5a0befabaef482e38c3f4f57f397 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 22 Jan 2026 18:37:09 +0000 Subject: [PATCH 167/240] auto-update benchmark weights --- pallets/subtensor/src/macros/dispatches.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 7526b23a13..06027348b6 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2474,7 +2474,7 @@ mod dispatches { /// #[pallet::call_index(128)] #[pallet::weight( - Weight::from_parts(4_609_000, 0) + Weight::from_parts(8_977_000, 0) .saturating_add(T::DbWeight::get().reads(0_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) )] From 682a4a903865d1120ad454fe72df5b7336ca1813 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Thu, 22 Jan 2026 14:29:30 -0500 Subject: [PATCH 168/240] Fix merge issue --- pallets/subtensor/src/staking/helpers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/staking/helpers.rs b/pallets/subtensor/src/staking/helpers.rs index c1d9e70f21..0c53c267b0 100644 --- a/pallets/subtensor/src/staking/helpers.rs +++ b/pallets/subtensor/src/staking/helpers.rs @@ -108,7 +108,7 @@ impl Pallet { let order = GetTaoForAlpha::::with_amount(alpha_stake); T::SwapInterface::sim_swap(netuid.into(), order) .map(|r| { - let fee: u64 = U96F32::saturating_from_num(r.fee_paid) + let fee: u64 = U64F64::saturating_from_num(r.fee_paid) .saturating_mul(T::SwapInterface::current_alpha_price( netuid.into(), )) From 7fdb0bfbe2c23587f529646e290296cdebe058eb Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Thu, 22 Jan 2026 15:08:44 -0500 Subject: [PATCH 169/240] Bump spec version --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index d9908d98b5..3271e75998 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -241,7 +241,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: 371, + spec_version: 372, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 5a1ad2c3bd8fd15e7733a0aaabdc58c45068f1ed Mon Sep 17 00:00:00 2001 From: Shamil Gadelshin Date: Fri, 23 Jan 2026 14:41:58 +0300 Subject: [PATCH 170/240] Bump spec version --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 27d6eba2d4..3fe2b10a20 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -242,7 +242,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: 371, + spec_version: 372, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 7da419b058531636adbbefa040eefed3c0ab223f Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Fri, 23 Jan 2026 10:40:14 -0300 Subject: [PATCH 171/240] move tx extension to own module --- pallets/subtensor/src/extensions/mod.rs | 3 +++ .../src/{transaction_extension.rs => extensions/subtensor.rs} | 0 pallets/subtensor/src/lib.rs | 2 +- pallets/subtensor/src/tests/registration.rs | 2 +- pallets/subtensor/src/tests/serving.rs | 2 +- pallets/subtensor/src/tests/swap_coldkey.rs | 2 +- pallets/subtensor/src/tests/weights.rs | 2 +- 7 files changed, 8 insertions(+), 5 deletions(-) create mode 100644 pallets/subtensor/src/extensions/mod.rs rename pallets/subtensor/src/{transaction_extension.rs => extensions/subtensor.rs} (100%) diff --git a/pallets/subtensor/src/extensions/mod.rs b/pallets/subtensor/src/extensions/mod.rs new file mode 100644 index 0000000000..9171c222be --- /dev/null +++ b/pallets/subtensor/src/extensions/mod.rs @@ -0,0 +1,3 @@ +mod subtensor; + +pub use subtensor::*; diff --git a/pallets/subtensor/src/transaction_extension.rs b/pallets/subtensor/src/extensions/subtensor.rs similarity index 100% rename from pallets/subtensor/src/transaction_extension.rs rename to pallets/subtensor/src/extensions/subtensor.rs diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 8c77c23397..9484c3416e 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -35,6 +35,7 @@ mod benchmarks; // ========================= pub mod coinbase; pub mod epoch; +pub mod extensions; pub mod macros; pub mod migrations; pub mod rpc_info; @@ -47,7 +48,6 @@ use macros::{config, dispatches, errors, events, genesis, hooks}; #[cfg(test)] mod tests; -pub mod transaction_extension; // apparently this is stabilized since rust 1.36 extern crate alloc; diff --git a/pallets/subtensor/src/tests/registration.rs b/pallets/subtensor/src/tests/registration.rs index c82e173907..635b996cea 100644 --- a/pallets/subtensor/src/tests/registration.rs +++ b/pallets/subtensor/src/tests/registration.rs @@ -13,7 +13,7 @@ use subtensor_runtime_common::{AlphaCurrency, Currency as CurrencyT, NetUid, Net use super::mock; use super::mock::*; -use crate::transaction_extension::SubtensorTransactionExtension; +use crate::extensions::SubtensorTransactionExtension; use crate::{AxonInfoOf, CustomTransactionError, Error}; /******************************************** diff --git a/pallets/subtensor/src/tests/serving.rs b/pallets/subtensor/src/tests/serving.rs index b52666bf26..552af372e3 100644 --- a/pallets/subtensor/src/tests/serving.rs +++ b/pallets/subtensor/src/tests/serving.rs @@ -2,7 +2,7 @@ use super::mock::*; use crate::Error; -use crate::transaction_extension::SubtensorTransactionExtension; +use crate::extensions::SubtensorTransactionExtension; use crate::*; use frame_support::assert_noop; use frame_support::{ diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index fea013fe0a..a006307863 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -26,7 +26,7 @@ use subtensor_swap_interface::{SwapEngine, SwapHandler}; use super::mock; use super::mock::*; -use crate::transaction_extension::SubtensorTransactionExtension; +use crate::extensions::SubtensorTransactionExtension; use crate::*; use crate::{Call, Error}; diff --git a/pallets/subtensor/src/tests/weights.rs b/pallets/subtensor/src/tests/weights.rs index 20ace5ee0d..2c64b23d32 100644 --- a/pallets/subtensor/src/tests/weights.rs +++ b/pallets/subtensor/src/tests/weights.rs @@ -34,7 +34,7 @@ use w3f_bls::EngineBLS; use super::mock; use super::mock::*; use crate::coinbase::reveal_commits::{LegacyWeightsTlockPayload, WeightsTlockPayload}; -use crate::transaction_extension::SubtensorTransactionExtension; +use crate::extensions::SubtensorTransactionExtension; use crate::*; /*************************** pub fn set_weights() tests From 63ff1c1bf31fb3709a738c08363d7a80bd938609 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Fri, 23 Jan 2026 10:41:29 -0300 Subject: [PATCH 172/240] fix types --- pallets/subtensor/src/extensions/subtensor.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pallets/subtensor/src/extensions/subtensor.rs b/pallets/subtensor/src/extensions/subtensor.rs index 026a2580f4..220af968f5 100644 --- a/pallets/subtensor/src/extensions/subtensor.rs +++ b/pallets/subtensor/src/extensions/subtensor.rs @@ -86,8 +86,7 @@ where } impl - TransactionExtension<::RuntimeCall> - for SubtensorTransactionExtension + TransactionExtension> for SubtensorTransactionExtension where CallOf: Dispatchable + IsSubType> @@ -302,5 +301,5 @@ where } } - impl_tx_ext_default!(::RuntimeCall; weight prepare); + impl_tx_ext_default!(CallOf; weight prepare); } From 3d4ae5f9b9dfa54f024a04d4e259076416fa8140 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Fri, 23 Jan 2026 11:42:25 -0300 Subject: [PATCH 173/240] move check ck swap to its own tx ext and handle proxies --- .../src/extensions/check_coldkey_swap.rs | 97 +++++++++++++++++++ pallets/subtensor/src/extensions/mod.rs | 2 + pallets/subtensor/src/extensions/subtensor.rs | 33 +------ pallets/subtensor/src/lib.rs | 2 + 4 files changed, 105 insertions(+), 29 deletions(-) create mode 100644 pallets/subtensor/src/extensions/check_coldkey_swap.rs diff --git a/pallets/subtensor/src/extensions/check_coldkey_swap.rs b/pallets/subtensor/src/extensions/check_coldkey_swap.rs new file mode 100644 index 0000000000..8c2755ea64 --- /dev/null +++ b/pallets/subtensor/src/extensions/check_coldkey_swap.rs @@ -0,0 +1,97 @@ +use crate::{Call, ColdkeySwapAnnouncements, ColdkeySwapDisputes, Config, CustomTransactionError}; +use codec::{Decode, DecodeWithMemTracking, Encode}; +use frame_support::dispatch::{DispatchInfo, PostDispatchInfo}; +use frame_support::traits::IsSubType; +use pallet_subtensor_proxy::Call as ProxyCall; +use scale_info::TypeInfo; +use sp_runtime::{ + impl_tx_ext_default, + traits::{ + AsSystemOriginSigner, DispatchInfoOf, Dispatchable, Implication, StaticLookup, + TransactionExtension, ValidateResult, + }, + transaction_validity::TransactionSource, +}; +use sp_std::marker::PhantomData; +use subtensor_macros::freeze_struct; + +type CallOf = ::RuntimeCall; +type OriginOf = ::RuntimeOrigin; +type LookupOf = ::Lookup; + +#[freeze_struct("483277dc74a5aa56")] +#[derive(Default, Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, TypeInfo)] +pub struct CheckColdkeySwap(pub PhantomData); + +impl sp_std::fmt::Debug for CheckColdkeySwap { + fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { + write!(f, "CheckColdkeySwap") + } +} + +impl + TransactionExtension> for CheckColdkeySwap +where + CallOf: Dispatchable + + IsSubType> + + IsSubType> + + IsSubType>, + OriginOf: AsSystemOriginSigner + Clone, +{ + const IDENTIFIER: &'static str = "CheckColdkeySwap"; + + type Implicit = (); + type Val = (); + type Pre = (); + + fn validate( + &self, + origin: OriginOf, + call: &CallOf, + _info: &DispatchInfoOf>, + _len: usize, + _self_implicit: Self::Implicit, + _inherited_implication: &impl Implication, + _source: TransactionSource, + ) -> ValidateResult> { + // Ensure the transaction is signed, else we just skip the extension. + let Some(who) = origin.as_system_origin_signer() else { + return Ok((Default::default(), (), origin)); + }; + + // Get the real account if we are behind a proxy. + let who = + if let Some(ProxyCall::proxy { real, .. } | ProxyCall::proxy_announced { real, .. }) = + call.is_sub_type() + { + LookupOf::::lookup(real.clone()) + .map_err(|_| CustomTransactionError::InvalidRealAccount)? + } else { + who.clone() + }; + + if ColdkeySwapAnnouncements::::contains_key(&who) { + if ColdkeySwapDisputes::::contains_key(&who) { + return Err(CustomTransactionError::ColdkeySwapDisputed.into()); + } + + let is_allowed_direct = matches!( + call.is_sub_type(), + Some(Call::announce_coldkey_swap { .. } | Call::swap_coldkey_announced { .. }) + ); + + let is_mev_protected = matches!( + IsSubType::>::is_sub_type(call), + Some(pallet_shield::Call::submit_encrypted { .. }) + ); + + if !is_allowed_direct && !is_mev_protected { + return Err(CustomTransactionError::ColdkeySwapAnnounced.into()); + } + } + + Ok((Default::default(), (), origin)) + } + + impl_tx_ext_default!(CallOf; weight prepare); +} diff --git a/pallets/subtensor/src/extensions/mod.rs b/pallets/subtensor/src/extensions/mod.rs index 9171c222be..d77987c583 100644 --- a/pallets/subtensor/src/extensions/mod.rs +++ b/pallets/subtensor/src/extensions/mod.rs @@ -1,3 +1,5 @@ +mod check_coldkey_swap; mod subtensor; +pub use check_coldkey_swap::*; pub use subtensor::*; diff --git a/pallets/subtensor/src/extensions/subtensor.rs b/pallets/subtensor/src/extensions/subtensor.rs index 220af968f5..4a8330ad20 100644 --- a/pallets/subtensor/src/extensions/subtensor.rs +++ b/pallets/subtensor/src/extensions/subtensor.rs @@ -1,7 +1,4 @@ -use crate::{ - BalancesCall, Call, ColdkeySwapAnnouncements, ColdkeySwapDisputes, Config, - CustomTransactionError, Error, Pallet, TransactionType, -}; +use crate::{BalancesCall, Call, Config, CustomTransactionError, Error, Pallet, TransactionType}; use codec::{Decode, DecodeWithMemTracking, Encode}; use frame_support::dispatch::{DispatchInfo, PostDispatchInfo}; use frame_support::traits::IsSubType; @@ -85,13 +82,12 @@ where } } -impl - TransactionExtension> for SubtensorTransactionExtension +impl TransactionExtension> + for SubtensorTransactionExtension where CallOf: Dispatchable + IsSubType> - + IsSubType> - + IsSubType>, + + IsSubType>, OriginOf: AsSystemOriginSigner + Clone, { const IDENTIFIER: &'static str = "SubtensorTransactionExtension"; @@ -115,27 +111,6 @@ where return Ok((Default::default(), (), origin)); }; - if ColdkeySwapAnnouncements::::contains_key(who) { - if ColdkeySwapDisputes::::contains_key(who) { - return Err(CustomTransactionError::ColdkeySwapDisputed.into()); - } - - let is_allowed_direct = matches!( - call.is_sub_type(), - Some(Call::announce_coldkey_swap { .. }) - | Some(Call::swap_coldkey_announced { .. }) - ); - - let is_mev_protected = matches!( - IsSubType::>::is_sub_type(call), - Some(pallet_shield::Call::submit_encrypted { .. }) - ); - - if !is_allowed_direct && !is_mev_protected { - return Err(CustomTransactionError::ColdkeySwapAnnounced.into()); - } - } - match call.is_sub_type() { Some(Call::commit_weights { netuid, .. }) => { if Self::check_weights_min_stake(who, *netuid) { diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 9484c3416e..b1d86e38c6 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -2456,6 +2456,7 @@ pub enum CustomTransactionError { UidNotFound, EvmKeyAssociateRateLimitExceeded, ColdkeySwapDisputed, + InvalidRealAccount, } impl From for u8 { @@ -2484,6 +2485,7 @@ impl From for u8 { CustomTransactionError::UidNotFound => 19, CustomTransactionError::EvmKeyAssociateRateLimitExceeded => 20, CustomTransactionError::ColdkeySwapDisputed => 21, + CustomTransactionError::InvalidRealAccount => 22, } } } From 7bbfbda84a898444d9a6022f5a13689801ed2023 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Fri, 23 Jan 2026 19:05:44 -0300 Subject: [PATCH 174/240] embed check coldkey swap tests --- node/src/benchmarking.rs | 41 ++- node/src/mev_shield/author.rs | 40 ++- .../src/extensions/check_coldkey_swap.rs | 303 +++++++++++++++++- pallets/subtensor/src/extensions/subtensor.rs | 30 +- pallets/subtensor/src/lib.rs | 2 + pallets/subtensor/src/tests/mod.rs | 2 +- pallets/subtensor/src/tests/swap_coldkey.rs | 167 ---------- precompiles/src/extensions.rs | 2 +- runtime/src/lib.rs | 5 +- 9 files changed, 361 insertions(+), 231 deletions(-) diff --git a/node/src/benchmarking.rs b/node/src/benchmarking.rs index 5430a75d9e..02e31e750e 100644 --- a/node/src/benchmarking.rs +++ b/node/src/benchmarking.rs @@ -124,28 +124,25 @@ pub fn create_benchmark_extrinsic( .checked_next_power_of_two() .map(|c| c / 2) .unwrap_or(2) as u64; - let extra: runtime::TransactionExtensions = - ( - frame_system::CheckNonZeroSender::::new(), - frame_system::CheckSpecVersion::::new(), - frame_system::CheckTxVersion::::new(), - frame_system::CheckGenesis::::new(), - frame_system::CheckEra::::from(sp_runtime::generic::Era::mortal( - period, - best_block.saturated_into(), - )), - check_nonce::CheckNonce::::from(nonce), - frame_system::CheckWeight::::new(), - transaction_payment_wrapper::ChargeTransactionPaymentWrapper::new( - pallet_transaction_payment::ChargeTransactionPayment::::from(0), - ), - sudo_wrapper::SudoTransactionExtension::::new(), - pallet_subtensor::transaction_extension::SubtensorTransactionExtension::< - runtime::Runtime, - >::new(), - pallet_drand::drand_priority::DrandPriority::::new(), - frame_metadata_hash_extension::CheckMetadataHash::::new(true), - ); + let extra: runtime::TransactionExtensions = ( + frame_system::CheckNonZeroSender::::new(), + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckEra::::from(sp_runtime::generic::Era::mortal( + period, + best_block.saturated_into(), + )), + check_nonce::CheckNonce::::from(nonce), + frame_system::CheckWeight::::new(), + transaction_payment_wrapper::ChargeTransactionPaymentWrapper::new( + pallet_transaction_payment::ChargeTransactionPayment::::from(0), + ), + sudo_wrapper::SudoTransactionExtension::::new(), + pallet_subtensor::SubtensorTransactionExtension::::new(), + pallet_drand::drand_priority::DrandPriority::::new(), + frame_metadata_hash_extension::CheckMetadataHash::::new(true), + ); let raw_payload = runtime::SignedPayload::from_raw( call.clone(), diff --git a/node/src/mev_shield/author.rs b/node/src/mev_shield/author.rs index 8ac57b1d52..35f362df3d 100644 --- a/node/src/mev_shield/author.rs +++ b/node/src/mev_shield/author.rs @@ -375,28 +375,24 @@ where let current_block: u64 = info.best_number.saturated_into(); let era = Era::mortal(ERA_PERIOD, current_block); - let extra: Extra = - ( - frame_system::CheckNonZeroSender::::new(), - frame_system::CheckSpecVersion::::new(), - frame_system::CheckTxVersion::::new(), - frame_system::CheckGenesis::::new(), - frame_system::CheckEra::::from(era), - node_subtensor_runtime::check_nonce::CheckNonce::::from(nonce).into(), - frame_system::CheckWeight::::new(), - node_subtensor_runtime::transaction_payment_wrapper::ChargeTransactionPaymentWrapper::< - runtime::Runtime, - >::new(pallet_transaction_payment::ChargeTransactionPayment::< - runtime::Runtime, - >::from(0u64)), - node_subtensor_runtime::sudo_wrapper::SudoTransactionExtension::::new( - ), - pallet_subtensor::transaction_extension::SubtensorTransactionExtension::< - runtime::Runtime, - >::new(), - pallet_drand::drand_priority::DrandPriority::::new(), - frame_metadata_hash_extension::CheckMetadataHash::::new(false), - ); + let extra: Extra = ( + frame_system::CheckNonZeroSender::::new(), + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckEra::::from(era), + node_subtensor_runtime::check_nonce::CheckNonce::::from(nonce).into(), + frame_system::CheckWeight::::new(), + node_subtensor_runtime::transaction_payment_wrapper::ChargeTransactionPaymentWrapper::< + runtime::Runtime, + >::new(pallet_transaction_payment::ChargeTransactionPayment::< + runtime::Runtime, + >::from(0u64)), + node_subtensor_runtime::sudo_wrapper::SudoTransactionExtension::::new(), + pallet_subtensor::SubtensorTransactionExtension::::new(), + pallet_drand::drand_priority::DrandPriority::::new(), + frame_metadata_hash_extension::CheckMetadataHash::::new(false), + ); // 3) Manually construct the `Implicit` tuple that the runtime will also derive. type Implicit = >::Implicit; diff --git a/pallets/subtensor/src/extensions/check_coldkey_swap.rs b/pallets/subtensor/src/extensions/check_coldkey_swap.rs index 8c2755ea64..f9ba364b35 100644 --- a/pallets/subtensor/src/extensions/check_coldkey_swap.rs +++ b/pallets/subtensor/src/extensions/check_coldkey_swap.rs @@ -23,6 +23,12 @@ type LookupOf = ::Lookup; #[derive(Default, Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, TypeInfo)] pub struct CheckColdkeySwap(pub PhantomData); +impl CheckColdkeySwap { + pub fn new() -> Self { + Self(Default::default()) + } +} + impl sp_std::fmt::Debug for CheckColdkeySwap { fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { write!(f, "CheckColdkeySwap") @@ -59,16 +65,17 @@ where return Ok((Default::default(), (), origin)); }; - // Get the real account if we are behind a proxy. - let who = - if let Some(ProxyCall::proxy { real, .. } | ProxyCall::proxy_announced { real, .. }) = - call.is_sub_type() - { - LookupOf::::lookup(real.clone()) - .map_err(|_| CustomTransactionError::InvalidRealAccount)? - } else { - who.clone() - }; + // Get the real account and origin if we are behind a proxy. + let (who, call) = if let Some( + ProxyCall::proxy { real, call, .. } | ProxyCall::proxy_announced { real, call, .. }, + ) = call.is_sub_type() + { + let real = LookupOf::::lookup(real.clone()) + .map_err(|_| CustomTransactionError::InvalidRealAccount)?; + (real, (*call.clone()).into()) + } else { + (who.clone(), call.clone()) + }; if ColdkeySwapAnnouncements::::contains_key(&who) { if ColdkeySwapDisputes::::contains_key(&who) { @@ -81,7 +88,7 @@ where ); let is_mev_protected = matches!( - IsSubType::>::is_sub_type(call), + IsSubType::>::is_sub_type(&call), Some(pallet_shield::Call::submit_encrypted { .. }) ); @@ -95,3 +102,277 @@ where impl_tx_ext_default!(CallOf; weight prepare); } + +#[cfg(test)] +mod tests { + use super::*; + use crate::{BalancesCall, DefaultMinStake, tests::mock::*}; + use frame_support::testing_prelude::*; + use frame_support::{dispatch::GetDispatchInfo, traits::OriginTrait}; + use frame_system::Call as SystemCall; + use sp_core::U256; + use sp_runtime::{ + BoundedVec, + traits::{AsTransactionAuthorizedOrigin, Hash, TxBaseImplication}, + }; + use subtensor_runtime_common::{Currency, NetUid}; + + type HashingOf = ::Hashing; + + const CALL: RuntimeCall = RuntimeCall::System(SystemCall::remark { remark: vec![] }); + + #[test] + fn skipped_for_non_signed_origins() { + new_test_ext(1).execute_with(|| { + let info = CALL.get_dispatch_info(); + let len = 0_usize; + + let (_, _, origin) = CheckColdkeySwap::::new() + .validate( + None.into(), + &CALL, + &info, + len, + (), + &TxBaseImplication(CALL), + TransactionSource::External, + ) + .unwrap(); + assert!(!origin.is_transaction_authorized()); + + let (_, _, origin) = CheckColdkeySwap::::new() + .validate( + RuntimeOrigin::root().into(), + &CALL, + &info, + len, + (), + &TxBaseImplication(CALL), + TransactionSource::External, + ) + .unwrap(); + assert!(origin.as_system_ref().unwrap().is_root()); + }) + } + + #[test] + fn skipped_if_no_active_swap() { + new_test_ext(1).execute_with(|| { + let who = U256::from(1); + let info = CALL.get_dispatch_info(); + let len = 0_usize; + + let (_, _, origin) = CheckColdkeySwap::::new() + .validate( + RuntimeOrigin::signed(who).into(), + &CALL, + &info, + len, + (), + &TxBaseImplication(CALL), + TransactionSource::External, + ) + .unwrap(); + assert_eq!(origin.as_signer(), Some(&who)); + }) + } + + #[test] + fn validate_calls_correctly() { + new_test_ext(1).execute_with(|| { + let netuid = NetUid::from(1); + let stake = DefaultMinStake::::get().to_u64(); + let who = U256::from(1); + let now = System::block_number(); + let another_coldkey = U256::from(3); + let another_coldkey_hash = HashingOf::::hash_of(&another_coldkey); + let new_coldkey = U256::from(42); + let new_coldkey_hash = HashingOf::::hash_of(&new_coldkey); + ColdkeySwapAnnouncements::::insert(who, (now, new_coldkey_hash)); + + let reserve = stake * 10; + setup_reserves(netuid, reserve.into(), reserve.into()); + + // Setup network and neuron + let hotkey = U256::from(2); + add_network(netuid, 1, 0); + register_ok_neuron(netuid, hotkey, who, 0); + + SubtensorModule::add_balance_to_coldkey_account(&who, u64::MAX); + + let forbidden_calls: Vec = vec![ + RuntimeCall::SubtensorModule(SubtensorCall::dissolve_network { + netuid, + coldkey: who, + }), + RuntimeCall::SubtensorModule(SubtensorCall::add_stake { + hotkey, + netuid, + amount_staked: stake.into(), + }), + RuntimeCall::SubtensorModule(SubtensorCall::add_stake_limit { + hotkey, + netuid, + amount_staked: stake.into(), + limit_price: stake.into(), + allow_partial: false, + }), + RuntimeCall::SubtensorModule(SubtensorCall::swap_stake { + hotkey, + origin_netuid: netuid, + destination_netuid: netuid, + alpha_amount: stake.into(), + }), + RuntimeCall::SubtensorModule(SubtensorCall::swap_stake_limit { + hotkey, + origin_netuid: netuid, + destination_netuid: netuid, + alpha_amount: stake.into(), + limit_price: stake.into(), + allow_partial: false, + }), + RuntimeCall::SubtensorModule(SubtensorCall::move_stake { + origin_hotkey: hotkey, + destination_hotkey: hotkey, + origin_netuid: netuid, + destination_netuid: netuid, + alpha_amount: stake.into(), + }), + RuntimeCall::SubtensorModule(SubtensorCall::transfer_stake { + destination_coldkey: new_coldkey, + hotkey, + origin_netuid: netuid, + destination_netuid: netuid, + alpha_amount: stake.into(), + }), + RuntimeCall::SubtensorModule(SubtensorCall::remove_stake { + hotkey, + netuid, + amount_unstaked: (DefaultMinStake::::get().to_u64() * 2).into(), + }), + RuntimeCall::SubtensorModule(SubtensorCall::remove_stake_limit { + hotkey, + netuid, + amount_unstaked: (stake * 2).into(), + limit_price: 123456789.into(), + allow_partial: true, + }), + RuntimeCall::SubtensorModule(SubtensorCall::burned_register { netuid, hotkey }), + RuntimeCall::Balances(BalancesCall::transfer_all { + dest: new_coldkey, + keep_alive: false, + }), + RuntimeCall::Balances(BalancesCall::transfer_keep_alive { + dest: new_coldkey, + value: 100_000_000_000, + }), + RuntimeCall::Balances(BalancesCall::transfer_allow_death { + dest: new_coldkey, + value: 100_000_000_000, + }), + ]; + + // Forbidden calls through direct origin + for call in &forbidden_calls { + assert_eq!( + ext_validate(who, call.clone()).unwrap_err(), + CustomTransactionError::ColdkeySwapAnnounced.into() + ); + } + + let delegate = U256::from(2); + + // Forbidden calls through proxy + for call in &forbidden_calls { + let proxy_calls = build_proxy_calls(who, delegate, call.clone()); + for proxy_call in proxy_calls { + assert_eq!( + ext_validate(delegate, proxy_call.clone()).unwrap_err(), + CustomTransactionError::ColdkeySwapAnnounced.into() + ); + } + } + + let authorized_calls: Vec = vec![ + RuntimeCall::SubtensorModule(SubtensorCall::announce_coldkey_swap { + new_coldkey_hash: another_coldkey_hash, + }), + RuntimeCall::SubtensorModule(SubtensorCall::swap_coldkey_announced { new_coldkey }), + RuntimeCall::Shield(pallet_shield::Call::submit_encrypted { + commitment: ::Hashing::hash_of(&new_coldkey), + ciphertext: BoundedVec::truncate_from(vec![1, 2, 3, 4]), + }), + ]; + + // Authorized calls through direct origin + for call in &authorized_calls { + let (_, _, origin) = ext_validate(who, call.clone()).unwrap(); + assert_eq!(origin.as_signer(), Some(&who)); + } + + // Authorized calls through proxy + for call in &authorized_calls { + let proxy_calls = build_proxy_calls(who, delegate, call.clone()); + for proxy_call in proxy_calls { + let (_, _, origin) = ext_validate(delegate, proxy_call.clone()).unwrap(); + assert_eq!(origin.as_signer(), Some(&delegate)); + } + } + + ColdkeySwapDisputes::::insert(who, now); + + // All calls should fail when the coldkey swap is disputed + let all_calls = forbidden_calls.iter().chain(authorized_calls.iter()); + + // All calls through direct origin during dispute + for call in all_calls.clone() { + assert_eq!( + ext_validate(who, call.clone()).unwrap_err(), + CustomTransactionError::ColdkeySwapDisputed.into() + ); + } + + // All calls through proxy during dispute + for call in all_calls { + let proxy_calls = build_proxy_calls(who, delegate, call.clone()); + for proxy_call in proxy_calls { + assert_eq!( + ext_validate(delegate, proxy_call.clone()).unwrap_err(), + CustomTransactionError::ColdkeySwapDisputed.into() + ); + } + } + }) + } + + fn build_proxy_calls(who: U256, delegate: U256, call: RuntimeCall) -> Vec { + vec![ + RuntimeCall::Proxy(ProxyCall::proxy { + real: who, + force_proxy_type: None, + call: Box::new(call.clone()), + }), + RuntimeCall::Proxy(ProxyCall::proxy_announced { + delegate, + real: who, + force_proxy_type: None, + call: Box::new(call.clone()), + }), + ] + } + + fn ext_validate(who: U256, call: RuntimeCall) -> ValidateResult<(), RuntimeCall> { + let info = call.get_dispatch_info(); + let len = 0_usize; + + CheckColdkeySwap::::new().validate( + RuntimeOrigin::signed(who).into(), + &call.clone(), + &info, + len, + (), + &TxBaseImplication(call), + TransactionSource::External, + ) + } +} diff --git a/pallets/subtensor/src/extensions/subtensor.rs b/pallets/subtensor/src/extensions/subtensor.rs index 4a8330ad20..51478f4ec2 100644 --- a/pallets/subtensor/src/extensions/subtensor.rs +++ b/pallets/subtensor/src/extensions/subtensor.rs @@ -1,4 +1,7 @@ -use crate::{BalancesCall, Call, Config, CustomTransactionError, Error, Pallet, TransactionType}; +use crate::{ + BalancesCall, Call, CheckColdkeySwap, Config, CustomTransactionError, Error, Pallet, + TransactionType, +}; use codec::{Decode, DecodeWithMemTracking, Encode}; use frame_support::dispatch::{DispatchInfo, PostDispatchInfo}; use frame_support::traits::IsSubType; @@ -82,12 +85,20 @@ where } } -impl TransactionExtension> - for SubtensorTransactionExtension +impl TransactionExtension> for SubtensorTransactionExtension where + T: Config + + Send + + Sync + + TypeInfo + + pallet_balances::Config + + pallet_subtensor_proxy::Config + + pallet_shield::Config, CallOf: Dispatchable + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType> + + IsSubType>, OriginOf: AsSystemOriginSigner + Clone, { const IDENTIFIER: &'static str = "SubtensorTransactionExtension"; @@ -111,6 +122,17 @@ where return Ok((Default::default(), (), origin)); }; + // TODO: move into tx extension pipeline but require node upgrade + CheckColdkeySwap::::new().validate( + origin.clone(), + call, + _info, + _len, + _self_implicit, + _inherited_implication, + _source, + )?; + match call.is_sub_type() { Some(Call::commit_weights { netuid, .. }) => { if Self::check_weights_min_stake(who, *netuid) { diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index b1d86e38c6..db0d0ac3e2 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -46,6 +46,8 @@ pub mod utils; use crate::utils::rate_limiting::{Hyperparameter, TransactionType}; use macros::{config, dispatches, errors, events, genesis, hooks}; +pub use extensions::*; + #[cfg(test)] mod tests; diff --git a/pallets/subtensor/src/tests/mod.rs b/pallets/subtensor/src/tests/mod.rs index bbaf25af58..b209c87fd7 100644 --- a/pallets/subtensor/src/tests/mod.rs +++ b/pallets/subtensor/src/tests/mod.rs @@ -15,7 +15,7 @@ mod leasing; mod math; mod mechanism; mod migration; -mod mock; +pub(crate) mod mock; mod move_stake; mod networks; mod neuron_info; diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index a006307863..36d083344c 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -1497,173 +1497,6 @@ fn test_reset_coldkey_swap_with_bad_origin_fails() { }); } -#[test] -fn test_subtensor_extension_rejects_calls_when_announced_or_disputed() { - new_test_ext(0).execute_with(|| { - let netuid = NetUid::from(1); - let who = U256::from(0); - let new_coldkey = U256::from(1); - let another_coldkey = U256::from(3); - let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); - let another_coldkey_hash = - ::Hashing::hash_of(&another_coldkey); - let hotkey = U256::from(2); - let stake = DefaultMinStake::::get().to_u64(); - assert_ne!(hotkey, who); - - // Setup reserves - let reserve = stake * 10; - mock::setup_reserves(netuid, reserve.into(), reserve.into()); - - // Setup network and neuron - add_network(netuid, 1, 0); - register_ok_neuron(netuid, hotkey, who, 0); - - SubtensorModule::add_balance_to_coldkey_account(&who, u64::MAX); - - // Schedule the coldkey for a swap - assert_ok!(SubtensorModule::announce_coldkey_swap( - ::RuntimeOrigin::signed(who), - new_coldkey_hash, - )); - assert!(ColdkeySwapAnnouncements::::contains_key(who)); - - let forbidden_calls: Vec = vec![ - RuntimeCall::SubtensorModule(SubtensorCall::dissolve_network { - netuid, - coldkey: who, - }), - RuntimeCall::SubtensorModule(SubtensorCall::add_stake { - hotkey, - netuid, - amount_staked: stake.into(), - }), - RuntimeCall::SubtensorModule(SubtensorCall::add_stake_limit { - hotkey, - netuid, - amount_staked: stake.into(), - limit_price: stake.into(), - allow_partial: false, - }), - RuntimeCall::SubtensorModule(SubtensorCall::swap_stake { - hotkey, - origin_netuid: netuid, - destination_netuid: netuid, - alpha_amount: stake.into(), - }), - RuntimeCall::SubtensorModule(SubtensorCall::swap_stake_limit { - hotkey, - origin_netuid: netuid, - destination_netuid: netuid, - alpha_amount: stake.into(), - limit_price: stake.into(), - allow_partial: false, - }), - RuntimeCall::SubtensorModule(SubtensorCall::move_stake { - origin_hotkey: hotkey, - destination_hotkey: hotkey, - origin_netuid: netuid, - destination_netuid: netuid, - alpha_amount: stake.into(), - }), - RuntimeCall::SubtensorModule(SubtensorCall::transfer_stake { - destination_coldkey: new_coldkey, - hotkey, - origin_netuid: netuid, - destination_netuid: netuid, - alpha_amount: stake.into(), - }), - RuntimeCall::SubtensorModule(SubtensorCall::remove_stake { - hotkey, - netuid, - amount_unstaked: (DefaultMinStake::::get().to_u64() * 2).into(), - }), - RuntimeCall::SubtensorModule(SubtensorCall::remove_stake_limit { - hotkey, - netuid, - amount_unstaked: (stake * 2).into(), - limit_price: 123456789.into(), - allow_partial: true, - }), - RuntimeCall::SubtensorModule(SubtensorCall::burned_register { netuid, hotkey }), - RuntimeCall::Balances(BalancesCall::transfer_all { - dest: new_coldkey, - keep_alive: false, - }), - RuntimeCall::Balances(BalancesCall::transfer_keep_alive { - dest: new_coldkey, - value: 100_000_000_000, - }), - RuntimeCall::Balances(BalancesCall::transfer_allow_death { - dest: new_coldkey, - value: 100_000_000_000, - }), - ]; - - for call in &forbidden_calls { - let info = call.get_dispatch_info(); - let ext = SubtensorTransactionExtension::::new(); - assert_noop!( - ext.dispatch_transaction( - RuntimeOrigin::signed(who).into(), - call.clone(), - &info, - 0, - 0 - ), - CustomTransactionError::ColdkeySwapAnnounced - ); - } - - let authorized_calls: Vec = vec![ - RuntimeCall::SubtensorModule(SubtensorCall::announce_coldkey_swap { - new_coldkey_hash: another_coldkey_hash, - }), - RuntimeCall::SubtensorModule(SubtensorCall::swap_coldkey_announced { new_coldkey }), - RuntimeCall::Shield(pallet_shield::Call::submit_encrypted { - commitment: ::Hashing::hash_of(&new_coldkey), - ciphertext: BoundedVec::truncate_from(vec![1, 2, 3, 4]), - }), - ]; - - for call in &authorized_calls { - let info = call.get_dispatch_info(); - let ext = SubtensorTransactionExtension::::new(); - assert_ok!(ext.dispatch_transaction( - RuntimeOrigin::signed(who).into(), - call.clone(), - &info, - 0, - 0 - )); - } - - // Dispute the coldkey swap - assert_ok!(SubtensorModule::dispute_coldkey_swap( - ::RuntimeOrigin::signed(who), - )); - assert!(ColdkeySwapDisputes::::contains_key(who)); - - // All calls should fail when the coldkey swap is disputed - let all_calls = authorized_calls.iter().chain(forbidden_calls.iter()); - - for call in all_calls { - let info = call.get_dispatch_info(); - let ext = SubtensorTransactionExtension::::new(); - assert_noop!( - ext.dispatch_transaction( - RuntimeOrigin::signed(who).into(), - call.clone(), - &info, - 0, - 0 - ), - CustomTransactionError::ColdkeySwapDisputed - ); - } - }); -} - #[test] #[allow(deprecated)] fn test_schedule_swap_coldkey_deprecated() { diff --git a/precompiles/src/extensions.rs b/precompiles/src/extensions.rs index 0055a5a0a7..79b493d020 100644 --- a/precompiles/src/extensions.rs +++ b/precompiles/src/extensions.rs @@ -10,7 +10,7 @@ use pallet_evm::{ AddressMapping, BalanceConverter, EvmBalance, ExitError, GasWeightMapping, Precompile, PrecompileFailure, PrecompileHandle, PrecompileResult, }; -use pallet_subtensor::transaction_extension::SubtensorTransactionExtension; +use pallet_subtensor::SubtensorTransactionExtension; use precompile_utils::EvmResult; use scale_info::TypeInfo; use sp_core::{H160, U256, blake2_256}; diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 511066a87c..ec6c853a57 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -177,8 +177,7 @@ impl frame_system::offchain::CreateSignedTransaction pallet_transaction_payment::ChargeTransactionPayment::::from(0), ), SudoTransactionExtension::::new(), - pallet_subtensor::transaction_extension::SubtensorTransactionExtension::::new( - ), + pallet_subtensor::SubtensorTransactionExtension::::new(), pallet_drand::drand_priority::DrandPriority::::new(), frame_metadata_hash_extension::CheckMetadataHash::::new(true), ); @@ -1660,7 +1659,7 @@ pub type TransactionExtensions = ( frame_system::CheckWeight, ChargeTransactionPaymentWrapper, SudoTransactionExtension, - pallet_subtensor::transaction_extension::SubtensorTransactionExtension, + pallet_subtensor::SubtensorTransactionExtension, pallet_drand::drand_priority::DrandPriority, frame_metadata_hash_extension::CheckMetadataHash, ); From 15fab272913b95f19cae22be05a8f761fbabe12a Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Fri, 23 Jan 2026 19:12:28 -0300 Subject: [PATCH 175/240] fix weight zero --- pallets/subtensor/src/macros/dispatches.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 06027348b6..d6439b6a30 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -1331,7 +1331,7 @@ mod dispatches { /// /// WARNING: This function is deprecated, please migrate to `announce_coldkey_swap`/`coldkey_swap` #[pallet::call_index(73)] - #[pallet::weight(Weight::zero())] + #[pallet::weight(T::DbWeight::get().reads(5))] #[deprecated(note = "Deprecated, please migrate to `announce_coldkey_swap`/`coldkey_swap`")] pub fn schedule_swap_coldkey( _origin: OriginFor, From 989f6684121431332bc966d8b9eef82f9e08c808 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Fri, 23 Jan 2026 19:37:00 -0300 Subject: [PATCH 176/240] fix bounds --- precompiles/src/balance_transfer.rs | 8 ++++++-- precompiles/src/crowdloan.rs | 8 ++++++-- precompiles/src/extensions.rs | 4 +++- precompiles/src/leasing.rs | 8 ++++++-- precompiles/src/lib.rs | 10 ++++++++-- precompiles/src/neuron.rs | 8 ++++++-- precompiles/src/proxy.rs | 8 ++++++-- precompiles/src/staking.rs | 16 ++++++++++++---- precompiles/src/subnet.rs | 8 ++++++-- 9 files changed, 59 insertions(+), 19 deletions(-) diff --git a/precompiles/src/balance_transfer.rs b/precompiles/src/balance_transfer.rs index 54be7c551f..f4d5fb3025 100644 --- a/precompiles/src/balance_transfer.rs +++ b/precompiles/src/balance_transfer.rs @@ -19,6 +19,7 @@ where + pallet_evm::Config + pallet_subtensor::Config + pallet_shield::Config + + pallet_subtensor_proxy::Config + Send + Sync + scale_info::TypeInfo, @@ -28,7 +29,8 @@ where + Dispatchable + IsSubType> + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, ::RuntimeCall: From> + GetDispatchInfo + Dispatchable, @@ -46,6 +48,7 @@ where + pallet_evm::Config + pallet_subtensor::Config + pallet_shield::Config + + pallet_subtensor_proxy::Config + Send + Sync + scale_info::TypeInfo, @@ -55,7 +58,8 @@ where + Dispatchable + IsSubType> + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, ::RuntimeCall: From> + GetDispatchInfo + Dispatchable, diff --git a/precompiles/src/crowdloan.rs b/precompiles/src/crowdloan.rs index 731da22da6..c9926dfe3b 100644 --- a/precompiles/src/crowdloan.rs +++ b/precompiles/src/crowdloan.rs @@ -26,6 +26,7 @@ where + pallet_proxy::Config + pallet_subtensor::Config + pallet_shield::Config + + pallet_subtensor_proxy::Config + Send + Sync + scale_info::TypeInfo, @@ -36,7 +37,8 @@ where + Dispatchable + IsSubType> + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, ::AddressMapping: AddressMapping, { const INDEX: u64 = 2057; @@ -52,6 +54,7 @@ where + pallet_proxy::Config + pallet_subtensor::Config + pallet_shield::Config + + pallet_subtensor_proxy::Config + Send + Sync + scale_info::TypeInfo, @@ -62,7 +65,8 @@ where + Dispatchable + IsSubType> + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, ::RuntimeCall: From> + GetDispatchInfo + Dispatchable, diff --git a/precompiles/src/extensions.rs b/precompiles/src/extensions.rs index 79b493d020..3926c6466c 100644 --- a/precompiles/src/extensions.rs +++ b/precompiles/src/extensions.rs @@ -59,6 +59,7 @@ pub(crate) trait PrecompileHandleExt: PrecompileHandle { + pallet_evm::Config + pallet_subtensor::Config + pallet_shield::Config + + pallet_subtensor_proxy::Config + Send + Sync + TypeInfo, @@ -67,7 +68,8 @@ pub(crate) trait PrecompileHandleExt: PrecompileHandle { + Dispatchable + IsSubType> + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, ::RuntimeOrigin: From> + AsSystemOriginSigner + Clone, { diff --git a/precompiles/src/leasing.rs b/precompiles/src/leasing.rs index 9d83e8da8f..732e687c6f 100644 --- a/precompiles/src/leasing.rs +++ b/precompiles/src/leasing.rs @@ -27,6 +27,7 @@ where + pallet_subtensor::Config + pallet_crowdloan::Config + pallet_shield::Config + + pallet_subtensor_proxy::Config + Send + Sync + scale_info::TypeInfo, @@ -38,7 +39,8 @@ where + Dispatchable + IsSubType> + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, ::AddressMapping: AddressMapping, { const INDEX: u64 = 2058; @@ -53,6 +55,7 @@ where + pallet_subtensor::Config + pallet_crowdloan::Config + pallet_shield::Config + + pallet_subtensor_proxy::Config + Send + Sync + scale_info::TypeInfo, @@ -64,7 +67,8 @@ where + Dispatchable + IsSubType> + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, ::AddressMapping: AddressMapping, { #[precompile::public("getLease(uint32)")] diff --git a/precompiles/src/lib.rs b/precompiles/src/lib.rs index 8ed1e1f736..883da66589 100644 --- a/precompiles/src/lib.rs +++ b/precompiles/src/lib.rs @@ -71,6 +71,7 @@ where + pallet_proxy::Config + pallet_crowdloan::Config + pallet_shield::Config + + pallet_subtensor_proxy::Config + Send + Sync + scale_info::TypeInfo, @@ -85,7 +86,8 @@ where + Dispatchable + IsSubType> + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, ::AddressMapping: AddressMapping, ::Balance: TryFrom, <::Lookup as StaticLookup>::Source: From, @@ -106,6 +108,7 @@ where + pallet_proxy::Config + pallet_crowdloan::Config + pallet_shield::Config + + pallet_subtensor_proxy::Config + Send + Sync + scale_info::TypeInfo, @@ -120,7 +123,8 @@ where + Dispatchable + IsSubType> + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, ::AddressMapping: AddressMapping, ::Balance: TryFrom, <::Lookup as StaticLookup>::Source: From, @@ -171,6 +175,7 @@ where + pallet_proxy::Config + pallet_crowdloan::Config + pallet_shield::Config + + pallet_subtensor_proxy::Config + Send + Sync + scale_info::TypeInfo, @@ -186,6 +191,7 @@ where + IsSubType> + IsSubType> + IsSubType> + + IsSubType> + Decode, <::RuntimeCall as Dispatchable>::RuntimeOrigin: From>>, diff --git a/precompiles/src/neuron.rs b/precompiles/src/neuron.rs index b4ed6a25c4..e2602c0246 100644 --- a/precompiles/src/neuron.rs +++ b/precompiles/src/neuron.rs @@ -20,6 +20,7 @@ where + pallet_evm::Config + pallet_subtensor::Config + pallet_shield::Config + + pallet_subtensor_proxy::Config + Send + Sync + scale_info::TypeInfo, @@ -30,7 +31,8 @@ where + Dispatchable + IsSubType> + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, ::AddressMapping: AddressMapping, { const INDEX: u64 = 2052; @@ -44,6 +46,7 @@ where + pallet_evm::Config + pallet_subtensor::Config + pallet_shield::Config + + pallet_subtensor_proxy::Config + Send + Sync + scale_info::TypeInfo, @@ -54,7 +57,8 @@ where + Dispatchable + IsSubType> + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, ::AddressMapping: AddressMapping, { #[precompile::public("setWeights(uint16,uint16[],uint16[],uint64)")] diff --git a/precompiles/src/proxy.rs b/precompiles/src/proxy.rs index b387402322..3312b67194 100644 --- a/precompiles/src/proxy.rs +++ b/precompiles/src/proxy.rs @@ -31,6 +31,7 @@ where + pallet_subtensor::Config + pallet_proxy::Config + pallet_shield::Config + + pallet_subtensor_proxy::Config + Send + Sync + scale_info::TypeInfo, @@ -43,7 +44,8 @@ where + Dispatchable + IsSubType> + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, ::AddressMapping: AddressMapping, <::Lookup as StaticLookup>::Source: From, { @@ -59,6 +61,7 @@ where + pallet_subtensor::Config + pallet_proxy::Config + pallet_shield::Config + + pallet_subtensor_proxy::Config + Send + Sync + scale_info::TypeInfo, @@ -71,7 +74,8 @@ where + Dispatchable + IsSubType> + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, <::Lookup as StaticLookup>::Source: From, { #[precompile::public("createPureProxy(uint8,uint32,uint16)")] diff --git a/precompiles/src/staking.rs b/precompiles/src/staking.rs index b2e3ee36e7..2ed8891a5c 100644 --- a/precompiles/src/staking.rs +++ b/precompiles/src/staking.rs @@ -58,6 +58,7 @@ where + pallet_subtensor::Config + pallet_proxy::Config + pallet_shield::Config + + pallet_subtensor_proxy::Config + Send + Sync + scale_info::TypeInfo, @@ -69,7 +70,8 @@ where + Dispatchable + IsSubType> + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, ::AddressMapping: AddressMapping, <::Lookup as StaticLookup>::Source: From, { @@ -85,6 +87,7 @@ where + pallet_subtensor::Config + pallet_proxy::Config + pallet_shield::Config + + pallet_subtensor_proxy::Config + Send + Sync + scale_info::TypeInfo, @@ -96,7 +99,8 @@ where + Dispatchable + IsSubType> + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, ::AddressMapping: AddressMapping, <::Lookup as StaticLookup>::Source: From, { @@ -456,6 +460,7 @@ where + pallet_proxy::Config + pallet_balances::Config + pallet_shield::Config + + pallet_subtensor_proxy::Config + Send + Sync + scale_info::TypeInfo, @@ -468,7 +473,8 @@ where + Dispatchable + IsSubType> + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, ::AddressMapping: AddressMapping, ::Balance: TryFrom, <::Lookup as StaticLookup>::Source: From, @@ -485,6 +491,7 @@ where + pallet_proxy::Config + pallet_balances::Config + pallet_shield::Config + + pallet_subtensor_proxy::Config + Send + Sync + scale_info::TypeInfo, @@ -497,7 +504,8 @@ where + Dispatchable + IsSubType> + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, ::AddressMapping: AddressMapping, ::Balance: TryFrom, <::Lookup as StaticLookup>::Source: From, diff --git a/precompiles/src/subnet.rs b/precompiles/src/subnet.rs index 7ec65fbc07..9d1e24cc1e 100644 --- a/precompiles/src/subnet.rs +++ b/precompiles/src/subnet.rs @@ -23,6 +23,7 @@ where + pallet_subtensor::Config + pallet_admin_utils::Config + pallet_shield::Config + + pallet_subtensor_proxy::Config + Send + Sync + scale_info::TypeInfo, @@ -34,7 +35,8 @@ where + Dispatchable + IsSubType> + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, ::AddressMapping: AddressMapping, { const INDEX: u64 = 2051; @@ -49,6 +51,7 @@ where + pallet_subtensor::Config + pallet_shield::Config + pallet_admin_utils::Config + + pallet_subtensor_proxy::Config + Send + Sync + scale_info::TypeInfo, @@ -60,7 +63,8 @@ where + Dispatchable + IsSubType> + IsSubType> - + IsSubType>, + + IsSubType> + + IsSubType>, ::AddressMapping: AddressMapping, { #[precompile::public("registerNetwork(bytes32)")] From a07cdb2e0b3f521e44697c0c7443c5288b58a3c2 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Fri, 23 Jan 2026 20:43:24 -0300 Subject: [PATCH 177/240] commit Cargo.lock --- pallets/subtensor/src/extensions/check_coldkey_swap.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/pallets/subtensor/src/extensions/check_coldkey_swap.rs b/pallets/subtensor/src/extensions/check_coldkey_swap.rs index f9ba364b35..4fc6206a39 100644 --- a/pallets/subtensor/src/extensions/check_coldkey_swap.rs +++ b/pallets/subtensor/src/extensions/check_coldkey_swap.rs @@ -104,6 +104,7 @@ where } #[cfg(test)] +#[allow(clippy::expect_used)] mod tests { use super::*; use crate::{BalancesCall, DefaultMinStake, tests::mock::*}; From 639994deabf988cbac4a8991029f1b4ddc23633a Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Fri, 23 Jan 2026 20:45:55 -0300 Subject: [PATCH 178/240] commit Cargo.lock --- pallets/subtensor/src/extensions/check_coldkey_swap.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/extensions/check_coldkey_swap.rs b/pallets/subtensor/src/extensions/check_coldkey_swap.rs index 4fc6206a39..a5cede01d4 100644 --- a/pallets/subtensor/src/extensions/check_coldkey_swap.rs +++ b/pallets/subtensor/src/extensions/check_coldkey_swap.rs @@ -104,7 +104,7 @@ where } #[cfg(test)] -#[allow(clippy::expect_used)] +#[allow(clippy::expect_used, clippy::unwrap_used)] mod tests { use super::*; use crate::{BalancesCall, DefaultMinStake, tests::mock::*}; From 9f3cab58134bcef71ea082ccef5d0dc16256bd88 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 24 Jan 2026 02:42:24 +0000 Subject: [PATCH 179/240] auto-update benchmark weights --- pallets/admin-utils/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index ab02445c79..3ece3dd984 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -1671,7 +1671,7 @@ pub mod pallet { /// Weight is handled by the `#[pallet::weight]` attribute. #[pallet::call_index(65)] #[pallet::weight(( - Weight::from_parts(6_201_000, 0) + Weight::from_parts(3_465_000, 0) .saturating_add(T::DbWeight::get().reads(0_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)), DispatchClass::Operational, From bfc5d4ecf844eea26bf81379113cc37fb2439ad4 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Fri, 23 Jan 2026 23:47:36 -0300 Subject: [PATCH 180/240] allow dispute to go through --- pallets/subtensor/src/extensions/check_coldkey_swap.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pallets/subtensor/src/extensions/check_coldkey_swap.rs b/pallets/subtensor/src/extensions/check_coldkey_swap.rs index a5cede01d4..db603f9614 100644 --- a/pallets/subtensor/src/extensions/check_coldkey_swap.rs +++ b/pallets/subtensor/src/extensions/check_coldkey_swap.rs @@ -84,7 +84,11 @@ where let is_allowed_direct = matches!( call.is_sub_type(), - Some(Call::announce_coldkey_swap { .. } | Call::swap_coldkey_announced { .. }) + Some( + Call::announce_coldkey_swap { .. } + | Call::swap_coldkey_announced { .. } + | Call::dispute_coldkey_swap { .. } + ) ); let is_mev_protected = matches!( @@ -299,6 +303,7 @@ mod tests { new_coldkey_hash: another_coldkey_hash, }), RuntimeCall::SubtensorModule(SubtensorCall::swap_coldkey_announced { new_coldkey }), + RuntimeCall::SubtensorModule(SubtensorCall::dispute_coldkey_swap {}), RuntimeCall::Shield(pallet_shield::Call::submit_encrypted { commitment: ::Hashing::hash_of(&new_coldkey), ciphertext: BoundedVec::truncate_from(vec![1, 2, 3, 4]), From 3526657fa4699a95f1f62c9419fe5c966b08d643 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 26 Jan 2026 14:50:58 -0500 Subject: [PATCH 181/240] Fix add_liquidity signature --- pallets/swap/src/pallet/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/pallets/swap/src/pallet/mod.rs b/pallets/swap/src/pallet/mod.rs index 03097b0055..88a26c998a 100644 --- a/pallets/swap/src/pallet/mod.rs +++ b/pallets/swap/src/pallet/mod.rs @@ -348,8 +348,6 @@ mod pallet { origin: OriginFor, _hotkey: T::AccountId, _netuid: NetUid, - _tick_low: TickIndex, - _tick_high: TickIndex, _liquidity: u64, ) -> DispatchResult { ensure_signed(origin)?; From 77aa4b6d88d15813ac40ac2f76adef72dcabe6f2 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 26 Jan 2026 16:32:40 -0500 Subject: [PATCH 182/240] Add logging for errors in disable_lp --- pallets/swap/src/pallet/mod.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pallets/swap/src/pallet/mod.rs b/pallets/swap/src/pallet/mod.rs index 88a26c998a..8df88715cc 100644 --- a/pallets/swap/src/pallet/mod.rs +++ b/pallets/swap/src/pallet/mod.rs @@ -592,7 +592,13 @@ mod pallet { // Remove provided liquidity unconditionally because the network may have // user liquidity previously disabled // Ignore result to avoid early stopping - let _ = Self::do_dissolve_all_liquidity_providers(netuid); + if let Err(err) = Self::do_dissolve_all_liquidity_providers(netuid) { + log::error!( + "Error dissolving liquidity providers on netuid {}: {:?}", + netuid, + err + ); + } } Ok(()) From 34bc0123c50cb2419ce209629ce017fa5acff757 Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 28 Jan 2026 18:43:47 +0800 Subject: [PATCH 183/240] integration test zero code precompile --- Cargo.lock | 56 +++++++++++++++++++++++++++--------------------------- Cargo.toml | 54 ++++++++++++++++++++++++++-------------------------- 2 files changed, 55 insertions(+), 55 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3e93e6db44..d3d51462dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4415,7 +4415,7 @@ dependencies = [ [[package]] name = "fc-api" version = "1.0.0-dev" -source = "git+https://github.com/opentensor/frontier?rev=7c93fd974ad70faec1c1c4b87ab23874f365f27a#7c93fd974ad70faec1c1c4b87ab23874f365f27a" +source = "git+https://github.com/opentensor/frontier?rev=d8d6b87f7443ae7b93344b3b769fb35f809114ef#d8d6b87f7443ae7b93344b3b769fb35f809114ef" dependencies = [ "async-trait", "fp-storage", @@ -4427,7 +4427,7 @@ dependencies = [ [[package]] name = "fc-aura" version = "1.0.0-dev" -source = "git+https://github.com/opentensor/frontier?rev=7c93fd974ad70faec1c1c4b87ab23874f365f27a#7c93fd974ad70faec1c1c4b87ab23874f365f27a" +source = "git+https://github.com/opentensor/frontier?rev=d8d6b87f7443ae7b93344b3b769fb35f809114ef#d8d6b87f7443ae7b93344b3b769fb35f809114ef" dependencies = [ "fc-rpc", "fp-storage", @@ -4443,7 +4443,7 @@ dependencies = [ [[package]] name = "fc-babe" version = "1.0.0-dev" -source = "git+https://github.com/opentensor/frontier?rev=7c93fd974ad70faec1c1c4b87ab23874f365f27a#7c93fd974ad70faec1c1c4b87ab23874f365f27a" +source = "git+https://github.com/opentensor/frontier?rev=d8d6b87f7443ae7b93344b3b769fb35f809114ef#d8d6b87f7443ae7b93344b3b769fb35f809114ef" dependencies = [ "fc-rpc", "sc-client-api", @@ -4459,7 +4459,7 @@ dependencies = [ [[package]] name = "fc-consensus" version = "2.0.0-dev" -source = "git+https://github.com/opentensor/frontier?rev=7c93fd974ad70faec1c1c4b87ab23874f365f27a#7c93fd974ad70faec1c1c4b87ab23874f365f27a" +source = "git+https://github.com/opentensor/frontier?rev=d8d6b87f7443ae7b93344b3b769fb35f809114ef#d8d6b87f7443ae7b93344b3b769fb35f809114ef" dependencies = [ "async-trait", "fp-consensus", @@ -4475,7 +4475,7 @@ dependencies = [ [[package]] name = "fc-db" version = "2.0.0-dev" -source = "git+https://github.com/opentensor/frontier?rev=7c93fd974ad70faec1c1c4b87ab23874f365f27a#7c93fd974ad70faec1c1c4b87ab23874f365f27a" +source = "git+https://github.com/opentensor/frontier?rev=d8d6b87f7443ae7b93344b3b769fb35f809114ef#d8d6b87f7443ae7b93344b3b769fb35f809114ef" dependencies = [ "async-trait", "ethereum", @@ -4505,7 +4505,7 @@ dependencies = [ [[package]] name = "fc-mapping-sync" version = "2.0.0-dev" -source = "git+https://github.com/opentensor/frontier?rev=7c93fd974ad70faec1c1c4b87ab23874f365f27a#7c93fd974ad70faec1c1c4b87ab23874f365f27a" +source = "git+https://github.com/opentensor/frontier?rev=d8d6b87f7443ae7b93344b3b769fb35f809114ef#d8d6b87f7443ae7b93344b3b769fb35f809114ef" dependencies = [ "fc-db", "fc-storage", @@ -4528,7 +4528,7 @@ dependencies = [ [[package]] name = "fc-rpc" version = "2.0.0-dev" -source = "git+https://github.com/opentensor/frontier?rev=7c93fd974ad70faec1c1c4b87ab23874f365f27a#7c93fd974ad70faec1c1c4b87ab23874f365f27a" +source = "git+https://github.com/opentensor/frontier?rev=d8d6b87f7443ae7b93344b3b769fb35f809114ef#d8d6b87f7443ae7b93344b3b769fb35f809114ef" dependencies = [ "ethereum", "ethereum-types", @@ -4579,7 +4579,7 @@ dependencies = [ [[package]] name = "fc-rpc-core" version = "1.1.0-dev" -source = "git+https://github.com/opentensor/frontier?rev=7c93fd974ad70faec1c1c4b87ab23874f365f27a#7c93fd974ad70faec1c1c4b87ab23874f365f27a" +source = "git+https://github.com/opentensor/frontier?rev=d8d6b87f7443ae7b93344b3b769fb35f809114ef#d8d6b87f7443ae7b93344b3b769fb35f809114ef" dependencies = [ "ethereum", "ethereum-types", @@ -4594,7 +4594,7 @@ dependencies = [ [[package]] name = "fc-storage" version = "1.0.0-dev" -source = "git+https://github.com/opentensor/frontier?rev=7c93fd974ad70faec1c1c4b87ab23874f365f27a#7c93fd974ad70faec1c1c4b87ab23874f365f27a" +source = "git+https://github.com/opentensor/frontier?rev=d8d6b87f7443ae7b93344b3b769fb35f809114ef#d8d6b87f7443ae7b93344b3b769fb35f809114ef" dependencies = [ "ethereum", "ethereum-types", @@ -4786,7 +4786,7 @@ dependencies = [ [[package]] name = "fp-account" version = "1.0.0-dev" -source = "git+https://github.com/opentensor/frontier?rev=7c93fd974ad70faec1c1c4b87ab23874f365f27a#7c93fd974ad70faec1c1c4b87ab23874f365f27a" +source = "git+https://github.com/opentensor/frontier?rev=d8d6b87f7443ae7b93344b3b769fb35f809114ef#d8d6b87f7443ae7b93344b3b769fb35f809114ef" dependencies = [ "hex", "impl-serde", @@ -4804,7 +4804,7 @@ dependencies = [ [[package]] name = "fp-consensus" version = "2.0.0-dev" -source = "git+https://github.com/opentensor/frontier?rev=7c93fd974ad70faec1c1c4b87ab23874f365f27a#7c93fd974ad70faec1c1c4b87ab23874f365f27a" +source = "git+https://github.com/opentensor/frontier?rev=d8d6b87f7443ae7b93344b3b769fb35f809114ef#d8d6b87f7443ae7b93344b3b769fb35f809114ef" dependencies = [ "ethereum", "parity-scale-codec", @@ -4815,7 +4815,7 @@ dependencies = [ [[package]] name = "fp-ethereum" version = "1.0.0-dev" -source = "git+https://github.com/opentensor/frontier?rev=7c93fd974ad70faec1c1c4b87ab23874f365f27a#7c93fd974ad70faec1c1c4b87ab23874f365f27a" +source = "git+https://github.com/opentensor/frontier?rev=d8d6b87f7443ae7b93344b3b769fb35f809114ef#d8d6b87f7443ae7b93344b3b769fb35f809114ef" dependencies = [ "ethereum", "ethereum-types", @@ -4827,7 +4827,7 @@ dependencies = [ [[package]] name = "fp-evm" version = "3.0.0-dev" -source = "git+https://github.com/opentensor/frontier?rev=7c93fd974ad70faec1c1c4b87ab23874f365f27a#7c93fd974ad70faec1c1c4b87ab23874f365f27a" +source = "git+https://github.com/opentensor/frontier?rev=d8d6b87f7443ae7b93344b3b769fb35f809114ef#d8d6b87f7443ae7b93344b3b769fb35f809114ef" dependencies = [ "environmental", "evm", @@ -4843,7 +4843,7 @@ dependencies = [ [[package]] name = "fp-rpc" version = "3.0.0-dev" -source = "git+https://github.com/opentensor/frontier?rev=7c93fd974ad70faec1c1c4b87ab23874f365f27a#7c93fd974ad70faec1c1c4b87ab23874f365f27a" +source = "git+https://github.com/opentensor/frontier?rev=d8d6b87f7443ae7b93344b3b769fb35f809114ef#d8d6b87f7443ae7b93344b3b769fb35f809114ef" dependencies = [ "ethereum", "ethereum-types", @@ -4859,7 +4859,7 @@ dependencies = [ [[package]] name = "fp-self-contained" version = "1.0.0-dev" -source = "git+https://github.com/opentensor/frontier?rev=7c93fd974ad70faec1c1c4b87ab23874f365f27a#7c93fd974ad70faec1c1c4b87ab23874f365f27a" +source = "git+https://github.com/opentensor/frontier?rev=d8d6b87f7443ae7b93344b3b769fb35f809114ef#d8d6b87f7443ae7b93344b3b769fb35f809114ef" dependencies = [ "frame-support", "parity-scale-codec", @@ -4871,7 +4871,7 @@ dependencies = [ [[package]] name = "fp-storage" version = "2.0.0" -source = "git+https://github.com/opentensor/frontier?rev=7c93fd974ad70faec1c1c4b87ab23874f365f27a#7c93fd974ad70faec1c1c4b87ab23874f365f27a" +source = "git+https://github.com/opentensor/frontier?rev=d8d6b87f7443ae7b93344b3b769fb35f809114ef#d8d6b87f7443ae7b93344b3b769fb35f809114ef" dependencies = [ "parity-scale-codec", "serde", @@ -9067,7 +9067,7 @@ dependencies = [ [[package]] name = "pallet-base-fee" version = "1.0.0" -source = "git+https://github.com/opentensor/frontier?rev=7c93fd974ad70faec1c1c4b87ab23874f365f27a#7c93fd974ad70faec1c1c4b87ab23874f365f27a" +source = "git+https://github.com/opentensor/frontier?rev=d8d6b87f7443ae7b93344b3b769fb35f809114ef#d8d6b87f7443ae7b93344b3b769fb35f809114ef" dependencies = [ "fp-evm", "frame-support", @@ -9696,7 +9696,7 @@ dependencies = [ [[package]] name = "pallet-ethereum" version = "4.0.0-dev" -source = "git+https://github.com/opentensor/frontier?rev=7c93fd974ad70faec1c1c4b87ab23874f365f27a#7c93fd974ad70faec1c1c4b87ab23874f365f27a" +source = "git+https://github.com/opentensor/frontier?rev=d8d6b87f7443ae7b93344b3b769fb35f809114ef#d8d6b87f7443ae7b93344b3b769fb35f809114ef" dependencies = [ "ethereum", "ethereum-types", @@ -9719,7 +9719,7 @@ dependencies = [ [[package]] name = "pallet-evm" version = "6.0.0-dev" -source = "git+https://github.com/opentensor/frontier?rev=7c93fd974ad70faec1c1c4b87ab23874f365f27a#7c93fd974ad70faec1c1c4b87ab23874f365f27a" +source = "git+https://github.com/opentensor/frontier?rev=d8d6b87f7443ae7b93344b3b769fb35f809114ef#d8d6b87f7443ae7b93344b3b769fb35f809114ef" dependencies = [ "cumulus-primitives-storage-weight-reclaim", "environmental", @@ -9744,7 +9744,7 @@ dependencies = [ [[package]] name = "pallet-evm-chain-id" version = "1.0.0-dev" -source = "git+https://github.com/opentensor/frontier?rev=7c93fd974ad70faec1c1c4b87ab23874f365f27a#7c93fd974ad70faec1c1c4b87ab23874f365f27a" +source = "git+https://github.com/opentensor/frontier?rev=d8d6b87f7443ae7b93344b3b769fb35f809114ef#d8d6b87f7443ae7b93344b3b769fb35f809114ef" dependencies = [ "frame-support", "frame-system", @@ -9755,7 +9755,7 @@ dependencies = [ [[package]] name = "pallet-evm-precompile-bn128" version = "2.0.0-dev" -source = "git+https://github.com/opentensor/frontier?rev=7c93fd974ad70faec1c1c4b87ab23874f365f27a#7c93fd974ad70faec1c1c4b87ab23874f365f27a" +source = "git+https://github.com/opentensor/frontier?rev=d8d6b87f7443ae7b93344b3b769fb35f809114ef#d8d6b87f7443ae7b93344b3b769fb35f809114ef" dependencies = [ "fp-evm", "sp-core", @@ -9765,7 +9765,7 @@ dependencies = [ [[package]] name = "pallet-evm-precompile-dispatch" version = "2.0.0-dev" -source = "git+https://github.com/opentensor/frontier?rev=7c93fd974ad70faec1c1c4b87ab23874f365f27a#7c93fd974ad70faec1c1c4b87ab23874f365f27a" +source = "git+https://github.com/opentensor/frontier?rev=d8d6b87f7443ae7b93344b3b769fb35f809114ef#d8d6b87f7443ae7b93344b3b769fb35f809114ef" dependencies = [ "fp-evm", "frame-support", @@ -9777,7 +9777,7 @@ dependencies = [ [[package]] name = "pallet-evm-precompile-modexp" version = "2.0.0-dev" -source = "git+https://github.com/opentensor/frontier?rev=7c93fd974ad70faec1c1c4b87ab23874f365f27a#7c93fd974ad70faec1c1c4b87ab23874f365f27a" +source = "git+https://github.com/opentensor/frontier?rev=d8d6b87f7443ae7b93344b3b769fb35f809114ef#d8d6b87f7443ae7b93344b3b769fb35f809114ef" dependencies = [ "fp-evm", "num", @@ -9786,7 +9786,7 @@ dependencies = [ [[package]] name = "pallet-evm-precompile-sha3fips" version = "2.0.0-dev" -source = "git+https://github.com/opentensor/frontier?rev=7c93fd974ad70faec1c1c4b87ab23874f365f27a#7c93fd974ad70faec1c1c4b87ab23874f365f27a" +source = "git+https://github.com/opentensor/frontier?rev=d8d6b87f7443ae7b93344b3b769fb35f809114ef#d8d6b87f7443ae7b93344b3b769fb35f809114ef" dependencies = [ "fp-evm", "tiny-keccak", @@ -9795,7 +9795,7 @@ dependencies = [ [[package]] name = "pallet-evm-precompile-simple" version = "2.0.0-dev" -source = "git+https://github.com/opentensor/frontier?rev=7c93fd974ad70faec1c1c4b87ab23874f365f27a#7c93fd974ad70faec1c1c4b87ab23874f365f27a" +source = "git+https://github.com/opentensor/frontier?rev=d8d6b87f7443ae7b93344b3b769fb35f809114ef#d8d6b87f7443ae7b93344b3b769fb35f809114ef" dependencies = [ "fp-evm", "ripemd", @@ -9863,7 +9863,7 @@ dependencies = [ [[package]] name = "pallet-hotfix-sufficients" version = "1.0.0" -source = "git+https://github.com/opentensor/frontier?rev=7c93fd974ad70faec1c1c4b87ab23874f365f27a#7c93fd974ad70faec1c1c4b87ab23874f365f27a" +source = "git+https://github.com/opentensor/frontier?rev=d8d6b87f7443ae7b93344b3b769fb35f809114ef#d8d6b87f7443ae7b93344b3b769fb35f809114ef" dependencies = [ "frame-benchmarking", "frame-support", @@ -13114,7 +13114,7 @@ dependencies = [ [[package]] name = "precompile-utils" version = "0.1.0" -source = "git+https://github.com/opentensor/frontier?rev=7c93fd974ad70faec1c1c4b87ab23874f365f27a#7c93fd974ad70faec1c1c4b87ab23874f365f27a" +source = "git+https://github.com/opentensor/frontier?rev=d8d6b87f7443ae7b93344b3b769fb35f809114ef#d8d6b87f7443ae7b93344b3b769fb35f809114ef" dependencies = [ "environmental", "evm", @@ -13138,7 +13138,7 @@ dependencies = [ [[package]] name = "precompile-utils-macro" version = "0.1.0" -source = "git+https://github.com/opentensor/frontier?rev=7c93fd974ad70faec1c1c4b87ab23874f365f27a#7c93fd974ad70faec1c1c4b87ab23874f365f27a" +source = "git+https://github.com/opentensor/frontier?rev=d8d6b87f7443ae7b93344b3b769fb35f809114ef#d8d6b87f7443ae7b93344b3b769fb35f809114ef" dependencies = [ "case", "num_enum", diff --git a/Cargo.toml b/Cargo.toml index 4a51bda878..bedc428242 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -234,35 +234,35 @@ polkadot-sdk = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = " runtime-common = { package = "polkadot-runtime-common", git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-stable2503-6", default-features = false } # Frontier -fp-evm = { git = "https://github.com/opentensor/frontier", rev = "7c93fd974ad70faec1c1c4b87ab23874f365f27a", default-features = false } -fp-rpc = { git = "https://github.com/opentensor/frontier", rev = "7c93fd974ad70faec1c1c4b87ab23874f365f27a", default-features = false } -fp-self-contained = { git = "https://github.com/opentensor/frontier", rev = "7c93fd974ad70faec1c1c4b87ab23874f365f27a", default-features = false } -fp-account = { git = "https://github.com/opentensor/frontier", rev = "7c93fd974ad70faec1c1c4b87ab23874f365f27a", default-features = false } -fc-storage = { git = "https://github.com/opentensor/frontier", rev = "7c93fd974ad70faec1c1c4b87ab23874f365f27a", default-features = false } -fc-db = { git = "https://github.com/opentensor/frontier", rev = "7c93fd974ad70faec1c1c4b87ab23874f365f27a", default-features = false } -fc-consensus = { git = "https://github.com/opentensor/frontier", rev = "7c93fd974ad70faec1c1c4b87ab23874f365f27a", default-features = false } -fp-consensus = { git = "https://github.com/opentensor/frontier", rev = "7c93fd974ad70faec1c1c4b87ab23874f365f27a", default-features = false } -fp-dynamic-fee = { git = "https://github.com/opentensor/frontier", rev = "7c93fd974ad70faec1c1c4b87ab23874f365f27a", default-features = false } -fc-api = { git = "https://github.com/opentensor/frontier", rev = "7c93fd974ad70faec1c1c4b87ab23874f365f27a", default-features = false } -fc-rpc = { git = "https://github.com/opentensor/frontier", rev = "7c93fd974ad70faec1c1c4b87ab23874f365f27a", default-features = false } -fc-rpc-core = { git = "https://github.com/opentensor/frontier", rev = "7c93fd974ad70faec1c1c4b87ab23874f365f27a", default-features = false } -fc-aura = { git = "https://github.com/opentensor/frontier", rev = "7c93fd974ad70faec1c1c4b87ab23874f365f27a", default-features = false } -fc-babe = { git = "https://github.com/opentensor/frontier", rev = "7c93fd974ad70faec1c1c4b87ab23874f365f27a", default-features = false } -fc-mapping-sync = { git = "https://github.com/opentensor/frontier", rev = "7c93fd974ad70faec1c1c4b87ab23874f365f27a", default-features = false } -precompile-utils = { git = "https://github.com/opentensor/frontier", rev = "7c93fd974ad70faec1c1c4b87ab23874f365f27a", default-features = false } +fp-evm = { git = "https://github.com/opentensor/frontier", rev = "d8d6b87f7443ae7b93344b3b769fb35f809114ef", default-features = false } +fp-rpc = { git = "https://github.com/opentensor/frontier", rev = "d8d6b87f7443ae7b93344b3b769fb35f809114ef", default-features = false } +fp-self-contained = { git = "https://github.com/opentensor/frontier", rev = "d8d6b87f7443ae7b93344b3b769fb35f809114ef", default-features = false } +fp-account = { git = "https://github.com/opentensor/frontier", rev = "d8d6b87f7443ae7b93344b3b769fb35f809114ef", default-features = false } +fc-storage = { git = "https://github.com/opentensor/frontier", rev = "d8d6b87f7443ae7b93344b3b769fb35f809114ef", default-features = false } +fc-db = { git = "https://github.com/opentensor/frontier", rev = "d8d6b87f7443ae7b93344b3b769fb35f809114ef", default-features = false } +fc-consensus = { git = "https://github.com/opentensor/frontier", rev = "d8d6b87f7443ae7b93344b3b769fb35f809114ef", default-features = false } +fp-consensus = { git = "https://github.com/opentensor/frontier", rev = "d8d6b87f7443ae7b93344b3b769fb35f809114ef", default-features = false } +fp-dynamic-fee = { git = "https://github.com/opentensor/frontier", rev = "d8d6b87f7443ae7b93344b3b769fb35f809114ef", default-features = false } +fc-api = { git = "https://github.com/opentensor/frontier", rev = "d8d6b87f7443ae7b93344b3b769fb35f809114ef", default-features = false } +fc-rpc = { git = "https://github.com/opentensor/frontier", rev = "d8d6b87f7443ae7b93344b3b769fb35f809114ef", default-features = false } +fc-rpc-core = { git = "https://github.com/opentensor/frontier", rev = "d8d6b87f7443ae7b93344b3b769fb35f809114ef", default-features = false } +fc-aura = { git = "https://github.com/opentensor/frontier", rev = "d8d6b87f7443ae7b93344b3b769fb35f809114ef", default-features = false } +fc-babe = { git = "https://github.com/opentensor/frontier", rev = "d8d6b87f7443ae7b93344b3b769fb35f809114ef", default-features = false } +fc-mapping-sync = { git = "https://github.com/opentensor/frontier", rev = "d8d6b87f7443ae7b93344b3b769fb35f809114ef", default-features = false } +precompile-utils = { git = "https://github.com/opentensor/frontier", rev = "d8d6b87f7443ae7b93344b3b769fb35f809114ef", default-features = false } # Frontier FRAME -pallet-base-fee = { git = "https://github.com/opentensor/frontier", rev = "7c93fd974ad70faec1c1c4b87ab23874f365f27a", default-features = false } -pallet-dynamic-fee = { git = "https://github.com/opentensor/frontier", rev = "7c93fd974ad70faec1c1c4b87ab23874f365f27a", default-features = false } -pallet-ethereum = { git = "https://github.com/opentensor/frontier", rev = "7c93fd974ad70faec1c1c4b87ab23874f365f27a", default-features = false } -pallet-evm = { git = "https://github.com/opentensor/frontier", rev = "7c93fd974ad70faec1c1c4b87ab23874f365f27a", default-features = false } -pallet-evm-precompile-dispatch = { git = "https://github.com/opentensor/frontier", rev = "7c93fd974ad70faec1c1c4b87ab23874f365f27a", default-features = false } -pallet-evm-chain-id = { git = "https://github.com/opentensor/frontier", rev = "7c93fd974ad70faec1c1c4b87ab23874f365f27a", default-features = false } -pallet-evm-precompile-modexp = { git = "https://github.com/opentensor/frontier", rev = "7c93fd974ad70faec1c1c4b87ab23874f365f27a", default-features = false } -pallet-evm-precompile-sha3fips = { git = "https://github.com/opentensor/frontier", rev = "7c93fd974ad70faec1c1c4b87ab23874f365f27a", default-features = false } -pallet-evm-precompile-simple = { git = "https://github.com/opentensor/frontier", rev = "7c93fd974ad70faec1c1c4b87ab23874f365f27a", default-features = false } -pallet-evm-precompile-bn128 = { git = "https://github.com/opentensor/frontier", rev = "7c93fd974ad70faec1c1c4b87ab23874f365f27a", default-features = false } -pallet-hotfix-sufficients = { git = "https://github.com/opentensor/frontier", rev = "7c93fd974ad70faec1c1c4b87ab23874f365f27a", default-features = false } +pallet-base-fee = { git = "https://github.com/opentensor/frontier", rev = "d8d6b87f7443ae7b93344b3b769fb35f809114ef", default-features = false } +pallet-dynamic-fee = { git = "https://github.com/opentensor/frontier", rev = "d8d6b87f7443ae7b93344b3b769fb35f809114ef", default-features = false } +pallet-ethereum = { git = "https://github.com/opentensor/frontier", rev = "d8d6b87f7443ae7b93344b3b769fb35f809114ef", default-features = false } +pallet-evm = { git = "https://github.com/opentensor/frontier", rev = "d8d6b87f7443ae7b93344b3b769fb35f809114ef", default-features = false } +pallet-evm-precompile-dispatch = { git = "https://github.com/opentensor/frontier", rev = "d8d6b87f7443ae7b93344b3b769fb35f809114ef", default-features = false } +pallet-evm-chain-id = { git = "https://github.com/opentensor/frontier", rev = "d8d6b87f7443ae7b93344b3b769fb35f809114ef", default-features = false } +pallet-evm-precompile-modexp = { git = "https://github.com/opentensor/frontier", rev = "d8d6b87f7443ae7b93344b3b769fb35f809114ef", default-features = false } +pallet-evm-precompile-sha3fips = { git = "https://github.com/opentensor/frontier", rev = "d8d6b87f7443ae7b93344b3b769fb35f809114ef", default-features = false } +pallet-evm-precompile-simple = { git = "https://github.com/opentensor/frontier", rev = "d8d6b87f7443ae7b93344b3b769fb35f809114ef", default-features = false } +pallet-evm-precompile-bn128 = { git = "https://github.com/opentensor/frontier", rev = "d8d6b87f7443ae7b93344b3b769fb35f809114ef", default-features = false } +pallet-hotfix-sufficients = { git = "https://github.com/opentensor/frontier", rev = "d8d6b87f7443ae7b93344b3b769fb35f809114ef", default-features = false } #DRAND pallet-drand = { path = "pallets/drand", default-features = false } From 01c12d36aebef5c618db066d4b05b8377aae693c Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Wed, 28 Jan 2026 15:50:07 -0500 Subject: [PATCH 184/240] Spec bump --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index a1b7aa5257..524aa1f1b9 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -240,7 +240,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: 372, + spec_version: 373, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 5d6819cfa36ed66ced9808408dd9b9c4e54246ef Mon Sep 17 00:00:00 2001 From: Shamil Gadelshin Date: Wed, 28 Jan 2026 18:47:41 +0300 Subject: [PATCH 185/240] Introduce subnet_buyback() --- pallets/subtensor/src/macros/dispatches.rs | 22 +- pallets/subtensor/src/staking/add_stake.rs | 14 +- .../subtensor/src/staking/recycle_alpha.rs | 19 ++ pallets/subtensor/src/tests/recycle_alpha.rs | 224 +++++++++++++++++- 4 files changed, 263 insertions(+), 16 deletions(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 2a362783ef..d0cdc8eeff 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -720,7 +720,7 @@ mod dispatches { netuid: NetUid, amount_staked: TaoCurrency, ) -> DispatchResult { - Self::do_add_stake(origin, hotkey, netuid, amount_staked) + Self::do_add_stake(origin, hotkey, netuid, amount_staked).map(|_| ()) } /// Remove stake from the staking account. The call must be made @@ -1785,6 +1785,7 @@ mod dispatches { limit_price, allow_partial, ) + .map(|_| ()) } /// --- Removes stake from a hotkey on a subnet with a price limit. @@ -2432,5 +2433,24 @@ mod dispatches { Ok(()) } + + /// --- Subnet buyback + #[pallet::call_index(125)] + #[pallet::weight(( + Weight::from_parts(5_711_000, 0) // TODO: adjust weights + .saturating_add(T::DbWeight::get().reads(0_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)), + DispatchClass::Normal, + Pays::Yes + ))] + pub fn subnet_buyback( + origin: T::RuntimeOrigin, + hotkey: T::AccountId, + netuid: NetUid, + amount: TaoCurrency, + limit: Option, + ) -> DispatchResult { + Self::do_subnet_buyback(origin, hotkey, netuid, amount, limit) + } } } diff --git a/pallets/subtensor/src/staking/add_stake.rs b/pallets/subtensor/src/staking/add_stake.rs index ea33912bf1..b0c71a3a48 100644 --- a/pallets/subtensor/src/staking/add_stake.rs +++ b/pallets/subtensor/src/staking/add_stake.rs @@ -42,7 +42,7 @@ impl Pallet { hotkey: T::AccountId, netuid: NetUid, stake_to_be_added: TaoCurrency, - ) -> dispatch::DispatchResult { + ) -> Result { // 1. We check that the transaction is signed by the caller and retrieve the T::AccountId coldkey information. let coldkey = ensure_signed(origin)?; log::debug!( @@ -77,10 +77,7 @@ impl Pallet { T::SwapInterface::max_price(), true, false, - )?; - - // Ok and return. - Ok(()) + ) } /// ---- The implementation for the extrinsic add_stake_limit: Adds stake to a hotkey @@ -130,7 +127,7 @@ impl Pallet { stake_to_be_added: TaoCurrency, limit_price: TaoCurrency, allow_partial: bool, - ) -> dispatch::DispatchResult { + ) -> Result { // 1. We check that the transaction is signed by the caller and retrieve the T::AccountId coldkey information. let coldkey = ensure_signed(origin)?; log::debug!( @@ -173,10 +170,7 @@ impl Pallet { limit_price, true, false, - )?; - - // Ok and return. - Ok(()) + ) } // Returns the maximum amount of RAO that can be executed with price limit diff --git a/pallets/subtensor/src/staking/recycle_alpha.rs b/pallets/subtensor/src/staking/recycle_alpha.rs index 5229971ed0..60620f1862 100644 --- a/pallets/subtensor/src/staking/recycle_alpha.rs +++ b/pallets/subtensor/src/staking/recycle_alpha.rs @@ -134,6 +134,25 @@ impl Pallet { netuid, )); + Ok(()) + } + pub(crate) fn do_subnet_buyback( + origin: T::RuntimeOrigin, + hotkey: T::AccountId, + netuid: NetUid, + amount: TaoCurrency, + limit: Option, + ) -> DispatchResult { + Self::ensure_subnet_owner(origin.clone(), netuid)?; + + let alpha = if let Some(limit) = limit { + Self::do_add_stake_limit(origin.clone(), hotkey.clone(), netuid, amount, limit, false)? + } else { + Self::do_add_stake(origin.clone(), hotkey.clone(), netuid, amount)? + }; + + Self::do_burn_alpha(origin, hotkey, alpha, netuid)?; + Ok(()) } } diff --git a/pallets/subtensor/src/tests/recycle_alpha.rs b/pallets/subtensor/src/tests/recycle_alpha.rs index 32a95c700d..d75c78c3de 100644 --- a/pallets/subtensor/src/tests/recycle_alpha.rs +++ b/pallets/subtensor/src/tests/recycle_alpha.rs @@ -1,12 +1,12 @@ +use super::mock; +use super::mock::*; +use crate::*; use approx::assert_abs_diff_eq; use frame_support::{assert_noop, assert_ok, traits::Currency}; use sp_core::U256; -use substrate_fixed::types::U64F64; +use substrate_fixed::types::{U64F64, U96F32}; use subtensor_runtime_common::{AlphaCurrency, Currency as CurrencyT}; - -use super::mock; -use super::mock::*; -use crate::*; +use subtensor_swap_interface::SwapHandler; #[test] fn test_recycle_success() { @@ -618,3 +618,217 @@ fn test_burn_precision_loss() { ); }); } + +#[test] +fn test_subnet_buyback_success() { + new_test_ext(1).execute_with(|| { + let hotkey_account_id = U256::from(533453); + let coldkey_account_id = U256::from(55453); + let amount = DefaultMinStake::::get().to_u64() * 10; + + let netuid = add_dynamic_network(&hotkey_account_id, &coldkey_account_id); + + mock::setup_reserves( + netuid, + (amount * 1_000_000).into(), + (amount * 10_000_000).into(), + ); + + SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount); + + // Check we have zero staked before transfer + assert_eq!( + SubtensorModule::get_total_stake_for_hotkey(&hotkey_account_id), + TaoCurrency::ZERO + ); + + // Execute subnet_buyback - this stakes TAO to get Alpha, then burns the Alpha + assert_ok!(SubtensorModule::subnet_buyback( + RuntimeOrigin::signed(coldkey_account_id), + hotkey_account_id, + netuid, + amount.into(), + None, + )); + + // After buyback, hotkey should have zero stake since alpha is burned immediately + assert_eq!( + SubtensorModule::get_total_stake_for_hotkey(&hotkey_account_id), + TaoCurrency::ZERO + ); + + // We spent TAO + assert_abs_diff_eq!( + SubtensorModule::get_coldkey_balance(&coldkey_account_id), + 0u64, + epsilon = 1u64 + ); + + // Verify AlphaBurned event was emitted + assert!(System::events().iter().any(|e| { + matches!( + &e.event, + RuntimeEvent::SubtensorModule(Event::AlphaBurned(..)) + ) + })); + }); +} + +#[test] +fn test_subnet_buyback_with_limit_success() { + new_test_ext(1).execute_with(|| { + let hotkey_account_id = U256::from(533453); + let coldkey_account_id = U256::from(55453); + let amount: u64 = 100_000_000_000; // 100 TAO - moderate amount + + // Add network + let netuid = add_dynamic_network(&hotkey_account_id, &coldkey_account_id); + + // Setup reserves with large liquidity to minimize slippage + let tao_reserve = TaoCurrency::from(1_000_000_000_000); // 1000 TAO + let alpha_in = AlphaCurrency::from(1_000_000_000_000); // 1000 Alpha + mock::setup_reserves(netuid, tao_reserve, alpha_in); + + // Verify current price is 1.0 + let current_price = + ::SwapInterface::current_alpha_price(netuid.into()); + assert_eq!(current_price, U96F32::from_num(1.0)); + + // Give coldkey sufficient balance + SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount); + + let initial_balance = SubtensorModule::get_coldkey_balance(&coldkey_account_id); + + // Setup limit price at 2.0 TAO per Alpha + // With 100 TAO into 1000/1000 pool, price moves from 1.0 to ~1.21 + let limit_price = TaoCurrency::from(2_000_000_000); // 2.0 TAO per Alpha + + // Execute subnet_buyback with limit + assert_ok!(SubtensorModule::subnet_buyback( + RuntimeOrigin::signed(coldkey_account_id), + hotkey_account_id, + netuid, + amount.into(), + Some(limit_price), + )); + + // After buyback, hotkey should have zero stake since alpha is burned immediately + assert_eq!( + SubtensorModule::get_total_stake_for_hotkey(&hotkey_account_id), + TaoCurrency::ZERO + ); + + // TAO should have been spent + let final_balance = SubtensorModule::get_coldkey_balance(&coldkey_account_id); + assert!( + final_balance < initial_balance, + "TAO should have been spent" + ); + + // Final price should be between initial (1.0) and limit (2.0) + let final_price = + ::SwapInterface::current_alpha_price(netuid.into()); + assert!( + final_price.to_num::() >= 1.0 && final_price.to_num::() <= 2.0, + "Final price {} should be between 1.0 and 2.0", + final_price.to_num::() + ); + + // Verify AlphaBurned event was emitted + assert!(System::events().iter().any(|e| { + matches!( + &e.event, + RuntimeEvent::SubtensorModule(Event::AlphaBurned(..)) + ) + })); + }); +} + +#[test] +fn test_subnet_buyback_non_owner_fails() { + new_test_ext(1).execute_with(|| { + let hotkey_account_id = U256::from(1); + let coldkey_account_id = U256::from(2); + let non_owner_coldkey = U256::from(3); + let amount = DefaultMinStake::::get().to_u64() * 10; + + // Add network with coldkey_account_id as owner + let netuid = add_dynamic_network(&hotkey_account_id, &coldkey_account_id); + + mock::setup_reserves( + netuid, + (amount * 1_000_000).into(), + (amount * 10_000_000).into(), + ); + + // Give non-owner some balance + SubtensorModule::add_balance_to_coldkey_account(&non_owner_coldkey, amount); + + // Non-owner trying to call subnet_buyback should fail with BadOrigin + assert_noop!( + SubtensorModule::subnet_buyback( + RuntimeOrigin::signed(non_owner_coldkey), + hotkey_account_id, + netuid, + amount.into(), + None, + ), + DispatchError::BadOrigin + ); + }); +} + +#[test] +fn test_subnet_buyback_nonexistent_subnet_fails() { + new_test_ext(1).execute_with(|| { + let hotkey_account_id = U256::from(1); + let coldkey_account_id = U256::from(2); + let amount = DefaultMinStake::::get().to_u64() * 10; + + // Give some balance + SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount); + + // Try to call subnet_buyback on non-existent subnet + let nonexistent_netuid = NetUid::from(999); + assert_noop!( + SubtensorModule::subnet_buyback( + RuntimeOrigin::signed(coldkey_account_id), + hotkey_account_id, + nonexistent_netuid, + amount.into(), + None, + ), + DispatchError::BadOrigin + ); + }); +} + +#[test] +fn test_subnet_buyback_insufficient_balance_fails() { + new_test_ext(1).execute_with(|| { + let hotkey_account_id = U256::from(1); + let coldkey_account_id = U256::from(2); + let amount = DefaultMinStake::::get().to_u64() * 10; + + // Add network + let netuid = add_dynamic_network(&hotkey_account_id, &coldkey_account_id); + + mock::setup_reserves( + netuid, + (amount * 1_000_000).into(), + (amount * 10_000_000).into(), + ); + + // Try to call subnet_buyback without sufficient balance + assert_noop!( + SubtensorModule::subnet_buyback( + RuntimeOrigin::signed(coldkey_account_id), + hotkey_account_id, + netuid, + amount.into(), + None, + ), + Error::::NotEnoughBalanceToStake + ); + }); +} From 4f20dad9f08a9f34878a87816220458f6a789f3a Mon Sep 17 00:00:00 2001 From: Shamil Gadelshin Date: Thu, 29 Jan 2026 15:30:31 +0300 Subject: [PATCH 186/240] Add benchmark --- pallets/subtensor/src/benchmarks.rs | 43 ++++++++++++++++++++++ pallets/subtensor/src/macros/dispatches.rs | 6 +-- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index f61c35aede..8bc77b9070 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -1666,4 +1666,47 @@ mod pallet_benchmarks { #[extrinsic_call] _(RawOrigin::Root, netuid, 100); } + + #[benchmark] + fn subnet_buyback() { + let netuid = NetUid::from(1); + let tempo: u16 = 1; + let seed: u32 = 1; + + Subtensor::::init_new_network(netuid, tempo); + SubtokenEnabled::::insert(netuid, true); + Subtensor::::set_burn(netuid, 1.into()); + Subtensor::::set_network_registration_allowed(netuid, true); + Subtensor::::set_max_allowed_uids(netuid, 4096); + + let coldkey: T::AccountId = account("Test", 0, seed); + let hotkey: T::AccountId = account("Alice", 0, seed); + + SubnetOwner::::set(netuid, coldkey.clone()); + + let balance_update = 900_000_000_000; + let limit = TaoCurrency::from(6_000_000_000); + let amount = TaoCurrency::from(44_000_000_000); + Subtensor::::add_balance_to_coldkey_account(&coldkey.clone(), balance_update); + + let tao_reserve = TaoCurrency::from(150_000_000_000); + let alpha_in = AlphaCurrency::from(100_000_000_000); + SubnetTAO::::insert(netuid, tao_reserve); + SubnetAlphaIn::::insert(netuid, alpha_in); + + assert_ok!(Subtensor::::do_burned_registration( + RawOrigin::Signed(coldkey.clone()).into(), + netuid, + hotkey.clone() + )); + + #[extrinsic_call] + _( + RawOrigin::Signed(coldkey.clone()), + hotkey, + netuid, + amount, + Some(limit), + ); + } } diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index d0cdc8eeff..a56a30efd6 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2437,9 +2437,9 @@ mod dispatches { /// --- Subnet buyback #[pallet::call_index(125)] #[pallet::weight(( - Weight::from_parts(5_711_000, 0) // TODO: adjust weights - .saturating_add(T::DbWeight::get().reads(0_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)), + Weight::from_parts(368_000_000, 8556) + .saturating_add(RocksDbWeight::get().reads(26_u64)) + .saturating_add(RocksDbWeight::get().writes(16_u64)) DispatchClass::Normal, Pays::Yes ))] From 0f13af610ec104fe3dba234e27d736ec4c15cfa8 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Thu, 29 Jan 2026 09:52:01 -0300 Subject: [PATCH 187/240] added scripts for local deps --- scripts/update-deps-to-path.sh | 90 ++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100755 scripts/update-deps-to-path.sh diff --git a/scripts/update-deps-to-path.sh b/scripts/update-deps-to-path.sh new file mode 100755 index 0000000000..a1eab9b99c --- /dev/null +++ b/scripts/update-deps-to-path.sh @@ -0,0 +1,90 @@ +#!/bin/zsh +# update-deps-to-path.sh goal is to replace git dependencies with path dependencies +# in the target Cargo.toml file. +# +# The script will scan the source repos for packages and build a mapping of package name to path. +# It will then process the target Cargo.toml file line by line and replace git dependencies with path dependencies. +# +# The script will output the new Cargo.toml file to stdout. +# +# The script will exit with a non-zero status if the target Cargo.toml file is not found. +# Usage: ./scripts/update-deps-to-path.sh ./Cargo.toml ./polkadot-sdk-otf ../frontier-otf > Cargo.toml.new + +set -e + +TARGET_TOML="${1:?Usage: $0 [source-repo-2] ...}" +shift + +if [[ $# -eq 0 ]]; then + echo "Error: At least one source repo path required" >&2 + exit 1 +fi + +# Build package name -> path mapping from all source repos +typeset -A PKG_PATHS + +for SOURCE_PATH in "$@"; do + SOURCE_PATH="$(cd "$SOURCE_PATH" && pwd)" + echo "Scanning $SOURCE_PATH for packages..." >&2 + + for cargo_toml in $(find "$SOURCE_PATH" -name "Cargo.toml" -type f 2>/dev/null); do + pkg_name=$(yq -p toml -o yaml '.package.name // ""' "$cargo_toml" 2>/dev/null | tr -d '"') + + if [[ -n "$pkg_name" && "$pkg_name" != "null" ]]; then + pkg_dir="$(dirname "$cargo_toml")" + PKG_PATHS[$pkg_name]="$pkg_dir" + echo " Found: $pkg_name" >&2 + fi + done +done + +echo "Found ${#PKG_PATHS[@]} total packages" >&2 +echo "" >&2 + +# Process target Cargo.toml line by line +echo "Updating dependencies in $TARGET_TOML..." >&2 + +while IFS= read -r line; do + # Check if this line has a git dependency + if [[ "$line" =~ ^([a-zA-Z0-9_-]+|\"[^\"]+\")\ *=\ *\{.*git\ *=\ *\" ]]; then + # Extract package name (handle both quoted and unquoted) + dep_name=$(echo "$line" | sed -E 's/^"?([a-zA-Z0-9_-]+)"? *=.*/\1/') + + # Check for package alias + if [[ "$line" =~ package\ *=\ *\"([^\"]+)\" ]]; then + lookup_name="${match[1]}" + else + lookup_name="$dep_name" + fi + + # Check if we have this package + if [[ -n "${PKG_PATHS[$lookup_name]}" ]]; then + pkg_path="${PKG_PATHS[$lookup_name]}" + echo " $dep_name -> $pkg_path" >&2 + + # Extract features/default-features/package if present + extras="" + if [[ "$line" =~ default-features\ *=\ *false ]]; then + extras="$extras, default-features = false" + fi + if [[ "$line" =~ package\ *=\ *\"([^\"]+)\" ]]; then + extras="$extras, package = \"${match[1]}\"" + fi + if [[ "$line" =~ features\ *=\ *\[([^\]]*)\] ]]; then + extras="$extras, features = [${match[1]}]" + fi + + # Output new line with just path + echo "${dep_name} = { path = \"${pkg_path}\"${extras} }" + else + # Package not found in sources, keep original + echo "$line" + fi + else + # Not a git dependency, keep as-is + echo "$line" + fi +done < "$TARGET_TOML" + +echo "" >&2 +echo "Done!" >&2 From 9319e91fa935588ed55767655bdebb1a062ee09a Mon Sep 17 00:00:00 2001 From: Shamil Gadelshin Date: Thu, 29 Jan 2026 16:06:40 +0300 Subject: [PATCH 188/240] Add rate limit --- pallets/subtensor/src/lib.rs | 3 + pallets/subtensor/src/macros/dispatches.rs | 4 +- pallets/subtensor/src/macros/errors.rs | 2 + .../subtensor/src/staking/recycle_alpha.rs | 11 ++++ pallets/subtensor/src/tests/recycle_alpha.rs | 63 +++++++++++++++++++ pallets/subtensor/src/utils/rate_limiting.rs | 4 ++ 6 files changed, 85 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 6ae43ac384..964f21eb62 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -2674,6 +2674,9 @@ pub enum RateLimitKey { // Last tx block delegate key limit per account ID #[codec(index = 5)] LastTxBlockDelegateTake(AccountId), + // Subnet buyback rate limit + #[codec(index = 6)] + SubnetBuyback(NetUid), } pub trait ProxyInterface { diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index a56a30efd6..fc3df6aef4 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2438,8 +2438,8 @@ mod dispatches { #[pallet::call_index(125)] #[pallet::weight(( Weight::from_parts(368_000_000, 8556) - .saturating_add(RocksDbWeight::get().reads(26_u64)) - .saturating_add(RocksDbWeight::get().writes(16_u64)) + .saturating_add(T::DbWeight::get().reads(26_u64)) + .saturating_add(T::DbWeight::get().writes(16_u64)), DispatchClass::Normal, Pays::Yes ))] diff --git a/pallets/subtensor/src/macros/errors.rs b/pallets/subtensor/src/macros/errors.rs index 6c3d7a35df..6463583692 100644 --- a/pallets/subtensor/src/macros/errors.rs +++ b/pallets/subtensor/src/macros/errors.rs @@ -268,5 +268,7 @@ mod errors { InvalidSubnetNumber, /// Unintended precision loss when unstaking alpha PrecisionLoss, + /// Subnet buyback exceeded the operation rate limit + SubnetBuybackRateLimitExceeded, } } diff --git a/pallets/subtensor/src/staking/recycle_alpha.rs b/pallets/subtensor/src/staking/recycle_alpha.rs index 60620f1862..03c6a270eb 100644 --- a/pallets/subtensor/src/staking/recycle_alpha.rs +++ b/pallets/subtensor/src/staking/recycle_alpha.rs @@ -145,6 +145,15 @@ impl Pallet { ) -> 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::SubnetBuyback(netuid)); + let rate_limit = TransactionType::SubnetBuyback.rate_limit_on_subnet::(netuid); + + ensure!( + last_block.is_zero() || current_block.saturating_sub(last_block) >= rate_limit, + Error::::SubnetBuybackRateLimitExceeded + ); + let alpha = if let Some(limit) = limit { Self::do_add_stake_limit(origin.clone(), hotkey.clone(), netuid, amount, limit, false)? } else { @@ -153,6 +162,8 @@ impl Pallet { Self::do_burn_alpha(origin, hotkey, alpha, netuid)?; + Self::set_rate_limited_last_block(&RateLimitKey::SubnetBuyback(netuid), current_block); + Ok(()) } } diff --git a/pallets/subtensor/src/tests/recycle_alpha.rs b/pallets/subtensor/src/tests/recycle_alpha.rs index d75c78c3de..6ef21a1460 100644 --- a/pallets/subtensor/src/tests/recycle_alpha.rs +++ b/pallets/subtensor/src/tests/recycle_alpha.rs @@ -832,3 +832,66 @@ fn test_subnet_buyback_insufficient_balance_fails() { ); }); } + +#[test] +fn test_subnet_buyback_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 = TaoCurrency::from(1_000_000_000_000); + let alpha_in = AlphaCurrency::from(1_000_000_000_000); + mock::setup_reserves(netuid, tao_reserve, alpha_in); + + // Give coldkey sufficient balance for multiple buybacks + SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount * 10); + + assert_eq!( + SubtensorModule::get_rate_limited_last_block(&RateLimitKey::SubnetBuyback(netuid)), + 0 + ); + + // First buyback should succeed + assert_ok!(SubtensorModule::subnet_buyback( + RuntimeOrigin::signed(coldkey_account_id), + hotkey_account_id, + netuid, + amount.into(), + None, + )); + + assert_eq!( + SubtensorModule::get_rate_limited_last_block(&RateLimitKey::SubnetBuyback(netuid)), + SubtensorModule::get_current_block_as_u64() + ); + + // Second buyback immediately after should fail due to rate limit + assert_noop!( + SubtensorModule::subnet_buyback( + RuntimeOrigin::signed(coldkey_account_id), + hotkey_account_id, + netuid, + amount.into(), + None, + ), + Error::::SubnetBuybackRateLimitExceeded + ); + + // After stepping past the rate limit, buyback should succeed again + let rate_limit = TransactionType::SubnetBuyback.rate_limit_on_subnet::(netuid); + step_block(rate_limit as u16); + + assert_ok!(SubtensorModule::subnet_buyback( + RuntimeOrigin::signed(coldkey_account_id), + hotkey_account_id, + netuid, + amount.into(), + None, + )); + }); +} diff --git a/pallets/subtensor/src/utils/rate_limiting.rs b/pallets/subtensor/src/utils/rate_limiting.rs index 85f58cfc64..a5de73c417 100644 --- a/pallets/subtensor/src/utils/rate_limiting.rs +++ b/pallets/subtensor/src/utils/rate_limiting.rs @@ -16,6 +16,7 @@ pub enum TransactionType { MechanismCountUpdate, MechanismEmission, MaxUidsTrimming, + SubnetBuyback, } impl TransactionType { @@ -44,6 +45,7 @@ impl TransactionType { (Tempo::::get(netuid) as u64).saturating_mul(epochs) } Self::SetSNOwnerHotkey => DefaultSetSNOwnerHotkeyRateLimit::::get(), + Self::SubnetBuyback => Tempo::::get(netuid) as u64, _ => self.rate_limit::(), } @@ -141,6 +143,7 @@ impl From for u16 { TransactionType::MechanismCountUpdate => 7, TransactionType::MechanismEmission => 8, TransactionType::MaxUidsTrimming => 9, + TransactionType::SubnetBuyback => 10, } } } @@ -158,6 +161,7 @@ impl From for TransactionType { 7 => TransactionType::MechanismCountUpdate, 8 => TransactionType::MechanismEmission, 9 => TransactionType::MaxUidsTrimming, + 10 => TransactionType::SubnetBuyback, _ => TransactionType::Unknown, } } From 8b30ccde0ba2d2d73be83c9f9f56c715a9f55d0d Mon Sep 17 00:00:00 2001 From: Shamil Gadelshin Date: Thu, 29 Jan 2026 16:35:00 +0300 Subject: [PATCH 189/240] Boost priority of subnet_buyback() --- pallets/subtensor/src/transaction_extension.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pallets/subtensor/src/transaction_extension.rs b/pallets/subtensor/src/transaction_extension.rs index cf1d410ea9..c8d27638af 100644 --- a/pallets/subtensor/src/transaction_extension.rs +++ b/pallets/subtensor/src/transaction_extension.rs @@ -19,6 +19,8 @@ use sp_std::vec::Vec; use subtensor_macros::freeze_struct; use subtensor_runtime_common::{NetUid, NetUidStorageIndex}; +const SUBNET_BUYBACK_PRIORITY_BOOST: u64 = 100; + #[freeze_struct("2e02eb32e5cb25d3")] #[derive(Default, Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, TypeInfo)] pub struct SubtensorTransactionExtension(pub PhantomData); @@ -298,6 +300,11 @@ where Err(_) => Err(CustomTransactionError::UidNotFound.into()), } } + Some(Call::subnet_buyback { .. }) => Ok(( + Self::validity_ok(SUBNET_BUYBACK_PRIORITY_BOOST), + Some(who.clone()), + origin, + )), _ => Ok((Default::default(), Some(who.clone()), origin)), } } From e1ada501017ccde4dc4299d3bfd06555ea2f98e4 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Thu, 29 Jan 2026 16:19:52 -0500 Subject: [PATCH 190/240] Add get_base_needed_for_quote method to balancer (for alpha fees) --- pallets/swap/src/pallet/balancer.rs | 57 +++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/pallets/swap/src/pallet/balancer.rs b/pallets/swap/src/pallet/balancer.rs index b2f15abfe8..503d2803b4 100644 --- a/pallets/swap/src/pallet/balancer.rs +++ b/pallets/swap/src/pallet/balancer.rs @@ -5,6 +5,7 @@ use safe_math::*; use sp_arithmetic::Perquintill; use sp_core::U256; use sp_runtime::Saturating; +use sp_std::ops::Neg; use substrate_fixed::types::U64F64; use subtensor_macros::freeze_struct; @@ -92,14 +93,20 @@ impl Balancer { /// /// Here we use SafeInt from bigmath crate for high-precision exponentiation, /// which exposes the function pow_ratio_scaled. - fn exp_scaled(&self, x: u64, dx: u64, base_quote: bool) -> U64F64 { - let den = x.saturating_add(dx); - if den == 0 { + /// + /// Note: ∆x may be negative + fn exp_scaled(&self, x: u64, dx: i128, base_quote: bool) -> U64F64 { + let x_plus_dx = if dx >= 0 { + x.saturating_add(dx as u64) + } else { + x.saturating_sub(dx.neg() as u64) + }; + + if x_plus_dx == 0 { return U64F64::saturating_from_num(0); } let w1: u128 = self.get_base_weight().deconstruct() as u128; let w2: u128 = self.get_quote_weight().deconstruct() as u128; - let x_plus_dx = x.saturating_add(dx); let precision = 1024; let x_safe = SafeInt::from(x); @@ -149,14 +156,14 @@ impl Balancer { /// This method is used in sell swaps /// (∆x is given by user, ∆y is paid out by the pool) pub fn exp_base_quote(&self, x: u64, dx: u64) -> U64F64 { - self.exp_scaled(x, dx, true) + self.exp_scaled(x, dx as i128, true) } /// Calculates exponent of (y / (y + ∆y)) ^ (w_quote/w_base) /// This method is used in buy swaps /// (∆y is given by user, ∆x is paid out by the pool) pub fn exp_quote_base(&self, y: u64, dy: u64) -> U64F64 { - self.exp_scaled(y, dy, false) + self.exp_scaled(y, dy as i128, false) } /// Calculates price as (w1/w2) * (y/x), where @@ -373,6 +380,22 @@ impl Balancer { .to_u64() .unwrap_or(0) } + + /// Calculates amount of Alpha that needs to be sold to get a given amount of TAO + pub fn get_base_needed_for_quote( + &self, + tao_reserve: u64, + alpha_reserve: u64, + delta_tao: u64, + ) -> u64 { + let e = self.exp_scaled(tao_reserve, (delta_tao as i128).neg(), false); + let one = U64F64::from_num(1); + let alpha_reserve_fixed = U64F64::from_num(alpha_reserve); + // e > 1 in this case + alpha_reserve_fixed + .saturating_mul(e.saturating_sub(one)) + .saturating_to_num::() + } } // cargo test --package pallet-subtensor-swap --lib -- pallet::balancer::tests --nocapture @@ -1125,7 +1148,7 @@ mod tests { }) .for_each(|(quote_weight, reserve, d_reserve, base_quote, expected)| { let balancer = Balancer::new(quote_weight).unwrap(); - let result = balancer.exp_scaled(reserve, d_reserve, base_quote); + let result = balancer.exp_scaled(reserve, d_reserve as i128, base_quote); assert_abs_diff_eq!( result.to_num::(), expected.to_num::(), @@ -1133,4 +1156,24 @@ mod tests { ); }); } + + // cargo test --package pallet-subtensor-swap --lib -- pallet::balancer::tests::test_base_needed_for_quote --exact --nocapture + #[test] + fn test_base_needed_for_quote() { + let num = 250_000_000_000_u128; // w1 = 0.75 + let w_quote = Perquintill::from_rational(num, 1_000_000_000_000_u128); + let bal = Balancer::new(w_quote).unwrap(); + + let tao_reserve: u64 = 1_000_000_000; + let alpha_reserve: u64 = 1_000_000_000; + let tao_delta: u64 = 1_123_432; // typical fee range + + let dx = bal.get_base_needed_for_quote(tao_reserve, alpha_reserve, tao_delta); + + // ∆x = x•[(y/(y+∆y))^(w2/w1) - 1] + let dx_expected = tao_reserve as f64 + * ((tao_reserve as f64 / ((tao_reserve - tao_delta) as f64)).powf(0.25 / 0.75) - 1.0); + + assert_eq!(dx, dx_expected as u64,); + } } From b74cd9020d5535dc7fa382fdf2e1c9cb8a920654 Mon Sep 17 00:00:00 2001 From: Shamil Gadelshin Date: Fri, 30 Jan 2026 13:28:48 +0300 Subject: [PATCH 191/240] Fix merge --- pallets/subtensor/src/extensions/subtensor.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/pallets/subtensor/src/extensions/subtensor.rs b/pallets/subtensor/src/extensions/subtensor.rs index a293fb24fe..ee2155ef2c 100644 --- a/pallets/subtensor/src/extensions/subtensor.rs +++ b/pallets/subtensor/src/extensions/subtensor.rs @@ -296,12 +296,10 @@ where .map_err(|_| CustomTransactionError::EvmKeyAssociateRateLimitExceeded)?; Ok((Default::default(), (), origin)) } - Some(Call::subnet_buyback { .. }) => Ok(( - Self::validity_ok(SUBNET_BUYBACK_PRIORITY_BOOST), - Some(who.clone()), - origin, - )), - _ => Ok((Default::default(), Some(who.clone()), origin)), + Some(Call::subnet_buyback { .. }) => { + Ok((Self::validity_ok(SUBNET_BUYBACK_PRIORITY_BOOST), (), origin)) + } + _ => Ok((Default::default(), (), origin)), } } From fb2313fdc31e87a2122b0a048c736950b9f7d687 Mon Sep 17 00:00:00 2001 From: Shamil Gadelshin Date: Fri, 30 Jan 2026 14:01:57 +0300 Subject: [PATCH 192/240] Add subnet owner check in tx extention for subnet_buyback --- pallets/subtensor/src/extensions/subtensor.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pallets/subtensor/src/extensions/subtensor.rs b/pallets/subtensor/src/extensions/subtensor.rs index ee2155ef2c..798f30dbe8 100644 --- a/pallets/subtensor/src/extensions/subtensor.rs +++ b/pallets/subtensor/src/extensions/subtensor.rs @@ -10,6 +10,7 @@ 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}, @@ -296,7 +297,11 @@ where .map_err(|_| CustomTransactionError::EvmKeyAssociateRateLimitExceeded)?; Ok((Default::default(), (), origin)) } - Some(Call::subnet_buyback { .. }) => { + Some(Call::subnet_buyback { netuid, .. }) => { + Pallet::::ensure_subnet_owner(origin.clone(), *netuid).map_err(|_| { + TransactionValidityError::Invalid(InvalidTransaction::BadSigner) + })?; + Ok((Self::validity_ok(SUBNET_BUYBACK_PRIORITY_BOOST), (), origin)) } _ => Ok((Default::default(), (), origin)), From 0989ba2989495a76bfd0e6cb9068d0404c280e9a Mon Sep 17 00:00:00 2001 From: open-junius Date: Fri, 30 Jan 2026 21:09:37 +0800 Subject: [PATCH 193/240] add precompile wrapper test --- .../src/contracts/precompileWrapper.sol | 362 +++++++++ .../src/contracts/precompileWrapper.ts | 714 ++++++++++++++++++ .../precompileWrapper.direct-call.test.ts | 66 ++ 3 files changed, 1142 insertions(+) create mode 100644 contract-tests/src/contracts/precompileWrapper.sol create mode 100644 contract-tests/src/contracts/precompileWrapper.ts create mode 100644 contract-tests/test/precompileWrapper.direct-call.test.ts diff --git a/contract-tests/src/contracts/precompileWrapper.sol b/contract-tests/src/contracts/precompileWrapper.sol new file mode 100644 index 0000000000..8671cc1c8d --- /dev/null +++ b/contract-tests/src/contracts/precompileWrapper.sol @@ -0,0 +1,362 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +// Precompile addresses +address constant ISUBTENSOR_BALANCE_TRANSFER_ADDRESS = 0x0000000000000000000000000000000000000800; +address constant IMETAGRAPH_ADDRESS = 0x0000000000000000000000000000000000000802; +address constant ISUBNET_ADDRESS = 0x0000000000000000000000000000000000000803; +address constant INEURON_ADDRESS = 0x0000000000000000000000000000000000000804; +address constant ISTAKING_V2_ADDRESS = 0x0000000000000000000000000000000000000805; +address constant IUID_LOOKUP_ADDRESS = 0x0000000000000000000000000000000000000806; +address constant IALPHA_ADDRESS = 0x0000000000000000000000000000000000000808; +address constant ICROWDLOAN_ADDRESS = 0x0000000000000000000000000000000000000809; +address constant ILEASING_ADDRESS = 0x000000000000000000000000000000000000080a; +address constant IPROXY_ADDRESS = 0x000000000000000000000000000000000000080b; +address constant IADDRESS_MAPPING_ADDRESS = 0x000000000000000000000000000000000000080C; + +// Interface definitions +interface ISubtensorBalanceTransfer { + function transfer(bytes32 data) external payable; +} + +interface IMetagraph { + function getUidCount(uint16 netuid) external view returns (uint16); +} + +interface ISubnet { + function registerNetwork( + bytes32 hotkey, + string memory subnetName, + string memory githubRepo, + string memory subnetContact, + string memory subnetUrl, + string memory discord, + string memory description, + string memory additional + ) external payable; + function getServingRateLimit(uint16 netuid) external view returns (uint64); +} + +interface INeuron { + function burnedRegister(uint16 netuid, bytes32 hotkey) external payable; +} + +interface IStaking { + function addStake( + bytes32 hotkey, + uint256 amount, + uint256 netuid + ) external payable; + function removeStake( + bytes32 hotkey, + uint256 amount, + uint256 netuid + ) external payable; + function getTotalColdkeyStake( + bytes32 coldkey + ) external view returns (uint256); + function getTotalHotkeyStake( + bytes32 hotkey + ) external view returns (uint256); +} + +struct LookupItem { + uint16 uid; + uint64 block_associated; +} + +interface IUidLookup { + function uidLookup( + uint16 netuid, + address evm_address, + uint16 limit + ) external view returns (LookupItem[] memory); +} + +interface IAlpha { + function getAlphaPrice(uint16 netuid) external view returns (uint256); +} + +struct CrowdloanInfo { + bytes32 creator; + uint64 deposit; + uint64 min_contribution; + uint32 end; + uint64 cap; + bytes32 funds_account; + uint64 raised; + bool has_target_address; + bytes32 target_address; + bool finalized; + uint32 contributors_count; +} + +interface ICrowdloan { + function getCrowdloan( + uint32 crowdloanId + ) external view returns (CrowdloanInfo memory); + function getContribution( + uint32 crowdloanId, + bytes32 coldkey + ) external view returns (uint64); + function create( + uint64 deposit, + uint64 minContribution, + uint64 cap, + uint32 end, + address targetAddress + ) external payable; +} + +struct LeaseInfo { + bytes32 beneficiary; + bytes32 coldkey; + bytes32 hotkey; + uint8 emissions_share; + bool has_end_block; + uint32 end_block; + uint16 netuid; + uint64 cost; +} + +interface ILeasing { + function getContributorShare( + uint32 leaseId, + bytes32 contributor + ) external view returns (uint128, uint128); + function createLeaseCrowdloan( + uint64 crowdloanDeposit, + uint64 crowdloanMinContribution, + uint64 crowdloanCap, + uint32 crowdloanEnd, + uint8 leasingEmissionsShare, + bool hasLeasingEndBlock, + uint32 leasingEndBlock + ) external payable; +} + +interface IProxy { + struct ProxyInfo { + bytes32 delegate; + uint256 proxy_type; + uint256 delay; + } + + function addProxy( + bytes32 delegate, + uint8 proxy_type, + uint32 delay + ) external; + function proxyCall( + bytes32 real, + uint8[] memory force_proxy_type, + bytes memory call + ) external; + function getProxies( + bytes32 account + ) external view returns (ProxyInfo[] memory); +} + +interface IAddressMapping { + function addressMapping( + address target_address + ) external view returns (bytes32); +} + +/** + * @title PrecompileWrapper + * @dev A wrapper contract that calls all precompile functions directly + * instead of using low-level calls like address.call() + */ +contract PrecompileWrapper { + ISubtensorBalanceTransfer public constant balanceTransfer = + ISubtensorBalanceTransfer(ISUBTENSOR_BALANCE_TRANSFER_ADDRESS); + IMetagraph public constant metagraph = IMetagraph(IMETAGRAPH_ADDRESS); + ISubnet public constant subnet = ISubnet(ISUBNET_ADDRESS); + INeuron public constant neuron = INeuron(INEURON_ADDRESS); + IStaking public constant staking = IStaking(ISTAKING_V2_ADDRESS); + IUidLookup public constant uidLookupPrecompile = + IUidLookup(IUID_LOOKUP_ADDRESS); + IAlpha public constant alpha = IAlpha(IALPHA_ADDRESS); + ICrowdloan public constant crowdloan = ICrowdloan(ICROWDLOAN_ADDRESS); + ILeasing public constant leasing = ILeasing(ILEASING_ADDRESS); + IProxy public constant proxy = IProxy(IPROXY_ADDRESS); + IAddressMapping public constant addressMappingPrecompile = + IAddressMapping(IADDRESS_MAPPING_ADDRESS); + + // ============ SubtensorBalanceTransfer Functions ============ + function transfer(bytes32 data) external payable { + balanceTransfer.transfer{value: msg.value}(data); + } + + // ============ Metagraph Functions ============ + + function getUidCount(uint16 netuid) external view returns (uint16) { + return metagraph.getUidCount(netuid); + } + + // ============ Subnet Functions ============ + + function registerNetworkWithDetails( + bytes32 hotkey, + string memory subnetName, + string memory githubRepo, + string memory subnetContact, + string memory subnetUrl, + string memory discord, + string memory description, + string memory additional + ) external payable { + subnet.registerNetwork( + hotkey, + subnetName, + githubRepo, + subnetContact, + subnetUrl, + discord, + description, + additional + ); + } + + function getServingRateLimit(uint16 netuid) external view returns (uint64) { + return subnet.getServingRateLimit(netuid); + } + + // ============ Neuron Functions ============ + + function burnedRegister(uint16 netuid, bytes32 hotkey) external payable { + neuron.burnedRegister{value: msg.value}(netuid, hotkey); + } + + // ============ Staking Functions ============ + function addStake( + bytes32 hotkey, + uint256 amount, + uint256 netuid + ) external payable { + staking.addStake(hotkey, amount, netuid); + } + + function removeStake( + bytes32 hotkey, + uint256 amount, + uint256 netuid + ) external payable { + staking.removeStake(hotkey, amount, netuid); + } + + function getTotalColdkeyStake( + bytes32 coldkey + ) external view returns (uint256) { + return staking.getTotalColdkeyStake(coldkey); + } + + function getTotalHotkeyStake( + bytes32 hotkey + ) external view returns (uint256) { + return staking.getTotalHotkeyStake(hotkey); + } + + // ============ Alpha Functions ============ + + function getAlphaPrice(uint16 netuid) external view returns (uint256) { + return alpha.getAlphaPrice(netuid); + } + + // ============ Address Mapping Functions ============ + + function addressMapping( + address target_address + ) external view returns (bytes32) { + return addressMappingPrecompile.addressMapping(target_address); + } + + // ============ Proxy Functions ============ + + function proxyCall( + bytes32 real, + uint8[] memory force_proxy_type, + bytes memory call + ) external { + proxy.proxyCall(real, force_proxy_type, call); + } + + function addProxy( + bytes32 delegate, + uint8 proxy_type, + uint32 delay + ) external { + proxy.addProxy(delegate, proxy_type, delay); + } + + function getProxies( + bytes32 account + ) external view returns (IProxy.ProxyInfo[] memory) { + return proxy.getProxies(account); + } + + // ============ UID Lookup Functions ============ + + function uidLookup( + uint16 netuid, + address evm_address, + uint16 limit + ) external view returns (LookupItem[] memory) { + return uidLookupPrecompile.uidLookup(netuid, evm_address, limit); + } + + // ============ Crowdloan Functions ============ + + function getCrowdloan( + uint32 crowdloanId + ) external view returns (CrowdloanInfo memory) { + return crowdloan.getCrowdloan(crowdloanId); + } + + function getContribution( + uint32 crowdloanId, + bytes32 coldkey + ) external view returns (uint64) { + return crowdloan.getContribution(crowdloanId, coldkey); + } + + function createCrowdloan( + uint64 deposit, + uint64 minContribution, + uint64 cap, + uint32 end, + address targetAddress + ) external payable { + crowdloan.create(deposit, minContribution, cap, end, targetAddress); + } + + // ============ Leasing Functions ============ + + function getContributorShare( + uint32 leaseId, + bytes32 contributor + ) external view returns (uint128, uint128) { + return leasing.getContributorShare(leaseId, contributor); + } + + function createLeaseCrowdloan( + uint64 crowdloanDeposit, + uint64 crowdloanMinContribution, + uint64 crowdloanCap, + uint32 crowdloanEnd, + uint8 leasingEmissionsShare, + bool hasLeasingEndBlock, + uint32 leasingEndBlock + ) external payable { + leasing.createLeaseCrowdloan( + crowdloanDeposit, + crowdloanMinContribution, + crowdloanCap, + crowdloanEnd, + leasingEmissionsShare, + hasLeasingEndBlock, + leasingEndBlock + ); + } +} diff --git a/contract-tests/src/contracts/precompileWrapper.ts b/contract-tests/src/contracts/precompileWrapper.ts new file mode 100644 index 0000000000..c082983da2 --- /dev/null +++ b/contract-tests/src/contracts/precompileWrapper.ts @@ -0,0 +1,714 @@ +export const PRECOMPILE_WRAPPER_ABI = [ + { + "inputs": [ + { + "internalType": "bytes32", + "name": "delegate", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "proxy_type", + "type": "uint8" + }, + { + "internalType": "uint32", + "name": "delay", + "type": "uint32" + } + ], + "name": "addProxy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "netuid", + "type": "uint256" + } + ], + "name": "addStake", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target_address", + "type": "address" + } + ], + "name": "addressMapping", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "addressMappingPrecompile", + "outputs": [ + { + "internalType": "contract IAddressMapping", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "alpha", + "outputs": [ + { + "internalType": "contract IAlpha", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "balanceTransfer", + "outputs": [ + { + "internalType": "contract ISubtensorBalanceTransfer", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + } + ], + "name": "burnedRegister", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "deposit", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "minContribution", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "cap", + "type": "uint64" + }, + { + "internalType": "uint32", + "name": "end", + "type": "uint32" + }, + { + "internalType": "address", + "name": "targetAddress", + "type": "address" + } + ], + "name": "createCrowdloan", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "crowdloanDeposit", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "crowdloanMinContribution", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "crowdloanCap", + "type": "uint64" + }, + { + "internalType": "uint32", + "name": "crowdloanEnd", + "type": "uint32" + }, + { + "internalType": "uint8", + "name": "leasingEmissionsShare", + "type": "uint8" + }, + { + "internalType": "bool", + "name": "hasLeasingEndBlock", + "type": "bool" + }, + { + "internalType": "uint32", + "name": "leasingEndBlock", + "type": "uint32" + } + ], + "name": "createLeaseCrowdloan", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "crowdloan", + "outputs": [ + { + "internalType": "contract ICrowdloan", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getAlphaPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "crowdloanId", + "type": "uint32" + }, + { + "internalType": "bytes32", + "name": "coldkey", + "type": "bytes32" + } + ], + "name": "getContribution", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "leaseId", + "type": "uint32" + }, + { + "internalType": "bytes32", + "name": "contributor", + "type": "bytes32" + } + ], + "name": "getContributorShare", + "outputs": [ + { + "internalType": "uint128", + "name": "", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "crowdloanId", + "type": "uint32" + } + ], + "name": "getCrowdloan", + "outputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "creator", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "deposit", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "min_contribution", + "type": "uint64" + }, + { + "internalType": "uint32", + "name": "end", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "cap", + "type": "uint64" + }, + { + "internalType": "bytes32", + "name": "funds_account", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "raised", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "has_target_address", + "type": "bool" + }, + { + "internalType": "bytes32", + "name": "target_address", + "type": "bytes32" + }, + { + "internalType": "bool", + "name": "finalized", + "type": "bool" + }, + { + "internalType": "uint32", + "name": "contributors_count", + "type": "uint32" + } + ], + "internalType": "struct CrowdloanInfo", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "account", + "type": "bytes32" + } + ], + "name": "getProxies", + "outputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "delegate", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "proxy_type", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "delay", + "type": "uint256" + } + ], + "internalType": "struct IProxy.ProxyInfo[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getServingRateLimit", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "coldkey", + "type": "bytes32" + } + ], + "name": "getTotalColdkeyStake", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + } + ], + "name": "getTotalHotkeyStake", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getUidCount", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "leasing", + "outputs": [ + { + "internalType": "contract ILeasing", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "metagraph", + "outputs": [ + { + "internalType": "contract IMetagraph", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "neuron", + "outputs": [ + { + "internalType": "contract INeuron", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proxy", + "outputs": [ + { + "internalType": "contract IProxy", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "real", + "type": "bytes32" + }, + { + "internalType": "uint8[]", + "name": "force_proxy_type", + "type": "uint8[]" + }, + { + "internalType": "bytes", + "name": "call", + "type": "bytes" + } + ], + "name": "proxyCall", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "string", + "name": "subnetName", + "type": "string" + }, + { + "internalType": "string", + "name": "githubRepo", + "type": "string" + }, + { + "internalType": "string", + "name": "subnetContact", + "type": "string" + }, + { + "internalType": "string", + "name": "subnetUrl", + "type": "string" + }, + { + "internalType": "string", + "name": "discord", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + }, + { + "internalType": "string", + "name": "additional", + "type": "string" + } + ], + "name": "registerNetworkWithDetails", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "netuid", + "type": "uint256" + } + ], + "name": "removeStake", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "staking", + "outputs": [ + { + "internalType": "contract IStaking", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "subnet", + "outputs": [ + { + "internalType": "contract ISubnet", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "data", + "type": "bytes32" + } + ], + "name": "transfer", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "address", + "name": "evm_address", + "type": "address" + }, + { + "internalType": "uint16", + "name": "limit", + "type": "uint16" + } + ], + "name": "uidLookup", + "outputs": [ + { + "components": [ + { + "internalType": "uint16", + "name": "uid", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "block_associated", + "type": "uint64" + } + ], + "internalType": "struct LookupItem[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "uidLookupPrecompile", + "outputs": [ + { + "internalType": "contract IUidLookup", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +]; + +export const PRECOMPILE_WRAPPER_BYTECODE = "6080604052348015600e575f5ffd5b50612cab8061001c5f395ff3fe6080604052600436106101d7575f3560e01c80638bba466c11610101578063bfe252a211610094578063db1d0fd511610063578063db1d0fd5146106ac578063ec556889146106d6578063fa83f46714610700578063fc6679fb14610728576101d7565b8063bfe252a214610612578063caf2ebf21461063c578063cd6f4eb114610666578063d75e3e0d14610682576101d7565b8063a2176276116100d0578063a217627614610566578063ac3166bf14610590578063afed65f9146105ba578063b1f789ef146105d6576101d7565b80638bba466c1461047657806394e3ac6f146104b2578063998538c4146104ee5780639f246f6f1461052a576101d7565b80634cf088d91161017957806369e38bc31161014857806369e38bc3146103c657806371214e27146104025780637444dadc1461041e5780637d691e301461045a576101d7565b80634cf088d91461031a5780635b53ddde146103445780635b7210c51461036e5780635e25f3f8146103aa576101d7565b80631fc9b141116101b55780631fc9b1411461027b5780633175bd98146102975780634054ecca146102d45780634c378a96146102f0576101d7565b80630494cd9a146101db5780630cadeda5146102175780631f1935721461023f575b5f5ffd5b3480156101e6575f5ffd5b5061020160048036038101906101fc919061124e565b610752565b60405161020e9190611291565b60405180910390f35b348015610222575f5ffd5b5061023d60048036038101906102389190611343565b6107d4565b005b34801561024a575f5ffd5b50610265600480360381019061026091906113ca565b610845565b6040516102729190611404565b60405180910390f35b61029560048036038101906102909190611450565b6108c7565b005b3480156102a2575f5ffd5b506102bd60048036038101906102b891906114a0565b610938565b6040516102cb929190611508565b60405180910390f35b6102ee60048036038101906102e9919061152f565b6109c0565b005b3480156102fb575f5ffd5b50610304610a2e565b60405161031191906115c8565b60405180910390f35b348015610325575f5ffd5b5061032e610a34565b60405161033b9190611601565b60405180910390f35b34801561034f575f5ffd5b50610358610a3a565b604051610365919061163a565b60405180910390f35b348015610379575f5ffd5b50610394600480360381019061038f91906114a0565b610a40565b6040516103a19190611675565b60405180910390f35b6103c460048036038101906103bf91906117ca565b610ac5565b005b3480156103d1575f5ffd5b506103ec60048036038101906103e791906113ca565b610b45565b6040516103f9919061194e565b60405180910390f35b61041c60048036038101906104179190611991565b610bc7565b005b348015610429575f5ffd5b50610444600480360381019061043f91906113ca565b610c3e565b6040516104519190611675565b60405180910390f35b610474600480360381019061046f9190611450565b610cc0565b005b348015610481575f5ffd5b5061049c60048036038101906104979190611a08565b610d31565b6040516104a99190611b59565b60405180910390f35b3480156104bd575f5ffd5b506104d860048036038101906104d39190611b73565b610dbb565b6040516104e59190611c95565b60405180910390f35b3480156104f9575f5ffd5b50610514600480360381019061050f9190611b73565b610e41565b604051610521919061194e565b60405180910390f35b348015610535575f5ffd5b50610550600480360381019061054b9190611b73565b610ec3565b60405161055d919061194e565b60405180910390f35b348015610571575f5ffd5b5061057a610f45565b6040516105879190611cd5565b60405180910390f35b34801561059b575f5ffd5b506105a4610f4b565b6040516105b19190611d0e565b60405180910390f35b6105d460048036038101906105cf9190611d51565b610f51565b005b3480156105e1575f5ffd5b506105fc60048036038101906105f79190611dee565b610fce565b6040516106099190611f22565b60405180910390f35b34801561061d575f5ffd5b5061062661105a565b6040516106339190611f62565b60405180910390f35b348015610647575f5ffd5b50610650611060565b60405161065d9190611f9b565b60405180910390f35b610680600480360381019061067b9190611b73565b611066565b005b34801561068d575f5ffd5b506106966110d3565b6040516106a39190611fd4565b60405180910390f35b3480156106b7575f5ffd5b506106c06110d9565b6040516106cd919061200d565b60405180910390f35b3480156106e1575f5ffd5b506106ea6110df565b6040516106f79190612046565b60405180910390f35b34801561070b575f5ffd5b50610726600480360381019061072191906121c1565b6110e5565b005b348015610733575f5ffd5b5061073c611156565b6040516107499190612269565b60405180910390f35b5f61080c73ffffffffffffffffffffffffffffffffffffffff16630494cd9a836040518263ffffffff1660e01b815260040161078e9190612291565b602060405180830381865afa1580156107a9573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107cd91906122be565b9050919050565b61080b73ffffffffffffffffffffffffffffffffffffffff16630cadeda58484846040518463ffffffff1660e01b815260040161081393929190612307565b5f604051808303815f87803b15801561082a575f5ffd5b505af115801561083c573d5f5f3e3d5ffd5b50505050505050565b5f61080273ffffffffffffffffffffffffffffffffffffffff16631f193572836040518263ffffffff1660e01b81526004016108819190611404565b602060405180830381865afa15801561089c573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108c09190612350565b9050919050565b61080573ffffffffffffffffffffffffffffffffffffffff16631fc9b1418484846040518463ffffffff1660e01b81526004016109069392919061237b565b5f604051808303815f87803b15801561091d575f5ffd5b505af115801561092f573d5f5f3e3d5ffd5b50505050505050565b5f5f61080a73ffffffffffffffffffffffffffffffffffffffff16633175bd9885856040518363ffffffff1660e01b81526004016109779291906123b0565b6040805180830381865afa158015610991573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109b59190612401565b915091509250929050565b61080473ffffffffffffffffffffffffffffffffffffffff16634054ecca83836040518363ffffffff1660e01b81526004016109fd92919061243f565b5f604051808303815f87803b158015610a14575f5ffd5b505af1158015610a26573d5f5f3e3d5ffd5b505050505050565b61080481565b61080581565b61080a81565b5f61080973ffffffffffffffffffffffffffffffffffffffff16635b7210c584846040518363ffffffff1660e01b8152600401610a7e9291906123b0565b602060405180830381865afa158015610a99573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610abd919061247a565b905092915050565b61080373ffffffffffffffffffffffffffffffffffffffff16631cf98c6b89898989898989896040518963ffffffff1660e01b8152600401610b0e989796959493929190612505565b5f604051808303815f87803b158015610b25575f5ffd5b505af1158015610b37573d5f5f3e3d5ffd5b505050505050505050505050565b5f61080873ffffffffffffffffffffffffffffffffffffffff166369e38bc3836040518263ffffffff1660e01b8152600401610b819190611404565b602060405180830381865afa158015610b9c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bc091906125c6565b9050919050565b61080973ffffffffffffffffffffffffffffffffffffffff1663127e1adb86868686866040518663ffffffff1660e01b8152600401610c0a9594939291906125f1565b5f604051808303815f87803b158015610c21575f5ffd5b505af1158015610c33573d5f5f3e3d5ffd5b505050505050505050565b5f61080373ffffffffffffffffffffffffffffffffffffffff16637444dadc836040518263ffffffff1660e01b8152600401610c7a9190611404565b602060405180830381865afa158015610c95573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610cb9919061247a565b9050919050565b61080573ffffffffffffffffffffffffffffffffffffffff16637d691e308484846040518463ffffffff1660e01b8152600401610cff9392919061237b565b5f604051808303815f87803b158015610d16575f5ffd5b505af1158015610d28573d5f5f3e3d5ffd5b50505050505050565b610d3961115c565b61080973ffffffffffffffffffffffffffffffffffffffff16638bba466c836040518263ffffffff1660e01b8152600401610d749190612642565b61016060405180830381865afa158015610d90573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610db49190612790565b9050919050565b606061080b73ffffffffffffffffffffffffffffffffffffffff166394e3ac6f836040518263ffffffff1660e01b8152600401610df89190611291565b5f60405180830381865afa158015610e12573d5f5f3e3d5ffd5b505050506040513d5f823e3d601f19601f82011682018060405250810190610e3a91906128dd565b9050919050565b5f61080573ffffffffffffffffffffffffffffffffffffffff1663998538c4836040518263ffffffff1660e01b8152600401610e7d9190611291565b602060405180830381865afa158015610e98573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ebc91906125c6565b9050919050565b5f61080573ffffffffffffffffffffffffffffffffffffffff16639f246f6f836040518263ffffffff1660e01b8152600401610eff9190611291565b602060405180830381865afa158015610f1a573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f3e91906125c6565b9050919050565b61080681565b61080c81565b61080a73ffffffffffffffffffffffffffffffffffffffff1663afed65f9888888888888886040518863ffffffff1660e01b8152600401610f989796959493929190612933565b5f604051808303815f87803b158015610faf575f5ffd5b505af1158015610fc1573d5f5f3e3d5ffd5b5050505050505050505050565b606061080673ffffffffffffffffffffffffffffffffffffffff1663b1f789ef8585856040518463ffffffff1660e01b815260040161100f939291906129a0565b5f60405180830381865afa158015611029573d5f5f3e3d5ffd5b505050506040513d5f823e3d601f19601f820116820180604052508101906110519190612ae2565b90509392505050565b61080981565b61080381565b61080073ffffffffffffffffffffffffffffffffffffffff1663cd6f4eb134836040518363ffffffff1660e01b81526004016110a29190611291565b5f604051808303818588803b1580156110b9575f5ffd5b505af11580156110cb573d5f5f3e3d5ffd5b505050505050565b61080081565b61080881565b61080b81565b61080b73ffffffffffffffffffffffffffffffffffffffff1663fa83f4678484846040518463ffffffff1660e01b815260040161112493929190612c32565b5f604051808303815f87803b15801561113b575f5ffd5b505af115801561114d573d5f5f3e3d5ffd5b50505050505050565b61080281565b6040518061016001604052805f81526020015f67ffffffffffffffff1681526020015f67ffffffffffffffff1681526020015f63ffffffff1681526020015f67ffffffffffffffff1681526020015f81526020015f67ffffffffffffffff1681526020015f151581526020015f81526020015f151581526020015f63ffffffff1681525090565b5f604051905090565b5f5ffd5b5f5ffd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61121d826111f4565b9050919050565b61122d81611213565b8114611237575f5ffd5b50565b5f8135905061124881611224565b92915050565b5f60208284031215611263576112626111ec565b5b5f6112708482850161123a565b91505092915050565b5f819050919050565b61128b81611279565b82525050565b5f6020820190506112a45f830184611282565b92915050565b6112b381611279565b81146112bd575f5ffd5b50565b5f813590506112ce816112aa565b92915050565b5f60ff82169050919050565b6112e9816112d4565b81146112f3575f5ffd5b50565b5f81359050611304816112e0565b92915050565b5f63ffffffff82169050919050565b6113228161130a565b811461132c575f5ffd5b50565b5f8135905061133d81611319565b92915050565b5f5f5f6060848603121561135a576113596111ec565b5b5f611367868287016112c0565b9350506020611378868287016112f6565b92505060406113898682870161132f565b9150509250925092565b5f61ffff82169050919050565b6113a981611393565b81146113b3575f5ffd5b50565b5f813590506113c4816113a0565b92915050565b5f602082840312156113df576113de6111ec565b5b5f6113ec848285016113b6565b91505092915050565b6113fe81611393565b82525050565b5f6020820190506114175f8301846113f5565b92915050565b5f819050919050565b61142f8161141d565b8114611439575f5ffd5b50565b5f8135905061144a81611426565b92915050565b5f5f5f60608486031215611467576114666111ec565b5b5f611474868287016112c0565b93505060206114858682870161143c565b92505060406114968682870161143c565b9150509250925092565b5f5f604083850312156114b6576114b56111ec565b5b5f6114c38582860161132f565b92505060206114d4858286016112c0565b9150509250929050565b5f6fffffffffffffffffffffffffffffffff82169050919050565b611502816114de565b82525050565b5f60408201905061151b5f8301856114f9565b61152860208301846114f9565b9392505050565b5f5f60408385031215611545576115446111ec565b5b5f611552858286016113b6565b9250506020611563858286016112c0565b9150509250929050565b5f819050919050565b5f61159061158b611586846111f4565b61156d565b6111f4565b9050919050565b5f6115a182611576565b9050919050565b5f6115b282611597565b9050919050565b6115c2816115a8565b82525050565b5f6020820190506115db5f8301846115b9565b92915050565b5f6115eb82611597565b9050919050565b6115fb816115e1565b82525050565b5f6020820190506116145f8301846115f2565b92915050565b5f61162482611597565b9050919050565b6116348161161a565b82525050565b5f60208201905061164d5f83018461162b565b92915050565b5f67ffffffffffffffff82169050919050565b61166f81611653565b82525050565b5f6020820190506116885f830184611666565b92915050565b5f5ffd5b5f5ffd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6116dc82611696565b810181811067ffffffffffffffff821117156116fb576116fa6116a6565b5b80604052505050565b5f61170d6111e3565b905061171982826116d3565b919050565b5f67ffffffffffffffff821115611738576117376116a6565b5b61174182611696565b9050602081019050919050565b828183375f83830152505050565b5f61176e6117698461171e565b611704565b90508281526020810184848401111561178a57611789611692565b5b61179584828561174e565b509392505050565b5f82601f8301126117b1576117b061168e565b5b81356117c184826020860161175c565b91505092915050565b5f5f5f5f5f5f5f5f610100898b0312156117e7576117e66111ec565b5b5f6117f48b828c016112c0565b985050602089013567ffffffffffffffff811115611815576118146111f0565b5b6118218b828c0161179d565b975050604089013567ffffffffffffffff811115611842576118416111f0565b5b61184e8b828c0161179d565b965050606089013567ffffffffffffffff81111561186f5761186e6111f0565b5b61187b8b828c0161179d565b955050608089013567ffffffffffffffff81111561189c5761189b6111f0565b5b6118a88b828c0161179d565b94505060a089013567ffffffffffffffff8111156118c9576118c86111f0565b5b6118d58b828c0161179d565b93505060c089013567ffffffffffffffff8111156118f6576118f56111f0565b5b6119028b828c0161179d565b92505060e089013567ffffffffffffffff811115611923576119226111f0565b5b61192f8b828c0161179d565b9150509295985092959890939650565b6119488161141d565b82525050565b5f6020820190506119615f83018461193f565b92915050565b61197081611653565b811461197a575f5ffd5b50565b5f8135905061198b81611967565b92915050565b5f5f5f5f5f60a086880312156119aa576119a96111ec565b5b5f6119b78882890161197d565b95505060206119c88882890161197d565b94505060406119d98882890161197d565b93505060606119ea8882890161132f565b92505060806119fb8882890161123a565b9150509295509295909350565b5f60208284031215611a1d57611a1c6111ec565b5b5f611a2a8482850161132f565b91505092915050565b611a3c81611279565b82525050565b611a4b81611653565b82525050565b611a5a8161130a565b82525050565b5f8115159050919050565b611a7481611a60565b82525050565b61016082015f820151611a8f5f850182611a33565b506020820151611aa26020850182611a42565b506040820151611ab56040850182611a42565b506060820151611ac86060850182611a51565b506080820151611adb6080850182611a42565b5060a0820151611aee60a0850182611a33565b5060c0820151611b0160c0850182611a42565b5060e0820151611b1460e0850182611a6b565b50610100820151611b29610100850182611a33565b50610120820151611b3e610120850182611a6b565b50610140820151611b53610140850182611a51565b50505050565b5f61016082019050611b6d5f830184611a7a565b92915050565b5f60208284031215611b8857611b876111ec565b5b5f611b95848285016112c0565b91505092915050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b611bd08161141d565b82525050565b606082015f820151611bea5f850182611a33565b506020820151611bfd6020850182611bc7565b506040820151611c106040850182611bc7565b50505050565b5f611c218383611bd6565b60608301905092915050565b5f602082019050919050565b5f611c4382611b9e565b611c4d8185611ba8565b9350611c5883611bb8565b805f5b83811015611c88578151611c6f8882611c16565b9750611c7a83611c2d565b925050600181019050611c5b565b5085935050505092915050565b5f6020820190508181035f830152611cad8184611c39565b905092915050565b5f611cbf82611597565b9050919050565b611ccf81611cb5565b82525050565b5f602082019050611ce85f830184611cc6565b92915050565b5f611cf882611597565b9050919050565b611d0881611cee565b82525050565b5f602082019050611d215f830184611cff565b92915050565b611d3081611a60565b8114611d3a575f5ffd5b50565b5f81359050611d4b81611d27565b92915050565b5f5f5f5f5f5f5f60e0888a031215611d6c57611d6b6111ec565b5b5f611d798a828b0161197d565b9750506020611d8a8a828b0161197d565b9650506040611d9b8a828b0161197d565b9550506060611dac8a828b0161132f565b9450506080611dbd8a828b016112f6565b93505060a0611dce8a828b01611d3d565b92505060c0611ddf8a828b0161132f565b91505092959891949750929550565b5f5f5f60608486031215611e0557611e046111ec565b5b5f611e12868287016113b6565b9350506020611e238682870161123a565b9250506040611e34868287016113b6565b9150509250925092565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b611e7081611393565b82525050565b604082015f820151611e8a5f850182611e67565b506020820151611e9d6020850182611a42565b50505050565b5f611eae8383611e76565b60408301905092915050565b5f602082019050919050565b5f611ed082611e3e565b611eda8185611e48565b9350611ee583611e58565b805f5b83811015611f15578151611efc8882611ea3565b9750611f0783611eba565b925050600181019050611ee8565b5085935050505092915050565b5f6020820190508181035f830152611f3a8184611ec6565b905092915050565b5f611f4c82611597565b9050919050565b611f5c81611f42565b82525050565b5f602082019050611f755f830184611f53565b92915050565b5f611f8582611597565b9050919050565b611f9581611f7b565b82525050565b5f602082019050611fae5f830184611f8c565b92915050565b5f611fbe82611597565b9050919050565b611fce81611fb4565b82525050565b5f602082019050611fe75f830184611fc5565b92915050565b5f611ff782611597565b9050919050565b61200781611fed565b82525050565b5f6020820190506120205f830184611ffe565b92915050565b5f61203082611597565b9050919050565b61204081612026565b82525050565b5f6020820190506120595f830184612037565b92915050565b5f67ffffffffffffffff821115612079576120786116a6565b5b602082029050602081019050919050565b5f5ffd5b5f6120a061209b8461205f565b611704565b905080838252602082019050602084028301858111156120c3576120c261208a565b5b835b818110156120ec57806120d888826112f6565b8452602084019350506020810190506120c5565b5050509392505050565b5f82601f83011261210a5761210961168e565b5b813561211a84826020860161208e565b91505092915050565b5f67ffffffffffffffff82111561213d5761213c6116a6565b5b61214682611696565b9050602081019050919050565b5f61216561216084612123565b611704565b90508281526020810184848401111561218157612180611692565b5b61218c84828561174e565b509392505050565b5f82601f8301126121a8576121a761168e565b5b81356121b8848260208601612153565b91505092915050565b5f5f5f606084860312156121d8576121d76111ec565b5b5f6121e5868287016112c0565b935050602084013567ffffffffffffffff811115612206576122056111f0565b5b612212868287016120f6565b925050604084013567ffffffffffffffff811115612233576122326111f0565b5b61223f86828701612194565b9150509250925092565b5f61225382611597565b9050919050565b61226381612249565b82525050565b5f60208201905061227c5f83018461225a565b92915050565b61228b81611213565b82525050565b5f6020820190506122a45f830184612282565b92915050565b5f815190506122b8816112aa565b92915050565b5f602082840312156122d3576122d26111ec565b5b5f6122e0848285016122aa565b91505092915050565b6122f2816112d4565b82525050565b6123018161130a565b82525050565b5f60608201905061231a5f830186611282565b61232760208301856122e9565b61233460408301846122f8565b949350505050565b5f8151905061234a816113a0565b92915050565b5f60208284031215612365576123646111ec565b5b5f6123728482850161233c565b91505092915050565b5f60608201905061238e5f830186611282565b61239b602083018561193f565b6123a8604083018461193f565b949350505050565b5f6040820190506123c35f8301856122f8565b6123d06020830184611282565b9392505050565b6123e0816114de565b81146123ea575f5ffd5b50565b5f815190506123fb816123d7565b92915050565b5f5f60408385031215612417576124166111ec565b5b5f612424858286016123ed565b9250506020612435858286016123ed565b9150509250929050565b5f6040820190506124525f8301856113f5565b61245f6020830184611282565b9392505050565b5f8151905061247481611967565b92915050565b5f6020828403121561248f5761248e6111ec565b5b5f61249c84828501612466565b91505092915050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f6124d7826124a5565b6124e181856124af565b93506124f18185602086016124bf565b6124fa81611696565b840191505092915050565b5f610100820190506125195f83018b611282565b818103602083015261252b818a6124cd565b9050818103604083015261253f81896124cd565b9050818103606083015261255381886124cd565b9050818103608083015261256781876124cd565b905081810360a083015261257b81866124cd565b905081810360c083015261258f81856124cd565b905081810360e08301526125a381846124cd565b90509998505050505050505050565b5f815190506125c081611426565b92915050565b5f602082840312156125db576125da6111ec565b5b5f6125e8848285016125b2565b91505092915050565b5f60a0820190506126045f830188611666565b6126116020830187611666565b61261e6040830186611666565b61262b60608301856122f8565b6126386080830184612282565b9695505050505050565b5f6020820190506126555f8301846122f8565b92915050565b5f5ffd5b5f8151905061266d81611319565b92915050565b5f8151905061268181611d27565b92915050565b5f610160828403121561269d5761269c61265b565b5b6126a8610160611704565b90505f6126b7848285016122aa565b5f8301525060206126ca84828501612466565b60208301525060406126de84828501612466565b60408301525060606126f28482850161265f565b606083015250608061270684828501612466565b60808301525060a061271a848285016122aa565b60a08301525060c061272e84828501612466565b60c08301525060e061274284828501612673565b60e083015250610100612757848285016122aa565b6101008301525061012061276d84828501612673565b610120830152506101406127838482850161265f565b6101408301525092915050565b5f61016082840312156127a6576127a56111ec565b5b5f6127b384828501612687565b91505092915050565b5f67ffffffffffffffff8211156127d6576127d56116a6565b5b602082029050602081019050919050565b5f606082840312156127fc576127fb61265b565b5b6128066060611704565b90505f612815848285016122aa565b5f830152506020612828848285016125b2565b602083015250604061283c848285016125b2565b60408301525092915050565b5f61285a612855846127bc565b611704565b9050808382526020820190506060840283018581111561287d5761287c61208a565b5b835b818110156128a6578061289288826127e7565b84526020840193505060608101905061287f565b5050509392505050565b5f82601f8301126128c4576128c361168e565b5b81516128d4848260208601612848565b91505092915050565b5f602082840312156128f2576128f16111ec565b5b5f82015167ffffffffffffffff81111561290f5761290e6111f0565b5b61291b848285016128b0565b91505092915050565b61292d81611a60565b82525050565b5f60e0820190506129465f83018a611666565b6129536020830189611666565b6129606040830188611666565b61296d60608301876122f8565b61297a60808301866122e9565b61298760a0830185612924565b61299460c08301846122f8565b98975050505050505050565b5f6060820190506129b35f8301866113f5565b6129c06020830185612282565b6129cd60408301846113f5565b949350505050565b5f67ffffffffffffffff8211156129ef576129ee6116a6565b5b602082029050602081019050919050565b5f60408284031215612a1557612a1461265b565b5b612a1f6040611704565b90505f612a2e8482850161233c565b5f830152506020612a4184828501612466565b60208301525092915050565b5f612a5f612a5a846129d5565b611704565b90508083825260208201905060408402830185811115612a8257612a8161208a565b5b835b81811015612aab5780612a978882612a00565b845260208401935050604081019050612a84565b5050509392505050565b5f82601f830112612ac957612ac861168e565b5b8151612ad9848260208601612a4d565b91505092915050565b5f60208284031215612af757612af66111ec565b5b5f82015167ffffffffffffffff811115612b1457612b136111f0565b5b612b2084828501612ab5565b91505092915050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b612b5b816112d4565b82525050565b5f612b6c8383612b52565b60208301905092915050565b5f602082019050919050565b5f612b8e82612b29565b612b988185612b33565b9350612ba383612b43565b805f5b83811015612bd3578151612bba8882612b61565b9750612bc583612b78565b925050600181019050612ba6565b5085935050505092915050565b5f81519050919050565b5f82825260208201905092915050565b5f612c0482612be0565b612c0e8185612bea565b9350612c1e8185602086016124bf565b612c2781611696565b840191505092915050565b5f606082019050612c455f830186611282565b8181036020830152612c578185612b84565b90508181036040830152612c6b8184612bfa565b905094935050505056fea2646970667358221220173b5d48037ae11a1b4ddc1d3fa630e53a41fbdde44b962f2ec9d9038e92aaf264736f6c634300081e0033"; diff --git a/contract-tests/test/precompileWrapper.direct-call.test.ts b/contract-tests/test/precompileWrapper.direct-call.test.ts new file mode 100644 index 0000000000..6cd2c14660 --- /dev/null +++ b/contract-tests/test/precompileWrapper.direct-call.test.ts @@ -0,0 +1,66 @@ +import * as assert from "assert"; +import { getDevnetApi, getRandomSubstrateKeypair, getBalance } 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 { + forceSetBalanceToSs58Address, + addNewSubnetwork, + addStake, + startCall, + disableWhiteListCheck, +} from "../src/subtensor"; +import { ethers } from "ethers"; +import { generateRandomEthersWallet } from "../src/utils"; +import { PRECOMPILE_WRAPPER_ABI, PRECOMPILE_WRAPPER_BYTECODE } from "../src/contracts/precompileWrapper"; + +describe("PrecompileWrapper - Direct Call Tests", () => { + const hotkey = getRandomSubstrateKeypair(); + const coldkey = getRandomSubstrateKeypair(); + const wallet1 = generateRandomEthersWallet(); + + let api: TypedApi; + let wrapperContract: ethers.Contract; + let wrapperAddress: string; + let netuid: number; + + before(async () => { + api = await getDevnetApi(); + await disableWhiteListCheck(api, true) + await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(hotkey.publicKey)); + await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(coldkey.publicKey)); + await forceSetBalanceToSs58Address(api, convertH160ToSS58(wallet1.address)); + await addNewSubnetwork(api, hotkey, coldkey); + netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1; + await startCall(api, netuid, coldkey); + + const factory = new ethers.ContractFactory( + PRECOMPILE_WRAPPER_ABI, + PRECOMPILE_WRAPPER_BYTECODE, + wallet1 + ); + const deployContract = await factory.deploy(); + await deployContract.waitForDeployment(); + wrapperAddress = await deployContract.getAddress(); + await forceSetBalanceToSs58Address(api, convertH160ToSS58(wrapperAddress)); + + console.log("Wrapper contract deployed at:", wrapperAddress); + console.log("Testing in subnet:", netuid); + + wrapperContract = new ethers.Contract(wrapperAddress, PRECOMPILE_WRAPPER_ABI, wallet1); + }); + + describe("Balance Transfer Precompile Direct Calls", () => { + it("Should transfer balance via wrapper", async () => { + const keypair = getRandomSubstrateKeypair(); + const transfer = await wrapperContract.transfer(keypair.publicKey, { value: raoToEth(tao(1)).toString() }); + await transfer.wait(); + + const balance = await getBalance(api, convertPublicKeyToSs58(keypair.publicKey)); + + assert.equal(balance, tao(1), "Wrapper and direct calls should match"); + }) + }); + +}); From f90673483c0ae39673af9205dd1fec71814c66da Mon Sep 17 00:00:00 2001 From: open-junius Date: Fri, 30 Jan 2026 21:30:18 +0800 Subject: [PATCH 194/240] revert lock file --- Cargo.lock | 1487 ++++++++++++++++++++++++++-------------------------- 1 file changed, 743 insertions(+), 744 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b101c77acf..cab9c9172a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -78,7 +78,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", - "getrandom 0.3.4", + "getrandom 0.3.3", "once_cell", "version_check", "zerocopy", @@ -86,9 +86,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.4" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -101,25 +101,25 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy-core" -version = "1.5.4" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfbc46fa201350bf859add798d818bbe68b84882a8af832e4433791d28a975d" +checksum = "575053cea24ea8cb7e775e39d5c53c33b19cfd0ca1cf6c0fd653f3d8c682095f" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", - "alloy-primitives 1.5.4", + "alloy-primitives 1.4.0", "alloy-rlp", "alloy-sol-types", ] [[package]] name = "alloy-dyn-abi" -version = "1.5.4" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14ff5ee5f27aa305bda825c735f686ad71bb65508158f059f513895abe69b8c3" +checksum = "a6c2905bafc2df7ccd32ca3af13f0b0d82f2e2ff9dfbeb12196c0d978d5c0deb" dependencies = [ "alloy-json-abi", - "alloy-primitives 1.5.4", + "alloy-primitives 1.4.0", "alloy-sol-type-parser", "alloy-sol-types", "itoa", @@ -130,11 +130,11 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "1.5.4" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8708475665cc00e081c085886e68eada2f64cfa08fc668213a9231655093d4de" +checksum = "a2acb6637a9c0e1cdf8971e0ced8f3fa34c04c5e9dccf6bb184f6a64fe0e37d8" dependencies = [ - "alloy-primitives 1.5.4", + "alloy-primitives 1.4.0", "alloy-sol-type-parser", "serde", "serde_json", @@ -142,17 +142,17 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.8.26" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "777d58b30eb9a4db0e5f59bc30e8c2caef877fee7dc8734cf242a51a60f22e05" +checksum = "8c77490fe91a0ce933a1f219029521f20fc28c2c0ca95d53fa4da9c00b8d9d4e" dependencies = [ "alloy-rlp", "bytes", "cfg-if", "const-hex", - "derive_more 2.1.1", + "derive_more 2.0.1", "foldhash 0.1.5", - "indexmap 2.13.0", + "indexmap 2.11.4", "itoa", "k256", "keccak-asm", @@ -168,29 +168,29 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "1.5.4" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b88cf92ed20685979ed1d8472422f0c6c2d010cec77caf63aaa7669cc1a7bc2" +checksum = "5b77f7d5e60ad8ae6bd2200b8097919712a07a6db622a4b201e7ead6166f02e5" dependencies = [ "alloy-rlp", "bytes", "cfg-if", "const-hex", - "derive_more 2.1.1", + "derive_more 2.0.1", "foldhash 0.2.0", - "hashbrown 0.16.1", - "indexmap 2.13.0", + "hashbrown 0.16.0", + "indexmap 2.11.4", "itoa", "k256", "keccak-asm", "paste", "proptest", "rand 0.9.2", - "rapidhash", "ruint", "rustc-hash 2.1.1", "serde", "sha3", + "tiny-keccak", ] [[package]] @@ -205,41 +205,41 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "1.5.4" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5fa1ca7e617c634d2bd9fa71f9ec8e47c07106e248b9fcbd3eaddc13cabd625" +checksum = "78c84c3637bee9b5c4a4d2b93360ee16553d299c3b932712353caf1cea76d0e6" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] name = "alloy-sol-macro-expander" -version = "1.5.4" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27c00c0c3a75150a9dc7c8c679ca21853a137888b4e1c5569f92d7e2b15b5102" +checksum = "a882aa4e1790063362434b9b40d358942b188477ac1c44cfb8a52816ffc0cc17" dependencies = [ "alloy-sol-macro-input", "const-hex", "heck 0.5.0", - "indexmap 2.13.0", + "indexmap 2.11.4", "proc-macro-error2", "proc-macro2", "quote", - "sha3", - "syn 2.0.114", + "syn 2.0.106", "syn-solidity", + "tiny-keccak", ] [[package]] name = "alloy-sol-macro-input" -version = "1.5.4" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "297db260eb4d67c105f68d6ba11b8874eec681caec5505eab8fbebee97f790bc" +checksum = "18e5772107f9bb265d8d8c86e0733937bb20d0857ea5425b1b6ddf51a9804042" dependencies = [ "const-hex", "dunce", @@ -247,15 +247,15 @@ dependencies = [ "macro-string", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", "syn-solidity", ] [[package]] name = "alloy-sol-type-parser" -version = "1.5.4" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b91b13181d3bcd23680fd29d7bc861d1f33fbe90fdd0af67162434aeba902d" +checksum = "e188b939aa4793edfaaa099cb1be4e620036a775b4bdf24fdc56f1cd6fd45890" dependencies = [ "serde", "winnow", @@ -263,12 +263,12 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "1.5.4" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc442cc2a75207b708d481314098a0f8b6f7b58e3148dd8d8cc7407b0d6f9385" +checksum = "c3c8a9a909872097caffc05df134e5ef2253a1cdb56d3a9cf0052a042ac763f9" dependencies = [ "alloy-json-abi", - "alloy-primitives 1.5.4", + "alloy-primitives 1.4.0", "alloy-sol-macro", "serde", ] @@ -320,22 +320,22 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.5" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.11" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -364,16 +364,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.114", -] - -[[package]] -name = "ar_archive_writer" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb93bbb63b9c227414f6eb3a0adfddca591a8ce1e9b60661bb08969b87e340b" -dependencies = [ - "object 0.37.3", + "syn 2.0.106", ] [[package]] @@ -668,7 +659,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" dependencies = [ "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -706,7 +697,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -873,7 +864,7 @@ checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -935,9 +926,9 @@ dependencies = [ [[package]] name = "ark-vrf" -version = "0.1.1" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d63e9780640021b74d02b32895d8cec1b4abe8e5547b560a6bda6b14b78c6da" +checksum = "9501da18569b2afe0eb934fb7afd5a247d238b94116155af4dd068f319adfe6d" dependencies = [ "ark-bls12-381 0.5.0", "ark-ec 0.5.0", @@ -965,7 +956,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27d55334c98d756b32dcceb60248647ab34f027690f87f9a362fd292676ee927" dependencies = [ "smallvec", - "thiserror 2.0.18", + "thiserror 2.0.17", ] [[package]] @@ -1017,7 +1008,7 @@ dependencies = [ "nom", "num-traits", "rusticata-macros", - "thiserror 2.0.18", + "thiserror 2.0.17", "time", ] @@ -1029,7 +1020,7 @@ checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", "synstructure 0.13.2", ] @@ -1041,7 +1032,7 @@ checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", "synstructure 0.13.2", ] @@ -1053,7 +1044,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -1144,7 +1135,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8034a681df4aed8b8edbd7fbe472401ecf009251c8b40556b304567052e294c5" dependencies = [ - "async-lock 3.4.2", + "async-lock 3.4.1", "blocking", "futures-lite 2.6.1", ] @@ -1182,7 +1173,7 @@ dependencies = [ "futures-lite 2.6.1", "parking", "polling 3.11.0", - "rustix 1.1.3", + "rustix 1.1.2", "slab", "windows-sys 0.61.2", ] @@ -1198,9 +1189,9 @@ dependencies = [ [[package]] name = "async-lock" -version = "3.4.2" +version = "3.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" dependencies = [ "event-listener 5.4.1", "event-listener-strategy", @@ -1254,14 +1245,14 @@ checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" dependencies = [ "async-channel 2.5.0", "async-io 2.6.0", - "async-lock 3.4.2", + "async-lock 3.4.1", "async-signal", "async-task", "blocking", "cfg-if", "event-listener 5.4.1", "futures-lite 2.6.1", - "rustix 1.1.3", + "rustix 1.1.2", ] [[package]] @@ -1271,12 +1262,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43c070bbf59cd3570b6b2dd54cd772527c7c3620fce8be898406dd3ed6adc64c" dependencies = [ "async-io 2.6.0", - "async-lock 3.4.2", + "async-lock 3.4.1", "atomic-waker", "cfg-if", "futures-core", "futures-io", - "rustix 1.1.3", + "rustix 1.1.2", "signal-hook-registry", "slab", "windows-sys 0.61.2", @@ -1296,7 +1287,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -1365,7 +1356,7 @@ checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -1437,9 +1428,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.8.3" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" [[package]] name = "binary-merkle-tree" @@ -1478,7 +1469,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -1500,11 +1491,11 @@ dependencies = [ [[package]] name = "bip39" -version = "2.2.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90dbd31c98227229239363921e60fcf5e558e43ec69094d46fc4996f08d1d5bc" +checksum = "43d193de1f7487df1914d3a568b772458861d33f9c54249612cc2893d6915054" dependencies = [ - "bitcoin_hashes 0.14.1", + "bitcoin_hashes 0.13.0", "serde", "unicode-normalization", ] @@ -1532,9 +1523,9 @@ checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb" [[package]] name = "bitcoin-io" -version = "0.1.4" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dee39a0ee5b4095224a0cfc6bf4cc1baf0f9624b96b367e53b66d974e51d953" +checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" [[package]] name = "bitcoin_hashes" @@ -1548,12 +1539,12 @@ dependencies = [ [[package]] name = "bitcoin_hashes" -version = "0.14.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26ec84b80c482df901772e931a9a681e26a1b9ee2302edeff23cb30328745c8b" +checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" dependencies = [ "bitcoin-io", - "hex-conservative 0.2.2", + "hex-conservative 0.2.1", ] [[package]] @@ -1564,9 +1555,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.10.0" +version = "2.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" [[package]] name = "bitvec" @@ -1614,38 +1605,37 @@ dependencies = [ [[package]] name = "blake2b_simd" -version = "1.0.4" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b79834656f71332577234b50bfc009996f7449e0c056884e6a02492ded0ca2f3" +checksum = "06e903a20b159e944f91ec8499fe1e55651480c541ea0a584f5d967c49ad9d99" dependencies = [ "arrayref", "arrayvec 0.7.6", - "constant_time_eq 0.4.2", + "constant_time_eq 0.3.1", ] [[package]] name = "blake2s_simd" -version = "1.0.4" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee29928bad1e3f94c9d1528da29e07a1d3d04817ae8332de1e8b846c8439f4b3" +checksum = "e90f7deecfac93095eb874a40febd69427776e24e1bd7f87f33ac62d6f0174df" dependencies = [ "arrayref", "arrayvec 0.7.6", - "constant_time_eq 0.4.2", + "constant_time_eq 0.3.1", ] [[package]] name = "blake3" -version = "1.8.3" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2468ef7d57b3fb7e16b576e8377cdbde2320c60e1491e961d11da40fc4f02a2d" +checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0" dependencies = [ "arrayref", "arrayvec 0.7.6", "cc", "cfg-if", - "constant_time_eq 0.4.2", - "cpufeatures", + "constant_time_eq 0.3.1", ] [[package]] @@ -1938,9 +1928,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.1" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "byte-slice-cast" @@ -1968,9 +1958,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.11.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" dependencies = [ "serde", ] @@ -1997,9 +1987,9 @@ dependencies = [ [[package]] name = "camino" -version = "1.2.2" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" +checksum = "276a59bf2b2c967788139340c9f0c5b12d7fd6630315c15c217e559de85d2609" dependencies = [ "serde_core", ] @@ -2035,9 +2025,9 @@ checksum = "fd6c0e7b807d60291f42f33f58480c0bfafe28ed08286446f45e463728cf9c1c" [[package]] name = "cc" -version = "1.2.55" +version = "1.2.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b26a0954ae34af09b50f0de26458fa95369a0d478d8236d3f93082b219bd29" +checksum = "e1d05d92f4b1fd76aad469d46cdd858ca761576082cd37df81416691e50199fb" dependencies = [ "find-msvc-tools", "jobserver", @@ -2071,9 +2061,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.4" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] name = "cfg_aliases" @@ -2123,9 +2113,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.43" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" dependencies = [ "iana-time-zone", "js-sys", @@ -2193,9 +2183,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.56" +version = "4.5.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75ca66430e33a14957acc24c5077b503e7d374151b2b4b3a10c83b4ceb4be0e" +checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae" dependencies = [ "clap_builder", "clap_derive", @@ -2203,9 +2193,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.56" +version = "4.5.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793207c7fa6300a0608d1080b858e5fdbe713cdc1c8db9fb17777d8a13e63df0" +checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9" dependencies = [ "anstream", "anstyle", @@ -2216,27 +2206,27 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.55" +version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" +checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] name = "clap_lex" -version = "0.7.7" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "coarsetime" -version = "0.1.37" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e58eb270476aa4fc7843849f8a35063e8743b4dbcdf6dd0f8ea0886980c204c2" +checksum = "91849686042de1b41cd81490edc83afbcb0abe5a9b6f2c4114f23ce8cca1bcf4" dependencies = [ "libc", "wasix", @@ -2245,13 +2235,13 @@ dependencies = [ [[package]] name = "codespan-reporting" -version = "0.13.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af491d569909a7e4dee0ad7db7f5341fef5c614d5b8ec8cf765732aba3cff681" +checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" dependencies = [ "serde", "termcolor", - "unicode-width", + "unicode-width 0.2.2", ] [[package]] @@ -2272,7 +2262,7 @@ dependencies = [ "nom", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -2293,12 +2283,12 @@ dependencies = [ [[package]] name = "comfy-table" -version = "7.2.2" +version = "7.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "958c5d6ecf1f214b4c2bbbbf6ab9523a864bd136dcf71a7e8904799acfe1ad47" +checksum = "b03b7db8e0b4b2fdad6c551e634134e99ec000e5c8c3b6856c65e8bbaded7a3b" dependencies = [ "unicode-segmentation", - "unicode-width", + "unicode-width 0.2.2", ] [[package]] @@ -2318,22 +2308,22 @@ dependencies = [ [[package]] name = "console" -version = "0.15.11" +version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" dependencies = [ "encode_unicode", + "lazy_static", "libc", - "once_cell", - "unicode-width", - "windows-sys 0.59.0", + "unicode-width 0.1.14", + "windows-sys 0.52.0", ] [[package]] name = "const-hex" -version = "1.17.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bb320cac8a0750d7f25280aa97b09c26edfe161164238ecbbb31092b079e735" +checksum = "b6407bff74dea37e0fa3dc1c1c974e5d46405f0c987bf9997a0762adce71eda6" dependencies = [ "cfg-if", "cpufeatures", @@ -2362,7 +2352,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" dependencies = [ - "getrandom 0.2.17", + "getrandom 0.2.16", "once_cell", "tiny-keccak", ] @@ -2401,9 +2391,9 @@ checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" [[package]] name = "constant_time_eq" -version = "0.4.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" [[package]] name = "convert_case" @@ -2411,15 +2401,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" -[[package]] -name = "convert_case" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" -dependencies = [ - "unicode-segmentation", -] - [[package]] name = "core-foundation" version = "0.9.4" @@ -2583,9 +2564,9 @@ dependencies = [ [[package]] name = "crc" -version = "3.4.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" +checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" dependencies = [ "crc-catalog", ] @@ -2674,9 +2655,9 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.1.7" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array 0.14.7", "rand_core 0.6.4", @@ -3105,7 +3086,7 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -3470,7 +3451,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -3488,9 +3469,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.194" +version = "1.0.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747d8437319e3a2f43d93b341c137927ca70c0f5dabeea7a005a73665e247c7e" +checksum = "4e9c4fe7f2f5dc5c62871a1b43992d197da6fa1394656a94276ac2894a90a6fe" dependencies = [ "cc", "cxx-build", @@ -3503,49 +3484,50 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.194" +version = "1.0.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0f4697d190a142477b16aef7da8a99bfdc41e7e8b1687583c0d23a79c7afc1e" +checksum = "b5cf2909d37d80633ddd208676fc27c2608a7f035fff69c882421168038b26dd" dependencies = [ "cc", "codespan-reporting", - "indexmap 2.13.0", + "indexmap 2.11.4", "proc-macro2", "quote", "scratch", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] name = "cxxbridge-cmd" -version = "1.0.194" +version = "1.0.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0956799fa8678d4c50eed028f2de1c0552ae183c76e976cf7ca8c4e36a7c328" +checksum = "077f5ee3d3bfd8d27f83208fdaa96ddd50af7f096c77077cc4b94da10bfacefd" dependencies = [ "clap", "codespan-reporting", - "indexmap 2.13.0", + "indexmap 2.11.4", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] name = "cxxbridge-flags" -version = "1.0.194" +version = "1.0.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23384a836ab4f0ad98ace7e3955ad2de39de42378ab487dc28d3990392cb283a" +checksum = "b0108748615125b9f2e915dfafdffcbdabbca9b15102834f6d7e9a768f2f2864" [[package]] name = "cxxbridge-macro" -version = "1.0.194" +version = "1.0.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6acc6b5822b9526adfb4fc377b67128fdd60aac757cc4a741a6278603f763cf" +checksum = "e6e896681ef9b8dc462cfa6961d61909704bde0984b30bcb4082fe102b478890" dependencies = [ - "indexmap 2.13.0", + "indexmap 2.11.4", "proc-macro2", "quote", - "syn 2.0.114", + "rustversion", + "syn 2.0.106", ] [[package]] @@ -3579,7 +3561,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -3593,7 +3575,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -3604,7 +3586,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core 0.20.11", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -3615,7 +3597,7 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core 0.21.3", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -3633,15 +3615,15 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.10.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" [[package]] name = "data-encoding-macro" -version = "0.1.19" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8142a83c17aa9461d637e649271eae18bf2edd00e91f2e105df36c3c16355bdb" +checksum = "47ce6c96ea0102f01122a185683611bd5ac8d99e62bc59dd12e6bda344ee673d" dependencies = [ "data-encoding", "data-encoding-macro-internal", @@ -3649,12 +3631,12 @@ dependencies = [ [[package]] name = "data-encoding-macro-internal" -version = "0.1.17" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ab67060fc6b8ef687992d439ca0fa36e7ed17e9a0b16b25b601e8757df720de" +checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976" dependencies = [ "data-encoding", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -3698,9 +3680,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.5.5" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +checksum = "a41953f86f8a05768a6cda24def994fd2f424b04ec5c719cf89989779f199071" dependencies = [ "powerfmt", ] @@ -3724,7 +3706,7 @@ checksum = "d65d7ce8132b7c0e54497a4d9a55a1c2a0912a0d786cf894472ba818fba45762" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -3735,7 +3717,7 @@ checksum = "ef941ded77d15ca19b40374869ac6000af1c9f2a4c0f3d4c70926287e6364a8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -3744,11 +3726,11 @@ version = "0.99.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" dependencies = [ - "convert_case 0.4.0", + "convert_case", "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -3762,11 +3744,11 @@ dependencies = [ [[package]] name = "derive_more" -version = "2.1.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" dependencies = [ - "derive_more-impl 2.1.1", + "derive_more-impl 2.0.1", ] [[package]] @@ -3777,20 +3759,18 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] name = "derive_more-impl" -version = "2.1.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" dependencies = [ - "convert_case 0.10.0", "proc-macro2", "quote", - "rustc_version 0.4.1", - "syn 2.0.114", + "syn 2.0.106", "unicode-xid", ] @@ -3883,7 +3863,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -3907,7 +3887,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.114", + "syn 2.0.106", "termcolor", "toml 0.8.23", "walkdir", @@ -3933,9 +3913,9 @@ checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" [[package]] name = "dtoa" -version = "1.0.11" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590" +checksum = "d6add3b8cff394282be81f3fc1a0605db594ed69890078ca6e2cab1c408bcf04" [[package]] name = "dunce" @@ -3961,7 +3941,7 @@ checksum = "7e8671d54058979a37a26f3511fbf8d198ba1aa35ffb202c42587d918d77213a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -4035,7 +4015,7 @@ dependencies = [ "enum-ordinalize", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -4069,9 +4049,9 @@ dependencies = [ [[package]] name = "encode_unicode" -version = "1.0.0" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "enum-as-inner" @@ -4082,27 +4062,27 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] name = "enum-ordinalize" -version = "4.3.2" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a1091a7bb1f8f2c4b28f1fe2cef4980ca2d410a3d727d67ecc3178c9b0800f0" +checksum = "fea0dcfa4e54eeb516fe454635a95753ddd39acda650ce703031c6973e315dd5" dependencies = [ "enum-ordinalize-derive", ] [[package]] name = "enum-ordinalize-derive" -version = "4.3.2" +version = "4.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631" +checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -4122,7 +4102,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -4133,14 +4113,14 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] name = "env_filter" -version = "0.1.4" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" dependencies = [ "log", "regex", @@ -4356,7 +4336,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -4425,11 +4405,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb42427514b063d97ce21d5199f36c0c307d981434a6be32582bc79fe5bd2303" dependencies = [ "expander", - "indexmap 2.13.0", + "indexmap 2.11.4", "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -4489,7 +4469,7 @@ dependencies = [ "sp-block-builder", "sp-consensus", "sp-runtime", - "thiserror 2.0.18", + "thiserror 2.0.17", ] [[package]] @@ -4592,7 +4572,7 @@ dependencies = [ "sp-storage", "sp-trie", "substrate-prometheus-endpoint", - "thiserror 2.0.18", + "thiserror 2.0.17", "tokio", ] @@ -4676,13 +4656,14 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.27" +version = "0.2.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" +checksum = "bc0505cd1b6fa6580283f6bdf70a73fcf4aba1184038c90902b92b3dd0df63ed" dependencies = [ "cfg-if", "libc", "libredox", + "windows-sys 0.60.2", ] [[package]] @@ -4703,9 +4684,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.9" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +checksum = "0399f9d26e5191ce32c498bebd31e7a3ceabc2745f0ac54af3f335126c3f24b3" [[package]] name = "fixed-hash" @@ -5027,7 +5008,7 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -5090,9 +5071,9 @@ dependencies = [ [[package]] name = "frame-metadata" -version = "23.0.1" +version = "23.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ba5be0edbdb824843a0f9c6f0906ecfc66c5316218d74457003218b24909ed0" +checksum = "d8c26fcb0454397c522c05fdad5380c4e622f8a875638af33bff5a320d1fc965" dependencies = [ "cfg-if", "parity-scale-codec", @@ -5141,7 +5122,7 @@ dependencies = [ "bitflags 1.3.2", "docify", "environmental", - "frame-metadata 23.0.1", + "frame-metadata 23.0.0", "frame-support-procedural", "impl-trait-for-tuples", "k256", @@ -5188,7 +5169,7 @@ dependencies = [ "proc-macro2", "quote", "sp-crypto-hashing 0.1.0 (git+https://github.com/opentensor/polkadot-sdk.git?rev=58add17a1232888db9cfb70f4afb6dc7fd7b3a79)", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -5201,7 +5182,7 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -5213,7 +5194,7 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -5224,7 +5205,7 @@ checksum = "68672b9ec6fe72d259d3879dc212c5e42e977588cdac830c76f54d9f492aeb58" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -5234,7 +5215,7 @@ source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=58add17a1232888 dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -5432,7 +5413,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -5523,28 +5504,28 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.17" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "js-sys", "libc", - "wasi", + "wasi 0.11.1+wasi-snapshot-preview1", "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.3.4" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "js-sys", "libc", "r-efi", - "wasip2", + "wasi 0.14.7+wasi-0.2.4", "wasm-bindgen", ] @@ -5644,7 +5625,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.13.0", + "indexmap 2.11.4", "slab", "tokio", "tokio-util", @@ -5653,17 +5634,17 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.13" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" dependencies = [ "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "http 1.4.0", - "indexmap 2.13.0", + "http 1.3.1", + "indexmap 2.11.4", "slab", "tokio", "tokio-util", @@ -5744,13 +5725,12 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.16.1" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" dependencies = [ "foldhash 0.2.0", "serde", - "serde_core", ] [[package]] @@ -5812,9 +5792,9 @@ checksum = "212ab92002354b4819390025006c897e8140934349e8635c9b077f47b4dcbd20" [[package]] name = "hex-conservative" -version = "0.2.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda06d18ac606267c40c04e41b9947729bf8b9efe74bd4e82b61a5f26a510b9f" +checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" dependencies = [ "arrayvec 0.7.6", ] @@ -5868,7 +5848,7 @@ dependencies = [ "once_cell", "rand 0.9.2", "ring 0.17.14", - "thiserror 2.0.18", + "thiserror 2.0.17", "tinyvec", "tokio", "tracing", @@ -5912,7 +5892,7 @@ dependencies = [ "rand 0.9.2", "resolv-conf", "smallvec", - "thiserror 2.0.18", + "thiserror 2.0.17", "tokio", "tracing", ] @@ -5969,11 +5949,12 @@ dependencies = [ [[package]] name = "http" -version = "1.4.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", + "fnv", "itoa", ] @@ -5995,7 +5976,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.4.0", + "http 1.3.1", ] [[package]] @@ -6006,7 +5987,7 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http 1.4.0", + "http 1.3.1", "http-body 1.0.1", "pin-project-lite", ] @@ -6074,16 +6055,16 @@ dependencies = [ [[package]] name = "hyper" -version = "1.8.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" dependencies = [ "atomic-waker", "bytes", "futures-channel", "futures-core", - "h2 0.4.13", - "http 1.4.0", + "h2 0.4.12", + "http 1.3.1", "http-body 1.0.1", "httparse", "httpdate", @@ -6101,8 +6082,8 @@ version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "http 1.4.0", - "hyper 1.8.1", + "http 1.3.1", + "hyper 1.7.0", "hyper-util", "log", "rustls", @@ -6115,20 +6096,20 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.19" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" +checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" dependencies = [ "bytes", "futures-channel", "futures-core", "futures-util", - "http 1.4.0", + "http 1.3.1", "http-body 1.0.1", - "hyper 1.8.1", + "hyper 1.7.0", "libc", "pin-project-lite", - "socket2 0.6.2", + "socket2 0.6.0", "tokio", "tower-service", "tracing", @@ -6136,9 +6117,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.65" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -6160,9 +6141,9 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.1.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ "displaydoc", "potential_utf", @@ -6173,9 +6154,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.1.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" dependencies = [ "displaydoc", "litemap", @@ -6186,10 +6167,11 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.1.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" dependencies = [ + "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", @@ -6200,38 +6182,42 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.1.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] name = "icu_properties" -version = "2.1.2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" dependencies = [ + "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", "icu_provider", + "potential_utf", "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "2.1.2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" [[package]] name = "icu_provider" -version = "2.1.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" dependencies = [ "displaydoc", "icu_locale_core", + "stable_deref_trait", + "tinystr", "writeable", "yoke", "zerofrom", @@ -6373,7 +6359,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -6408,12 +6394,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.13.0" +version = "2.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" dependencies = [ "equivalent", - "hashbrown 0.16.1", + "hashbrown 0.16.0", "serde", "serde_core", ] @@ -6462,6 +6448,17 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "io-uring" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" +dependencies = [ + "bitflags 2.9.4", + "cfg-if", + "libc", +] + [[package]] name = "ip_network" version = "0.4.1" @@ -6488,13 +6485,13 @@ checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "is-terminal" -version = "0.4.17" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" +checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ "hermit-abi 0.5.2", "libc", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -6508,9 +6505,9 @@ dependencies = [ [[package]] name = "is_terminal_polyfill" -version = "1.70.2" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itertools" @@ -6559,9 +6556,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.17" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jni" @@ -6591,15 +6588,15 @@ version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.3.3", "libc", ] [[package]] name = "js-sys" -version = "0.3.85" +version = "0.3.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" dependencies = [ "once_cell", "wasm-bindgen", @@ -6607,9 +6604,9 @@ dependencies = [ [[package]] name = "jsonrpsee" -version = "0.24.10" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e281ae70cc3b98dac15fced3366a880949e65fc66e345ce857a5682d152f3e62" +checksum = "37b26c20e2178756451cfeb0661fb74c47dd5988cb7e3939de7e9241fd604d42" dependencies = [ "jsonrpsee-client-transport", "jsonrpsee-core", @@ -6623,13 +6620,13 @@ dependencies = [ [[package]] name = "jsonrpsee-client-transport" -version = "0.24.10" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc4280b709ac3bb5e16cf3bad5056a0ec8df55fa89edfe996361219aadc2c7ea" +checksum = "bacb85abf4117092455e1573625e21b8f8ef4dec8aff13361140b2dc266cdff2" dependencies = [ "base64 0.22.1", "futures-util", - "http 1.4.0", + "http 1.3.1", "jsonrpsee-core", "pin-project", "rustls", @@ -6646,15 +6643,15 @@ dependencies = [ [[package]] name = "jsonrpsee-core" -version = "0.24.10" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "348ee569eaed52926b5e740aae20863762b16596476e943c9e415a6479021622" +checksum = "456196007ca3a14db478346f58c7238028d55ee15c1df15115596e411ff27925" dependencies = [ "async-trait", "bytes", "futures-timer", "futures-util", - "http 1.4.0", + "http 1.3.1", "http-body 1.0.1", "http-body-util", "jsonrpsee-types", @@ -6672,28 +6669,28 @@ dependencies = [ [[package]] name = "jsonrpsee-proc-macros" -version = "0.24.10" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7398cddf5013cca4702862a2692b66c48a3bd6cf6ec681a47453c93d63cf8de5" +checksum = "5e65763c942dfc9358146571911b0cd1c361c2d63e2d2305622d40d36376ca80" dependencies = [ "heck 0.5.0", "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] name = "jsonrpsee-server" -version = "0.24.10" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21429bcdda37dcf2d43b68621b994adede0e28061f816b038b0f18c70c143d51" +checksum = "55e363146da18e50ad2b51a0a7925fc423137a0b1371af8235b1c231a0647328" dependencies = [ "futures-util", - "http 1.4.0", + "http 1.3.1", "http-body 1.0.1", "http-body-util", - "hyper 1.8.1", + "hyper 1.7.0", "hyper-util", "jsonrpsee-core", "jsonrpsee-types", @@ -6712,11 +6709,11 @@ dependencies = [ [[package]] name = "jsonrpsee-types" -version = "0.24.10" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0f05e0028e55b15dbd2107163b3c744cd3bb4474f193f95d9708acbf5677e44" +checksum = "08a8e70baf945b6b5752fc8eb38c918a48f1234daf11355e07106d963f860089" dependencies = [ - "http 1.4.0", + "http 1.3.1", "serde", "serde_json", "thiserror 1.0.69", @@ -6724,11 +6721,11 @@ dependencies = [ [[package]] name = "jsonrpsee-ws-client" -version = "0.24.10" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78fc744f17e7926d57f478cf9ca6e1ee5d8332bf0514860b1a3cdf1742e614cc" +checksum = "01b3323d890aa384f12148e8d2a1fd18eb66e9e7e825f9de4fa53bcc19b93eef" dependencies = [ - "http 1.4.0", + "http 1.3.1", "jsonrpsee-client-transport", "jsonrpsee-core", "jsonrpsee-types", @@ -6760,9 +6757,9 @@ dependencies = [ [[package]] name = "keccak-asm" -version = "0.1.5" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b646a74e746cd25045aa0fd42f4f7f78aa6d119380182c7e63a5593c4ab8df6f" +checksum = "505d1856a39b200489082f90d897c3f07c455563880bc5952e38eabf731c83b6" dependencies = [ "digest 0.10.7", "sha3-asm", @@ -6855,9 +6852,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.180" +version = "0.2.176" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" +checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" [[package]] name = "libloading" @@ -6871,9 +6868,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.16" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libp2p" @@ -6885,7 +6882,7 @@ dependencies = [ "either", "futures", "futures-timer", - "getrandom 0.2.17", + "getrandom 0.2.16", "libp2p-allow-block-list", "libp2p-connection-limits", "libp2p-core", @@ -7003,9 +7000,9 @@ dependencies = [ [[package]] name = "libp2p-identity" -version = "0.2.13" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c7892c221730ba55f7196e98b0b8ba5e04b4155651736036628e9f73ed6fc3" +checksum = "3104e13b51e4711ff5738caa1fb54467c8604c2e94d607e27745bcf709068774" dependencies = [ "bs58", "ed25519-dalek", @@ -7014,7 +7011,7 @@ dependencies = [ "quick-protobuf", "rand 0.8.5", "sha2 0.10.9", - "thiserror 2.0.18", + "thiserror 2.0.17", "tracing", "zeroize", ] @@ -7208,7 +7205,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -7296,18 +7293,18 @@ dependencies = [ "thiserror 1.0.69", "tracing", "yamux 0.12.1", - "yamux 0.13.8", + "yamux 0.13.7", ] [[package]] name = "libredox" -version = "0.1.12" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" +checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.9.4", "libc", - "redox_syscall 0.7.0", + "redox_syscall 0.5.18", ] [[package]] @@ -7386,9 +7383,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.23" +version = "1.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15d118bbf3771060e7311cc7bb0545b01d08a8b4a7de949198dec1fa0ca1c0f7" +checksum = "8b70e7a7df205e92a1a4cd9aaae7898dac0aa555503cc0a649494d0d60e7651d" dependencies = [ "cc", "pkg-config", @@ -7412,9 +7409,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linked_hash_set" -version = "0.1.6" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "984fb35d06508d1e69fc91050cceba9c0b748f983e6739fa2c7a9237154c52c8" +checksum = "bae85b5be22d9843c80e5fc80e9b64c8a3b1f98f867c709956eca3efff4e92e2" dependencies = [ "linked-hash-map", ] @@ -7466,9 +7463,9 @@ dependencies = [ [[package]] name = "litemap" -version = "0.8.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "litep2p" @@ -7484,7 +7481,7 @@ dependencies = [ "futures", "futures-timer", "hickory-resolver 0.25.2", - "indexmap 2.13.0", + "indexmap 2.11.4", "libc", "mockall", "multiaddr 0.17.1", @@ -7501,7 +7498,7 @@ dependencies = [ "smallvec", "snow", "socket2 0.5.10", - "thiserror 2.0.18", + "thiserror 2.0.17", "tokio", "tokio-stream", "tokio-tungstenite", @@ -7512,7 +7509,7 @@ dependencies = [ "url", "x25519-dalek", "x509-parser 0.17.0", - "yamux 0.13.8", + "yamux 0.13.7", "yasna", "zeroize", ] @@ -7528,9 +7525,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.29" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "lru" @@ -7598,7 +7595,7 @@ checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -7610,7 +7607,7 @@ dependencies = [ "macro_magic_core", "macro_magic_macros", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -7624,7 +7621,7 @@ dependencies = [ "macro_magic_core_macros", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -7635,7 +7632,7 @@ checksum = "b02abfe41815b5bd98dbd4260173db2c116dda171dc0fe7838cb206333b83308" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -7646,18 +7643,18 @@ checksum = "73ea28ee64b88876bf45277ed9a5817c1817df061a74f2b988971a12570e5869" dependencies = [ "macro_magic_core", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] name = "match-lookup" -version = "0.1.2" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "757aee279b8bdbb9f9e676796fd459e4207a1f986e87886700abf589f5abf771" +checksum = "1265724d8cb29dbbc2b0f06fffb8bf1a8c0cf73a78eede9ba73a4a66c52a981e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 1.0.109", ] [[package]] @@ -7691,7 +7688,7 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad38eb12aea514a0466ea40a80fd8cc83637065948eb4a426e4aa46261175227" dependencies = [ - "rustix 1.1.3", + "rustix 1.1.2", ] [[package]] @@ -7705,9 +7702,9 @@ dependencies = [ [[package]] name = "memmap2" -version = "0.9.9" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "744133e4a0e0a658e1374cf3bf8e415c4052a15a111acd372764c55b4177d490" +checksum = "843a98750cd611cc2965a8213b53b43e715f13c37a9e096c6408e69990961db7" dependencies = [ "libc", ] @@ -7740,7 +7737,7 @@ checksum = "b3e3e3f549d27d2dc054372f320ddf68045a833fab490563ff70d4cf1b9d91ea" dependencies = [ "array-bytes 9.3.0", "blake3", - "frame-metadata 23.0.1", + "frame-metadata 23.0.0", "parity-scale-codec", "scale-decode", "scale-info", @@ -7775,13 +7772,13 @@ dependencies = [ [[package]] name = "mio" -version = "1.1.1" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", - "wasi", - "windows-sys 0.61.2", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", ] [[package]] @@ -7811,9 +7808,9 @@ dependencies = [ [[package]] name = "ml-kem" -version = "0.2.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcaee19a45f916d98f24a551cc9a2cdae705a040e66f3cbc4f3a282ea6a2e982" +checksum = "97befee0c869cb56f3118f49d0f9bb68c9e3f380dec23c1100aedc4ec3ba239a" dependencies = [ "hybrid-array", "kem", @@ -7878,14 +7875,14 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] name = "moka" -version = "0.12.13" +version = "0.12.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ac832c50ced444ef6be0767a008b02c106a909ba79d1d830501e94b96f6b7e" +checksum = "8261cd88c312e0004c1d51baad2980c66528dfdb2bee62003e643a4d8f86b077" dependencies = [ "crossbeam-channel", "crossbeam-epoch", @@ -7893,6 +7890,7 @@ dependencies = [ "equivalent", "parking_lot 0.12.5", "portable-atomic", + "rustc_version 0.4.1", "smallvec", "tagptr", "uuid", @@ -8054,7 +8052,7 @@ dependencies = [ "libc", "log", "openssl", - "openssl-probe 0.1.6", + "openssl-probe", "openssl-sys", "schannel", "security-framework 2.11.1", @@ -8125,17 +8123,17 @@ dependencies = [ "log", "netlink-packet-core", "netlink-sys", - "thiserror 2.0.18", + "thiserror 2.0.17", ] [[package]] name = "netlink-sys" -version = "0.8.8" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd6c30ed10fa69cc491d491b85cc971f6bdeb8e7367b7cde2ee6cc878d583fae" +checksum = "16c903aa70590cb93691bf97a767c8d1d6122d2cc9070433deb3bbf36ce8bd23" dependencies = [ "bytes", - "futures-util", + "futures", "libc", "log", "tokio", @@ -8143,13 +8141,13 @@ dependencies = [ [[package]] name = "network-interface" -version = "2.0.5" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ddcb8865ad3d9950f22f42ffa0ef0aecbfbf191867b3122413602b0a360b2a6" +checksum = "07709a6d4eba90ab10ec170a0530b3aafc81cb8a2d380e4423ae41fc55fe5745" dependencies = [ "cc", "libc", - "thiserror 2.0.18", + "thiserror 2.0.17", "winapi", ] @@ -8170,7 +8168,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.9.4", "cfg-if", "cfg_aliases 0.2.1", "libc", @@ -8218,7 +8216,7 @@ dependencies = [ "hkdf", "jsonrpsee", "log", - "memmap2 0.9.9", + "memmap2 0.9.8", "ml-kem", "node-subtensor-runtime", "num-traits", @@ -8234,7 +8232,7 @@ dependencies = [ "parity-scale-codec", "polkadot-sdk", "rand 0.8.5", - "rand_core 0.9.5", + "rand_core 0.9.3", "sc-basic-authorship", "sc-chain-spec", "sc-chain-spec-derive", @@ -8306,14 +8304,14 @@ dependencies = [ "frame-benchmarking", "frame-election-provider-support", "frame-executive", - "frame-metadata 23.0.1", + "frame-metadata 23.0.0", "frame-metadata-hash-extension", "frame-support", "frame-system", "frame-system-benchmarking", "frame-system-rpc-runtime-api", "frame-try-runtime", - "getrandom 0.2.17", + "getrandom 0.2.16", "hex", "log", "pallet-admin-utils", @@ -8440,9 +8438,9 @@ checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21" [[package]] name = "ntapi" -version = "0.4.2" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c70f219e21142367c70c0b30c6a9e3a14d55b4d12a204d897fbec83a0363f081" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" dependencies = [ "winapi", ] @@ -8492,9 +8490,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.0" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num-derive" @@ -8504,7 +8502,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -8570,9 +8568,9 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.5" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" +checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" dependencies = [ "num_enum_derive", "rustversion", @@ -8580,14 +8578,14 @@ dependencies = [ [[package]] name = "num_enum_derive" -version = "0.7.5" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" +checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -8650,9 +8648,9 @@ dependencies = [ [[package]] name = "once_cell_polyfill" -version = "1.70.2" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" [[package]] name = "opaque-debug" @@ -8668,11 +8666,11 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.75" +version = "0.10.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" +checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.9.4", "cfg-if", "foreign-types", "libc", @@ -8689,7 +8687,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -8698,17 +8696,11 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" -[[package]] -name = "openssl-probe" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" - [[package]] name = "openssl-sys" -version = "0.9.111" +version = "0.9.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" +checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" dependencies = [ "cc", "libc", @@ -8746,7 +8738,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43dfaf083aef571385fccfdc3a2f8ede8d0a1863160455d4f2b014d8f7d04a3f" dependencies = [ "expander", - "indexmap 2.13.0", + "indexmap 2.11.4", "itertools 0.11.0", "petgraph 0.6.5", "proc-macro-crate 3.4.0", @@ -9441,7 +9433,7 @@ source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -9451,7 +9443,7 @@ source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=58add17a1232888 dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -10438,7 +10430,7 @@ source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=58add17a1232888 dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -10713,7 +10705,7 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -10841,7 +10833,7 @@ dependencies = [ name = "pallet-subtensor-swap" version = "0.1.0" dependencies = [ - "alloy-primitives 0.8.26", + "alloy-primitives 0.8.25", "approx", "frame-benchmarking", "frame-support", @@ -11295,7 +11287,7 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -11400,12 +11392,12 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "pem" -version = "3.0.6" +version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" +checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3" dependencies = [ "base64 0.22.1", - "serde_core", + "serde", ] [[package]] @@ -11425,9 +11417,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.5" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9eb05c21a464ea704b53158d358a31e6425db2f63a1a7312268b05fe2b75f7" +checksum = "989e7521a040efde50c3ab6bbadafbe15ab6dc042686926be59ac35d74607df4" dependencies = [ "memchr", "ucd-trie", @@ -11435,9 +11427,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.8.5" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f9dbced329c441fa79d80472764b1a2c7e57123553b8519b36663a2fb234ed" +checksum = "187da9a3030dbafabbbfb20cb323b976dc7b7ce91fcd84f2f74d6e31d378e2de" dependencies = [ "pest", "pest_generator", @@ -11445,22 +11437,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.5" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bb96d5051a78f44f43c8f712d8e810adb0ebf923fc9ed2655a7f66f63ba8ee5" +checksum = "49b401d98f5757ebe97a26085998d6c0eecec4995cad6ab7fc30ffdf4b052843" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] name = "pest_meta" -version = "2.8.5" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "602113b5b5e8621770cfd490cfd90b9f84ab29bd2b0e49ad83eb6d186cef2365" +checksum = "72f27a2cfee9f9039c4d86faa5af122a0ac3851441a34865b8a043b46be0065a" dependencies = [ "pest", "sha2 0.10.9", @@ -11473,7 +11465,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset 0.4.2", - "indexmap 2.13.0", + "indexmap 2.11.4", ] [[package]] @@ -11483,7 +11475,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" dependencies = [ "fixedbitset 0.5.7", - "indexmap 2.13.0", + "indexmap 2.11.4", ] [[package]] @@ -11503,7 +11495,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -11700,7 +11692,7 @@ dependencies = [ "fatality", "futures", "futures-timer", - "indexmap 2.13.0", + "indexmap 2.11.4", "parity-scale-codec", "polkadot-node-network-protocol", "polkadot-node-primitives", @@ -12952,7 +12944,7 @@ dependencies = [ "polkavm-common 0.21.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -12964,7 +12956,7 @@ dependencies = [ "polkavm-common 0.24.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -12974,7 +12966,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36837f6b7edfd6f4498f8d25d81da16cf03bd6992c3e56f3d477dfc90f4fefca" dependencies = [ "polkavm-derive-impl 0.21.0", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -12984,7 +12976,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba0ef0f17ad81413ea1ca5b1b67553aedf5650c88269b673d3ba015c83bc2651" dependencies = [ "polkavm-derive-impl 0.24.0", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -13057,7 +13049,7 @@ dependencies = [ "concurrent-queue", "hermit-abi 0.5.2", "pin-project-lite", - "rustix 1.1.3", + "rustix 1.1.2", "windows-sys 0.61.2", ] @@ -13086,9 +13078,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.13.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "portable-atomic-util" @@ -13101,9 +13093,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.4" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" dependencies = [ "zerovec", ] @@ -13158,7 +13150,7 @@ dependencies = [ "proc-macro2", "quote", "sp-crypto-hashing 0.1.0 (git+https://github.com/opentensor/polkadot-sdk.git?rev=58add17a1232888db9cfb70f4afb6dc7fd7b3a79)", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -13194,7 +13186,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -13255,7 +13247,7 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" dependencies = [ - "toml_edit 0.23.10+spec-1.0.0", + "toml_edit 0.23.6", ] [[package]] @@ -13301,7 +13293,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -13312,14 +13304,14 @@ checksum = "75eea531cfcd120e0851a3f8aed42c4841f78c889eefafd96339c72677ae42c3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] name = "proc-macro2" -version = "1.0.106" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] @@ -13340,7 +13332,7 @@ dependencies = [ "quote", "regex", "sp-crypto-hashing 0.1.0 (git+https://github.com/opentensor/polkadot-sdk.git?rev=58add17a1232888db9cfb70f4afb6dc7fd7b3a79)", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -13377,23 +13369,24 @@ checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] name = "proptest" -version = "1.9.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40" +checksum = "2bb0be07becd10686a0bb407298fb425360a5c44a663774406340c59a22de4ce" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.10.0", + "bitflags 2.9.4", + "lazy_static", "num-traits", "rand 0.9.2", "rand_chacha 0.9.0", "rand_xorshift", - "regex-syntax 0.8.8", + "regex-syntax 0.8.6", "rusty-fork", "tempfile", "unarray", @@ -13435,7 +13428,7 @@ dependencies = [ "prost 0.13.5", "prost-types", "regex", - "syn 2.0.114", + "syn 2.0.106", "tempfile", ] @@ -13449,7 +13442,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -13462,7 +13455,7 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -13476,11 +13469,10 @@ dependencies = [ [[package]] name = "psm" -version = "0.1.29" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa96cb91275ed31d6da3e983447320c4eb219ac180fa1679a0889ff32861e2d" +checksum = "e66fcd288453b748497d8fb18bccc83a16b0518e3906d4b8df0a8d42d93dbb1c" dependencies = [ - "ar_archive_writer", "cc", ] @@ -13494,7 +13486,7 @@ dependencies = [ "libc", "once_cell", "raw-cpuid", - "wasi", + "wasi 0.11.1+wasi-snapshot-preview1", "web-sys", "winapi", ] @@ -13541,8 +13533,8 @@ dependencies = [ "quinn-udp", "rustc-hash 2.1.1", "rustls", - "socket2 0.6.2", - "thiserror 2.0.18", + "socket2 0.6.0", + "thiserror 2.0.17", "tokio", "tracing", "web-time", @@ -13555,7 +13547,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" dependencies = [ "bytes", - "getrandom 0.3.4", + "getrandom 0.3.3", "lru-slab", "rand 0.9.2", "ring 0.17.14", @@ -13563,7 +13555,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.18", + "thiserror 2.0.17", "tinyvec", "tracing", "web-time", @@ -13578,16 +13570,16 @@ dependencies = [ "cfg_aliases 0.2.1", "libc", "once_cell", - "socket2 0.6.2", + "socket2 0.6.0", "tracing", "windows-sys 0.60.2", ] [[package]] name = "quote" -version = "1.0.44" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" dependencies = [ "proc-macro2", ] @@ -13622,7 +13614,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", - "rand_core 0.9.5", + "rand_core 0.9.3", "serde", ] @@ -13643,7 +13635,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.5", + "rand_core 0.9.3", ] [[package]] @@ -13652,16 +13644,16 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.17", + "getrandom 0.2.16", ] [[package]] name = "rand_core" -version = "0.9.5" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.3.3", "serde", ] @@ -13690,16 +13682,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" dependencies = [ - "rand_core 0.9.5", -] - -[[package]] -name = "rapidhash" -version = "4.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d8b5b858a440a0bc02625b62dd95131b9201aa9f69f411195dd4a7cfb1de3d7" -dependencies = [ - "rustversion", + "rand_core 0.9.3", ] [[package]] @@ -13708,7 +13691,7 @@ version = "11.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.9.4", ] [[package]] @@ -13764,16 +13747,7 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.10.0", -] - -[[package]] -name = "redox_syscall" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27" -dependencies = [ - "bitflags 2.10.0", + "bitflags 2.9.4", ] [[package]] @@ -13782,7 +13756,7 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom 0.2.17", + "getrandom 0.2.16", "libredox", "thiserror 1.0.69", ] @@ -13816,7 +13790,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -13846,14 +13820,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.12.2" +version = "1.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.13", - "regex-syntax 0.8.8", + "regex-automata 0.4.11", + "regex-syntax 0.8.6", ] [[package]] @@ -13867,13 +13841,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.13" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.8", + "regex-syntax 0.8.6", ] [[package]] @@ -13884,15 +13858,15 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.8" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" [[package]] name = "resolv-conf" -version = "0.7.6" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7" +checksum = "6b3789b30bd25ba102de4beabd95d21ac45b69b1be7d14522bab988c526d6799" [[package]] name = "rfc6979" @@ -13927,7 +13901,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.17", + "getrandom 0.2.16", "libc", "untrusted 0.9.0", "windows-sys 0.52.0", @@ -13971,7 +13945,7 @@ checksum = "652db34deaaa57929e10ca18e5454a32cb0efc351ae80d320334bbf907b908b3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -14145,9 +14119,9 @@ dependencies = [ [[package]] name = "ruint" -version = "1.17.2" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c141e807189ad38a07276942c6623032d3753c8859c146104ac2e4d68865945a" +checksum = "a68df0380e5c9d20ce49534f292a36a7514ae21350726efe1865bdb1fa91d278" dependencies = [ "alloy-rlp", "ark-ff 0.3.0", @@ -14179,9 +14153,9 @@ checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" [[package]] name = "rustc-demangle" -version = "0.1.27" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustc-hash" @@ -14271,7 +14245,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.9.4", "errno", "libc", "linux-raw-sys 0.4.15", @@ -14280,11 +14254,11 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.3" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.9.4", "errno", "libc", "linux-raw-sys 0.11.0", @@ -14293,26 +14267,26 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.36" +version = "0.23.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" +checksum = "cd3c25631629d034ce7cd9940adc9d45762d46de2b0f57193c4443b92c6d4d40" dependencies = [ "log", "once_cell", "ring 0.17.14", "rustls-pki-types", - "rustls-webpki 0.103.9", + "rustls-webpki 0.103.7", "subtle 2.6.1", "zeroize", ] [[package]] name = "rustls-native-certs" -version = "0.8.3" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" dependencies = [ - "openssl-probe 0.2.1", + "openssl-probe", "rustls-pki-types", "schannel", "security-framework 3.5.1", @@ -14320,9 +14294,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.14.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" dependencies = [ "web-time", "zeroize", @@ -14342,7 +14316,7 @@ dependencies = [ "rustls", "rustls-native-certs", "rustls-platform-verifier-android", - "rustls-webpki 0.103.9", + "rustls-webpki 0.103.7", "security-framework 3.5.1", "security-framework-sys", "webpki-root-certs 0.26.11", @@ -14367,9 +14341,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.9" +version = "0.103.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" +checksum = "e10b3f4191e8a80e6b43eebabfac91e5dcecebb27a71f04e820c47ec41d314bf" dependencies = [ "ring 0.17.14", "rustls-pki-types", @@ -14428,9 +14402,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.22" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "safe-math" @@ -14564,7 +14538,7 @@ dependencies = [ "array-bytes 6.2.3", "clap", "docify", - "memmap2 0.9.9", + "memmap2 0.9.8", "parity-scale-codec", "sc-chain-spec-derive", "sc-client-api", @@ -14591,7 +14565,7 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -15315,7 +15289,7 @@ dependencies = [ "futures", "futures-timer", "http-body-util", - "hyper 1.8.1", + "hyper 1.7.0", "hyper-rustls", "hyper-util", "num_cpus", @@ -15409,9 +15383,9 @@ dependencies = [ "forwarded-header-value", "futures", "governor", - "http 1.4.0", + "http 1.3.1", "http-body-util", - "hyper 1.8.1", + "hyper 1.7.0", "ip_network", "jsonrpsee", "log", @@ -15673,7 +15647,7 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -15684,7 +15658,7 @@ dependencies = [ "async-trait", "futures", "futures-timer", - "indexmap 2.13.0", + "indexmap 2.11.4", "itertools 0.11.0", "linked-hash-map", "parity-scale-codec", @@ -15714,7 +15688,7 @@ source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=58add17a1232888 dependencies = [ "async-trait", "futures", - "indexmap 2.13.0", + "indexmap 2.11.4", "log", "parity-scale-codec", "serde", @@ -15752,9 +15726,9 @@ dependencies = [ [[package]] name = "scale-decode" -version = "0.16.2" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d6ed61699ad4d54101ab5a817169259b5b0efc08152f8632e61482d8a27ca3d" +checksum = "4d78196772d25b90a98046794ce0fe2588b39ebdfbdc1e45b4c6c85dd43bebad" dependencies = [ "parity-scale-codec", "primitive-types 0.13.1", @@ -15762,26 +15736,26 @@ dependencies = [ "scale-decode-derive", "scale-type-resolver", "smallvec", - "thiserror 2.0.18", + "thiserror 2.0.17", ] [[package]] name = "scale-decode-derive" -version = "0.16.2" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65cb245f7fdb489e7ba43a616cbd34427fe3ba6fe0edc1d0d250085e6c84f3ec" +checksum = "2f4b54a1211260718b92832b661025d1f1a4b6930fbadd6908e00edd265fa5f7" dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] name = "scale-encode" -version = "0.10.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2a976d73564a59e482b74fd5d95f7518b79ca8c8ca5865398a4d629dd15ee50" +checksum = "64901733157f9d25ef86843bd783eda439fac7efb0ad5a615d12d2cf3a29464b" dependencies = [ "parity-scale-codec", "primitive-types 0.13.1", @@ -15789,20 +15763,20 @@ dependencies = [ "scale-encode-derive", "scale-type-resolver", "smallvec", - "thiserror 2.0.18", + "thiserror 2.0.17", ] [[package]] name = "scale-encode-derive" -version = "0.10.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17020f2d59baabf2ddcdc20a4e567f8210baf089b8a8d4785f5fd5e716f92038" +checksum = "78a3993a13b4eafa89350604672c8757b7ea84c7c5947d4b3691e3169c96379b" dependencies = [ "darling 0.20.11", "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -15828,7 +15802,7 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -15850,15 +15824,15 @@ dependencies = [ "proc-macro2", "quote", "scale-info", - "syn 2.0.114", - "thiserror 2.0.18", + "syn 2.0.106", + "thiserror 2.0.17", ] [[package]] name = "scale-value" -version = "0.18.1" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884aab179aba344c67ddcd1d7dd8e3f8fee202f2e570d97ec34ec8688442a5b3" +checksum = "8ca8b26b451ecb7fd7b62b259fa28add63d12ec49bbcac0e01fcb4b5ae0c09aa" dependencies = [ "base58", "blake2 0.10.6", @@ -15869,7 +15843,7 @@ dependencies = [ "scale-encode", "scale-type-resolver", "serde", - "thiserror 2.0.18", + "thiserror 2.0.17", "yap", ] @@ -16000,7 +15974,7 @@ version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" dependencies = [ - "bitcoin_hashes 0.14.1", + "bitcoin_hashes 0.14.0", "rand 0.8.5", "secp256k1-sys 0.10.1", ] @@ -16056,7 +16030,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.9.4", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -16069,7 +16043,7 @@ version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.9.4", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -16185,20 +16159,20 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ "itoa", "memchr", + "ryu", "serde", "serde_core", - "zmij", ] [[package]] @@ -16224,9 +16198,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.16.1" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" +checksum = "6093cd8c01b25262b84927e0f7151692158fab02d961e04c979d3903eba7ecc5" dependencies = [ "base64 0.22.1", "chrono", @@ -16239,14 +16213,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.16.1" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" +checksum = "a7e6c180db0816026a61afa1cff5344fb7ebded7e4d3062772179f2501481c27" dependencies = [ "darling 0.21.3", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -16319,9 +16293,9 @@ dependencies = [ [[package]] name = "sha3-asm" -version = "0.1.5" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b31139435f327c93c6038ed350ae4588e2c70a13d50599509fee6349967ba35a" +checksum = "c28efc5e327c837aa837c59eae585fc250715ef939ac32881bcc11677cd02d46" dependencies = [ "cc", "cfg-if", @@ -16353,11 +16327,10 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.8" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" dependencies = [ - "errno", "libc", ] @@ -16390,7 +16363,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dee851d0e5e7af3721faea1843e8015e820a234f81fda3dea9247e15bac9a86a" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.9.4", ] [[package]] @@ -16407,9 +16380,9 @@ checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "siphasher" -version = "1.0.2" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" [[package]] name = "slab" @@ -16436,9 +16409,9 @@ dependencies = [ [[package]] name = "slotmap" -version = "1.1.1" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" dependencies = [ "version_check", ] @@ -16476,7 +16449,7 @@ dependencies = [ "async-executor", "async-fs 2.2.0", "async-io 2.6.0", - "async-lock 3.4.2", + "async-lock 3.4.1", "async-net 2.0.0", "async-process 2.5.0", "blocking", @@ -16544,7 +16517,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "966e72d77a3b2171bb7461d0cb91f43670c63558c62d7cf42809cae6c8b6b818" dependencies = [ "arrayvec 0.7.6", - "async-lock 3.4.2", + "async-lock 3.4.1", "atomic-take", "base64 0.22.1", "bip39", @@ -16581,7 +16554,7 @@ dependencies = [ "serde_json", "sha2 0.10.9", "sha3", - "siphasher 1.0.2", + "siphasher 1.0.1", "slab", "smallvec", "soketto 0.8.1", @@ -16634,7 +16607,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a33b06891f687909632ce6a4e3fd7677b24df930365af3d0bcb078310129f3f" dependencies = [ "async-channel 2.5.0", - "async-lock 3.4.2", + "async-lock 3.4.1", "base64 0.22.1", "blake2-rfc", "bs58", @@ -16656,7 +16629,7 @@ dependencies = [ "rand_chacha 0.3.1", "serde", "serde_json", - "siphasher 1.0.2", + "siphasher 1.0.1", "slab", "smol 2.0.2", "smoldot 0.18.0", @@ -16731,12 +16704,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] @@ -16763,7 +16736,7 @@ dependencies = [ "base64 0.22.1", "bytes", "futures", - "http 1.4.0", + "http 1.3.1", "httparse", "log", "rand 0.8.5", @@ -16803,7 +16776,7 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -17071,7 +17044,7 @@ source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=58add17a1232888 dependencies = [ "quote", "sp-crypto-hashing 0.1.0 (git+https://github.com/opentensor/polkadot-sdk.git?rev=58add17a1232888db9cfb70f4afb6dc7fd7b3a79)", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -17090,7 +17063,7 @@ source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=58add17a1232888 dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -17189,7 +17162,7 @@ name = "sp-metadata-ir" version = "0.11.0" source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=58add17a1232888db9cfb70f4afb6dc7fd7b3a79#58add17a1232888db9cfb70f4afb6dc7fd7b3a79" dependencies = [ - "frame-metadata 23.0.1", + "frame-metadata 23.0.0", "parity-scale-codec", "scale-info", ] @@ -17322,7 +17295,7 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -17510,7 +17483,7 @@ dependencies = [ "proc-macro-warning", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -17602,7 +17575,7 @@ dependencies = [ "futures-util", "hashbrown 0.15.5", "hashlink 0.10.0", - "indexmap 2.13.0", + "indexmap 2.11.4", "log", "memchr", "native-tls", @@ -17611,7 +17584,7 @@ dependencies = [ "serde", "sha2 0.10.9", "smallvec", - "thiserror 2.0.18", + "thiserror 2.0.17", "tokio", "tokio-stream", "tracing", @@ -17628,7 +17601,7 @@ dependencies = [ "quote", "sqlx-core", "sqlx-macros-core", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -17649,7 +17622,7 @@ dependencies = [ "sha2 0.10.9", "sqlx-core", "sqlx-sqlite", - "syn 2.0.114", + "syn 2.0.106", "tokio", "url", ] @@ -17673,7 +17646,7 @@ dependencies = [ "serde", "serde_urlencoded", "sqlx-core", - "thiserror 2.0.18", + "thiserror 2.0.17", "tracing", "url", ] @@ -17695,9 +17668,9 @@ dependencies = [ [[package]] name = "stable_deref_trait" -version = "1.2.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "staging-chain-spec-builder" @@ -17897,7 +17870,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -17967,7 +17940,7 @@ version = "0.17.6" source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=58add17a1232888db9cfb70f4afb6dc7fd7b3a79#58add17a1232888db9cfb70f4afb6dc7fd7b3a79" dependencies = [ "http-body-util", - "hyper 1.8.1", + "hyper 1.7.0", "hyper-util", "log", "prometheus", @@ -18012,7 +17985,7 @@ dependencies = [ "cargo_metadata", "console", "filetime", - "frame-metadata 23.0.1", + "frame-metadata 23.0.0", "jobserver", "merkleized-metadata", "parity-scale-codec", @@ -18040,7 +18013,7 @@ dependencies = [ "quote", "rayon", "subtensor-linting", - "syn 2.0.114", + "syn 2.0.106", "walkdir", ] @@ -18105,7 +18078,7 @@ dependencies = [ "proc-macro2", "procedural-fork", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -18115,7 +18088,7 @@ dependencies = [ "ahash", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -18269,7 +18242,7 @@ dependencies = [ "subxt-macro", "subxt-metadata", "subxt-rpcs", - "thiserror 2.0.18", + "thiserror 2.0.17", "tokio", "tokio-util", "tracing", @@ -18290,8 +18263,8 @@ dependencies = [ "scale-info", "scale-typegen", "subxt-metadata", - "syn 2.0.114", - "thiserror 2.0.18", + "syn 2.0.106", + "thiserror 2.0.17", ] [[package]] @@ -18320,7 +18293,7 @@ dependencies = [ "serde_json", "sp-crypto-hashing 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "subxt-metadata", - "thiserror 2.0.18", + "thiserror 2.0.17", "tracing", ] @@ -18335,7 +18308,7 @@ dependencies = [ "serde", "serde_json", "smoldot-light 0.16.2", - "thiserror 2.0.18", + "thiserror 2.0.17", "tokio", "tokio-stream", "tracing", @@ -18354,7 +18327,7 @@ dependencies = [ "scale-typegen", "subxt-codegen", "subxt-utils-fetchmetadata", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -18369,7 +18342,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-crypto-hashing 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror 2.0.18", + "thiserror 2.0.17", ] [[package]] @@ -18390,7 +18363,7 @@ dependencies = [ "serde_json", "subxt-core", "subxt-lightclient", - "thiserror 2.0.18", + "thiserror 2.0.17", "tracing", "url", ] @@ -18421,7 +18394,7 @@ dependencies = [ "sha2 0.10.9", "sp-crypto-hashing 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "subxt-core", - "thiserror 2.0.18", + "thiserror 2.0.17", "zeroize", ] @@ -18433,7 +18406,7 @@ checksum = "fc868b55fe2303788dc7703457af390111940c3da4714b510983284501780ed5" dependencies = [ "hex", "parity-scale-codec", - "thiserror 2.0.18", + "thiserror 2.0.17", ] [[package]] @@ -18449,9 +18422,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.114" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", @@ -18460,14 +18433,14 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "1.5.4" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2379beea9476b89d0237078be761cf8e012d92d5ae4ae0c9a329f974838870fc" +checksum = "2375c17f6067adc651d8c2c51658019cef32edfff4a982adaf1d7fd1c039f08b" dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -18490,7 +18463,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -18514,7 +18487,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.9.4", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -18549,22 +18522,22 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tempfile" -version = "3.24.0" +version = "3.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" +checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" dependencies = [ "fastrand 2.3.0", - "getrandom 0.3.4", + "getrandom 0.3.3", "once_cell", - "rustix 1.1.3", + "rustix 1.1.2", "windows-sys 0.61.2", ] [[package]] name = "termcolor" -version = "1.4.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] @@ -18575,7 +18548,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0" dependencies = [ - "rustix 1.1.3", + "rustix 1.1.2", "windows-sys 0.60.2", ] @@ -18596,11 +18569,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.18" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ - "thiserror-impl 2.0.18", + "thiserror-impl 2.0.17", ] [[package]] @@ -18620,7 +18593,7 @@ checksum = "e4c60d69f36615a077cc7663b9cb8e42275722d23e58a7fa3d2c7f2915d09d04" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -18631,18 +18604,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] name = "thiserror-impl" -version = "2.0.18" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -18692,30 +18665,30 @@ dependencies = [ [[package]] name = "time" -version = "0.3.46" +version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9da98b7d9b7dad93488a84b8248efc35352b0b2657397d4167e7ad67e5d535e5" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" dependencies = [ "deranged", "itoa", "num-conv", "powerfmt", - "serde_core", + "serde", "time-core", "time-macros", ] [[package]] name = "time-core" -version = "0.1.8" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" [[package]] name = "time-macros" -version = "0.2.26" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78cc610bac2dcee56805c99642447d4c5dbde4d01f752ffea0199aee1f601dc4" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" dependencies = [ "num-conv", "time-core", @@ -18732,9 +18705,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.2" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ "displaydoc", "zerovec", @@ -18785,30 +18758,33 @@ dependencies = [ [[package]] name = "tokio" -version = "1.49.0" +version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ + "backtrace", "bytes", + "io-uring", "libc", "mio", "parking_lot 0.12.5", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.2", + "slab", + "socket2 0.6.0", "tokio-macros", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] name = "tokio-macros" -version = "2.6.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -18823,9 +18799,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.18" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ "futures-core", "pin-project-lite", @@ -18851,9 +18827,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.18" +version = "0.7.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" dependencies = [ "bytes", "futures-core", @@ -18895,9 +18871,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.7.5+spec-1.1.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +checksum = "32f1085dec27c2b6632b04c80b3bb1b4300d6495d1e129693bdda7d91e72eec1" dependencies = [ "serde_core", ] @@ -18908,7 +18884,7 @@ version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.13.0", + "indexmap 2.11.4", "serde", "serde_spanned", "toml_datetime 0.6.11", @@ -18918,21 +18894,21 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.23.10+spec-1.0.0" +version = "0.23.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" +checksum = "f3effe7c0e86fdff4f69cdd2ccc1b96f933e24811c5441d44904e8683e27184b" dependencies = [ - "indexmap 2.13.0", - "toml_datetime 0.7.5+spec-1.1.0", + "indexmap 2.11.4", + "toml_datetime 0.7.2", "toml_parser", "winnow", ] [[package]] name = "toml_parser" -version = "1.0.6+spec-1.1.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" +checksum = "4cf893c33be71572e0e9aa6dd15e6677937abd686b066eac3f8cd3531688a627" dependencies = [ "winnow", ] @@ -18964,9 +18940,9 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.9.4", "bytes", - "http 1.4.0", + "http 1.3.1", "http-body 1.0.1", "http-body-util", "pin-project-lite", @@ -18988,9 +18964,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.44" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "log", "pin-project-lite", @@ -19000,20 +18976,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.31" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] name = "tracing-core" -version = "0.1.36" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", "valuable", @@ -19049,7 +19025,7 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -19133,14 +19109,14 @@ checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" dependencies = [ "bytes", "data-encoding", - "http 1.4.0", + "http 1.3.1", "httparse", "log", "rand 0.9.2", "rustls", "rustls-pki-types", "sha1", - "thiserror 2.0.18", + "thiserror 2.0.17", "url", "utf-8", ] @@ -19207,9 +19183,9 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-ident" -version = "1.0.22" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" [[package]] name = "unicode-normalization" @@ -19226,6 +19202,12 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + [[package]] name = "unicode-width" version = "0.2.2" @@ -19284,9 +19266,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.8" +version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" dependencies = [ "form_urlencoded", "idna", @@ -19314,11 +19296,11 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.20.0" +version = "1.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.3.3", "js-sys", "wasm-bindgen", ] @@ -19493,29 +19475,38 @@ version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +[[package]] +name = "wasi" +version = "0.14.7+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" +dependencies = [ + "wasip2", +] + [[package]] name = "wasip2" -version = "1.0.2+wasi-0.2.9" +version = "1.0.1+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" dependencies = [ "wit-bindgen", ] [[package]] name = "wasix" -version = "0.13.1" +version = "0.12.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1757e0d1f8456693c7e5c6c629bdb54884e032aa0bb53c155f6a39f94440d332" +checksum = "c1fbb4ef9bbca0c1170e0b00dd28abc9e3b68669821600cad1caaed606583c6d" dependencies = [ - "wasi", + "wasi 0.11.1+wasi-snapshot-preview1", ] [[package]] name = "wasm-bindgen" -version = "0.2.108" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" dependencies = [ "cfg-if", "once_cell", @@ -19524,14 +19515,27 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.106", + "wasm-bindgen-shared", +] + [[package]] name = "wasm-bindgen-futures" -version = "0.4.58" +version = "0.4.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" +checksum = "7e038d41e478cc73bae0ff9b36c60cff1c98b8f38f8d7e8061e79ee63608ac5c" dependencies = [ "cfg-if", - "futures-util", "js-sys", "once_cell", "wasm-bindgen", @@ -19540,9 +19544,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.108" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -19550,22 +19554,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.108" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" dependencies = [ - "bumpalo", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", + "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.108" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" dependencies = [ "unicode-ident", ] @@ -19921,9 +19925,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.85" +version = "0.3.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" +checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120" dependencies = [ "js-sys", "wasm-bindgen", @@ -19945,14 +19949,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" dependencies = [ - "webpki-root-certs 1.0.5", + "webpki-root-certs 1.0.3", ] [[package]] name = "webpki-root-certs" -version = "1.0.5" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36a29fc0408b113f68cf32637857ab740edfafdf460c326cd2afaa2d84cc05dc" +checksum = "05d651ec480de84b762e7be71e6efa7461699c19d9e2c272c8d93455f567786e" dependencies = [ "rustls-pki-types", ] @@ -20098,9 +20102,9 @@ dependencies = [ [[package]] name = "widestring" -version = "1.2.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" +checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d" [[package]] name = "winapi" @@ -20193,7 +20197,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -20204,7 +20208,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -20539,9 +20543,9 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" -version = "0.7.14" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" dependencies = [ "memchr", ] @@ -20558,15 +20562,15 @@ dependencies = [ [[package]] name = "wit-bindgen" -version = "0.51.0" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "writeable" -version = "0.6.2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "wyz" @@ -20619,7 +20623,7 @@ dependencies = [ "nom", "oid-registry 0.8.1", "rusticata-macros", - "thiserror 2.0.18", + "thiserror 2.0.17", "time", ] @@ -20631,7 +20635,7 @@ dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -20671,9 +20675,9 @@ dependencies = [ [[package]] name = "xml-rs" -version = "0.8.28" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" +checksum = "6fd8403733700263c6eb89f192880191f1b83e332f7a20371ddcf421c4a337c7" [[package]] name = "xmltree" @@ -20701,9 +20705,9 @@ dependencies = [ [[package]] name = "yamux" -version = "0.13.8" +version = "0.13.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deab71f2e20691b4728b349c6cee8fc7223880fa67b6b4f92225ec32225447e5" +checksum = "6927cfe0edfae4b26a369df6bad49cd0ef088c0ec48f4045b2084bcaedc10246" dependencies = [ "futures", "log", @@ -20732,10 +20736,11 @@ dependencies = [ [[package]] name = "yoke" -version = "0.8.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" dependencies = [ + "serde", "stable_deref_trait", "yoke-derive", "zerofrom", @@ -20743,34 +20748,34 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", "synstructure 0.13.2", ] [[package]] name = "zerocopy" -version = "0.8.36" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dafd85c832c1b68bbb4ec0c72c7f6f4fc5179627d2bc7c26b30e4c0cc11e76cc" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.36" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cb7e4e8436d9db52fbd6625dbf2f45243ab84994a72882ec8227b99e72b439a" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] @@ -20790,7 +20795,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", "synstructure 0.13.2", ] @@ -20805,20 +20810,20 @@ dependencies = [ [[package]] name = "zeroize_derive" -version = "1.4.3" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] [[package]] name = "zerotrie" -version = "0.2.3" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" dependencies = [ "displaydoc", "yoke", @@ -20827,9 +20832,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.5" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" dependencies = [ "yoke", "zerofrom", @@ -20838,21 +20843,15 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.2" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.106", ] -[[package]] -name = "zmij" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02aae0f83f69aafc94776e879363e9771d7ecbffe2c7fbb6c14c5e00dfe88439" - [[package]] name = "zstd" version = "0.11.2+zstd.1.5.2" From 42083cba8392ec2534d7c6a8b5877a3753e12936 Mon Sep 17 00:00:00 2001 From: open-junius Date: Mon, 2 Feb 2026 10:25:57 +0800 Subject: [PATCH 195/240] test is ok --- .../src/contracts/precompileWrapper.sol | 4 +- .../src/contracts/precompileWrapper.ts | 6 +- .../precompileWrapper.direct-call.test.ts | 365 +++++++++++++++++- 3 files changed, 358 insertions(+), 17 deletions(-) diff --git a/contract-tests/src/contracts/precompileWrapper.sol b/contract-tests/src/contracts/precompileWrapper.sol index 8671cc1c8d..9f5fe242c1 100644 --- a/contract-tests/src/contracts/precompileWrapper.sol +++ b/contract-tests/src/contracts/precompileWrapper.sol @@ -150,7 +150,7 @@ interface IProxy { function proxyCall( bytes32 real, uint8[] memory force_proxy_type, - bytes memory call + uint8[] memory call ) external; function getProxies( bytes32 account @@ -277,7 +277,7 @@ contract PrecompileWrapper { function proxyCall( bytes32 real, uint8[] memory force_proxy_type, - bytes memory call + uint8[] memory call ) external { proxy.proxyCall(real, force_proxy_type, call); } diff --git a/contract-tests/src/contracts/precompileWrapper.ts b/contract-tests/src/contracts/precompileWrapper.ts index c082983da2..9916b735e9 100644 --- a/contract-tests/src/contracts/precompileWrapper.ts +++ b/contract-tests/src/contracts/precompileWrapper.ts @@ -535,9 +535,9 @@ export const PRECOMPILE_WRAPPER_ABI = [ "type": "uint8[]" }, { - "internalType": "bytes", + "internalType": "uint8[]", "name": "call", - "type": "bytes" + "type": "uint8[]" } ], "name": "proxyCall", @@ -711,4 +711,4 @@ export const PRECOMPILE_WRAPPER_ABI = [ } ]; -export const PRECOMPILE_WRAPPER_BYTECODE = "6080604052348015600e575f5ffd5b50612cab8061001c5f395ff3fe6080604052600436106101d7575f3560e01c80638bba466c11610101578063bfe252a211610094578063db1d0fd511610063578063db1d0fd5146106ac578063ec556889146106d6578063fa83f46714610700578063fc6679fb14610728576101d7565b8063bfe252a214610612578063caf2ebf21461063c578063cd6f4eb114610666578063d75e3e0d14610682576101d7565b8063a2176276116100d0578063a217627614610566578063ac3166bf14610590578063afed65f9146105ba578063b1f789ef146105d6576101d7565b80638bba466c1461047657806394e3ac6f146104b2578063998538c4146104ee5780639f246f6f1461052a576101d7565b80634cf088d91161017957806369e38bc31161014857806369e38bc3146103c657806371214e27146104025780637444dadc1461041e5780637d691e301461045a576101d7565b80634cf088d91461031a5780635b53ddde146103445780635b7210c51461036e5780635e25f3f8146103aa576101d7565b80631fc9b141116101b55780631fc9b1411461027b5780633175bd98146102975780634054ecca146102d45780634c378a96146102f0576101d7565b80630494cd9a146101db5780630cadeda5146102175780631f1935721461023f575b5f5ffd5b3480156101e6575f5ffd5b5061020160048036038101906101fc919061124e565b610752565b60405161020e9190611291565b60405180910390f35b348015610222575f5ffd5b5061023d60048036038101906102389190611343565b6107d4565b005b34801561024a575f5ffd5b50610265600480360381019061026091906113ca565b610845565b6040516102729190611404565b60405180910390f35b61029560048036038101906102909190611450565b6108c7565b005b3480156102a2575f5ffd5b506102bd60048036038101906102b891906114a0565b610938565b6040516102cb929190611508565b60405180910390f35b6102ee60048036038101906102e9919061152f565b6109c0565b005b3480156102fb575f5ffd5b50610304610a2e565b60405161031191906115c8565b60405180910390f35b348015610325575f5ffd5b5061032e610a34565b60405161033b9190611601565b60405180910390f35b34801561034f575f5ffd5b50610358610a3a565b604051610365919061163a565b60405180910390f35b348015610379575f5ffd5b50610394600480360381019061038f91906114a0565b610a40565b6040516103a19190611675565b60405180910390f35b6103c460048036038101906103bf91906117ca565b610ac5565b005b3480156103d1575f5ffd5b506103ec60048036038101906103e791906113ca565b610b45565b6040516103f9919061194e565b60405180910390f35b61041c60048036038101906104179190611991565b610bc7565b005b348015610429575f5ffd5b50610444600480360381019061043f91906113ca565b610c3e565b6040516104519190611675565b60405180910390f35b610474600480360381019061046f9190611450565b610cc0565b005b348015610481575f5ffd5b5061049c60048036038101906104979190611a08565b610d31565b6040516104a99190611b59565b60405180910390f35b3480156104bd575f5ffd5b506104d860048036038101906104d39190611b73565b610dbb565b6040516104e59190611c95565b60405180910390f35b3480156104f9575f5ffd5b50610514600480360381019061050f9190611b73565b610e41565b604051610521919061194e565b60405180910390f35b348015610535575f5ffd5b50610550600480360381019061054b9190611b73565b610ec3565b60405161055d919061194e565b60405180910390f35b348015610571575f5ffd5b5061057a610f45565b6040516105879190611cd5565b60405180910390f35b34801561059b575f5ffd5b506105a4610f4b565b6040516105b19190611d0e565b60405180910390f35b6105d460048036038101906105cf9190611d51565b610f51565b005b3480156105e1575f5ffd5b506105fc60048036038101906105f79190611dee565b610fce565b6040516106099190611f22565b60405180910390f35b34801561061d575f5ffd5b5061062661105a565b6040516106339190611f62565b60405180910390f35b348015610647575f5ffd5b50610650611060565b60405161065d9190611f9b565b60405180910390f35b610680600480360381019061067b9190611b73565b611066565b005b34801561068d575f5ffd5b506106966110d3565b6040516106a39190611fd4565b60405180910390f35b3480156106b7575f5ffd5b506106c06110d9565b6040516106cd919061200d565b60405180910390f35b3480156106e1575f5ffd5b506106ea6110df565b6040516106f79190612046565b60405180910390f35b34801561070b575f5ffd5b50610726600480360381019061072191906121c1565b6110e5565b005b348015610733575f5ffd5b5061073c611156565b6040516107499190612269565b60405180910390f35b5f61080c73ffffffffffffffffffffffffffffffffffffffff16630494cd9a836040518263ffffffff1660e01b815260040161078e9190612291565b602060405180830381865afa1580156107a9573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107cd91906122be565b9050919050565b61080b73ffffffffffffffffffffffffffffffffffffffff16630cadeda58484846040518463ffffffff1660e01b815260040161081393929190612307565b5f604051808303815f87803b15801561082a575f5ffd5b505af115801561083c573d5f5f3e3d5ffd5b50505050505050565b5f61080273ffffffffffffffffffffffffffffffffffffffff16631f193572836040518263ffffffff1660e01b81526004016108819190611404565b602060405180830381865afa15801561089c573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108c09190612350565b9050919050565b61080573ffffffffffffffffffffffffffffffffffffffff16631fc9b1418484846040518463ffffffff1660e01b81526004016109069392919061237b565b5f604051808303815f87803b15801561091d575f5ffd5b505af115801561092f573d5f5f3e3d5ffd5b50505050505050565b5f5f61080a73ffffffffffffffffffffffffffffffffffffffff16633175bd9885856040518363ffffffff1660e01b81526004016109779291906123b0565b6040805180830381865afa158015610991573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109b59190612401565b915091509250929050565b61080473ffffffffffffffffffffffffffffffffffffffff16634054ecca83836040518363ffffffff1660e01b81526004016109fd92919061243f565b5f604051808303815f87803b158015610a14575f5ffd5b505af1158015610a26573d5f5f3e3d5ffd5b505050505050565b61080481565b61080581565b61080a81565b5f61080973ffffffffffffffffffffffffffffffffffffffff16635b7210c584846040518363ffffffff1660e01b8152600401610a7e9291906123b0565b602060405180830381865afa158015610a99573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610abd919061247a565b905092915050565b61080373ffffffffffffffffffffffffffffffffffffffff16631cf98c6b89898989898989896040518963ffffffff1660e01b8152600401610b0e989796959493929190612505565b5f604051808303815f87803b158015610b25575f5ffd5b505af1158015610b37573d5f5f3e3d5ffd5b505050505050505050505050565b5f61080873ffffffffffffffffffffffffffffffffffffffff166369e38bc3836040518263ffffffff1660e01b8152600401610b819190611404565b602060405180830381865afa158015610b9c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bc091906125c6565b9050919050565b61080973ffffffffffffffffffffffffffffffffffffffff1663127e1adb86868686866040518663ffffffff1660e01b8152600401610c0a9594939291906125f1565b5f604051808303815f87803b158015610c21575f5ffd5b505af1158015610c33573d5f5f3e3d5ffd5b505050505050505050565b5f61080373ffffffffffffffffffffffffffffffffffffffff16637444dadc836040518263ffffffff1660e01b8152600401610c7a9190611404565b602060405180830381865afa158015610c95573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610cb9919061247a565b9050919050565b61080573ffffffffffffffffffffffffffffffffffffffff16637d691e308484846040518463ffffffff1660e01b8152600401610cff9392919061237b565b5f604051808303815f87803b158015610d16575f5ffd5b505af1158015610d28573d5f5f3e3d5ffd5b50505050505050565b610d3961115c565b61080973ffffffffffffffffffffffffffffffffffffffff16638bba466c836040518263ffffffff1660e01b8152600401610d749190612642565b61016060405180830381865afa158015610d90573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610db49190612790565b9050919050565b606061080b73ffffffffffffffffffffffffffffffffffffffff166394e3ac6f836040518263ffffffff1660e01b8152600401610df89190611291565b5f60405180830381865afa158015610e12573d5f5f3e3d5ffd5b505050506040513d5f823e3d601f19601f82011682018060405250810190610e3a91906128dd565b9050919050565b5f61080573ffffffffffffffffffffffffffffffffffffffff1663998538c4836040518263ffffffff1660e01b8152600401610e7d9190611291565b602060405180830381865afa158015610e98573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ebc91906125c6565b9050919050565b5f61080573ffffffffffffffffffffffffffffffffffffffff16639f246f6f836040518263ffffffff1660e01b8152600401610eff9190611291565b602060405180830381865afa158015610f1a573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f3e91906125c6565b9050919050565b61080681565b61080c81565b61080a73ffffffffffffffffffffffffffffffffffffffff1663afed65f9888888888888886040518863ffffffff1660e01b8152600401610f989796959493929190612933565b5f604051808303815f87803b158015610faf575f5ffd5b505af1158015610fc1573d5f5f3e3d5ffd5b5050505050505050505050565b606061080673ffffffffffffffffffffffffffffffffffffffff1663b1f789ef8585856040518463ffffffff1660e01b815260040161100f939291906129a0565b5f60405180830381865afa158015611029573d5f5f3e3d5ffd5b505050506040513d5f823e3d601f19601f820116820180604052508101906110519190612ae2565b90509392505050565b61080981565b61080381565b61080073ffffffffffffffffffffffffffffffffffffffff1663cd6f4eb134836040518363ffffffff1660e01b81526004016110a29190611291565b5f604051808303818588803b1580156110b9575f5ffd5b505af11580156110cb573d5f5f3e3d5ffd5b505050505050565b61080081565b61080881565b61080b81565b61080b73ffffffffffffffffffffffffffffffffffffffff1663fa83f4678484846040518463ffffffff1660e01b815260040161112493929190612c32565b5f604051808303815f87803b15801561113b575f5ffd5b505af115801561114d573d5f5f3e3d5ffd5b50505050505050565b61080281565b6040518061016001604052805f81526020015f67ffffffffffffffff1681526020015f67ffffffffffffffff1681526020015f63ffffffff1681526020015f67ffffffffffffffff1681526020015f81526020015f67ffffffffffffffff1681526020015f151581526020015f81526020015f151581526020015f63ffffffff1681525090565b5f604051905090565b5f5ffd5b5f5ffd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61121d826111f4565b9050919050565b61122d81611213565b8114611237575f5ffd5b50565b5f8135905061124881611224565b92915050565b5f60208284031215611263576112626111ec565b5b5f6112708482850161123a565b91505092915050565b5f819050919050565b61128b81611279565b82525050565b5f6020820190506112a45f830184611282565b92915050565b6112b381611279565b81146112bd575f5ffd5b50565b5f813590506112ce816112aa565b92915050565b5f60ff82169050919050565b6112e9816112d4565b81146112f3575f5ffd5b50565b5f81359050611304816112e0565b92915050565b5f63ffffffff82169050919050565b6113228161130a565b811461132c575f5ffd5b50565b5f8135905061133d81611319565b92915050565b5f5f5f6060848603121561135a576113596111ec565b5b5f611367868287016112c0565b9350506020611378868287016112f6565b92505060406113898682870161132f565b9150509250925092565b5f61ffff82169050919050565b6113a981611393565b81146113b3575f5ffd5b50565b5f813590506113c4816113a0565b92915050565b5f602082840312156113df576113de6111ec565b5b5f6113ec848285016113b6565b91505092915050565b6113fe81611393565b82525050565b5f6020820190506114175f8301846113f5565b92915050565b5f819050919050565b61142f8161141d565b8114611439575f5ffd5b50565b5f8135905061144a81611426565b92915050565b5f5f5f60608486031215611467576114666111ec565b5b5f611474868287016112c0565b93505060206114858682870161143c565b92505060406114968682870161143c565b9150509250925092565b5f5f604083850312156114b6576114b56111ec565b5b5f6114c38582860161132f565b92505060206114d4858286016112c0565b9150509250929050565b5f6fffffffffffffffffffffffffffffffff82169050919050565b611502816114de565b82525050565b5f60408201905061151b5f8301856114f9565b61152860208301846114f9565b9392505050565b5f5f60408385031215611545576115446111ec565b5b5f611552858286016113b6565b9250506020611563858286016112c0565b9150509250929050565b5f819050919050565b5f61159061158b611586846111f4565b61156d565b6111f4565b9050919050565b5f6115a182611576565b9050919050565b5f6115b282611597565b9050919050565b6115c2816115a8565b82525050565b5f6020820190506115db5f8301846115b9565b92915050565b5f6115eb82611597565b9050919050565b6115fb816115e1565b82525050565b5f6020820190506116145f8301846115f2565b92915050565b5f61162482611597565b9050919050565b6116348161161a565b82525050565b5f60208201905061164d5f83018461162b565b92915050565b5f67ffffffffffffffff82169050919050565b61166f81611653565b82525050565b5f6020820190506116885f830184611666565b92915050565b5f5ffd5b5f5ffd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6116dc82611696565b810181811067ffffffffffffffff821117156116fb576116fa6116a6565b5b80604052505050565b5f61170d6111e3565b905061171982826116d3565b919050565b5f67ffffffffffffffff821115611738576117376116a6565b5b61174182611696565b9050602081019050919050565b828183375f83830152505050565b5f61176e6117698461171e565b611704565b90508281526020810184848401111561178a57611789611692565b5b61179584828561174e565b509392505050565b5f82601f8301126117b1576117b061168e565b5b81356117c184826020860161175c565b91505092915050565b5f5f5f5f5f5f5f5f610100898b0312156117e7576117e66111ec565b5b5f6117f48b828c016112c0565b985050602089013567ffffffffffffffff811115611815576118146111f0565b5b6118218b828c0161179d565b975050604089013567ffffffffffffffff811115611842576118416111f0565b5b61184e8b828c0161179d565b965050606089013567ffffffffffffffff81111561186f5761186e6111f0565b5b61187b8b828c0161179d565b955050608089013567ffffffffffffffff81111561189c5761189b6111f0565b5b6118a88b828c0161179d565b94505060a089013567ffffffffffffffff8111156118c9576118c86111f0565b5b6118d58b828c0161179d565b93505060c089013567ffffffffffffffff8111156118f6576118f56111f0565b5b6119028b828c0161179d565b92505060e089013567ffffffffffffffff811115611923576119226111f0565b5b61192f8b828c0161179d565b9150509295985092959890939650565b6119488161141d565b82525050565b5f6020820190506119615f83018461193f565b92915050565b61197081611653565b811461197a575f5ffd5b50565b5f8135905061198b81611967565b92915050565b5f5f5f5f5f60a086880312156119aa576119a96111ec565b5b5f6119b78882890161197d565b95505060206119c88882890161197d565b94505060406119d98882890161197d565b93505060606119ea8882890161132f565b92505060806119fb8882890161123a565b9150509295509295909350565b5f60208284031215611a1d57611a1c6111ec565b5b5f611a2a8482850161132f565b91505092915050565b611a3c81611279565b82525050565b611a4b81611653565b82525050565b611a5a8161130a565b82525050565b5f8115159050919050565b611a7481611a60565b82525050565b61016082015f820151611a8f5f850182611a33565b506020820151611aa26020850182611a42565b506040820151611ab56040850182611a42565b506060820151611ac86060850182611a51565b506080820151611adb6080850182611a42565b5060a0820151611aee60a0850182611a33565b5060c0820151611b0160c0850182611a42565b5060e0820151611b1460e0850182611a6b565b50610100820151611b29610100850182611a33565b50610120820151611b3e610120850182611a6b565b50610140820151611b53610140850182611a51565b50505050565b5f61016082019050611b6d5f830184611a7a565b92915050565b5f60208284031215611b8857611b876111ec565b5b5f611b95848285016112c0565b91505092915050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b611bd08161141d565b82525050565b606082015f820151611bea5f850182611a33565b506020820151611bfd6020850182611bc7565b506040820151611c106040850182611bc7565b50505050565b5f611c218383611bd6565b60608301905092915050565b5f602082019050919050565b5f611c4382611b9e565b611c4d8185611ba8565b9350611c5883611bb8565b805f5b83811015611c88578151611c6f8882611c16565b9750611c7a83611c2d565b925050600181019050611c5b565b5085935050505092915050565b5f6020820190508181035f830152611cad8184611c39565b905092915050565b5f611cbf82611597565b9050919050565b611ccf81611cb5565b82525050565b5f602082019050611ce85f830184611cc6565b92915050565b5f611cf882611597565b9050919050565b611d0881611cee565b82525050565b5f602082019050611d215f830184611cff565b92915050565b611d3081611a60565b8114611d3a575f5ffd5b50565b5f81359050611d4b81611d27565b92915050565b5f5f5f5f5f5f5f60e0888a031215611d6c57611d6b6111ec565b5b5f611d798a828b0161197d565b9750506020611d8a8a828b0161197d565b9650506040611d9b8a828b0161197d565b9550506060611dac8a828b0161132f565b9450506080611dbd8a828b016112f6565b93505060a0611dce8a828b01611d3d565b92505060c0611ddf8a828b0161132f565b91505092959891949750929550565b5f5f5f60608486031215611e0557611e046111ec565b5b5f611e12868287016113b6565b9350506020611e238682870161123a565b9250506040611e34868287016113b6565b9150509250925092565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b611e7081611393565b82525050565b604082015f820151611e8a5f850182611e67565b506020820151611e9d6020850182611a42565b50505050565b5f611eae8383611e76565b60408301905092915050565b5f602082019050919050565b5f611ed082611e3e565b611eda8185611e48565b9350611ee583611e58565b805f5b83811015611f15578151611efc8882611ea3565b9750611f0783611eba565b925050600181019050611ee8565b5085935050505092915050565b5f6020820190508181035f830152611f3a8184611ec6565b905092915050565b5f611f4c82611597565b9050919050565b611f5c81611f42565b82525050565b5f602082019050611f755f830184611f53565b92915050565b5f611f8582611597565b9050919050565b611f9581611f7b565b82525050565b5f602082019050611fae5f830184611f8c565b92915050565b5f611fbe82611597565b9050919050565b611fce81611fb4565b82525050565b5f602082019050611fe75f830184611fc5565b92915050565b5f611ff782611597565b9050919050565b61200781611fed565b82525050565b5f6020820190506120205f830184611ffe565b92915050565b5f61203082611597565b9050919050565b61204081612026565b82525050565b5f6020820190506120595f830184612037565b92915050565b5f67ffffffffffffffff821115612079576120786116a6565b5b602082029050602081019050919050565b5f5ffd5b5f6120a061209b8461205f565b611704565b905080838252602082019050602084028301858111156120c3576120c261208a565b5b835b818110156120ec57806120d888826112f6565b8452602084019350506020810190506120c5565b5050509392505050565b5f82601f83011261210a5761210961168e565b5b813561211a84826020860161208e565b91505092915050565b5f67ffffffffffffffff82111561213d5761213c6116a6565b5b61214682611696565b9050602081019050919050565b5f61216561216084612123565b611704565b90508281526020810184848401111561218157612180611692565b5b61218c84828561174e565b509392505050565b5f82601f8301126121a8576121a761168e565b5b81356121b8848260208601612153565b91505092915050565b5f5f5f606084860312156121d8576121d76111ec565b5b5f6121e5868287016112c0565b935050602084013567ffffffffffffffff811115612206576122056111f0565b5b612212868287016120f6565b925050604084013567ffffffffffffffff811115612233576122326111f0565b5b61223f86828701612194565b9150509250925092565b5f61225382611597565b9050919050565b61226381612249565b82525050565b5f60208201905061227c5f83018461225a565b92915050565b61228b81611213565b82525050565b5f6020820190506122a45f830184612282565b92915050565b5f815190506122b8816112aa565b92915050565b5f602082840312156122d3576122d26111ec565b5b5f6122e0848285016122aa565b91505092915050565b6122f2816112d4565b82525050565b6123018161130a565b82525050565b5f60608201905061231a5f830186611282565b61232760208301856122e9565b61233460408301846122f8565b949350505050565b5f8151905061234a816113a0565b92915050565b5f60208284031215612365576123646111ec565b5b5f6123728482850161233c565b91505092915050565b5f60608201905061238e5f830186611282565b61239b602083018561193f565b6123a8604083018461193f565b949350505050565b5f6040820190506123c35f8301856122f8565b6123d06020830184611282565b9392505050565b6123e0816114de565b81146123ea575f5ffd5b50565b5f815190506123fb816123d7565b92915050565b5f5f60408385031215612417576124166111ec565b5b5f612424858286016123ed565b9250506020612435858286016123ed565b9150509250929050565b5f6040820190506124525f8301856113f5565b61245f6020830184611282565b9392505050565b5f8151905061247481611967565b92915050565b5f6020828403121561248f5761248e6111ec565b5b5f61249c84828501612466565b91505092915050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f6124d7826124a5565b6124e181856124af565b93506124f18185602086016124bf565b6124fa81611696565b840191505092915050565b5f610100820190506125195f83018b611282565b818103602083015261252b818a6124cd565b9050818103604083015261253f81896124cd565b9050818103606083015261255381886124cd565b9050818103608083015261256781876124cd565b905081810360a083015261257b81866124cd565b905081810360c083015261258f81856124cd565b905081810360e08301526125a381846124cd565b90509998505050505050505050565b5f815190506125c081611426565b92915050565b5f602082840312156125db576125da6111ec565b5b5f6125e8848285016125b2565b91505092915050565b5f60a0820190506126045f830188611666565b6126116020830187611666565b61261e6040830186611666565b61262b60608301856122f8565b6126386080830184612282565b9695505050505050565b5f6020820190506126555f8301846122f8565b92915050565b5f5ffd5b5f8151905061266d81611319565b92915050565b5f8151905061268181611d27565b92915050565b5f610160828403121561269d5761269c61265b565b5b6126a8610160611704565b90505f6126b7848285016122aa565b5f8301525060206126ca84828501612466565b60208301525060406126de84828501612466565b60408301525060606126f28482850161265f565b606083015250608061270684828501612466565b60808301525060a061271a848285016122aa565b60a08301525060c061272e84828501612466565b60c08301525060e061274284828501612673565b60e083015250610100612757848285016122aa565b6101008301525061012061276d84828501612673565b610120830152506101406127838482850161265f565b6101408301525092915050565b5f61016082840312156127a6576127a56111ec565b5b5f6127b384828501612687565b91505092915050565b5f67ffffffffffffffff8211156127d6576127d56116a6565b5b602082029050602081019050919050565b5f606082840312156127fc576127fb61265b565b5b6128066060611704565b90505f612815848285016122aa565b5f830152506020612828848285016125b2565b602083015250604061283c848285016125b2565b60408301525092915050565b5f61285a612855846127bc565b611704565b9050808382526020820190506060840283018581111561287d5761287c61208a565b5b835b818110156128a6578061289288826127e7565b84526020840193505060608101905061287f565b5050509392505050565b5f82601f8301126128c4576128c361168e565b5b81516128d4848260208601612848565b91505092915050565b5f602082840312156128f2576128f16111ec565b5b5f82015167ffffffffffffffff81111561290f5761290e6111f0565b5b61291b848285016128b0565b91505092915050565b61292d81611a60565b82525050565b5f60e0820190506129465f83018a611666565b6129536020830189611666565b6129606040830188611666565b61296d60608301876122f8565b61297a60808301866122e9565b61298760a0830185612924565b61299460c08301846122f8565b98975050505050505050565b5f6060820190506129b35f8301866113f5565b6129c06020830185612282565b6129cd60408301846113f5565b949350505050565b5f67ffffffffffffffff8211156129ef576129ee6116a6565b5b602082029050602081019050919050565b5f60408284031215612a1557612a1461265b565b5b612a1f6040611704565b90505f612a2e8482850161233c565b5f830152506020612a4184828501612466565b60208301525092915050565b5f612a5f612a5a846129d5565b611704565b90508083825260208201905060408402830185811115612a8257612a8161208a565b5b835b81811015612aab5780612a978882612a00565b845260208401935050604081019050612a84565b5050509392505050565b5f82601f830112612ac957612ac861168e565b5b8151612ad9848260208601612a4d565b91505092915050565b5f60208284031215612af757612af66111ec565b5b5f82015167ffffffffffffffff811115612b1457612b136111f0565b5b612b2084828501612ab5565b91505092915050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b612b5b816112d4565b82525050565b5f612b6c8383612b52565b60208301905092915050565b5f602082019050919050565b5f612b8e82612b29565b612b988185612b33565b9350612ba383612b43565b805f5b83811015612bd3578151612bba8882612b61565b9750612bc583612b78565b925050600181019050612ba6565b5085935050505092915050565b5f81519050919050565b5f82825260208201905092915050565b5f612c0482612be0565b612c0e8185612bea565b9350612c1e8185602086016124bf565b612c2781611696565b840191505092915050565b5f606082019050612c455f830186611282565b8181036020830152612c578185612b84565b90508181036040830152612c6b8184612bfa565b905094935050505056fea2646970667358221220173b5d48037ae11a1b4ddc1d3fa630e53a41fbdde44b962f2ec9d9038e92aaf264736f6c634300081e0033"; +export const PRECOMPILE_WRAPPER_BYTECODE = "6080604052348015600e575f5ffd5b50612bb98061001c5f395ff3fe6080604052600436106101d6575f3560e01c80637d691e3011610101578063b1f789ef11610094578063d75e3e0d11610063578063d75e3e0d146106a9578063db1d0fd5146106d3578063ec556889146106fd578063fc6679fb14610727576101d6565b8063b1f789ef146105fd578063bfe252a214610639578063caf2ebf214610663578063cd6f4eb11461068d576101d6565b80639f246f6f116100d05780639f246f6f14610551578063a21762761461058d578063ac3166bf146105b7578063afed65f9146105e1576101d6565b80637d691e30146104815780638bba466c1461049d57806394e3ac6f146104d9578063998538c414610515576101d6565b80634c378a96116101795780635e25f3f8116101485780635e25f3f8146103d157806369e38bc3146103ed57806371214e27146104295780637444dadc14610445576101d6565b80634c378a96146103175780634cf088d9146103415780635b53ddde1461036b5780635b7210c514610395576101d6565b80631f193572116101b55780631f193572146102665780631fc9b141146102a25780633175bd98146102be5780634054ecca146102fb576101d6565b80620ae759146101da5780630494cd9a146102025780630cadeda51461023e575b5f5ffd5b3480156101e5575f5ffd5b5061020060048036038101906101fb91906113ab565b610751565b005b34801561020d575f5ffd5b506102286004803603810190610223919061148d565b6107c1565b60405161023591906114c7565b60405180910390f35b348015610249575f5ffd5b50610264600480360381019061025f9190611519565b610843565b005b348015610271575f5ffd5b5061028c600480360381019061028791906115a0565b6108b4565b60405161029991906115da565b60405180910390f35b6102bc60048036038101906102b79190611626565b610936565b005b3480156102c9575f5ffd5b506102e460048036038101906102df9190611676565b6109a7565b6040516102f29291906116de565b60405180910390f35b61031560048036038101906103109190611705565b610a2f565b005b348015610322575f5ffd5b5061032b610a9d565b604051610338919061179e565b60405180910390f35b34801561034c575f5ffd5b50610355610aa3565b60405161036291906117d7565b60405180910390f35b348015610376575f5ffd5b5061037f610aa9565b60405161038c9190611810565b60405180910390f35b3480156103a0575f5ffd5b506103bb60048036038101906103b69190611676565b610aaf565b6040516103c8919061184b565b60405180910390f35b6103eb60048036038101906103e69190611914565b610b34565b005b3480156103f8575f5ffd5b50610413600480360381019061040e91906115a0565b610bb4565b6040516104209190611a98565b60405180910390f35b610443600480360381019061043e9190611adb565b610c36565b005b348015610450575f5ffd5b5061046b600480360381019061046691906115a0565b610cad565b604051610478919061184b565b60405180910390f35b61049b60048036038101906104969190611626565b610d2f565b005b3480156104a8575f5ffd5b506104c360048036038101906104be9190611b52565b610da0565b6040516104d09190611ca3565b60405180910390f35b3480156104e4575f5ffd5b506104ff60048036038101906104fa9190611cbd565b610e2a565b60405161050c9190611ddf565b60405180910390f35b348015610520575f5ffd5b5061053b60048036038101906105369190611cbd565b610eb0565b6040516105489190611a98565b60405180910390f35b34801561055c575f5ffd5b5061057760048036038101906105729190611cbd565b610f32565b6040516105849190611a98565b60405180910390f35b348015610598575f5ffd5b506105a1610fb4565b6040516105ae9190611e1f565b60405180910390f35b3480156105c2575f5ffd5b506105cb610fba565b6040516105d89190611e58565b60405180910390f35b6105fb60048036038101906105f69190611e9b565b610fc0565b005b348015610608575f5ffd5b50610623600480360381019061061e9190611f38565b61103d565b604051610630919061206c565b60405180910390f35b348015610644575f5ffd5b5061064d6110c9565b60405161065a91906120ac565b60405180910390f35b34801561066e575f5ffd5b506106776110cf565b60405161068491906120e5565b60405180910390f35b6106a760048036038101906106a29190611cbd565b6110d5565b005b3480156106b4575f5ffd5b506106bd611142565b6040516106ca919061211e565b60405180910390f35b3480156106de575f5ffd5b506106e7611148565b6040516106f49190612157565b60405180910390f35b348015610708575f5ffd5b5061071161114e565b60405161071e9190612190565b60405180910390f35b348015610732575f5ffd5b5061073b611154565b60405161074891906121c9565b60405180910390f35b61080b73ffffffffffffffffffffffffffffffffffffffff16620ae7598484846040518463ffffffff1660e01b815260040161078f93929190612299565b5f604051808303815f87803b1580156107a6575f5ffd5b505af11580156107b8573d5f5f3e3d5ffd5b50505050505050565b5f61080c73ffffffffffffffffffffffffffffffffffffffff16630494cd9a836040518263ffffffff1660e01b81526004016107fd91906122eb565b602060405180830381865afa158015610818573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061083c9190612318565b9050919050565b61080b73ffffffffffffffffffffffffffffffffffffffff16630cadeda58484846040518463ffffffff1660e01b815260040161088293929190612361565b5f604051808303815f87803b158015610899575f5ffd5b505af11580156108ab573d5f5f3e3d5ffd5b50505050505050565b5f61080273ffffffffffffffffffffffffffffffffffffffff16631f193572836040518263ffffffff1660e01b81526004016108f091906115da565b602060405180830381865afa15801561090b573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061092f91906123aa565b9050919050565b61080573ffffffffffffffffffffffffffffffffffffffff16631fc9b1418484846040518463ffffffff1660e01b8152600401610975939291906123d5565b5f604051808303815f87803b15801561098c575f5ffd5b505af115801561099e573d5f5f3e3d5ffd5b50505050505050565b5f5f61080a73ffffffffffffffffffffffffffffffffffffffff16633175bd9885856040518363ffffffff1660e01b81526004016109e692919061240a565b6040805180830381865afa158015610a00573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a24919061245b565b915091509250929050565b61080473ffffffffffffffffffffffffffffffffffffffff16634054ecca83836040518363ffffffff1660e01b8152600401610a6c929190612499565b5f604051808303815f87803b158015610a83575f5ffd5b505af1158015610a95573d5f5f3e3d5ffd5b505050505050565b61080481565b61080581565b61080a81565b5f61080973ffffffffffffffffffffffffffffffffffffffff16635b7210c584846040518363ffffffff1660e01b8152600401610aed92919061240a565b602060405180830381865afa158015610b08573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b2c91906124d4565b905092915050565b61080373ffffffffffffffffffffffffffffffffffffffff16631cf98c6b89898989898989896040518963ffffffff1660e01b8152600401610b7d98979695949392919061255f565b5f604051808303815f87803b158015610b94575f5ffd5b505af1158015610ba6573d5f5f3e3d5ffd5b505050505050505050505050565b5f61080873ffffffffffffffffffffffffffffffffffffffff166369e38bc3836040518263ffffffff1660e01b8152600401610bf091906115da565b602060405180830381865afa158015610c0b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c2f9190612620565b9050919050565b61080973ffffffffffffffffffffffffffffffffffffffff1663127e1adb86868686866040518663ffffffff1660e01b8152600401610c7995949392919061264b565b5f604051808303815f87803b158015610c90575f5ffd5b505af1158015610ca2573d5f5f3e3d5ffd5b505050505050505050565b5f61080373ffffffffffffffffffffffffffffffffffffffff16637444dadc836040518263ffffffff1660e01b8152600401610ce991906115da565b602060405180830381865afa158015610d04573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d2891906124d4565b9050919050565b61080573ffffffffffffffffffffffffffffffffffffffff16637d691e308484846040518463ffffffff1660e01b8152600401610d6e939291906123d5565b5f604051808303815f87803b158015610d85575f5ffd5b505af1158015610d97573d5f5f3e3d5ffd5b50505050505050565b610da861115a565b61080973ffffffffffffffffffffffffffffffffffffffff16638bba466c836040518263ffffffff1660e01b8152600401610de3919061269c565b61016060405180830381865afa158015610dff573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e2391906127ea565b9050919050565b606061080b73ffffffffffffffffffffffffffffffffffffffff166394e3ac6f836040518263ffffffff1660e01b8152600401610e6791906114c7565b5f60405180830381865afa158015610e81573d5f5f3e3d5ffd5b505050506040513d5f823e3d601f19601f82011682018060405250810190610ea99190612937565b9050919050565b5f61080573ffffffffffffffffffffffffffffffffffffffff1663998538c4836040518263ffffffff1660e01b8152600401610eec91906114c7565b602060405180830381865afa158015610f07573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f2b9190612620565b9050919050565b5f61080573ffffffffffffffffffffffffffffffffffffffff16639f246f6f836040518263ffffffff1660e01b8152600401610f6e91906114c7565b602060405180830381865afa158015610f89573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610fad9190612620565b9050919050565b61080681565b61080c81565b61080a73ffffffffffffffffffffffffffffffffffffffff1663afed65f9888888888888886040518863ffffffff1660e01b8152600401611007979695949392919061298d565b5f604051808303815f87803b15801561101e575f5ffd5b505af1158015611030573d5f5f3e3d5ffd5b5050505050505050505050565b606061080673ffffffffffffffffffffffffffffffffffffffff1663b1f789ef8585856040518463ffffffff1660e01b815260040161107e939291906129fa565b5f60405180830381865afa158015611098573d5f5f3e3d5ffd5b505050506040513d5f823e3d601f19601f820116820180604052508101906110c09190612b3c565b90509392505050565b61080981565b61080381565b61080073ffffffffffffffffffffffffffffffffffffffff1663cd6f4eb134836040518363ffffffff1660e01b815260040161111191906114c7565b5f604051808303818588803b158015611128575f5ffd5b505af115801561113a573d5f5f3e3d5ffd5b505050505050565b61080081565b61080881565b61080b81565b61080281565b6040518061016001604052805f81526020015f67ffffffffffffffff1681526020015f67ffffffffffffffff1681526020015f63ffffffff1681526020015f67ffffffffffffffff1681526020015f81526020015f67ffffffffffffffff1681526020015f151581526020015f81526020015f151581526020015f63ffffffff1681525090565b5f604051905090565b5f5ffd5b5f5ffd5b5f819050919050565b611204816111f2565b811461120e575f5ffd5b50565b5f8135905061121f816111fb565b92915050565b5f5ffd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b61126f82611229565b810181811067ffffffffffffffff8211171561128e5761128d611239565b5b80604052505050565b5f6112a06111e1565b90506112ac8282611266565b919050565b5f67ffffffffffffffff8211156112cb576112ca611239565b5b602082029050602081019050919050565b5f5ffd5b5f60ff82169050919050565b6112f5816112e0565b81146112ff575f5ffd5b50565b5f81359050611310816112ec565b92915050565b5f611328611323846112b1565b611297565b9050808382526020820190506020840283018581111561134b5761134a6112dc565b5b835b8181101561137457806113608882611302565b84526020840193505060208101905061134d565b5050509392505050565b5f82601f83011261139257611391611225565b5b81356113a2848260208601611316565b91505092915050565b5f5f5f606084860312156113c2576113c16111ea565b5b5f6113cf86828701611211565b935050602084013567ffffffffffffffff8111156113f0576113ef6111ee565b5b6113fc8682870161137e565b925050604084013567ffffffffffffffff81111561141d5761141c6111ee565b5b6114298682870161137e565b9150509250925092565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61145c82611433565b9050919050565b61146c81611452565b8114611476575f5ffd5b50565b5f8135905061148781611463565b92915050565b5f602082840312156114a2576114a16111ea565b5b5f6114af84828501611479565b91505092915050565b6114c1816111f2565b82525050565b5f6020820190506114da5f8301846114b8565b92915050565b5f63ffffffff82169050919050565b6114f8816114e0565b8114611502575f5ffd5b50565b5f81359050611513816114ef565b92915050565b5f5f5f606084860312156115305761152f6111ea565b5b5f61153d86828701611211565b935050602061154e86828701611302565b925050604061155f86828701611505565b9150509250925092565b5f61ffff82169050919050565b61157f81611569565b8114611589575f5ffd5b50565b5f8135905061159a81611576565b92915050565b5f602082840312156115b5576115b46111ea565b5b5f6115c28482850161158c565b91505092915050565b6115d481611569565b82525050565b5f6020820190506115ed5f8301846115cb565b92915050565b5f819050919050565b611605816115f3565b811461160f575f5ffd5b50565b5f81359050611620816115fc565b92915050565b5f5f5f6060848603121561163d5761163c6111ea565b5b5f61164a86828701611211565b935050602061165b86828701611612565b925050604061166c86828701611612565b9150509250925092565b5f5f6040838503121561168c5761168b6111ea565b5b5f61169985828601611505565b92505060206116aa85828601611211565b9150509250929050565b5f6fffffffffffffffffffffffffffffffff82169050919050565b6116d8816116b4565b82525050565b5f6040820190506116f15f8301856116cf565b6116fe60208301846116cf565b9392505050565b5f5f6040838503121561171b5761171a6111ea565b5b5f6117288582860161158c565b925050602061173985828601611211565b9150509250929050565b5f819050919050565b5f61176661176161175c84611433565b611743565b611433565b9050919050565b5f6117778261174c565b9050919050565b5f6117888261176d565b9050919050565b6117988161177e565b82525050565b5f6020820190506117b15f83018461178f565b92915050565b5f6117c18261176d565b9050919050565b6117d1816117b7565b82525050565b5f6020820190506117ea5f8301846117c8565b92915050565b5f6117fa8261176d565b9050919050565b61180a816117f0565b82525050565b5f6020820190506118235f830184611801565b92915050565b5f67ffffffffffffffff82169050919050565b61184581611829565b82525050565b5f60208201905061185e5f83018461183c565b92915050565b5f5ffd5b5f67ffffffffffffffff82111561188257611881611239565b5b61188b82611229565b9050602081019050919050565b828183375f83830152505050565b5f6118b86118b384611868565b611297565b9050828152602081018484840111156118d4576118d3611864565b5b6118df848285611898565b509392505050565b5f82601f8301126118fb576118fa611225565b5b813561190b8482602086016118a6565b91505092915050565b5f5f5f5f5f5f5f5f610100898b031215611931576119306111ea565b5b5f61193e8b828c01611211565b985050602089013567ffffffffffffffff81111561195f5761195e6111ee565b5b61196b8b828c016118e7565b975050604089013567ffffffffffffffff81111561198c5761198b6111ee565b5b6119988b828c016118e7565b965050606089013567ffffffffffffffff8111156119b9576119b86111ee565b5b6119c58b828c016118e7565b955050608089013567ffffffffffffffff8111156119e6576119e56111ee565b5b6119f28b828c016118e7565b94505060a089013567ffffffffffffffff811115611a1357611a126111ee565b5b611a1f8b828c016118e7565b93505060c089013567ffffffffffffffff811115611a4057611a3f6111ee565b5b611a4c8b828c016118e7565b92505060e089013567ffffffffffffffff811115611a6d57611a6c6111ee565b5b611a798b828c016118e7565b9150509295985092959890939650565b611a92816115f3565b82525050565b5f602082019050611aab5f830184611a89565b92915050565b611aba81611829565b8114611ac4575f5ffd5b50565b5f81359050611ad581611ab1565b92915050565b5f5f5f5f5f60a08688031215611af457611af36111ea565b5b5f611b0188828901611ac7565b9550506020611b1288828901611ac7565b9450506040611b2388828901611ac7565b9350506060611b3488828901611505565b9250506080611b4588828901611479565b9150509295509295909350565b5f60208284031215611b6757611b666111ea565b5b5f611b7484828501611505565b91505092915050565b611b86816111f2565b82525050565b611b9581611829565b82525050565b611ba4816114e0565b82525050565b5f8115159050919050565b611bbe81611baa565b82525050565b61016082015f820151611bd95f850182611b7d565b506020820151611bec6020850182611b8c565b506040820151611bff6040850182611b8c565b506060820151611c126060850182611b9b565b506080820151611c256080850182611b8c565b5060a0820151611c3860a0850182611b7d565b5060c0820151611c4b60c0850182611b8c565b5060e0820151611c5e60e0850182611bb5565b50610100820151611c73610100850182611b7d565b50610120820151611c88610120850182611bb5565b50610140820151611c9d610140850182611b9b565b50505050565b5f61016082019050611cb75f830184611bc4565b92915050565b5f60208284031215611cd257611cd16111ea565b5b5f611cdf84828501611211565b91505092915050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b611d1a816115f3565b82525050565b606082015f820151611d345f850182611b7d565b506020820151611d476020850182611d11565b506040820151611d5a6040850182611d11565b50505050565b5f611d6b8383611d20565b60608301905092915050565b5f602082019050919050565b5f611d8d82611ce8565b611d978185611cf2565b9350611da283611d02565b805f5b83811015611dd2578151611db98882611d60565b9750611dc483611d77565b925050600181019050611da5565b5085935050505092915050565b5f6020820190508181035f830152611df78184611d83565b905092915050565b5f611e098261176d565b9050919050565b611e1981611dff565b82525050565b5f602082019050611e325f830184611e10565b92915050565b5f611e428261176d565b9050919050565b611e5281611e38565b82525050565b5f602082019050611e6b5f830184611e49565b92915050565b611e7a81611baa565b8114611e84575f5ffd5b50565b5f81359050611e9581611e71565b92915050565b5f5f5f5f5f5f5f60e0888a031215611eb657611eb56111ea565b5b5f611ec38a828b01611ac7565b9750506020611ed48a828b01611ac7565b9650506040611ee58a828b01611ac7565b9550506060611ef68a828b01611505565b9450506080611f078a828b01611302565b93505060a0611f188a828b01611e87565b92505060c0611f298a828b01611505565b91505092959891949750929550565b5f5f5f60608486031215611f4f57611f4e6111ea565b5b5f611f5c8682870161158c565b9350506020611f6d86828701611479565b9250506040611f7e8682870161158c565b9150509250925092565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b611fba81611569565b82525050565b604082015f820151611fd45f850182611fb1565b506020820151611fe76020850182611b8c565b50505050565b5f611ff88383611fc0565b60408301905092915050565b5f602082019050919050565b5f61201a82611f88565b6120248185611f92565b935061202f83611fa2565b805f5b8381101561205f5781516120468882611fed565b975061205183612004565b925050600181019050612032565b5085935050505092915050565b5f6020820190508181035f8301526120848184612010565b905092915050565b5f6120968261176d565b9050919050565b6120a68161208c565b82525050565b5f6020820190506120bf5f83018461209d565b92915050565b5f6120cf8261176d565b9050919050565b6120df816120c5565b82525050565b5f6020820190506120f85f8301846120d6565b92915050565b5f6121088261176d565b9050919050565b612118816120fe565b82525050565b5f6020820190506121315f83018461210f565b92915050565b5f6121418261176d565b9050919050565b61215181612137565b82525050565b5f60208201905061216a5f830184612148565b92915050565b5f61217a8261176d565b9050919050565b61218a81612170565b82525050565b5f6020820190506121a35f830184612181565b92915050565b5f6121b38261176d565b9050919050565b6121c3816121a9565b82525050565b5f6020820190506121dc5f8301846121ba565b92915050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b612214816112e0565b82525050565b5f612225838361220b565b60208301905092915050565b5f602082019050919050565b5f612247826121e2565b61225181856121ec565b935061225c836121fc565b805f5b8381101561228c578151612273888261221a565b975061227e83612231565b92505060018101905061225f565b5085935050505092915050565b5f6060820190506122ac5f8301866114b8565b81810360208301526122be818561223d565b905081810360408301526122d2818461223d565b9050949350505050565b6122e581611452565b82525050565b5f6020820190506122fe5f8301846122dc565b92915050565b5f81519050612312816111fb565b92915050565b5f6020828403121561232d5761232c6111ea565b5b5f61233a84828501612304565b91505092915050565b61234c816112e0565b82525050565b61235b816114e0565b82525050565b5f6060820190506123745f8301866114b8565b6123816020830185612343565b61238e6040830184612352565b949350505050565b5f815190506123a481611576565b92915050565b5f602082840312156123bf576123be6111ea565b5b5f6123cc84828501612396565b91505092915050565b5f6060820190506123e85f8301866114b8565b6123f56020830185611a89565b6124026040830184611a89565b949350505050565b5f60408201905061241d5f830185612352565b61242a60208301846114b8565b9392505050565b61243a816116b4565b8114612444575f5ffd5b50565b5f8151905061245581612431565b92915050565b5f5f60408385031215612471576124706111ea565b5b5f61247e85828601612447565b925050602061248f85828601612447565b9150509250929050565b5f6040820190506124ac5f8301856115cb565b6124b960208301846114b8565b9392505050565b5f815190506124ce81611ab1565b92915050565b5f602082840312156124e9576124e86111ea565b5b5f6124f6848285016124c0565b91505092915050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f612531826124ff565b61253b8185612509565b935061254b818560208601612519565b61255481611229565b840191505092915050565b5f610100820190506125735f83018b6114b8565b8181036020830152612585818a612527565b905081810360408301526125998189612527565b905081810360608301526125ad8188612527565b905081810360808301526125c18187612527565b905081810360a08301526125d58186612527565b905081810360c08301526125e98185612527565b905081810360e08301526125fd8184612527565b90509998505050505050505050565b5f8151905061261a816115fc565b92915050565b5f60208284031215612635576126346111ea565b5b5f6126428482850161260c565b91505092915050565b5f60a08201905061265e5f83018861183c565b61266b602083018761183c565b612678604083018661183c565b6126856060830185612352565b61269260808301846122dc565b9695505050505050565b5f6020820190506126af5f830184612352565b92915050565b5f5ffd5b5f815190506126c7816114ef565b92915050565b5f815190506126db81611e71565b92915050565b5f61016082840312156126f7576126f66126b5565b5b612702610160611297565b90505f61271184828501612304565b5f830152506020612724848285016124c0565b6020830152506040612738848285016124c0565b604083015250606061274c848285016126b9565b6060830152506080612760848285016124c0565b60808301525060a061277484828501612304565b60a08301525060c0612788848285016124c0565b60c08301525060e061279c848285016126cd565b60e0830152506101006127b184828501612304565b610100830152506101206127c7848285016126cd565b610120830152506101406127dd848285016126b9565b6101408301525092915050565b5f6101608284031215612800576127ff6111ea565b5b5f61280d848285016126e1565b91505092915050565b5f67ffffffffffffffff8211156128305761282f611239565b5b602082029050602081019050919050565b5f60608284031215612856576128556126b5565b5b6128606060611297565b90505f61286f84828501612304565b5f8301525060206128828482850161260c565b60208301525060406128968482850161260c565b60408301525092915050565b5f6128b46128af84612816565b611297565b905080838252602082019050606084028301858111156128d7576128d66112dc565b5b835b8181101561290057806128ec8882612841565b8452602084019350506060810190506128d9565b5050509392505050565b5f82601f83011261291e5761291d611225565b5b815161292e8482602086016128a2565b91505092915050565b5f6020828403121561294c5761294b6111ea565b5b5f82015167ffffffffffffffff811115612969576129686111ee565b5b6129758482850161290a565b91505092915050565b61298781611baa565b82525050565b5f60e0820190506129a05f83018a61183c565b6129ad602083018961183c565b6129ba604083018861183c565b6129c76060830187612352565b6129d46080830186612343565b6129e160a083018561297e565b6129ee60c0830184612352565b98975050505050505050565b5f606082019050612a0d5f8301866115cb565b612a1a60208301856122dc565b612a2760408301846115cb565b949350505050565b5f67ffffffffffffffff821115612a4957612a48611239565b5b602082029050602081019050919050565b5f60408284031215612a6f57612a6e6126b5565b5b612a796040611297565b90505f612a8884828501612396565b5f830152506020612a9b848285016124c0565b60208301525092915050565b5f612ab9612ab484612a2f565b611297565b90508083825260208201905060408402830185811115612adc57612adb6112dc565b5b835b81811015612b055780612af18882612a5a565b845260208401935050604081019050612ade565b5050509392505050565b5f82601f830112612b2357612b22611225565b5b8151612b33848260208601612aa7565b91505092915050565b5f60208284031215612b5157612b506111ea565b5b5f82015167ffffffffffffffff811115612b6e57612b6d6111ee565b5b612b7a84828501612b0f565b9150509291505056fea2646970667358221220768c64014d2253c661e44d07f480f7a203eb9e422f680d00272498325a4f6ad964736f6c634300081e0033"; diff --git a/contract-tests/test/precompileWrapper.direct-call.test.ts b/contract-tests/test/precompileWrapper.direct-call.test.ts index 6cd2c14660..81201eace5 100644 --- a/contract-tests/test/precompileWrapper.direct-call.test.ts +++ b/contract-tests/test/precompileWrapper.direct-call.test.ts @@ -1,36 +1,44 @@ import * as assert from "assert"; -import { getDevnetApi, getRandomSubstrateKeypair, getBalance } from "../src/substrate"; +import { getDevnetApi, getRandomSubstrateKeypair, getBalance, getSignerFromKeypair } from "../src/substrate"; import { devnet } from "@polkadot-api/descriptors"; -import { TypedApi } from "polkadot-api"; -import { convertH160ToSS58, convertPublicKeyToSs58 } from "../src/address-utils"; +import { TypedApi, Binary } from "polkadot-api"; +import { convertH160ToSS58, convertPublicKeyToSs58, convertH160ToPublicKey } from "../src/address-utils"; import { tao, raoToEth } from "../src/balance-math"; import { forceSetBalanceToSs58Address, addNewSubnetwork, - addStake, startCall, disableWhiteListCheck, + forceSetBalanceToEthAddress, + } from "../src/subtensor"; import { ethers } from "ethers"; -import { generateRandomEthersWallet } from "../src/utils"; +import { generateRandomEthersWallet, getPublicClient } from "../src/utils"; import { PRECOMPILE_WRAPPER_ABI, PRECOMPILE_WRAPPER_BYTECODE } from "../src/contracts/precompileWrapper"; +import { ETH_LOCAL_URL } from "../src/config"; +import { PublicClient } from "viem"; +import { IProxyABI, IPROXY_ADDRESS } from "../src/contracts/proxy" describe("PrecompileWrapper - Direct Call Tests", () => { const hotkey = getRandomSubstrateKeypair(); const coldkey = getRandomSubstrateKeypair(); const wallet1 = generateRandomEthersWallet(); + const wallet2 = generateRandomEthersWallet(); let api: TypedApi; + let publicClient: PublicClient; let wrapperContract: ethers.Contract; let wrapperAddress: string; let netuid: number; before(async () => { api = await getDevnetApi(); - await disableWhiteListCheck(api, true) + publicClient = await getPublicClient(ETH_LOCAL_URL); + await disableWhiteListCheck(api, true); await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(hotkey.publicKey)); await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(coldkey.publicKey)); - await forceSetBalanceToSs58Address(api, convertH160ToSS58(wallet1.address)); + await forceSetBalanceToEthAddress(api, wallet1.address); + await forceSetBalanceToEthAddress(api, wallet2.address); await addNewSubnetwork(api, hotkey, coldkey); netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1; await startCall(api, netuid, coldkey); @@ -43,7 +51,7 @@ describe("PrecompileWrapper - Direct Call Tests", () => { const deployContract = await factory.deploy(); await deployContract.waitForDeployment(); wrapperAddress = await deployContract.getAddress(); - await forceSetBalanceToSs58Address(api, convertH160ToSS58(wrapperAddress)); + await forceSetBalanceToEthAddress(api, wrapperAddress); console.log("Wrapper contract deployed at:", wrapperAddress); console.log("Testing in subnet:", netuid); @@ -54,13 +62,346 @@ describe("PrecompileWrapper - Direct Call Tests", () => { describe("Balance Transfer Precompile Direct Calls", () => { it("Should transfer balance via wrapper", async () => { const keypair = getRandomSubstrateKeypair(); - const transfer = await wrapperContract.transfer(keypair.publicKey, { value: raoToEth(tao(1)).toString() }); - await transfer.wait(); + const transferAmount = raoToEth(tao(1)); + + // Transfer via wrapper + const transferTx = await wrapperContract.transfer(keypair.publicKey, { value: transferAmount.toString() }); + await transferTx.wait(); const balance = await getBalance(api, convertPublicKeyToSs58(keypair.publicKey)); + assert.ok(balance >= tao(1), "Balance should be transferred"); + }); + }); + + describe("Metagraph Precompile Direct Calls", () => { + it("Should get UID count via wrapper", async () => { + const uidCountViaWrapper = await wrapperContract.getUidCount(netuid); + assert.ok(uidCountViaWrapper !== undefined, "UID count should be not undefined"); + }); + }); + + describe("Subnet Precompile Direct Calls", () => { + it("Should get serving rate limit via wrapper", async () => { + const rateLimitViaWrapper = await wrapperContract.getServingRateLimit(netuid); + + assert.ok(rateLimitViaWrapper !== undefined, "Rate limit should be not undefined"); + }); + + it("Should register network with details via wrapper", async () => { + const newHotkey = getRandomSubstrateKeypair(); + await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(newHotkey.publicKey)); + + const totalNetworksBefore = await api.query.SubtensorModule.TotalNetworks.getValue(); + + const registerTx = await wrapperContract.registerNetworkWithDetails( + newHotkey.publicKey, + "Test Subnet", + "https://github.com/test/repo", + "test@example.com", + "https://test.example.com", + "test#1234", + "Test description", + "Additional info", + { value: raoToEth(tao(100)).toString() } + ); + await registerTx.wait(); - assert.equal(balance, tao(1), "Wrapper and direct calls should match"); - }) + const totalNetworksAfter = await api.query.SubtensorModule.TotalNetworks.getValue(); + const beforeValue = typeof totalNetworksBefore === 'bigint' ? totalNetworksBefore : BigInt(totalNetworksBefore); + assert.equal(totalNetworksAfter, beforeValue + BigInt(1), "Network should be registered"); + }); }); + describe("Neuron Precompile Direct Calls", () => { + it("Should register neuron via wrapper", async () => { + const newHotkey = getRandomSubstrateKeypair(); + const newColdkey = getRandomSubstrateKeypair(); + await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(newHotkey.publicKey)); + await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(newColdkey.publicKey)); + + // Use a reasonable burn amount (100 TAO) + const burnAmount = tao(100); + + const registerTx = await wrapperContract.burnedRegister( + netuid, + newHotkey.publicKey, + { value: raoToEth(burnAmount).toString() } + ); + await registerTx.wait(); + + const uid = await api.query.SubtensorModule.Uids.getValue(netuid, convertPublicKeyToSs58(newHotkey.publicKey)); + assert.ok(uid !== undefined, "Neuron should be registered"); + }); + }); + + describe("Staking Precompile Direct Calls", () => { + it("Should get total coldkey stake via wrapper", async () => { + const stakeViaWrapper = await wrapperContract.getTotalColdkeyStake(coldkey.publicKey); + assert.ok(stakeViaWrapper !== undefined, "Total coldkey stake should be not undefined"); + }); + + it("Should get total hotkey stake via wrapper", async () => { + const stakeViaWrapper = await wrapperContract.getTotalHotkeyStake(hotkey.publicKey); + assert.ok(stakeViaWrapper !== undefined, "Total hotkey stake should be not undefined"); + }); + + it("Should add stake via wrapper", async () => { + const stakeAmount = tao(2); + const stakeBefore = await api.query.SubtensorModule.Alpha.getValue( + convertPublicKeyToSs58(hotkey.publicKey), + convertH160ToSS58(wrapperAddress), + netuid + ); + + const addStakeTx = await wrapperContract.addStake( + hotkey.publicKey, + stakeAmount.toString(), + netuid, + { value: raoToEth(stakeAmount).toString() } + ); + await addStakeTx.wait(); + + const stakeAfter = await api.query.SubtensorModule.Alpha.getValue( + convertPublicKeyToSs58(hotkey.publicKey), + convertH160ToSS58(wrapperAddress), + netuid + ); + assert.ok(stakeAfter > stakeBefore, "Stake should be increased"); + }); + + it("Should remove stake via wrapper", async () => { + const removeAmount = tao(1); + const stakeBefore = await api.query.SubtensorModule.Alpha.getValue( + convertPublicKeyToSs58(hotkey.publicKey), + convertH160ToSS58(wrapperAddress), + netuid + ); + + const removeStakeTx = await wrapperContract.removeStake( + hotkey.publicKey, + removeAmount.toString(), + netuid + ); + await removeStakeTx.wait(); + + const stakeAfter = await api.query.SubtensorModule.Alpha.getValue( + convertPublicKeyToSs58(hotkey.publicKey), + convertH160ToSS58(wrapperAddress), + netuid + ); + assert.ok(stakeAfter < stakeBefore, "Stake should be decreased"); + }); + }); + + describe("UID Lookup Precompile Direct Calls", () => { + it("Should lookup UID via wrapper", async () => { + const evmAddress = wallet1.address; + const limit = 10; + const lookupViaWrapper = await wrapperContract.uidLookup(netuid, evmAddress, limit); + + assert.ok(Array.isArray(lookupViaWrapper), "Lookup should return an array"); + }); + }); + + describe("Alpha Precompile Direct Calls", () => { + it("Should get alpha price via wrapper", async () => { + const priceViaWrapper = await wrapperContract.getAlphaPrice(netuid); + assert.ok(priceViaWrapper !== undefined, "Alpha price should be not undefined"); + }); + }); + + describe("Crowdloan Precompile Direct Calls", () => { + it("Should get crowdloan via wrapper", async () => { + // First create a crowdloan via substrate + const nextId = await api.query.Crowdloan.NextCrowdloanId.getValue(); + const end = await api.query.System.Number.getValue() + 100; + const deposit = BigInt(15_000_000_000); // 15 TAO + const minContribution = BigInt(1_000_000_000); // 1 TAO + const cap = BigInt(100_000_000_000); // 100 TAO + + const signer = getSignerFromKeypair(coldkey); + await api.tx.Crowdloan.create({ + deposit, + min_contribution: minContribution, + cap, + end, + target_address: undefined, + call: api.tx.System.remark({ remark: Binary.fromText("test") }).decodedCall + }).signAndSubmit(signer); + + // Wait a bit for the transaction to be included + await new Promise(resolve => setTimeout(resolve, 2000)); + + const crowdloanViaWrapper = await wrapperContract.getCrowdloan(nextId); + + assert.ok(crowdloanViaWrapper !== undefined, "Crowdloan should be not undefined"); + }); + + it("Should get contribution via wrapper", async () => { + const nextId = await api.query.Crowdloan.NextCrowdloanId.getValue(); + const contributionViaWrapper = await wrapperContract.getContribution(nextId - 1, coldkey.publicKey); + + assert.ok(contributionViaWrapper !== undefined, "Contribution should be not undefined"); + }); + + it("Should create crowdloan via wrapper", 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 = Number(await api.query.System.Number.getValue()) + 100; + const targetAddress = wallet2.address; + + const nextIdBefore = await api.query.Crowdloan.NextCrowdloanId.getValue(); + + const createTx = await wrapperContract.createCrowdloan( + deposit.toString(), + minContribution.toString(), + cap.toString(), + end, + targetAddress, + { value: raoToEth(deposit).toString() } + ); + await createTx.wait(); + + const nextIdAfter = await api.query.Crowdloan.NextCrowdloanId.getValue(); + const beforeId = typeof nextIdBefore === 'bigint' ? nextIdBefore : BigInt(nextIdBefore); + assert.equal(nextIdAfter, beforeId + BigInt(1), "Crowdloan should be created"); + }); + }); + + + describe("Leasing Precompile Direct Calls", () => { + it("Should get contributor share via wrapper", async () => { + // First create a lease crowdloan + const nextCrowdloanId = await api.query.Crowdloan.NextCrowdloanId.getValue(); + const crowdloanDeposit = BigInt(100_000_000_000); // 100 TAO + const networkLastLockCost = await api.query.SubtensorModule.NetworkLastLockCost.getValue(); + const lockCostValue = typeof networkLastLockCost === 'bigint' ? networkLastLockCost : BigInt(networkLastLockCost); + const crowdloanCap = lockCostValue * BigInt(2); + const currentBlock = await api.query.System.Number.getValue(); + const crowdloanEnd = currentBlock + 100; + const leasingEmissionsShare = 15; + const leasingEndBlock = currentBlock + 300; + + const signer = getSignerFromKeypair(coldkey); + await api.tx.Crowdloan.create({ + deposit: crowdloanDeposit, + min_contribution: BigInt(1_000_000_000), + cap: crowdloanCap, + end: crowdloanEnd, + target_address: undefined, + call: api.tx.SubtensorModule.register_leased_network({ + emissions_share: leasingEmissionsShare, + end_block: leasingEndBlock, + }).decodedCall + }).signAndSubmit(signer); + + await new Promise(resolve => setTimeout(resolve, 2000)); + + const nextLeaseId = await api.query.SubtensorModule.NextSubnetLeaseId.getValue(); + + // Get contributor share + const shareViaWrapper = await wrapperContract.getContributorShare(nextLeaseId, coldkey.publicKey); + + assert.ok(shareViaWrapper !== undefined, "Share should be not undefined"); + + }); + + it("Should create lease crowdloan via wrapper", async () => { + const crowdloanDeposit = BigInt(100_000_000_000); // 100 TAO + const crowdloanMinContribution = BigInt(1_000_000_000); // 1 TAO + const networkLastLockCost = await api.query.SubtensorModule.NetworkLastLockCost.getValue(); + const lockCostValue = typeof networkLastLockCost === 'bigint' ? networkLastLockCost : BigInt(networkLastLockCost); + const crowdloanCap = lockCostValue * BigInt(2); + const currentBlock = await api.query.System.Number.getValue(); + const currentBlockValue = typeof currentBlock === 'bigint' ? Number(currentBlock) : currentBlock; + const crowdloanEnd = currentBlockValue + 100; + const leasingEmissionsShare = 15; + const hasLeasingEndBlock = true; + const leasingEndBlock = currentBlockValue + 300; + + const nextCrowdloanIdBefore = await api.query.Crowdloan.NextCrowdloanId.getValue(); + + const createTx = await wrapperContract.createLeaseCrowdloan( + crowdloanDeposit.toString(), + crowdloanMinContribution.toString(), + crowdloanCap.toString(), + crowdloanEnd, + leasingEmissionsShare, + hasLeasingEndBlock, + leasingEndBlock, + { value: raoToEth(crowdloanDeposit).toString() } + ); + await createTx.wait(); + + const nextCrowdloanIdAfter = await api.query.Crowdloan.NextCrowdloanId.getValue(); + const beforeId = typeof nextCrowdloanIdBefore === 'bigint' ? nextCrowdloanIdBefore : BigInt(nextCrowdloanIdBefore); + assert.equal(nextCrowdloanIdAfter, beforeId + BigInt(1), "Lease crowdloan should be created"); + }); + }); + + + describe("Proxy Precompile Direct Calls", () => { + it("Should get proxies via wrapper", async () => { + const accountKey = convertH160ToPublicKey(wallet1.address); + const proxiesViaWrapper = await wrapperContract.getProxies(accountKey); + + assert.ok(proxiesViaWrapper !== undefined, "Proxies should be not undefined"); + assert.ok(Array.isArray(proxiesViaWrapper), "Proxies should be an array"); + }); + it("Should add proxy via wrapper", async () => { + const delegate = getRandomSubstrateKeypair(); + await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(delegate.publicKey)); + const delegateKey = delegate.publicKey; + const proxyType = 0; + const delay = 0; + + const proxiesBefore = await api.query.Proxy.Proxies.getValue(convertH160ToSS58(wrapperAddress)); + + const addProxyTx = await wrapperContract.addProxy(delegateKey, proxyType, delay); + await addProxyTx.wait(); + + const proxiesAfter = await api.query.Proxy.Proxies.getValue(convertH160ToSS58(wrapperAddress)); + assert.ok(proxiesAfter[0].length > proxiesBefore[0].length, "Proxy should be added"); + }); + + it("Should proxy call via wrapper", async () => { + const proxyType = 0; + const delay = 0; + + const proxyContract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, wallet1); + const addProxyTx = await proxyContract.addProxy(convertH160ToPublicKey(wrapperAddress), proxyType, delay); + await addProxyTx.wait(); + + // Create a simple call (remark) + const remarkCall = api.tx.System.remark({ remark: Binary.fromText("") }); + + const callData = await remarkCall.getEncodedData(); + const data = callData.asBytes(); + + const proxyCallTx = await wrapperContract.proxyCall( + convertH160ToPublicKey(wallet1.address), + [proxyType], + [...data] + ); + await proxyCallTx.wait(); + + // Verify the call was executed (no error means success) + assert.ok(proxyCallTx, "Proxy call should succeed"); + }); + }); + + describe("Address Mapping Precompile Direct Calls", () => { + it("Should map address via wrapper", async () => { + const testAddress = wallet1.address; + const mappedViaWrapper = await wrapperContract.addressMapping(testAddress); + + assert.ok(mappedViaWrapper !== undefined, "Mapped address should be not undefined"); + assert.ok(mappedViaWrapper !== "0x0000000000000000000000000000000000000000000000000000000000000000", "Mapped address should not be zero"); + }); + }); + + + + }); From 0a83c4217d6e05038f2757a6c78bcdfbc0e16e8c Mon Sep 17 00:00:00 2001 From: open-junius Date: Mon, 2 Feb 2026 15:22:29 +0800 Subject: [PATCH 196/240] format code --- contract-tests/test/precompileWrapper.direct-call.test.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/contract-tests/test/precompileWrapper.direct-call.test.ts b/contract-tests/test/precompileWrapper.direct-call.test.ts index 81201eace5..5d63dfbb44 100644 --- a/contract-tests/test/precompileWrapper.direct-call.test.ts +++ b/contract-tests/test/precompileWrapper.direct-call.test.ts @@ -400,8 +400,4 @@ describe("PrecompileWrapper - Direct Call Tests", () => { assert.ok(mappedViaWrapper !== "0x0000000000000000000000000000000000000000000000000000000000000000", "Mapped address should not be zero"); }); }); - - - - }); From 57caa89f2f4a2cdf043b853f542512b532aedb3b Mon Sep 17 00:00:00 2001 From: Shamil Gadelshin Date: Mon, 2 Feb 2026 14:13:41 +0300 Subject: [PATCH 197/240] Add SubnetBuyback event --- pallets/subtensor/src/macros/dispatches.rs | 3 ++- pallets/subtensor/src/macros/events.rs | 12 ++++++++++++ pallets/subtensor/src/staking/recycle_alpha.rs | 9 ++++++++- pallets/subtensor/src/tests/recycle_alpha.rs | 8 ++++++++ 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 60c43a1346..c4cbc09f21 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2580,7 +2580,8 @@ mod dispatches { Self::do_set_voting_power_ema_alpha(netuid, alpha) } - /// --- Subnet buyback + /// --- Subnet buyback: the extrinsic is a combination of add_stake(add_stake_limit) and + /// burn_alpha. We buy alpha token first and immediately burn the acquired amount of alpha. #[pallet::call_index(132)] #[pallet::weight(( Weight::from_parts(368_000_000, 8556) diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index 107bbf975c..9f0c2bdfd5 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -516,5 +516,17 @@ mod events { /// The amount of alpha distributed alpha: AlphaCurrency, }, + + /// Subnet buyback event: alpha token was purchased and burned. + SubnetBuyback { + /// The subnet ID + netuid: NetUid, + /// hotky account ID + hotkey: T::AccountId, + /// Tao provided + amount: TaoCurrency, + /// Alpha burned + alpha: AlphaCurrency, + }, } } diff --git a/pallets/subtensor/src/staking/recycle_alpha.rs b/pallets/subtensor/src/staking/recycle_alpha.rs index 03c6a270eb..4dd362028e 100644 --- a/pallets/subtensor/src/staking/recycle_alpha.rs +++ b/pallets/subtensor/src/staking/recycle_alpha.rs @@ -160,10 +160,17 @@ impl Pallet { Self::do_add_stake(origin.clone(), hotkey.clone(), netuid, amount)? }; - Self::do_burn_alpha(origin, hotkey, alpha, netuid)?; + Self::do_burn_alpha(origin, hotkey.clone(), alpha, netuid)?; Self::set_rate_limited_last_block(&RateLimitKey::SubnetBuyback(netuid), current_block); + Self::deposit_event(Event::SubnetBuyback { + netuid, + hotkey, + amount, + alpha, + }); + Ok(()) } } diff --git a/pallets/subtensor/src/tests/recycle_alpha.rs b/pallets/subtensor/src/tests/recycle_alpha.rs index 6ef21a1460..a49a029805 100644 --- a/pallets/subtensor/src/tests/recycle_alpha.rs +++ b/pallets/subtensor/src/tests/recycle_alpha.rs @@ -671,6 +671,14 @@ fn test_subnet_buyback_success() { RuntimeEvent::SubtensorModule(Event::AlphaBurned(..)) ) })); + + // Verify SubnetBuyback event was emitted + assert!(System::events().iter().any(|e| { + matches!( + &e.event, + RuntimeEvent::SubtensorModule(Event::SubnetBuyback { .. }) + ) + })); }); } From 6ec23e8e7c2586104d4c1ed387de5cbb915fc944 Mon Sep 17 00:00:00 2001 From: Shamil Gadelshin Date: Mon, 2 Feb 2026 14:34:31 +0300 Subject: [PATCH 198/240] Bump spec version --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index dd61c2f99e..4d060a8572 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -241,7 +241,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: 372, + spec_version: 373, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From a72d9f5b5a41e7697d776de02cba81e7dfeda596 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 2 Feb 2026 14:30:50 +0000 Subject: [PATCH 199/240] auto-update benchmark weights --- pallets/subtensor/src/macros/dispatches.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index c4cbc09f21..f46f5da4d0 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2585,8 +2585,8 @@ mod dispatches { #[pallet::call_index(132)] #[pallet::weight(( Weight::from_parts(368_000_000, 8556) - .saturating_add(T::DbWeight::get().reads(26_u64)) - .saturating_add(T::DbWeight::get().writes(16_u64)), + .saturating_add(T::DbWeight::get().reads(28_u64)) + .saturating_add(T::DbWeight::get().writes(17_u64)), DispatchClass::Normal, Pays::Yes ))] From 0729e364de85ea00f2e337f295a1ca54a0b181fc Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 2 Feb 2026 09:58:02 -0500 Subject: [PATCH 200/240] Spec bump --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 3c3c627d7b..4dd1bd745d 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -241,7 +241,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: 372, + spec_version: 375, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 1087711fb9c47fec499c406cb7218db07badd8d1 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 2 Feb 2026 09:59:40 -0500 Subject: [PATCH 201/240] Spec bump --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index dd61c2f99e..82e13597ba 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -241,7 +241,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: 372, + spec_version: 375, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 18035dc78d01e49cd76f570b5256b129fcb289bc Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 2 Feb 2026 10:01:49 -0500 Subject: [PATCH 202/240] Spec bump --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 731d3870ca..12e99c031e 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -241,7 +241,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: 373, + spec_version: 375, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From a68189dd22ff4a77b6c3033f788f0b53dc40779f Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 2 Feb 2026 10:20:58 -0500 Subject: [PATCH 203/240] Balancer documentation --- pallets/swap/src/pallet/balancer.rs | 35 +++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/pallets/swap/src/pallet/balancer.rs b/pallets/swap/src/pallet/balancer.rs index 503d2803b4..97973c2712 100644 --- a/pallets/swap/src/pallet/balancer.rs +++ b/pallets/swap/src/pallet/balancer.rs @@ -1,3 +1,38 @@ +// Balancer swap +// +// Unlike uniswap v2 or v3, it allows adding liquidity unproportionally to price. This is +// achieved by introducing the weights w1 and w2 so that w1 + w2 = 1. In these formulas x +// means base currency (alpha) and y means quote currency (tao). The w1 weight in the code +// below is referred as weight_base, and w2 as weight_quote. Because of the w1 + w2 = 1 +// constraint, only weight_quote is stored, and weight_base is always calculated. +// +// The formulas used for pool operation are following: +// +// Price: p = (w1*y) / (w2*x) +// +// Reserve deltas / (or -1 * payouts) in swaps are computed by: +// +// if ∆x is given (sell) ∆y = y * ((x / (x+∆x))^(w1/w2) - 1) +// if ∆y is given (buy) ∆x = x * ((y / (y+∆y))^(w2/w1) - 1) +// +// When swaps are executing the orders with slippage control, we need to know what amount +// we can swap before the price reaches the limit value of p': +// +// If p' < p (sell): ∆x = x * ((p / p')^w2 - 1) +// If p' < p (buy): ∆y = y * ((p' / p)^w1 - 1) +// +// In order to initialize weights with existing reserve values and price: +// +// w1 = px / (px + y) +// w2 = y / (px + y) +// +// Weights are adjusted when some amounts are added to the reserves. This prevents price +// from changing. +// +// new_w1 = p * (x + ∆x) / (p * (x + ∆x) + y + ∆y) +// new_w2 = (y + ∆y) / (p * (x + ∆x) + y + ∆y) +// + use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::pallet_prelude::*; use safe_bigmath::*; From 2a6e8075f9b38dbbbb39e475c600355a4de896c4 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 2 Feb 2026 17:23:30 -0500 Subject: [PATCH 204/240] More balancer docs --- pallets/swap/src/pallet/balancer.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pallets/swap/src/pallet/balancer.rs b/pallets/swap/src/pallet/balancer.rs index 97973c2712..f624500bce 100644 --- a/pallets/swap/src/pallet/balancer.rs +++ b/pallets/swap/src/pallet/balancer.rs @@ -1,6 +1,6 @@ // Balancer swap // -// Unlike uniswap v2 or v3, it allows adding liquidity unproportionally to price. This is +// Unlike uniswap v2 or v3, it allows adding liquidity disproportionally to price. This is // achieved by introducing the weights w1 and w2 so that w1 + w2 = 1. In these formulas x // means base currency (alpha) and y means quote currency (tao). The w1 weight in the code // below is referred as weight_base, and w2 as weight_quote. Because of the w1 + w2 = 1 @@ -32,6 +32,12 @@ // new_w1 = p * (x + ∆x) / (p * (x + ∆x) + y + ∆y) // new_w2 = (y + ∆y) / (p * (x + ∆x) + y + ∆y) // +// Weights are limited to stay within [0.1, 0.9] range to avoid precision issues in exponentiation. +// Practically, these limitations will not be achieved, but if they are, the swap will not allow injection +// that will push the weights out of this interval because we prefer chain and swap stability over success +// of a single injection. Currently, we only allow the protocol to inject disproportionally to price, and +// the amount of disproportion will not cause weigths to get far from 0.5. +// use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::pallet_prelude::*; From d07405a78b9c8bb84ee8f533e71658d201f8c5fc Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 3 Feb 2026 23:28:33 +0800 Subject: [PATCH 205/240] bump version --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index dd61c2f99e..4d060a8572 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -241,7 +241,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: 372, + spec_version: 373, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From a67c9e7e13e9c26dfa90e76f10218f72e82666e7 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Tue, 3 Feb 2026 15:27:57 -0500 Subject: [PATCH 206/240] Add current_alpha_price_all swap RPC, add slippage to sim_swap_tao_for_alpha and sim_swap_alpha_for_tao --- Cargo.lock | 2 ++ pallets/swap-interface/src/lib.rs | 2 ++ pallets/swap/rpc/src/lib.rs | 16 ++++++++++++++- pallets/swap/runtime-api/Cargo.toml | 2 ++ pallets/swap/runtime-api/src/lib.rs | 14 +++++++++++++- pallets/swap/src/pallet/balancer.rs | 2 +- pallets/swap/src/pallet/impls.rs | 6 ++++++ runtime/Cargo.toml | 2 ++ runtime/src/lib.rs | 30 ++++++++++++++++++++++++++--- 9 files changed, 70 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 64fdf39d95..712c444b16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8423,6 +8423,7 @@ dependencies = [ "polkadot-runtime-common", "precompile-utils", "rand_chacha 0.3.1", + "safe-math", "scale-info", "serde_json", "sha2 0.10.9", @@ -10939,6 +10940,7 @@ dependencies = [ "frame-support", "parity-scale-codec", "scale-info", + "serde", "sp-api", "sp-std", "subtensor-macros", diff --git a/pallets/swap-interface/src/lib.rs b/pallets/swap-interface/src/lib.rs index 0a86c960cf..3fc25230c2 100644 --- a/pallets/swap-interface/src/lib.rs +++ b/pallets/swap-interface/src/lib.rs @@ -2,6 +2,7 @@ use core::ops::Neg; use frame_support::pallet_prelude::*; +use scale_info::prelude::vec::Vec; use substrate_fixed::types::U64F64; use subtensor_macros::freeze_struct; use subtensor_runtime_common::{AlphaCurrency, Currency, NetUid, TaoCurrency}; @@ -52,6 +53,7 @@ pub trait SwapHandler { fn toggle_user_liquidity(netuid: NetUid, enabled: bool); fn clear_protocol_liquidity(netuid: NetUid) -> DispatchResult; fn init_swap(netuid: NetUid, maybe_price: Option); + fn get_all_subnet_netuids() -> Vec; } pub trait DefaultPriceLimit diff --git a/pallets/swap/rpc/src/lib.rs b/pallets/swap/rpc/src/lib.rs index b83a083ad5..a58334dea8 100644 --- a/pallets/swap/rpc/src/lib.rs +++ b/pallets/swap/rpc/src/lib.rs @@ -13,12 +13,14 @@ use sp_blockchain::HeaderBackend; use sp_runtime::traits::Block as BlockT; use subtensor_runtime_common::{AlphaCurrency, NetUid, TaoCurrency}; -pub use pallet_subtensor_swap_runtime_api::SwapRuntimeApi; +pub use pallet_subtensor_swap_runtime_api::{SubnetPrice, SwapRuntimeApi}; #[rpc(client, server)] pub trait SwapRpcApi { #[method(name = "swap_currentAlphaPrice")] fn current_alpha_price(&self, netuid: NetUid, at: Option) -> RpcResult; + #[method(name = "swap_currentAlphaPriceAll")] + fn current_alpha_price_all(&self, at: Option) -> RpcResult>; #[method(name = "swap_simSwapTaoForAlpha")] fn sim_swap_tao_for_alpha( &self, @@ -92,6 +94,18 @@ where }) } + fn current_alpha_price_all( + &self, + at: Option<::Hash>, + ) -> RpcResult> { + let api = self.client.runtime_api(); + let at = at.unwrap_or_else(|| self.client.info().best_hash); + + api.current_alpha_price_all(at).map_err(|e| { + Error::RuntimeError(format!("Unable to get all current alpha prices: {e:?}")).into() + }) + } + fn sim_swap_tao_for_alpha( &self, netuid: NetUid, diff --git a/pallets/swap/runtime-api/Cargo.toml b/pallets/swap/runtime-api/Cargo.toml index 042875fdd0..50f92d19e2 100644 --- a/pallets/swap/runtime-api/Cargo.toml +++ b/pallets/swap/runtime-api/Cargo.toml @@ -7,6 +7,7 @@ edition.workspace = true [dependencies] codec = { workspace = true, features = ["derive"] } frame-support.workspace = true +serde.workspace = true scale-info.workspace = true sp-api.workspace = true sp-std.workspace = true @@ -20,6 +21,7 @@ std = [ "codec/std", "frame-support/std", "scale-info/std", + "serde/std", "sp-api/std", "sp-std/std", "subtensor-runtime-common/std", diff --git a/pallets/swap/runtime-api/src/lib.rs b/pallets/swap/runtime-api/src/lib.rs index 01d2ccf23e..ce61a93dc6 100644 --- a/pallets/swap/runtime-api/src/lib.rs +++ b/pallets/swap/runtime-api/src/lib.rs @@ -1,21 +1,33 @@ #![cfg_attr(not(feature = "std"), no_std)] use frame_support::pallet_prelude::*; +use scale_info::prelude::vec::Vec; +use serde::{Deserialize, Serialize}; use subtensor_macros::freeze_struct; use subtensor_runtime_common::{AlphaCurrency, NetUid, TaoCurrency}; -#[freeze_struct("3a4fd213b5de5eb6")] +#[freeze_struct("ee2ba1ec4ee58ae6")] #[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)] pub struct SimSwapResult { pub tao_amount: TaoCurrency, pub alpha_amount: AlphaCurrency, pub tao_fee: TaoCurrency, pub alpha_fee: AlphaCurrency, + pub tao_slippage: TaoCurrency, + pub alpha_slippage: AlphaCurrency, +} + +#[freeze_struct("d7bbb761fc2b2eac")] +#[derive(Decode, Deserialize, Encode, PartialEq, Eq, Clone, Debug, Serialize, TypeInfo)] +pub struct SubnetPrice { + pub netuid: NetUid, + pub price: u64, } sp_api::decl_runtime_apis! { pub trait SwapRuntimeApi { fn current_alpha_price(netuid: NetUid) -> u64; + fn current_alpha_price_all() -> Vec; fn sim_swap_tao_for_alpha(netuid: NetUid, tao: TaoCurrency) -> SimSwapResult; fn sim_swap_alpha_for_tao(netuid: NetUid, alpha: AlphaCurrency) -> SimSwapResult; } diff --git a/pallets/swap/src/pallet/balancer.rs b/pallets/swap/src/pallet/balancer.rs index f624500bce..14e5623091 100644 --- a/pallets/swap/src/pallet/balancer.rs +++ b/pallets/swap/src/pallet/balancer.rs @@ -34,7 +34,7 @@ // // Weights are limited to stay within [0.1, 0.9] range to avoid precision issues in exponentiation. // Practically, these limitations will not be achieved, but if they are, the swap will not allow injection -// that will push the weights out of this interval because we prefer chain and swap stability over success +// that will push the weights out of this interval because we prefer chain and swap stability over success // of a single injection. Currently, we only allow the protocol to inject disproportionally to price, and // the amount of disproportion will not cause weigths to get far from 0.5. // diff --git a/pallets/swap/src/pallet/impls.rs b/pallets/swap/src/pallet/impls.rs index 1f900a47e9..a27c74987a 100644 --- a/pallets/swap/src/pallet/impls.rs +++ b/pallets/swap/src/pallet/impls.rs @@ -6,6 +6,7 @@ use sp_arithmetic::{ Perquintill, }; use sp_runtime::{DispatchResult, traits::AccountIdConversion}; +use sp_std::vec::Vec; use substrate_fixed::types::U64F64; use subtensor_runtime_common::{ AlphaCurrency, @@ -743,4 +744,9 @@ impl SwapHandler for Pallet { fn init_swap(netuid: NetUid, maybe_price: Option) { Self::maybe_initialize_palswap(netuid, maybe_price).unwrap_or_default(); } + fn get_all_subnet_netuids() -> Vec { + PalSwapInitialized::::iter() + .map(|(netuid, _)| netuid) + .collect() + } } diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index b3aced2160..d965e5b5c7 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -21,6 +21,7 @@ subtensor-custom-rpc-runtime-api.workspace = true smallvec.workspace = true log.workspace = true codec = { workspace = true, features = ["derive"] } +safe-math.workspace = true scale-info = { workspace = true, features = ["derive"] } serde_json = { workspace = true, features = ["alloc"] } pallet-aura = { workspace = true } @@ -198,6 +199,7 @@ std = [ "pallet-preimage/std", "pallet-commitments/std", "precompile-utils/std", + "safe-math/std", "sp-api/std", "sp-block-builder/std", "sp-core/std", diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 4dd1bd745d..15a074bc74 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -43,9 +43,10 @@ use pallet_subtensor::rpc_info::{ }; use pallet_subtensor::{CommitmentsInterface, ProxyInterface}; use pallet_subtensor_proxy as pallet_proxy; -use pallet_subtensor_swap_runtime_api::SimSwapResult; +use pallet_subtensor_swap_runtime_api::{SimSwapResult, SubnetPrice}; use pallet_subtensor_utility as pallet_utility; use runtime_common::prod_or_fast; +use safe_math::FixedExt; use sp_api::impl_runtime_apis; use sp_consensus_aura::sr25519::AuthorityId as AuraId; use sp_consensus_babe::BabeConfiguration; @@ -71,6 +72,7 @@ use sp_std::prelude::*; #[cfg(feature = "std")] use sp_version::NativeVersion; use sp_version::RuntimeVersion; +use substrate_fixed::types::U64F64; use subtensor_precompiles::Precompiles; use subtensor_runtime_common::{AlphaCurrency, TaoCurrency, time::*, *}; use subtensor_swap_interface::{Order, SwapHandler}; @@ -2457,14 +2459,26 @@ impl_runtime_apis! { impl pallet_subtensor_swap_runtime_api::SwapRuntimeApi for Runtime { fn current_alpha_price(netuid: NetUid) -> u64 { - use substrate_fixed::types::U64F64; - pallet_subtensor_swap::Pallet::::current_price(netuid.into()) .saturating_mul(U64F64::from_num(1_000_000_000)) .saturating_to_num() } + fn current_alpha_price_all() -> Vec { + pallet_subtensor::Pallet::::get_all_subnet_netuids() + .into_iter() + .map(|netuid| { + SubnetPrice { + netuid, + price: Self::current_alpha_price(netuid), + } + }) + .collect() + } + fn sim_swap_tao_for_alpha(netuid: NetUid, tao: TaoCurrency) -> SimSwapResult { + let price = pallet_subtensor_swap::Pallet::::current_price(netuid.into()); + let no_slippage_alpha = U64F64::saturating_from_num(u64::from(tao)).safe_div(price).saturating_to_num::(); let order = pallet_subtensor::GetAlphaForTao::::with_amount(tao); pallet_subtensor_swap::Pallet::::sim_swap( netuid.into(), @@ -2476,17 +2490,23 @@ impl_runtime_apis! { alpha_amount: 0.into(), tao_fee: 0.into(), alpha_fee: 0.into(), + tao_slippage: 0.into(), + alpha_slippage: 0.into(), }, |sr| SimSwapResult { tao_amount: sr.amount_paid_in.into(), alpha_amount: sr.amount_paid_out.into(), tao_fee: sr.fee_paid.into(), alpha_fee: 0.into(), + tao_slippage: 0.into(), + alpha_slippage: no_slippage_alpha.saturating_sub(sr.amount_paid_out.into()).into(), }, ) } fn sim_swap_alpha_for_tao(netuid: NetUid, alpha: AlphaCurrency) -> SimSwapResult { + let price = pallet_subtensor_swap::Pallet::::current_price(netuid.into()); + let no_slippage_tao = U64F64::saturating_from_num(u64::from(alpha)).saturating_mul(price).saturating_to_num::(); let order = pallet_subtensor::GetTaoForAlpha::::with_amount(alpha); pallet_subtensor_swap::Pallet::::sim_swap( netuid.into(), @@ -2498,12 +2518,16 @@ impl_runtime_apis! { alpha_amount: 0.into(), tao_fee: 0.into(), alpha_fee: 0.into(), + tao_slippage: 0.into(), + alpha_slippage: 0.into(), }, |sr| SimSwapResult { tao_amount: sr.amount_paid_out.into(), alpha_amount: sr.amount_paid_in.into(), tao_fee: 0.into(), alpha_fee: sr.fee_paid.into(), + tao_slippage: no_slippage_tao.saturating_sub(sr.amount_paid_out.into()).into(), + alpha_slippage: 0.into(), }, ) } From 119d64b36f3c35831d0c76c158c3be89051ffd30 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Tue, 3 Feb 2026 15:39:39 -0500 Subject: [PATCH 207/240] Fix try-runtime build --- pallets/admin-utils/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index aed8109d22..8d1d735052 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -2195,7 +2195,7 @@ pub mod pallet { } /// Sets the global maximum number of mechanisms in a subnet - #[pallet::call_index(86)] + #[pallet::call_index(88)] #[pallet::weight(Weight::from_parts(15_000_000, 0) .saturating_add(::DbWeight::get().reads(1_u64)) .saturating_add(::DbWeight::get().writes(1_u64)))] From f1b090d006921c4d8841b91a045391d6ed435803 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Tue, 3 Feb 2026 17:42:10 -0500 Subject: [PATCH 208/240] Remove unused user liquidity code --- pallets/subtensor/src/coinbase/root.rs | 1 - pallets/subtensor/src/rpc_info/subnet_info.rs | 3 +- pallets/subtensor/src/staking/helpers.rs | 4 - pallets/subtensor/src/staking/stake_utils.rs | 4 - pallets/subtensor/src/tests/coinbase.rs | 44 - pallets/subtensor/src/tests/networks.rs | 449 ---- pallets/subtensor/src/tests/staking.rs | 196 -- pallets/subtensor/src/tests/subnet.rs | 57 - pallets/swap-interface/src/lib.rs | 6 - pallets/swap/src/benchmarking.rs | 86 - pallets/swap/src/lib.rs | 1 - pallets/swap/src/mock.rs | 8 - pallets/swap/src/pallet/impls.rs | 323 +-- .../migrations/migrate_swapv3_to_balancer.rs | 1 + pallets/swap/src/pallet/mod.rs | 408 +--- pallets/swap/src/pallet/tests.rs | 1822 +---------------- pallets/swap/src/position.rs | 100 - pallets/swap/src/weights.rs | 56 - 18 files changed, 7 insertions(+), 3562 deletions(-) delete mode 100644 pallets/swap/src/position.rs diff --git a/pallets/subtensor/src/coinbase/root.rs b/pallets/subtensor/src/coinbase/root.rs index 287a94e899..ba7d54ed3c 100644 --- a/pallets/subtensor/src/coinbase/root.rs +++ b/pallets/subtensor/src/coinbase/root.rs @@ -213,7 +213,6 @@ impl Pallet { Self::finalize_all_subnet_root_dividends(netuid); // --- Perform the cleanup before removing the network. - T::SwapInterface::dissolve_all_liquidity_providers(netuid)?; Self::destroy_alpha_in_out_stakes(netuid)?; T::SwapInterface::clear_protocol_liquidity(netuid)?; T::CommitmentsInterface::purge_netuid(netuid); diff --git a/pallets/subtensor/src/rpc_info/subnet_info.rs b/pallets/subtensor/src/rpc_info/subnet_info.rs index 6a7966b4fb..7169561d30 100644 --- a/pallets/subtensor/src/rpc_info/subnet_info.rs +++ b/pallets/subtensor/src/rpc_info/subnet_info.rs @@ -365,7 +365,6 @@ impl Pallet { let subnet_token_enabled = Self::get_subtoken_enabled(netuid); let transfers_enabled = Self::get_transfer_toggle(netuid); let bonds_reset = Self::get_bonds_reset(netuid); - let user_liquidity_enabled: bool = Self::is_user_liquidity_enabled(netuid); Some(SubnetHyperparamsV2 { rho: rho.into(), @@ -400,7 +399,7 @@ impl Pallet { subnet_is_active: subnet_token_enabled, transfers_enabled, bonds_reset_enabled: bonds_reset, - user_liquidity_enabled, + user_liquidity_enabled: false, }) } diff --git a/pallets/subtensor/src/staking/helpers.rs b/pallets/subtensor/src/staking/helpers.rs index 0c53c267b0..998d4f0086 100644 --- a/pallets/subtensor/src/staking/helpers.rs +++ b/pallets/subtensor/src/staking/helpers.rs @@ -350,10 +350,6 @@ impl Pallet { 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: AlphaCurrency) { // TODO: record recycled alpha in a tracker SubnetAlphaOut::::mutate(netuid, |total| { diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index 95559e8cf7..f7528935b4 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -21,10 +21,6 @@ impl Pallet { SubnetAlphaIn::::get(netuid).saturating_add(SubnetAlphaOut::::get(netuid)) } - pub fn get_protocol_tao(netuid: NetUid) -> TaoCurrency { - T::SwapInterface::get_protocol_tao(netuid) - } - pub fn get_moving_alpha_price(netuid: NetUid) -> U96F32 { let one = U96F32::saturating_from_num(1.0); if netuid.is_root() { diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 091d0563aa..65e542f83d 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -12,7 +12,6 @@ use crate::*; use alloc::collections::BTreeMap; use approx::assert_abs_diff_eq; use frame_support::assert_ok; -use pallet_subtensor_swap::position::PositionId; use sp_core::U256; use substrate_fixed::{ transcendental::sqrt, @@ -2693,49 +2692,6 @@ fn test_run_coinbase_not_started_start_after() { }); } -// TODO: Revise when user liquidity is available -// Test that coinbase updates protocol position liquidity -// cargo test --package pallet-subtensor --lib -- tests::coinbase::test_coinbase_v3_liquidity_update --exact --show-output -// #[test] -// fn test_coinbase_v3_liquidity_update() { -// new_test_ext(1).execute_with(|| { -// let owner_hotkey = U256::from(1); -// let owner_coldkey = U256::from(2); - -// // add network -// let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); - -// // Force the swap to initialize -// ::SwapInterface::init_swap(netuid); - -// let protocol_account_id = pallet_subtensor_swap::Pallet::::protocol_account_id(); -// let position = pallet_subtensor_swap::Positions::::get(( -// netuid, -// protocol_account_id, -// PositionId::from(1), -// )) -// .unwrap(); -// let liquidity_before = position.liquidity; - -// // Enable emissions and run coinbase (which will increase position liquidity) -// let emission: u64 = 1_234_567; -// // Set the TAO flow to non-zero -// SubnetTaoFlow::::insert(netuid, 8348383_i64); -// FirstEmissionBlockNumber::::insert(netuid, 0); -// SubtensorModule::run_coinbase(U96F32::from_num(emission)); - -// let position_after = pallet_subtensor_swap::Positions::::get(( -// netuid, -// protocol_account_id, -// PositionId::from(1), -// )) -// .unwrap(); -// let liquidity_after = position_after.liquidity; - -// assert!(liquidity_before < liquidity_after); -// }); -// } - // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_drain_alpha_childkey_parentkey_with_burn --exact --show-output --nocapture #[test] fn test_drain_alpha_childkey_parentkey_with_burn() { diff --git a/pallets/subtensor/src/tests/networks.rs b/pallets/subtensor/src/tests/networks.rs index 572acd9930..5176ae05dc 100644 --- a/pallets/subtensor/src/tests/networks.rs +++ b/pallets/subtensor/src/tests/networks.rs @@ -1779,455 +1779,6 @@ fn test_tempo_greater_than_weight_set_rate_limit() { }) } -// TODO: Revise when user liquidity is available -// #[allow(clippy::indexing_slicing)] -// #[test] -// fn massive_dissolve_refund_and_reregistration_flow_is_lossless_and_cleans_state() { -// new_test_ext(0).execute_with(|| { -// // ──────────────────────────────────────────────────────────────────── -// // 0) Constants and helpers (distinct hotkeys & coldkeys) -// // ──────────────────────────────────────────────────────────────────── -// const NUM_NETS: usize = 4; - -// // Six LP coldkeys -// let cold_lps: [U256; 6] = [ -// U256::from(3001), -// U256::from(3002), -// U256::from(3003), -// U256::from(3004), -// U256::from(3005), -// U256::from(3006), -// ]; - -// // For each coldkey, define two DISTINCT hotkeys it owns. -// let mut cold_to_hots: BTreeMap = BTreeMap::new(); -// for &c in cold_lps.iter() { -// let h1 = U256::from(c.low_u64().saturating_add(100_000)); -// let h2 = U256::from(c.low_u64().saturating_add(200_000)); -// cold_to_hots.insert(c, [h1, h2]); -// } - -// // Distinct τ pot sizes per net. -// let pots: [u64; NUM_NETS] = [12_345, 23_456, 34_567, 45_678]; - -// let lp_sets_per_net: [&[U256]; NUM_NETS] = [ -// &cold_lps[0..4], // net0: A,B,C,D -// &cold_lps[2..6], // net1: C,D,E,F -// &cold_lps[0..6], // net2: A..F -// &cold_lps[1..5], // net3: B,C,D,E -// ]; - -// // Multiple bands/sizes → many positions per cold across nets, using mixed hotkeys. -// let bands: [i32; 3] = [5, 13, 30]; -// let liqs: [u64; 3] = [400_000, 700_000, 1_100_000]; - -// // Helper: add a V3 position via a (hot, cold) pair. -// let add_pos = |net: NetUid, hot: U256, cold: U256, band: i32, liq: u64| { -// let ct = pallet_subtensor_swap::CurrentTick::::get(net); -// let lo = ct.saturating_sub(band); -// let hi = ct.saturating_add(band); -// pallet_subtensor_swap::EnabledUserLiquidity::::insert(net, true); -// assert_ok!(pallet_subtensor_swap::Pallet::::add_liquidity( -// RuntimeOrigin::signed(cold), -// hot, -// net, -// lo, -// hi, -// liq -// )); -// }; - -// // ──────────────────────────────────────────────────────────────────── -// // 1) Create many subnets, enable V3, fix price at tick=0 (sqrt≈1) -// // ──────────────────────────────────────────────────────────────────── -// let mut nets: Vec = Vec::new(); -// for i in 0..NUM_NETS { -// let owner_hot = U256::from(10_000 + (i as u64)); -// let owner_cold = U256::from(20_000 + (i as u64)); -// let net = add_dynamic_network(&owner_hot, &owner_cold); -// SubtensorModule::set_max_registrations_per_block(net, 1_000u16); -// SubtensorModule::set_target_registrations_per_interval(net, 1_000u16); -// Emission::::insert(net, Vec::::new()); -// SubtensorModule::set_subnet_locked_balance(net, TaoCurrency::from(0)); - -// assert_ok!( -// pallet_subtensor_swap::Pallet::::toggle_user_liquidity( -// RuntimeOrigin::root(), -// net, -// true -// ) -// ); - -// // Price/tick pinned so LP math stays stable (sqrt(1)). -// let ct0 = pallet_subtensor_swap::tick::TickIndex::new_unchecked(0); -// let sqrt1 = ct0.try_to_sqrt_price().expect("sqrt(1) price"); -// pallet_subtensor_swap::CurrentTick::::set(net, ct0); -// pallet_subtensor_swap::AlphaSqrtPrice::::set(net, sqrt1); - -// nets.push(net); -// } - -// // Map net → index for quick lookups. -// let mut net_index: BTreeMap = BTreeMap::new(); -// for (i, &n) in nets.iter().enumerate() { -// net_index.insert(n, i); -// } - -// // ──────────────────────────────────────────────────────────────────── -// // 2) Pre-create a handful of small (hot, cold) pairs so accounts exist -// // ──────────────────────────────────────────────────────────────────── -// for id in 0u64..10 { -// let cold_acc = U256::from(1_000_000 + id); -// let hot_acc = U256::from(2_000_000 + id); -// for &net in nets.iter() { -// register_ok_neuron(net, hot_acc, cold_acc, 100_000 + id); -// } -// } - -// // ──────────────────────────────────────────────────────────────────── -// // 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); -// } - -// // τ balances before LP adds (after staking): -// let mut tao_before: BTreeMap = BTreeMap::new(); - -// // Ordered α snapshot per net at **pair granularity** (pre‑LP): -// let mut alpha_pairs_per_net: BTreeMap> = BTreeMap::new(); - -// // Register both hotkeys for each participating cold on each net and stake τ→α. -// for (ni, &net) in nets.iter().enumerate() { -// let participants = lp_sets_per_net[ni]; -// for &cold in participants.iter() { -// let [hot1, hot2] = cold_to_hots[&cold]; - -// // Ensure (hot, cold) neurons exist on this net. -// register_ok_neuron( -// net, -// hot1, -// cold, -// (ni as u64) * 10_000 + (hot1.low_u64() % 10_000), -// ); -// register_ok_neuron( -// net, -// hot2, -// cold, -// (ni as u64) * 10_000 + (hot2.low_u64() % 10_000) + 1, -// ); - -// // Stake τ (split across the two hotkeys). -// let base: u64 = -// 5_000_000 + ((ni as u64) * 1_000_000) + ((cold.low_u64() % 10) * 250_000); -// let stake1: u64 = base.saturating_mul(3) / 5; // 60% -// let stake2: u64 = base.saturating_sub(stake1); // 40% - -// assert_ok!(SubtensorModule::do_add_stake( -// RuntimeOrigin::signed(cold), -// hot1, -// net, -// stake1.into() -// )); -// assert_ok!(SubtensorModule::do_add_stake( -// RuntimeOrigin::signed(cold), -// hot2, -// net, -// stake2.into() -// )); -// } -// } - -// // Record τ balances now (post‑stake, pre‑LP). -// for &cold in cold_lps.iter() { -// tao_before.insert(cold, SubtensorModule::get_coldkey_balance(&cold)); -// } -// // Capture **pair‑level** α snapshot per net (pre‑LP). -// for ((hot, cold, net), amt) in Alpha::::iter() { -// if let Some(&ni) = net_index.get(&net) -// && lp_sets_per_net[ni].contains(&cold) { -// let a: u128 = amt.saturating_to_num(); -// if a > 0 { -// alpha_pairs_per_net -// .entry(net) -// .or_default() -// .push(((hot, cold), a)); -// } -// } -// } - -// // ──────────────────────────────────────────────────────────────────── -// // 4) Add many V3 positions per cold across nets, alternating hotkeys -// // ──────────────────────────────────────────────────────────────────── -// for (ni, &net) in nets.iter().enumerate() { -// let participants = lp_sets_per_net[ni]; -// for (pi, &cold) in participants.iter().enumerate() { -// let [hot1, hot2] = cold_to_hots[&cold]; -// let hots = [hot1, hot2]; -// for k in 0..3 { -// let band = bands[(pi + k) % bands.len()]; -// let liq = liqs[(ni + k) % liqs.len()]; -// let hot = hots[k % hots.len()]; -// add_pos(net, hot, cold, band, liq); -// } -// } -// } - -// // Snapshot τ balances AFTER LP adds (to measure actual principal debit). -// let mut tao_after_adds: BTreeMap = BTreeMap::new(); -// for &cold in cold_lps.iter() { -// tao_after_adds.insert(cold, SubtensorModule::get_coldkey_balance(&cold)); -// } - -// // ──────────────────────────────────────────────────────────────────── -// // 5) Compute Hamilton-apportionment BASE shares per cold and total leftover -// // from the **pair-level** pre‑LP α snapshot; also count pairs per cold. -// // ──────────────────────────────────────────────────────────────────── -// let mut base_share_cold: BTreeMap = -// cold_lps.iter().copied().map(|c| (c, 0_u64)).collect(); -// let mut pair_count_cold: BTreeMap = -// cold_lps.iter().copied().map(|c| (c, 0_u32)).collect(); - -// let mut leftover_total: u64 = 0; - -// for (ni, &net) in nets.iter().enumerate() { -// let pot = pots[ni]; -// let pairs = alpha_pairs_per_net.get(&net).cloned().unwrap_or_default(); -// if pot == 0 || pairs.is_empty() { -// continue; -// } -// let total_alpha: u128 = pairs.iter().map(|(_, a)| *a).sum(); -// if total_alpha == 0 { -// continue; -// } - -// let mut base_sum_net: u64 = 0; -// for ((_, cold), a) in pairs.iter().copied() { -// // quota = a * pot / total_alpha -// let prod: u128 = a.saturating_mul(pot as u128); -// let base: u64 = (prod / total_alpha) as u64; -// base_sum_net = base_sum_net.saturating_add(base); -// *base_share_cold.entry(cold).or_default() = -// base_share_cold[&cold].saturating_add(base); -// *pair_count_cold.entry(cold).or_default() += 1; -// } -// let leftover_net = pot.saturating_sub(base_sum_net); -// leftover_total = leftover_total.saturating_add(leftover_net); -// } - -// // ──────────────────────────────────────────────────────────────────── -// // 6) Seed τ pots and dissolve *all* networks (liquidates LPs + refunds) -// // ──────────────────────────────────────────────────────────────────── -// for (ni, &net) in nets.iter().enumerate() { -// SubnetTAO::::insert(net, TaoCurrency::from(pots[ni])); -// } -// for &net in nets.iter() { -// assert_ok!(SubtensorModule::do_dissolve_network(net)); -// } - -// // ──────────────────────────────────────────────────────────────────── -// // 7) Assertions: τ balances, α gone, nets removed, swap state clean -// // (Hamilton invariants enforced at cold-level without relying on tie-break) -// // ──────────────────────────────────────────────────────────────────── -// // Collect actual pot credits per cold (principal cancels out against adds when comparing before→after). -// let mut actual_pot_cold: BTreeMap = -// cold_lps.iter().copied().map(|c| (c, 0_u64)).collect(); -// for &cold in cold_lps.iter() { -// let before = tao_before[&cold]; -// let after = SubtensorModule::get_coldkey_balance(&cold); -// actual_pot_cold.insert(cold, after.saturating_sub(before)); -// } - -// // (a) Sum of actual pot credits equals total pots. -// let total_actual: u64 = actual_pot_cold.values().copied().sum(); -// let total_pots: u64 = pots.iter().copied().sum(); -// assert_eq!( -// total_actual, total_pots, -// "total τ pot credited across colds must equal sum of pots" -// ); - -// // (b) Each cold’s pot is within Hamilton bounds: base ≤ actual ≤ base + #pairs. -// let mut extra_accum: u64 = 0; -// for &cold in cold_lps.iter() { -// let base = *base_share_cold.get(&cold).unwrap_or(&0); -// let pairs = *pair_count_cold.get(&cold).unwrap_or(&0) as u64; -// let actual = *actual_pot_cold.get(&cold).unwrap_or(&0); - -// assert!( -// actual >= base, -// "cold {cold:?} actual pot {actual} is below base {base}" -// ); -// assert!( -// actual <= base.saturating_add(pairs), -// "cold {cold:?} actual pot {actual} exceeds base + pairs ({base} + {pairs})" -// ); - -// extra_accum = extra_accum.saturating_add(actual.saturating_sub(base)); -// } - -// // (c) The total “extra beyond base” equals the computed leftover_total across nets. -// assert_eq!( -// extra_accum, leftover_total, -// "sum of extras beyond base must equal total leftover" -// ); - -// // (d) τ principal was fully refunded (compare after_adds → after). -// for &cold in cold_lps.iter() { -// let before = tao_before[&cold]; -// let mid = tao_after_adds[&cold]; -// let after = SubtensorModule::get_coldkey_balance(&cold); -// let principal_actual = before.saturating_sub(mid); -// let actual_pot = after.saturating_sub(before); -// assert_eq!( -// after.saturating_sub(mid), -// principal_actual.saturating_add(actual_pot), -// "cold {cold:?} τ balance incorrect vs 'after_adds'" -// ); -// } - -// // For each dissolved net, check α ledgers gone, network removed, and swap state clean. -// for &net in nets.iter() { -// assert!( -// Alpha::::iter().all(|((_h, _c, n), _)| n != net), -// "alpha ledger not fully cleared for net {net:?}" -// ); -// assert!( -// !SubtensorModule::if_subnet_exist(net), -// "subnet {net:?} still exists" -// ); -// assert!( -// pallet_subtensor_swap::Ticks::::iter_prefix(net) -// .next() -// .is_none(), -// "ticks not cleared for net {net:?}" -// ); -// assert!( -// !pallet_subtensor_swap::Positions::::iter() -// .any(|((n, _owner, _pid), _)| n == net), -// "swap positions not fully cleared for net {net:?}" -// ); -// assert_eq!( -// pallet_subtensor_swap::FeeGlobalTao::::get(net).saturating_to_num::(), -// 0, -// "FeeGlobalTao nonzero for net {net:?}" -// ); -// assert_eq!( -// pallet_subtensor_swap::FeeGlobalAlpha::::get(net).saturating_to_num::(), -// 0, -// "FeeGlobalAlpha nonzero for net {net:?}" -// ); -// assert_eq!( -// pallet_subtensor_swap::CurrentLiquidity::::get(net), -// 0, -// "CurrentLiquidity not zero for net {net:?}" -// ); -// assert!( -// !pallet_subtensor_swap::PalSwapInitialized::::get(net), -// "PalSwapInitialized still set" -// ); -// assert!( -// !pallet_subtensor_swap::EnabledUserLiquidity::::get(net), -// "EnabledUserLiquidity still set" -// ); -// assert!( -// pallet_subtensor_swap::TickIndexBitmapWords::::iter_prefix((net,)) -// .next() -// .is_none(), -// "TickIndexBitmapWords not cleared for net {net:?}" -// ); -// } - -// // ──────────────────────────────────────────────────────────────────── -// // 8) Re-register a fresh subnet and re‑stake using the pallet’s min rule -// // Assert αΔ equals the sim-swap result for the exact τ staked. -// // ──────────────────────────────────────────────────────────────────── -// let new_owner_hot = U256::from(99_000); -// let new_owner_cold = U256::from(99_001); -// let net_new = add_dynamic_network(&new_owner_hot, &new_owner_cold); -// SubtensorModule::set_max_registrations_per_block(net_new, 1_000u16); -// SubtensorModule::set_target_registrations_per_interval(net_new, 1_000u16); -// Emission::::insert(net_new, Vec::::new()); -// SubtensorModule::set_subnet_locked_balance(net_new, TaoCurrency::from(0)); - -// assert_ok!( -// pallet_subtensor_swap::Pallet::::toggle_user_liquidity( -// RuntimeOrigin::root(), -// net_new, -// true -// ) -// ); -// let ct0 = pallet_subtensor_swap::tick::TickIndex::new_unchecked(0); -// let sqrt1 = ct0.try_to_sqrt_price().expect("sqrt(1)"); -// pallet_subtensor_swap::CurrentTick::::set(net_new, ct0); -// pallet_subtensor_swap::AlphaSqrtPrice::::set(net_new, sqrt1); - -// // Compute the exact min stake per the pallet rule: DefaultMinStake + fee(DefaultMinStake). -// let min_stake = DefaultMinStake::::get(); -// let order = GetAlphaForTao::::with_amount(min_stake); -// let fee_for_min = pallet_subtensor_swap::Pallet::::sim_swap( -// net_new, -// order, -// ) -// .map(|r| r.fee_paid) -// .unwrap_or_else(|_e| { -// as subtensor_swap_interface::SwapHandler>::approx_fee_amount(net_new, min_stake) -// }); -// let min_amount_required = min_stake.saturating_add(fee_for_min).to_u64(); - -// // Re‑stake from three coldkeys; choose a specific DISTINCT hotkey per cold. -// for &cold in &cold_lps[0..3] { -// let [hot1, _hot2] = cold_to_hots[&cold]; -// register_ok_neuron(net_new, hot1, cold, 7777); - -// let before_tao = SubtensorModule::get_coldkey_balance(&cold); -// let a_prev: u64 = Alpha::::get((hot1, cold, net_new)).saturating_to_num(); - -// // Expected α for this exact τ, using the same sim path as the pallet. -// let order = GetAlphaForTao::::with_amount(min_amount_required); -// let expected_alpha_out = pallet_subtensor_swap::Pallet::::sim_swap( -// net_new, -// order, -// ) -// .map(|r| r.amount_paid_out) -// .expect("sim_swap must succeed for fresh net and min amount"); - -// assert_ok!(SubtensorModule::do_add_stake( -// RuntimeOrigin::signed(cold), -// hot1, -// net_new, -// min_amount_required.into() -// )); - -// let after_tao = SubtensorModule::get_coldkey_balance(&cold); -// let a_new: u64 = Alpha::::get((hot1, cold, net_new)).saturating_to_num(); -// let a_delta = a_new.saturating_sub(a_prev); - -// // τ decreased by exactly the amount we sent. -// assert_eq!( -// after_tao, -// before_tao.saturating_sub(min_amount_required), -// "τ did not decrease by the min required restake amount for cold {cold:?}" -// ); - -// // α minted equals the simulated swap’s net out for that same τ. -// assert_eq!( -// a_delta, expected_alpha_out.to_u64(), -// "α minted mismatch for cold {cold:?} (hot {hot1:?}) on new net (αΔ {a_delta}, expected {expected_alpha_out})" -// ); -// } - -// // Ensure V3 still functional on new net: add a small position for the first cold using its hot1 -// let who_cold = cold_lps[0]; -// let [who_hot, _] = cold_to_hots[&who_cold]; -// add_pos(net_new, who_hot, who_cold, 8, 123_456); -// assert!( -// pallet_subtensor_swap::Positions::::iter() -// .any(|((n, owner, _pid), _)| n == net_new && owner == who_cold), -// "new position not recorded on the re-registered net" -// ); -// }); -// } - #[test] fn dissolve_clears_all_mechanism_scoped_maps_for_all_mechanisms() { new_test_ext(0).execute_with(|| { diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index 4437eaf699..0dfd687959 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -6,7 +6,6 @@ use frame_support::dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo, Pays use frame_support::sp_runtime::DispatchError; use frame_support::{assert_err, assert_noop, assert_ok, traits::Currency}; use frame_system::RawOrigin; -// use pallet_subtensor_swap::Call as SwapCall; use safe_math::FixedExt; use sp_core::{Get, H256, U256}; // use sp_runtime::traits::Dispatchable; @@ -4841,8 +4840,6 @@ fn test_unstake_full_amount() { /// Test correctness of swap fees: /// 1. TAO is not minted or burned /// 2. Fees match FeeRate -/// -/// TODO: Revise when user liquidity is available #[test] fn test_swap_fees_tao_correctness() { new_test_ext(1).execute_with(|| { @@ -4859,7 +4856,6 @@ fn test_swap_fees_tao_correctness() { SubtensorModule::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; - 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 = TaoCurrency::from(100_000_000_000); @@ -4879,7 +4875,6 @@ fn test_swap_fees_tao_correctness() { )); let mut fees = (fee_rate * amount as f64) as u64; - // TODO: Revise when user liquidity is available // Add owner coldkey Alpha as concentrated liquidity // between current price current price + 0.01 let current_price = @@ -4887,18 +4882,6 @@ fn test_swap_fees_tao_correctness() { .to_num::() + 0.0001; let limit_price = current_price + 0.01; - // let tick_low = price_to_tick(current_price); - // let tick_high = price_to_tick(limit_price); - // let liquidity = amount; - - // assert_ok!(::SwapInterface::do_add_liquidity( - // netuid.into(), - // &owner_coldkey, - // &owner_hotkey, - // tick_low, - // tick_high, - // liquidity, - // )); // Limit-buy and then sell all alpha for user to hit owner liquidity assert_ok!(SubtensorModule::add_stake_limit( @@ -5178,181 +5161,6 @@ fn test_default_min_stake_sufficiency() { }); } -// TODO: Revise when user liquidity is availble -// 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); -// SubtensorModule::add_balance_to_coldkey_account(&coldkey, amount * 100); -// 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 = TaoCurrency::from(100_000_000_000); -// let alpha_in = AlphaCurrency::from(400_000_000_000); -// mock::setup_reserves(netuid, tao_reserve, alpha_in); - -// // 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 -// let owner_tao_before = SubtensorModule::get_coldkey_balance(&owner_coldkey); -// let owner_alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( -// &owner_hotkey, -// &owner_coldkey, -// netuid, -// ); - -// // 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 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 > owner_tao_before); -// assert!(owner_alpha_after_add > owner_alpha_before); // always greater because of claimed fees - -// // 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); -// } -// }); -// }); -// } - -// fn setup_positions(netuid: NetUid) { -// for (coldkey, hotkey, low_price, high_price, liquidity) in [ -// (2, 12, 0.1, 0.20, 1_000_000_000_000_u64), -// (3, 13, 0.15, 0.25, 200_000_000_000_u64), -// (4, 14, 0.25, 0.5, 3_000_000_000_000_u64), -// (5, 15, 0.3, 0.6, 300_000_000_000_u64), -// (6, 16, 0.4, 0.7, 8_000_000_000_000_u64), -// (7, 17, 0.5, 0.8, 600_000_000_000_u64), -// (8, 18, 0.6, 0.9, 700_000_000_000_u64), -// (9, 19, 0.7, 1.0, 100_000_000_000_u64), -// (10, 20, 0.8, 1.1, 300_000_000_000_u64), -// ] { -// SubtensorModule::create_account_if_non_existent(&U256::from(coldkey), &U256::from(hotkey)); -// SubtensorModule::add_balance_to_coldkey_account( -// &U256::from(coldkey), -// 1_000_000_000_000_000, -// ); -// SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( -// &U256::from(hotkey), -// &U256::from(coldkey), -// netuid.into(), -// 1_000_000_000_000_000.into(), -// ); - -// let tick_low = price_to_tick(low_price); -// let tick_high = price_to_tick(high_price); -// let add_lq_call = SwapCall::::add_liquidity { -// hotkey: U256::from(hotkey), -// netuid: netuid.into(), -// tick_low, -// tick_high, -// liquidity, -// }; -// assert_ok!( -// RuntimeCall::Swap(add_lq_call).dispatch(RuntimeOrigin::signed(U256::from(coldkey))) -// ); -// } -// } - #[test] fn test_large_swap() { new_test_ext(1).execute_with(|| { @@ -5363,7 +5171,6 @@ fn test_large_swap() { // add network let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_000); - pallet_subtensor_swap::EnabledUserLiquidity::::insert(NetUid::from(netuid), true); let tao = TaoCurrency::from(100_000_000u64); let alpha = AlphaCurrency::from(1_000_000_000_000_000_u64); SubnetTAO::::insert(netuid, tao); @@ -5372,9 +5179,6 @@ fn test_large_swap() { // Force the swap to initialize ::SwapInterface::init_swap(netuid, None); - // TODO: Revise when user liquidity is available - // setup_positions(netuid.into()); - let swap_amount = TaoCurrency::from(100_000_000_000_000); assert_ok!(SubtensorModule::add_stake( RuntimeOrigin::signed(coldkey), diff --git a/pallets/subtensor/src/tests/subnet.rs b/pallets/subtensor/src/tests/subnet.rs index a547b30a14..4eb13fe5fb 100644 --- a/pallets/subtensor/src/tests/subnet.rs +++ b/pallets/subtensor/src/tests/subnet.rs @@ -714,63 +714,6 @@ fn test_subtoken_enable_ok_for_burn_register_before_enable() { }); } -// #[test] -// fn test_user_liquidity_access_control() { -// new_test_ext(1).execute_with(|| { -// let owner_hotkey = U256::from(1); -// let owner_coldkey = U256::from(2); -// let not_owner = U256::from(999); // arbitrary non-owner - -// // add network -// let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); - -// // Not owner, not root: should fail -// assert_noop!( -// Swap::toggle_user_liquidity(RuntimeOrigin::signed(not_owner), netuid, true), -// DispatchError::BadOrigin -// ); - -// // Subnet owner can enable -// assert_ok!(Swap::toggle_user_liquidity( -// RuntimeOrigin::signed(owner_coldkey), -// netuid, -// true -// )); -// assert!(pallet_subtensor_swap::EnabledUserLiquidity::::get( -// NetUid::from(netuid) -// )); - -// // Root can disable -// assert_ok!(Swap::toggle_user_liquidity( -// RuntimeOrigin::root(), -// netuid, -// false -// )); -// assert!(!pallet_subtensor_swap::EnabledUserLiquidity::::get( -// NetUid::from(netuid) -// )); - -// // Root can enable again -// assert_ok!(Swap::toggle_user_liquidity( -// RuntimeOrigin::root(), -// netuid, -// true -// )); -// assert!(pallet_subtensor_swap::EnabledUserLiquidity::::get( -// NetUid::from(netuid) -// )); - -// // Subnet owner cannot disable (only root can disable) -// assert_noop!( -// Swap::toggle_user_liquidity(RuntimeOrigin::signed(owner_coldkey), netuid, false), -// DispatchError::BadOrigin -// ); -// assert!(pallet_subtensor_swap::EnabledUserLiquidity::::get( -// NetUid::from(netuid) -// )); -// }); -// } - // cargo test --package pallet-subtensor --lib -- tests::subnet::test_no_duplicates_in_symbol_static --exact --show-output #[test] fn test_no_duplicates_in_symbol_static() { diff --git a/pallets/swap-interface/src/lib.rs b/pallets/swap-interface/src/lib.rs index 3fc25230c2..10804bd2e4 100644 --- a/pallets/swap-interface/src/lib.rs +++ b/pallets/swap-interface/src/lib.rs @@ -2,7 +2,6 @@ use core::ops::Neg; use frame_support::pallet_prelude::*; -use scale_info::prelude::vec::Vec; use substrate_fixed::types::U64F64; use subtensor_macros::freeze_struct; use subtensor_runtime_common::{AlphaCurrency, Currency, NetUid, TaoCurrency}; @@ -40,7 +39,6 @@ pub trait SwapHandler { fn approx_fee_amount(netuid: NetUid, amount: T) -> T; fn current_alpha_price(netuid: NetUid) -> U64F64; - fn get_protocol_tao(netuid: NetUid) -> TaoCurrency; fn max_price() -> C; fn min_price() -> C; fn adjust_protocol_liquidity( @@ -48,12 +46,8 @@ pub trait SwapHandler { tao_delta: TaoCurrency, alpha_delta: AlphaCurrency, ) -> (TaoCurrency, AlphaCurrency); - fn is_user_liquidity_enabled(netuid: NetUid) -> bool; - fn dissolve_all_liquidity_providers(netuid: NetUid) -> DispatchResult; - fn toggle_user_liquidity(netuid: NetUid, enabled: bool); fn clear_protocol_liquidity(netuid: NetUid) -> DispatchResult; fn init_swap(netuid: NetUid, maybe_price: Option); - fn get_all_subnet_netuids() -> Vec; } pub trait DefaultPriceLimit diff --git a/pallets/swap/src/benchmarking.rs b/pallets/swap/src/benchmarking.rs index 4ad898a8f3..c4a6afa87e 100644 --- a/pallets/swap/src/benchmarking.rs +++ b/pallets/swap/src/benchmarking.rs @@ -26,91 +26,5 @@ mod benchmarks { set_fee_rate(RawOrigin::Root, netuid, rate); } - // TODO: Revise when user liquidity is available - // #[benchmark] - // fn add_liquidity() { - // let netuid = NetUid::from(1); - - // init_swap::(netuid); - - // let caller: T::AccountId = whitelisted_caller(); - // let hotkey: T::AccountId = account("hotkey", 0, 0); - - // #[extrinsic_call] - // add_liquidity(RawOrigin::Signed(caller), hotkey, netuid, 1000); - // } - - // TODO: Revise when user liquidity is available - // #[benchmark] - // fn remove_liquidity() { - // let netuid = NetUid::from(1); - - // init_swap::(netuid); - - // let caller: T::AccountId = whitelisted_caller(); - // let hotkey: T::AccountId = account("hotkey", 0, 0); - // let id = PositionId::from(1u128); - - // PositionsV2::::insert( - // (netuid, caller.clone(), id), - // Position { - // id, - // netuid, - // // liquidity: 1000, - // // fees_tao: I64F64::from_num(0), - // // fees_alpha: I64F64::from_num(0), - // _phantom: PhantomData, - // }, - // ); - - // #[extrinsic_call] - // remove_liquidity(RawOrigin::Signed(caller), hotkey, netuid.into(), id.into()); - // } - - // TODO: Revise when user liquidity is available - // #[benchmark] - // fn modify_position() { - // let netuid = NetUid::from(1); - - // init_swap::(netuid); - - // let caller: T::AccountId = whitelisted_caller(); - // let hotkey: T::AccountId = account("hotkey", 0, 0); - // let id = PositionId::from(1u128); - - // PositionsV2::::insert( - // (netuid, caller.clone(), id), - // Position { - // id, - // netuid, - // // liquidity: 10000, - // // fees_tao: I64F64::from_num(0), - // // fees_alpha: I64F64::from_num(0), - // _phantom: PhantomData, - // }, - // ); - - // #[extrinsic_call] - // modify_position( - // RawOrigin::Signed(caller), - // hotkey, - // netuid.into(), - // id.into(), - // -5000, - // ); - // } - - // #[benchmark] - // fn toggle_user_liquidity() { - // let netuid = NetUid::from(101); - - // assert!(!EnabledUserLiquidity::::get(netuid)); - - // #[extrinsic_call] - // toggle_user_liquidity(RawOrigin::Root, netuid.into(), true); - - // assert!(EnabledUserLiquidity::::get(netuid)); - // } - impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test); } diff --git a/pallets/swap/src/lib.rs b/pallets/swap/src/lib.rs index 6ce2d20e7e..b51c3351dc 100644 --- a/pallets/swap/src/lib.rs +++ b/pallets/swap/src/lib.rs @@ -1,7 +1,6 @@ #![cfg_attr(not(feature = "std"), no_std)] pub mod pallet; -pub mod position; pub mod weights; pub use pallet::*; diff --git a/pallets/swap/src/mock.rs b/pallets/swap/src/mock.rs index fed68195f8..52ab578ea9 100644 --- a/pallets/swap/src/mock.rs +++ b/pallets/swap/src/mock.rs @@ -27,8 +27,6 @@ use subtensor_runtime_common::{ }; use subtensor_swap_interface::Order; -use crate::pallet::EnabledUserLiquidity; - construct_runtime!( pub enum Test { System: frame_system = 0, @@ -321,12 +319,6 @@ pub fn new_test_ext() -> sp_io::TestExternalities { let mut ext = sp_io::TestExternalities::new(storage); ext.execute_with(|| { System::set_block_number(1); - - for netuid in 0u16..=100 { - // enable V3 for this range of netuids - EnabledUserLiquidity::::set(NetUid::from(netuid), true); - } - EnabledUserLiquidity::::set(NetUid::from(WRAPPING_FEES_NETUID), true); }); ext } diff --git a/pallets/swap/src/pallet/impls.rs b/pallets/swap/src/pallet/impls.rs index a27c74987a..c499e0bb49 100644 --- a/pallets/swap/src/pallet/impls.rs +++ b/pallets/swap/src/pallet/impls.rs @@ -6,7 +6,6 @@ use sp_arithmetic::{ Perquintill, }; use sp_runtime::{DispatchResult, traits::AccountIdConversion}; -use sp_std::vec::Vec; use substrate_fixed::types::U64F64; use subtensor_runtime_common::{ AlphaCurrency, @@ -23,25 +22,7 @@ use subtensor_swap_interface::{ use super::pallet::*; use super::swap_step::{BasicSwapStep, SwapStep}; -use crate::{pallet::Balancer, pallet::balancer::BalancerError, position::PositionId}; - -#[derive(Debug, PartialEq)] -pub struct UpdateLiquidityResult { - pub tao: TaoCurrency, - pub alpha: AlphaCurrency, - pub fee_tao: TaoCurrency, - pub fee_alpha: AlphaCurrency, - pub removed: bool, -} - -#[derive(Debug, PartialEq)] -pub struct RemoveLiquidityResult { - pub tao: TaoCurrency, - pub alpha: AlphaCurrency, - pub fee_tao: TaoCurrency, - pub fee_alpha: AlphaCurrency, - pub liquidity: u64, -} +use crate::{pallet::Balancer, pallet::balancer::BalancerError}; impl Pallet { pub fn current_price(netuid: NetUid) -> U64F64 { @@ -105,27 +86,6 @@ impl Pallet { balancer.calculate_current_liquidity(u64::from(tao_reserve), u64::from(alpha_reserve)); CurrentLiquidity::::insert(netuid, liquidity); - // TODO: Review when/if we have user liquidity - // Initialize the pal-swap: - - // Set initial (protocol owned) liquidity and positions - // Protocol liquidity makes one position - // We are using the sp_arithmetic sqrt here, which works for u128 - // let liquidity = helpers_128bit::sqrt( - // (tao_reserve.to_u64() as u128).saturating_mul(alpha_reserve.to_u64() as u128), - // ) as u64; - // let protocol_account_id = Self::protocol_account_id(); - - // let (position, _, _) = Self::add_liquidity_not_insert( - // netuid, - // &protocol_account_id, - // TickIndex::MIN, - // TickIndex::MAX, - // liquidity, - // )?; - - // Positions::::insert(&(netuid, protocol_account_id, position.id), position); - PalSwapInitialized::::insert(netuid, true); Ok(()) @@ -139,7 +99,6 @@ impl Pallet { alpha_delta: AlphaCurrency, ) -> (TaoCurrency, AlphaCurrency) { // Collect fees - // TODO: Revise when user liquidity is available let tao_fees = FeesTao::::get(netuid); let alpha_fees = FeesAlpha::::get(netuid); FeesTao::::insert(netuid, TaoCurrency::ZERO); @@ -314,91 +273,6 @@ impl Pallet { } } - /// Adds liquidity to the specified price range. - /// - /// This function allows an account to provide liquidity to a given range of price ticks. The - /// amount of liquidity to be added can be determined using - /// [`get_tao_based_liquidity`] and [`get_alpha_based_liquidity`], which compute the required - /// liquidity based on TAO and Alpha balances for the current price tick. - /// - /// ### Behavior: - /// - If the `protocol` flag is **not set** (`false`), the function will attempt to - /// **withdraw balances** from the account using `state_ops.withdraw_balances()`. - /// - If the `protocol` flag is **set** (`true`), the liquidity is added without modifying balances. - /// - If swap V3 was not initialized before, updates the value in storage. - /// - /// ### Parameters: - /// - `coldkey_account_id`: A reference to the account coldkey that is providing liquidity. - /// - `hotkey_account_id`: A reference to the account hotkey that is providing liquidity. - /// - `tick_low`: The lower bound of the price tick range. - /// - `tick_high`: The upper bound of the price tick range. - /// - `liquidity`: The amount of liquidity to be added. - /// - /// ### Returns: - /// - `Ok((u64, u64))`: (tao, alpha) amounts at new position - /// - `Err(SwapError)`: If the operation fails due to insufficient balance, invalid tick range, - /// or other swap-related errors. - /// - /// ### Errors: - /// - [`SwapError::InsufficientBalance`] if the account does not have enough balance. - /// - [`SwapError::InvalidTickRange`] if `tick_low` is greater than or equal to `tick_high`. - /// - Other [`SwapError`] variants as applicable. - pub fn do_add_liquidity( - _netuid: NetUid, - _coldkey_account_id: &T::AccountId, - _hotkey_account_id: &T::AccountId, - _liquidity: u64, - ) -> Result<(PositionId, u64, u64), Error> { - // ensure!( - // EnabledUserLiquidity::::get(netuid), - // Error::::UserLiquidityDisabled - // ); - - // TODO: Revise when user liquidity is enabled - Err(Error::::UserLiquidityDisabled) - } - - /// Remove liquidity and credit balances back to (coldkey_account_id, hotkey_account_id) stake. - /// Removing is allowed even when user liquidity is enabled. - /// - /// Account ID and Position ID identify position in the storage map - pub fn do_remove_liquidity( - _netuid: NetUid, - _coldkey_account_id: &T::AccountId, - _position_id: PositionId, - ) -> Result> { - // TODO: Revise when user liquidity is enabled - Err(Error::::UserLiquidityDisabled) - } - - pub fn do_modify_position( - _netuid: NetUid, - _coldkey_account_id: &T::AccountId, - _hotkey_account_id: &T::AccountId, - _position_id: PositionId, - _liquidity_delta: i64, - ) -> Result> { - // ensure!( - // EnabledUserLiquidity::::get(netuid), - // Error::::UserLiquidityDisabled - // ); - - // TODO: Revise when user liquidity is enabled - Err(Error::::UserLiquidityDisabled) - } - - // /// Returns the number of positions for an account in a specific subnet - // /// - // /// # Arguments - // /// * `netuid` - The subnet ID - // /// * `account_id` - The account ID - // /// - // /// # Returns - // /// The number of positions that the account has in the specified subnet - // pub(super) fn count_positions(netuid: NetUid, account_id: &T::AccountId) -> usize { - // PositionsV2::::iter_prefix_values((netuid, account_id.clone())).count() - // } - pub(crate) fn min_price_inner() -> C { u64::from(1_000_u64).into() } @@ -415,128 +289,6 @@ impl Pallet { T::ProtocolId::get().into_account_truncating() } - /// Dissolve all LPs and clean state. - pub fn do_dissolve_all_liquidity_providers(_netuid: NetUid) -> DispatchResult { - // TODO: Revise when user liquidity is available - Ok(()) - - // if PalSwapInitialized::::get(netuid) { - // // 1) Snapshot only *non‑protocol* positions: (owner, position_id). - // struct CloseItem { - // owner: A, - // pos_id: PositionId, - // } - // let protocol_account = Self::protocol_account_id(); - - // let mut to_close: sp_std::vec::Vec> = sp_std::vec::Vec::new(); - // for ((owner, pos_id), _pos) in Positions::::iter_prefix((netuid,)) { - // 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(()); - // } - - // let mut user_refunded_tao = TaoCurrency::ZERO; - // let mut user_staked_alpha = AlphaCurrency::ZERO; - - // let trust: Vec = T::SubnetInfo::get_validator_trust(netuid.into()); - // let permit: Vec = T::SubnetInfo::get_validator_permit(netuid.into()); - - // // 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) => { - // // α withdrawn from the pool = principal + accrued fees - // let alpha_total_from_pool: AlphaCurrency = - // rm.alpha.saturating_add(rm.fee_alpha); - - // // ---------------- USER: refund τ and convert α → stake ---------------- - - // // 1) Refund τ principal directly. - // let tao_total_from_pool: TaoCurrency = rm.tao.saturating_add(rm.fee_tao); - // if tao_total_from_pool > TaoCurrency::ZERO { - // T::BalanceOps::increase_balance(&owner, tao_total_from_pool); - // user_refunded_tao = - // user_refunded_tao.saturating_add(tao_total_from_pool); - // T::TaoReserve::decrease_provided(netuid, tao_total_from_pool); - // } - - // // 2) Stake ALL withdrawn α (principal + fees) to the best permitted validator. - // if alpha_total_from_pool > AlphaCurrency::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", - // ), - // )?; - - // // Stake α from LP owner (coldkey) to chosen validator (hotkey). - // T::BalanceOps::increase_stake( - // &owner, - // &validator_hotkey, - // netuid, - // alpha_total_from_pool, - // )?; - - // 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); - // } - // } - // Err(e) => { - // log::debug!( - // "dissolve_all_lp: force-close failed: netuid={netuid:?}, owner={owner:?}, pos_id={pos_id:?}, err={e:?}" - // ); - // 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(()); - // } - - // log::debug!( - // "dissolve_all_liquidity_providers: netuid={netuid:?}, mode=V2-or-nonV3, leaving all liquidity/state intact" - // ); - - // Ok(()) - } - - /// TODO: Revise when user liquidity is available /// Clear **protocol-owned** liquidity and wipe all swap state for `netuid`. pub fn do_clear_protocol_liquidity(netuid: NetUid) -> DispatchResult { // let protocol_account = Self::protocol_account_id(); @@ -548,51 +300,10 @@ impl Pallet { T::TaoReserve::decrease_provided(netuid.into(), burned_tao); T::AlphaReserve::decrease_provided(netuid.into(), burned_alpha); - // // Collect protocol position IDs first to avoid mutating while iterating. - // let protocol_pos_ids: sp_std::vec::Vec = Positions::::iter_prefix((netuid,)) - // .filter_map(|((owner, pos_id), _)| { - // if owner == protocol_account { - // Some(pos_id) - // } else { - // None - // } - // }) - // .collect(); - - // for pos_id in protocol_pos_ids { - // match Self::do_remove_liquidity(netuid, &protocol_account, pos_id) { - // Ok(rm) => { - // let alpha_total_from_pool: AlphaCurrency = - // rm.alpha.saturating_add(rm.fee_alpha); - // let tao_total_from_pool: TaoCurrency = rm.tao.saturating_add(rm.fee_tao); - - // if tao_total_from_pool > TaoCurrency::ZERO { - // burned_tao = burned_tao.saturating_add(tao_total_from_pool); - // } - // if alpha_total_from_pool > AlphaCurrency::ZERO { - // burned_alpha = burned_alpha.saturating_add(alpha_total_from_pool); - // } - - // log::debug!( - // "clear_protocol_liquidity: burned protocol pos: netuid={netuid:?}, pos_id={pos_id:?}, τ={tao_total_from_pool:?}, α_total={alpha_total_from_pool:?}" - // ); - // } - // Err(e) => { - // log::debug!( - // "clear_protocol_liquidity: force-close failed: netuid={netuid:?}, pos_id={pos_id:?}, err={e:?}" - // ); - // continue; - // } - // } - // } - - let _ = PositionsV2::::clear_prefix((netuid,), u32::MAX, None); - FeesTao::::remove(netuid); FeesAlpha::::remove(netuid); PalSwapInitialized::::remove(netuid); FeeRate::::remove(netuid); - EnabledUserLiquidity::::remove(netuid); SwapBalancer::::remove(netuid); log::debug!( @@ -695,24 +406,6 @@ impl SwapHandler for Pallet { Self::current_price(netuid.into()) } - fn get_protocol_tao(netuid: NetUid) -> TaoCurrency { - let protocol_account_id = Self::protocol_account_id(); - let mut positions = - PositionsV2::::iter_prefix_values((netuid, protocol_account_id.clone())) - .collect::>(); - - if let Some(position) = positions.get_mut(0) { - let price = Self::current_price(netuid); - // Adjust liquidity - let maybe_token_amounts = position.to_token_amounts(price); - if let Ok((tao, _)) = maybe_token_amounts { - return tao.into(); - } - } - - TaoCurrency::ZERO - } - fn min_price() -> C { Self::min_price_inner() } @@ -729,24 +422,10 @@ impl SwapHandler for Pallet { Self::adjust_protocol_liquidity(netuid, tao_delta, alpha_delta) } - fn is_user_liquidity_enabled(netuid: NetUid) -> bool { - EnabledUserLiquidity::::get(netuid) - } - fn dissolve_all_liquidity_providers(netuid: NetUid) -> DispatchResult { - Self::do_dissolve_all_liquidity_providers(netuid) - } - fn toggle_user_liquidity(netuid: NetUid, enabled: bool) { - EnabledUserLiquidity::::insert(netuid, enabled) - } fn clear_protocol_liquidity(netuid: NetUid) -> DispatchResult { Self::do_clear_protocol_liquidity(netuid) } fn init_swap(netuid: NetUid, maybe_price: Option) { Self::maybe_initialize_palswap(netuid, maybe_price).unwrap_or_default(); } - fn get_all_subnet_netuids() -> Vec { - PalSwapInitialized::::iter() - .map(|(netuid, _)| netuid) - .collect() - } } diff --git a/pallets/swap/src/pallet/migrations/migrate_swapv3_to_balancer.rs b/pallets/swap/src/pallet/migrations/migrate_swapv3_to_balancer.rs index a504c6197c..a36e6cc714 100644 --- a/pallets/swap/src/pallet/migrations/migrate_swapv3_to_balancer.rs +++ b/pallets/swap/src/pallet/migrations/migrate_swapv3_to_balancer.rs @@ -52,6 +52,7 @@ pub fn migrate_swapv3_to_balancer() -> Weight { // ------------------------------ remove_prefix::("Swap", "AlphaSqrtPrice", &mut weight); remove_prefix::("Swap", "CurrentTick", &mut weight); + remove_prefix::("Swap", "EnabledUserLiquidity", &mut weight); remove_prefix::("Swap", "FeeGlobalTao", &mut weight); remove_prefix::("Swap", "FeeGlobalAlpha", &mut weight); // Scrap reservoirs can be just cleaned because they are already included in reserves diff --git a/pallets/swap/src/pallet/mod.rs b/pallets/swap/src/pallet/mod.rs index 8df88715cc..fff070f79b 100644 --- a/pallets/swap/src/pallet/mod.rs +++ b/pallets/swap/src/pallet/mod.rs @@ -1,18 +1,13 @@ use core::num::NonZeroU64; -use core::ops::Neg; use frame_support::{PalletId, pallet_prelude::*, traits::Get}; use frame_system::pallet_prelude::*; // use safe_math::SafeDiv; use subtensor_runtime_common::{ - AlphaCurrency, BalanceOps, Currency, CurrencyReserve, NetUid, SubnetInfo, TaoCurrency, + AlphaCurrency, BalanceOps, CurrencyReserve, NetUid, SubnetInfo, TaoCurrency, }; -use crate::{ - pallet::balancer::Balancer, - position::{Position, PositionId}, - weights::WeightInfo, -}; +use crate::{pallet::balancer::Balancer, weights::WeightInfo}; pub use pallet::*; @@ -32,7 +27,7 @@ type MigrationKeyMaxLen = ConstU32<128>; #[allow(clippy::expect_used)] mod pallet { use super::*; - use frame_system::{ensure_root, ensure_signed}; + use frame_system::ensure_root; #[pallet::pallet] pub struct Pallet(_); @@ -92,26 +87,6 @@ mod pallet { #[pallet::storage] pub type CurrentLiquidity = StorageMap<_, Twox64Concat, NetUid, u64, ValueQuery>; - /// Indicates whether a subnet has been switched to V3 swap from V2. - /// If `true`, the subnet is permanently on V3 swap mode allowing add/remove liquidity - /// operations. Once set to `true` for a subnet, it cannot be changed back to `false`. - #[pallet::storage] - pub type EnabledUserLiquidity = StorageMap<_, Twox64Concat, NetUid, bool, ValueQuery>; - - /// Storage for user positions, using subnet ID and account ID as keys - /// The value is a bounded vector of Position structs with details about the liquidity positions - #[pallet::storage] - pub type PositionsV2 = StorageNMap< - _, - ( - NMapKey, // Subnet ID - NMapKey, // Account ID - NMapKey, // Position ID - ), - Position, - OptionQuery, - >; - /// Position ID counter. #[pallet::storage] pub type LastPositionId = StorageValue<_, u128, ValueQuery>; @@ -151,73 +126,6 @@ mod pallet { pub enum Event { /// Event emitted when the fee rate has been updated for a subnet FeeRateSet { netuid: NetUid, rate: u16 }, - - /// Event emitted when user liquidity operations are enabled for a subnet. - /// First enable even indicates a switch from V2 to V3 swap. - UserLiquidityToggled { netuid: NetUid, enable: bool }, - - /// Event emitted when a liquidity position is added to a subnet's liquidity pool. - LiquidityAddedV2 { - /// The coldkey account that owns the position - coldkey: T::AccountId, - /// The hotkey account where Alpha comes from - hotkey: T::AccountId, - /// The subnet identifier - netuid: NetUid, - /// Unique identifier for the liquidity position - position_id: PositionId, - /// The amount of liquidity added to the position - liquidity: u64, - /// The amount of TAO tokens committed to the position - tao: TaoCurrency, - /// The amount of Alpha tokens committed to the position - alpha: AlphaCurrency, - }, - - /// Event emitted when a liquidity position is removed from a subnet's liquidity pool. - LiquidityRemovedV2 { - /// The coldkey account that owns the position - coldkey: T::AccountId, - /// The hotkey account where Alpha goes to - hotkey: T::AccountId, - /// The subnet identifier - netuid: NetUid, - /// Unique identifier for the liquidity position - position_id: PositionId, - /// The amount of liquidity removed from the position - liquidity: u64, - /// The amount of TAO tokens returned to the user - tao: TaoCurrency, - /// The amount of Alpha tokens returned to the user - alpha: AlphaCurrency, - /// The amount of TAO fees earned from the position - fee_tao: TaoCurrency, - /// The amount of Alpha fees earned from the position - fee_alpha: AlphaCurrency, - }, - - /// Event emitted when a liquidity position is modified in a subnet's liquidity pool. - /// Modifying causes the fees to be claimed. - LiquidityModifiedV2 { - /// The coldkey account that owns the position - coldkey: T::AccountId, - /// The hotkey account where Alpha comes from or goes to - hotkey: T::AccountId, - /// The subnet identifier - netuid: NetUid, - /// Unique identifier for the liquidity position - position_id: PositionId, - /// The amount of liquidity added to or removed from the position - liquidity: i64, - /// The amount of TAO tokens returned to the user - tao: i64, - /// The amount of Alpha tokens returned to the user - alpha: i64, - /// The amount of TAO fees earned from the position - fee_tao: TaoCurrency, - /// The amount of Alpha fees earned from the position - fee_alpha: AlphaCurrency, - }, } #[pallet::error] @@ -259,9 +167,6 @@ mod pallet { /// The subnet does not exist. MechanismDoesNotExist, - /// User liquidity operations are disabled for this subnet - UserLiquidityDisabled, - /// The subnet does not have subtoken enabled SubtokenDisabled, @@ -296,312 +201,5 @@ mod pallet { Ok(()) } - - /// Enable user liquidity operations for a specific subnet. This switches the - /// subnet from V2 to V3 swap mode. Thereafter, adding new user liquidity can be disabled - /// by toggling this flag to false, but the swap mode will remain V3 because of existing - /// user liquidity until all users withdraw their liquidity. - /// - /// Only sudo or subnet owner can enable user liquidity. - /// Only sudo can disable user liquidity. - #[pallet::call_index(4)] - #[pallet::weight(::WeightInfo::toggle_user_liquidity())] - pub fn toggle_user_liquidity( - origin: OriginFor, - netuid: NetUid, - enable: bool, - ) -> DispatchResult { - if ensure_root(origin.clone()).is_err() { - let account_id: T::AccountId = ensure_signed(origin)?; - // Only enabling is allowed to subnet owner - ensure!( - T::SubnetInfo::is_owner(&account_id, netuid.into()) && enable, - DispatchError::BadOrigin - ); - } - - ensure!( - T::SubnetInfo::exists(netuid.into()), - Error::::MechanismDoesNotExist - ); - - // EnabledUserLiquidity::::insert(netuid, enable); - - // Self::deposit_event(Event::UserLiquidityToggled { netuid, enable }); - - Ok(()) - } - - /// Add liquidity to a specific price range for a subnet. - /// - /// Parameters: - /// - origin: The origin of the transaction - /// - netuid: Subnet ID - /// - tick_low: Lower bound of the price range - /// - tick_high: Upper bound of the price range - /// - liquidity: Amount of liquidity to add - /// - /// Emits `Event::LiquidityAdded` on success - #[pallet::call_index(1)] - #[pallet::weight(::WeightInfo::add_liquidity())] - pub fn add_liquidity( - origin: OriginFor, - _hotkey: T::AccountId, - _netuid: NetUid, - _liquidity: u64, - ) -> DispatchResult { - ensure_signed(origin)?; - - // Extrinsic should have no effect. This fix may have to be reverted later, - // so leaving the code in for now. - - // // Ensure that the subnet exists. - // ensure!( - // T::SubnetInfo::exists(netuid.into()), - // Error::::MechanismDoesNotExist - // ); - - // ensure!( - // T::SubnetInfo::is_subtoken_enabled(netuid.into()), - // Error::::SubtokenDisabled - // ); - - // let (position_id, tao, alpha) = Self::do_add_liquidity( - // netuid.into(), - // &coldkey, - // &hotkey, - // tick_low, - // tick_high, - // liquidity, - // )?; - // let alpha = AlphaCurrency::from(alpha); - // let tao = TaoCurrency::from(tao); - - // // Remove TAO and Alpha balances or fail transaction if they can't be removed exactly - // let tao_provided = T::BalanceOps::decrease_balance(&coldkey, tao)?; - // ensure!(tao_provided == tao, Error::::InsufficientBalance); - - // let alpha_provided = - // T::BalanceOps::decrease_stake(&coldkey, &hotkey, netuid.into(), alpha)?; - // ensure!(alpha_provided == alpha, Error::::InsufficientBalance); - - // // Add provided liquidity to user-provided reserves - // T::TaoReserve::increase_provided(netuid.into(), tao_provided); - // T::AlphaReserve::increase_provided(netuid.into(), alpha_provided); - - // // Emit an event - // Self::deposit_event(Event::LiquidityAdded { - // coldkey, - // hotkey, - // netuid, - // position_id, - // liquidity, - // tao, - // alpha, - // tick_low, - // tick_high, - // }); - - // Ok(()) - - Err(Error::::UserLiquidityDisabled.into()) - } - - /// Remove liquidity from a specific position. - /// - /// Parameters: - /// - origin: The origin of the transaction - /// - netuid: Subnet ID - /// - position_id: ID of the position to remove - /// - /// Emits `Event::LiquidityRemoved` on success - #[pallet::call_index(2)] - #[pallet::weight(::WeightInfo::remove_liquidity())] - pub fn remove_liquidity( - 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::LiquidityRemovedV2 { - 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, - }); - - Ok(()) - } - - /// Modify a liquidity position. - /// - /// Parameters: - /// - origin: The origin of the transaction - /// - netuid: Subnet ID - /// - position_id: ID of the position to remove - /// - liquidity_delta: Liquidity to add (if positive) or remove (if negative) - /// - /// Emits `Event::LiquidityRemoved` on success - #[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, - ) -> 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); - - let alpha_provided = - T::BalanceOps::decrease_stake(&coldkey, &hotkey, netuid.into(), result.alpha)?; - ensure!( - alpha_provided == result.alpha, - Error::::InsufficientBalance - ); - - // Emit an event - Self::deposit_event(Event::LiquidityModifiedV2 { - 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, - }); - } 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::LiquidityRemovedV2 { - 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, - }); - } else { - Self::deposit_event(Event::LiquidityModifiedV2 { - 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, - }); - } - } - - // Credit accrued fees to user account (no matter if liquidity is added or removed) - if result.fee_tao > TaoCurrency::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, - )?; - } - - Ok(()) - } - - /// Disable user liquidity in all subnets. - /// - /// Emits `Event::UserLiquidityToggled` on success - #[pallet::call_index(5)] - #[pallet::weight(::WeightInfo::modify_position())] - pub fn disable_lp(origin: OriginFor) -> DispatchResult { - ensure_root(origin)?; - - for netuid in 1..=128 { - let netuid = NetUid::from(netuid as u16); - if EnabledUserLiquidity::::get(netuid) { - EnabledUserLiquidity::::insert(netuid, false); - Self::deposit_event(Event::UserLiquidityToggled { - netuid, - enable: false, - }); - } - - // Remove provided liquidity unconditionally because the network may have - // user liquidity previously disabled - // Ignore result to avoid early stopping - if let Err(err) = Self::do_dissolve_all_liquidity_providers(netuid) { - log::error!( - "Error dissolving liquidity providers on netuid {}: {:?}", - netuid, - err - ); - } - } - - Ok(()) - } } } diff --git a/pallets/swap/src/pallet/tests.rs b/pallets/swap/src/pallet/tests.rs index 0637efb1a4..a085e8a64f 100644 --- a/pallets/swap/src/pallet/tests.rs +++ b/pallets/swap/src/pallet/tests.rs @@ -10,7 +10,7 @@ use frame_support::{assert_noop, assert_ok}; use sp_arithmetic::{Perquintill, helpers_128bit}; use sp_runtime::DispatchError; use substrate_fixed::types::U64F64; -use subtensor_runtime_common::NetUid; +use subtensor_runtime_common::{Currency, NetUid}; use subtensor_swap_interface::Order as OrderT; use super::*; @@ -395,43 +395,6 @@ mod dispatchables { assert_eq!(FeesAlpha::::get(netuid), AlphaCurrency::ZERO); }); } - - // #[test] - // fn test_toggle_user_liquidity() { - // new_test_ext().execute_with(|| { - // let netuid = NetUid::from(101); - - // assert!(!EnabledUserLiquidity::::get(netuid)); - - // assert_ok!(Swap::toggle_user_liquidity( - // RuntimeOrigin::root(), - // netuid.into(), - // true - // )); - - // assert!(EnabledUserLiquidity::::get(netuid)); - - // assert_noop!( - // Swap::toggle_user_liquidity(RuntimeOrigin::signed(666), netuid.into(), true), - // DispatchError::BadOrigin - // ); - - // assert_ok!(Swap::toggle_user_liquidity( - // RuntimeOrigin::signed(1), - // netuid.into(), - // true - // )); - - // assert_noop!( - // Swap::toggle_user_liquidity( - // RuntimeOrigin::root(), - // NON_EXISTENT_NETUID.into(), - // true - // ), - // Error::::MechanismDoesNotExist - // ); - // }); - // } } #[test] @@ -473,25 +436,6 @@ fn test_swap_initialization() { expected_liquidity, epsilon = 1 ); - - // TODO: Revise when user liquidity is available - // // Calculate expected liquidity - // let expected_liquidity = - // helpers_128bit::sqrt((tao.to_u64() as u128).saturating_mul(alpha.to_u64() as u128)) - // as u64; - - // // Get the protocol account - // let protocol_account_id = Pallet::::protocol_account_id(); - - // // Verify position created for protocol account - // let positions = Positions::::iter_prefix_values((netuid, protocol_account_id)) - // .collect::>(); - // assert_eq!(positions.len(), 1); - - // let position = &positions[0]; - // assert_eq!(position.liquidity, expected_liquidity); - // assert_eq!(position.fees_tao, 0); - // assert_eq!(position.fees_alpha, 0); }); } @@ -524,547 +468,6 @@ fn test_swap_initialization_with_price() { }); } -// TODO: Revise when user liquidity is available -// Test adding liquidity on top of the existing protocol liquidity -// #[test] -// fn test_add_liquidity_basic() { -// new_test_ext().execute_with(|| { -// let min_price = tick_to_price(TickIndex::MIN); -// let max_price = tick_to_price(TickIndex::MAX); -// let max_tick = price_to_tick(max_price); -// assert_eq!(max_tick, TickIndex::MAX); - -// assert_ok!(Pallet::::maybe_initialize_palswap(NetUid::from(1))); -// let current_price = Pallet::::current_price(NetUid::from(1)).to_num::(); -// let (current_price_low, current_price_high) = get_ticked_prices_around_current_price(); - -// // As a user add liquidity with all possible corner cases -// // - Initial price is 0.25 -// // - liquidity is expressed in RAO units -// // Test case is (price_low, price_high, liquidity, tao, alpha) -// [ -// // Repeat the protocol liquidity at maximum range: Expect all the same values -// ( -// min_price, -// max_price, -// 2_000_000_000_u64, -// 1_000_000_000_u64, -// 4_000_000_000_u64, -// ), -// // Repeat the protocol liquidity at current to max range: Expect the same alpha -// ( -// current_price_high, -// max_price, -// 2_000_000_000_u64, -// 0, -// 4_000_000_000, -// ), -// // Repeat the protocol liquidity at min to current range: Expect all the same tao -// ( -// min_price, -// current_price_low, -// 2_000_000_000_u64, -// 1_000_000_000, -// 0, -// ), -// // Half to double price - just some sane wothdraw amounts -// (0.125, 0.5, 2_000_000_000_u64, 293_000_000, 1_171_000_000), -// // Both below price - tao is non-zero, alpha is zero -// (0.12, 0.13, 2_000_000_000_u64, 28_270_000, 0), -// // Both above price - tao is zero, alpha is non-zero -// (0.3, 0.4, 2_000_000_000_u64, 0, 489_200_000), -// ] -// .into_iter() -// .enumerate() -// .map(|(n, v)| (NetUid::from(n as u16 + 1), v.0, v.1, v.2, v.3, v.4)) -// .for_each( -// |(netuid, price_low, price_high, liquidity, expected_tao, expected_alpha)| { -// assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - -// // Calculate ticks (assuming tick math is tested separately) -// let tick_low = price_to_tick(price_low); -// let tick_high = price_to_tick(price_high); - -// // Get tick infos and liquidity before adding (to account for protocol liquidity) -// let tick_low_info_before = Ticks::::get(netuid, tick_low).unwrap_or_default(); -// let tick_high_info_before = -// Ticks::::get(netuid, tick_high).unwrap_or_default(); -// let liquidity_before = CurrentLiquidity::::get(netuid); - -// // Add liquidity -// let (position_id, tao, alpha) = Pallet::::do_add_liquidity( -// netuid, -// &OK_COLDKEY_ACCOUNT_ID, -// &OK_HOTKEY_ACCOUNT_ID, -// tick_low, -// tick_high, -// liquidity, -// ) -// .unwrap(); - -// assert_abs_diff_eq!(tao, expected_tao, epsilon = tao / 1000); -// assert_abs_diff_eq!(alpha, expected_alpha, epsilon = alpha / 1000); - -// // Check that low and high ticks appear in the state and are properly updated -// let tick_low_info = Ticks::::get(netuid, tick_low).unwrap(); -// let tick_high_info = Ticks::::get(netuid, tick_high).unwrap(); -// let expected_liquidity_net_low = liquidity as i128; -// let expected_liquidity_gross_low = liquidity; -// let expected_liquidity_net_high = -(liquidity as i128); -// let expected_liquidity_gross_high = liquidity; - -// assert_eq!( -// tick_low_info.liquidity_net - tick_low_info_before.liquidity_net, -// expected_liquidity_net_low, -// ); -// assert_eq!( -// tick_low_info.liquidity_gross - tick_low_info_before.liquidity_gross, -// expected_liquidity_gross_low, -// ); -// assert_eq!( -// tick_high_info.liquidity_net - tick_high_info_before.liquidity_net, -// expected_liquidity_net_high, -// ); -// assert_eq!( -// tick_high_info.liquidity_gross - tick_high_info_before.liquidity_gross, -// expected_liquidity_gross_high, -// ); - -// // Liquidity position at correct ticks -// assert_eq!( -// Pallet::::count_positions(netuid, &OK_COLDKEY_ACCOUNT_ID), -// 1 -// ); - -// let position = -// Positions::::get((netuid, OK_COLDKEY_ACCOUNT_ID, position_id)).unwrap(); -// assert_eq!(position.liquidity, liquidity); -// assert_eq!(position.tick_low, tick_low); -// assert_eq!(position.tick_high, tick_high); -// assert_eq!(position.fees_alpha, 0); -// assert_eq!(position.fees_tao, 0); - -// // Current liquidity is updated only when price range includes the current price -// let expected_liquidity = -// if (price_high > current_price) && (price_low <= current_price) { -// liquidity_before + liquidity -// } else { -// liquidity_before -// }; - -// assert_eq!(CurrentLiquidity::::get(netuid), expected_liquidity) -// }, -// ); -// }); -// } - -// TODO: Revise when user liquidity is available -// #[test] -// fn test_add_liquidity_max_limit_enforced() { -// new_test_ext().execute_with(|| { -// let netuid = NetUid::from(1); -// let liquidity = 2_000_000_000_u64; -// assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - -// let limit = MaxPositions::get() as usize; - -// for _ in 0..limit { -// Pallet::::do_add_liquidity( -// netuid, -// &OK_COLDKEY_ACCOUNT_ID, -// &OK_HOTKEY_ACCOUNT_ID, -// TickIndex::MIN, -// TickIndex::MAX, -// liquidity, -// ) -// .unwrap(); -// } - -// let test_result = Pallet::::do_add_liquidity( -// netuid, -// &OK_COLDKEY_ACCOUNT_ID, -// &OK_HOTKEY_ACCOUNT_ID, -// TickIndex::MIN, -// TickIndex::MAX, -// liquidity, -// ); - -// assert_err!(test_result, Error::::MaxPositionsExceeded); -// }); -// } - -// TODO: Revise when user liquidity is available -// #[test] -// fn test_add_liquidity_out_of_bounds() { -// new_test_ext().execute_with(|| { -// [ -// // For our tests, we'll construct TickIndex values that are intentionally -// // outside the valid range for testing purposes only -// ( -// TickIndex::new_unchecked(TickIndex::MIN.get() - 1), -// TickIndex::MAX, -// 1_000_000_000_u64, -// ), -// ( -// TickIndex::MIN, -// TickIndex::new_unchecked(TickIndex::MAX.get() + 1), -// 1_000_000_000_u64, -// ), -// ( -// TickIndex::new_unchecked(TickIndex::MIN.get() - 1), -// TickIndex::new_unchecked(TickIndex::MAX.get() + 1), -// 1_000_000_000_u64, -// ), -// ( -// TickIndex::new_unchecked(TickIndex::MIN.get() - 100), -// TickIndex::new_unchecked(TickIndex::MAX.get() + 100), -// 1_000_000_000_u64, -// ), -// // Inverted ticks: high < low -// ( -// TickIndex::new_unchecked(-900), -// TickIndex::new_unchecked(-1000), -// 1_000_000_000_u64, -// ), -// // Equal ticks: high == low -// ( -// TickIndex::new_unchecked(-10_000), -// TickIndex::new_unchecked(-10_000), -// 1_000_000_000_u64, -// ), -// ] -// .into_iter() -// .enumerate() -// .map(|(n, v)| (NetUid::from(n as u16 + 1), v.0, v.1, v.2)) -// .for_each(|(netuid, tick_low, tick_high, liquidity)| { -// assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - -// // Add liquidity -// assert_err!( -// Swap::do_add_liquidity( -// netuid, -// &OK_COLDKEY_ACCOUNT_ID, -// &OK_HOTKEY_ACCOUNT_ID, -// tick_low, -// tick_high, -// liquidity -// ), -// Error::::InvalidTickRange, -// ); -// }); -// }); -// } - -// TODO: Revise when user liquidity is available -// #[test] -// fn test_add_liquidity_over_balance() { -// new_test_ext().execute_with(|| { -// let coldkey_account_id = 3; -// let hotkey_account_id = 1002; - -// [ -// // Lower than price (not enough tao) -// (0.1, 0.2, 100_000_000_000_u64), -// // Higher than price (not enough alpha) -// (0.3, 0.4, 100_000_000_000_u64), -// // Around the price (not enough both) -// (0.1, 0.4, 100_000_000_000_u64), -// ] -// .into_iter() -// .enumerate() -// .map(|(n, v)| (NetUid::from(n as u16 + 1), v.0, v.1, v.2)) -// .for_each(|(netuid, price_low, price_high, liquidity)| { -// // Calculate ticks -// let tick_low = price_to_tick(price_low); -// let tick_high = price_to_tick(price_high); - -// assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - -// // Add liquidity -// assert_err!( -// Pallet::::do_add_liquidity( -// netuid, -// &coldkey_account_id, -// &hotkey_account_id, -// tick_low, -// tick_high, -// liquidity -// ), -// Error::::InsufficientBalance, -// ); -// }); -// }); -// } - -// TODO: Revise when user liquidity is available -// cargo test --package pallet-subtensor-swap --lib -- pallet::impls::tests::test_remove_liquidity_basic --exact --show-output -// #[test] -// fn test_remove_liquidity_basic() { -// new_test_ext().execute_with(|| { -// let min_price = tick_to_price(TickIndex::MIN); -// let max_price = tick_to_price(TickIndex::MAX); -// let max_tick = price_to_tick(max_price); -// assert_eq!(max_tick, TickIndex::MAX); - -// let (current_price_low, current_price_high) = get_ticked_prices_around_current_price(); - -// // As a user add liquidity with all possible corner cases -// // - Initial price is 0.25 -// // - liquidity is expressed in RAO units -// // Test case is (price_low, price_high, liquidity, tao, alpha) -// [ -// // Repeat the protocol liquidity at maximum range: Expect all the same values -// ( -// min_price, -// max_price, -// 2_000_000_000_u64, -// 1_000_000_000_u64, -// 4_000_000_000_u64, -// ), -// // Repeat the protocol liquidity at current to max range: Expect the same alpha -// ( -// current_price_high, -// max_price, -// 2_000_000_000_u64, -// 0, -// 4_000_000_000, -// ), -// // Repeat the protocol liquidity at min to current range: Expect all the same tao -// ( -// min_price, -// current_price_low, -// 2_000_000_000_u64, -// 1_000_000_000, -// 0, -// ), -// // Half to double price - just some sane wothdraw amounts -// (0.125, 0.5, 2_000_000_000_u64, 293_000_000, 1_171_000_000), -// // Both below price - tao is non-zero, alpha is zero -// (0.12, 0.13, 2_000_000_000_u64, 28_270_000, 0), -// // Both above price - tao is zero, alpha is non-zero -// (0.3, 0.4, 2_000_000_000_u64, 0, 489_200_000), -// ] -// .into_iter() -// .enumerate() -// .map(|(n, v)| (NetUid::from(n as u16 + 1), v.0, v.1, v.2, v.3, v.4)) -// .for_each(|(netuid, price_low, price_high, liquidity, tao, alpha)| { -// // Calculate ticks (assuming tick math is tested separately) -// let tick_low = price_to_tick(price_low); -// let tick_high = price_to_tick(price_high); - -// assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); -// let liquidity_before = CurrentLiquidity::::get(netuid); - -// // Add liquidity -// let (position_id, _, _) = Pallet::::do_add_liquidity( -// netuid, -// &OK_COLDKEY_ACCOUNT_ID, -// &OK_HOTKEY_ACCOUNT_ID, -// tick_low, -// tick_high, -// liquidity, -// ) -// .unwrap(); - -// // Remove liquidity -// let remove_result = -// Pallet::::do_remove_liquidity(netuid, &OK_COLDKEY_ACCOUNT_ID, position_id) -// .unwrap(); -// assert_abs_diff_eq!(remove_result.tao.to_u64(), tao, epsilon = tao / 1000); -// assert_abs_diff_eq!( -// u64::from(remove_result.alpha), -// alpha, -// epsilon = alpha / 1000 -// ); -// assert_eq!(remove_result.fee_tao, TaoCurrency::ZERO); -// assert_eq!(remove_result.fee_alpha, AlphaCurrency::ZERO); - -// // Liquidity position is removed -// assert_eq!( -// Pallet::::count_positions(netuid, &OK_COLDKEY_ACCOUNT_ID), -// 0 -// ); -// assert!(Positions::::get((netuid, OK_COLDKEY_ACCOUNT_ID, position_id)).is_none()); - -// // Current liquidity is updated (back where it was) -// assert_eq!(CurrentLiquidity::::get(netuid), liquidity_before); -// }); -// }); -// } - -// TODO: Revise when user liquidity is available -// #[test] -// fn test_remove_liquidity_nonexisting_position() { -// new_test_ext().execute_with(|| { -// let min_price = tick_to_price(TickIndex::MIN); -// let max_price = tick_to_price(TickIndex::MAX); -// let max_tick = price_to_tick(max_price); -// assert_eq!(max_tick.get(), TickIndex::MAX.get()); - -// let liquidity = 2_000_000_000_u64; -// let netuid = NetUid::from(1); - -// // Calculate ticks (assuming tick math is tested separately) -// let tick_low = price_to_tick(min_price); -// let tick_high = price_to_tick(max_price); - -// // Setup swap -// assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - -// // Add liquidity -// assert_ok!(Pallet::::do_add_liquidity( -// netuid, -// &OK_COLDKEY_ACCOUNT_ID, -// &OK_HOTKEY_ACCOUNT_ID, -// tick_low, -// tick_high, -// liquidity, -// )); - -// assert!(Pallet::::count_positions(netuid, &OK_COLDKEY_ACCOUNT_ID) > 0); - -// // Remove liquidity -// assert_err!( -// Pallet::::do_remove_liquidity( -// netuid, -// &OK_COLDKEY_ACCOUNT_ID, -// PositionId::new::() -// ), -// Error::::LiquidityNotFound, -// ); -// }); -// } - -// TODO: Revise when user liquidity is available -// cargo test --package pallet-subtensor-swap --lib -- pallet::tests::test_modify_position_basic --exact --show-output -// #[test] -// fn test_modify_position_basic() { -// new_test_ext().execute_with(|| { -// let max_price = tick_to_price(TickIndex::MAX); -// let max_tick = price_to_tick(max_price); -// let limit_price = 1000.0_f64; -// assert_eq!(max_tick, TickIndex::MAX); -// let (current_price_low, _current_price_high) = get_ticked_prices_around_current_price(); - -// // As a user add liquidity with all possible corner cases -// // - Initial price is 0.25 -// // - liquidity is expressed in RAO units -// // Test case is (price_low, price_high, liquidity, tao, alpha) -// [ -// // Repeat the protocol liquidity at current to max range: Expect the same alpha -// ( -// current_price_low, -// max_price, -// 2_000_000_000_u64, -// 4_000_000_000, -// ), -// ] -// .into_iter() -// .enumerate() -// .map(|(n, v)| (NetUid::from(n as u16 + 1), v.0, v.1, v.2, v.3)) -// .for_each(|(netuid, price_low, price_high, liquidity, alpha)| { -// // Calculate ticks (assuming tick math is tested separately) -// let tick_low = price_to_tick(price_low); -// let tick_high = price_to_tick(price_high); - -// assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - -// // Add liquidity -// let (position_id, _, _) = Pallet::::do_add_liquidity( -// netuid, -// &OK_COLDKEY_ACCOUNT_ID, -// &OK_HOTKEY_ACCOUNT_ID, -// tick_low, -// tick_high, -// liquidity, -// ) -// .unwrap(); - -// // Get tick infos before the swap/update -// let tick_low_info_before = Ticks::::get(netuid, tick_low).unwrap(); -// let tick_high_info_before = Ticks::::get(netuid, tick_high).unwrap(); - -// // Swap to create fees on the position -// let sqrt_limit_price = SqrtPrice::from_num((limit_price).sqrt()); -// let order = GetAlphaForTao::with_amount(liquidity / 10); -// Pallet::::do_swap(netuid, order, sqrt_limit_price, false, false).unwrap(); - -// // Modify liquidity (also causes claiming of fees) -// let liquidity_before = CurrentLiquidity::::get(netuid); -// let modify_result = Pallet::::do_modify_position( -// netuid, -// &OK_COLDKEY_ACCOUNT_ID, -// &OK_HOTKEY_ACCOUNT_ID, -// position_id, -// -((liquidity / 10) as i64), -// ) -// .unwrap(); -// assert_abs_diff_eq!( -// u64::from(modify_result.alpha), -// alpha / 10, -// epsilon = alpha / 1000 -// ); -// assert!(modify_result.fee_tao > TaoCurrency::ZERO); -// assert_eq!(modify_result.fee_alpha, AlphaCurrency::ZERO); - -// // Liquidity position is reduced -// assert_eq!( -// Pallet::::count_positions(netuid, &OK_COLDKEY_ACCOUNT_ID), -// 1 -// ); - -// // Current liquidity is reduced with modify_position -// assert!(CurrentLiquidity::::get(netuid) < liquidity_before); - -// // Position liquidity is reduced -// let position = -// Positions::::get((netuid, OK_COLDKEY_ACCOUNT_ID, position_id)).unwrap(); -// assert_eq!(position.liquidity, liquidity * 9 / 10); -// assert_eq!(position.tick_low, tick_low); -// assert_eq!(position.tick_high, tick_high); - -// // Tick liquidity is updated properly for low and high position ticks -// let tick_low_info_after = Ticks::::get(netuid, tick_low).unwrap(); -// let tick_high_info_after = Ticks::::get(netuid, tick_high).unwrap(); - -// assert_eq!( -// tick_low_info_before.liquidity_net - (liquidity / 10) as i128, -// tick_low_info_after.liquidity_net, -// ); -// assert_eq!( -// tick_low_info_before.liquidity_gross - (liquidity / 10), -// tick_low_info_after.liquidity_gross, -// ); -// assert_eq!( -// tick_high_info_before.liquidity_net + (liquidity / 10) as i128, -// tick_high_info_after.liquidity_net, -// ); -// assert_eq!( -// tick_high_info_before.liquidity_gross - (liquidity / 10), -// tick_high_info_after.liquidity_gross, -// ); - -// // Modify liquidity again (ensure fees aren't double-collected) -// let modify_result = Pallet::::do_modify_position( -// netuid, -// &OK_COLDKEY_ACCOUNT_ID, -// &OK_HOTKEY_ACCOUNT_ID, -// position_id, -// -((liquidity / 100) as i64), -// ) -// .unwrap(); - -// assert_abs_diff_eq!( -// u64::from(modify_result.alpha), -// alpha / 100, -// epsilon = alpha / 1000 -// ); -// assert_eq!(modify_result.fee_tao, TaoCurrency::ZERO); -// assert_eq!(modify_result.fee_alpha, AlphaCurrency::ZERO); -// }); -// }); -// } - // cargo test --package pallet-subtensor-swap --lib -- pallet::tests::test_swap_basic --exact --nocapture #[test] fn test_swap_basic() { @@ -1166,23 +569,6 @@ fn test_swap_basic() { ); } - // Liquidity position should not be updated - // TODO: Revise when user liquidity is in place - // let protocol_id = Pallet::::protocol_account_id(); - // let positions = - // PositionsV2::::iter_prefix_values((netuid, protocol_id)).collect::>(); - // let position = positions.first().unwrap(); - - // assert_eq!( - // position.liquidity, - // helpers_128bit::sqrt( - // TaoReserve::reserve(netuid.into()).to_u64() as u128 - // * AlphaReserve::reserve(netuid.into()).to_u64() as u128 - // ) as u64 - // ); - // assert_eq!(position.fees_alpha, 0); - // assert_eq!(position.fees_tao, 0); - // Assert that price movement is in correct direction let current_price_after = Pallet::::current_price(netuid); assert_eq!( @@ -1362,142 +748,6 @@ fn test_convert_deltas() { }); } -// #[test] -// fn test_user_liquidity_disabled() { -// new_test_ext().execute_with(|| { -// // Use a netuid above 100 since our mock enables liquidity for 0-100 -// let netuid = NetUid::from(101); -// let tick_low = TickIndex::new_unchecked(-1000); -// let tick_high = TickIndex::new_unchecked(1000); -// let position_id = PositionId::from(1); -// let liquidity = 1_000_000_000; -// let liquidity_delta = 500_000_000; - -// assert!(!EnabledUserLiquidity::::get(netuid)); - -// assert_noop!( -// Swap::do_add_liquidity( -// netuid, -// &OK_COLDKEY_ACCOUNT_ID, -// &OK_HOTKEY_ACCOUNT_ID, -// tick_low, -// tick_high, -// liquidity -// ), -// Error::::UserLiquidityDisabled -// ); - -// assert_noop!( -// Swap::do_remove_liquidity(netuid, &OK_COLDKEY_ACCOUNT_ID, position_id), -// Error::::LiquidityNotFound -// ); - -// assert_noop!( -// Swap::modify_position( -// RuntimeOrigin::signed(OK_COLDKEY_ACCOUNT_ID), -// OK_HOTKEY_ACCOUNT_ID, -// netuid, -// position_id, -// liquidity_delta -// ), -// Error::::UserLiquidityDisabled -// ); - -// assert_ok!(Swap::toggle_user_liquidity( -// RuntimeOrigin::root(), -// netuid, -// true -// )); - -// let position_id = Swap::do_add_liquidity( -// netuid, -// &OK_COLDKEY_ACCOUNT_ID, -// &OK_HOTKEY_ACCOUNT_ID, -// tick_low, -// tick_high, -// liquidity, -// ) -// .unwrap() -// .0; - -// assert_ok!(Swap::do_modify_position( -// netuid.into(), -// &OK_COLDKEY_ACCOUNT_ID, -// &OK_HOTKEY_ACCOUNT_ID, -// position_id, -// liquidity_delta, -// )); - -// assert_ok!(Swap::do_remove_liquidity( -// netuid, -// &OK_COLDKEY_ACCOUNT_ID, -// position_id, -// )); -// }); -// } - -// TODO: revise when user liquidity is available -// Test correctness of swap fees: -// - Fees are distribued to (concentrated) liquidity providers -// -// #[test] -// fn test_swap_fee_correctness() { -// new_test_ext().execute_with(|| { -// let min_price = get_min_price(); -// let max_price = get_max_price(); -// let netuid = NetUid::from(1); - -// // Provide very spread liquidity at the range from min to max that matches protocol liquidity -// let liquidity = 2_000_000_000_000_u64; // 1x of protocol liquidity - -// assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - -// // Add user liquidity -// let (position_id, _tao, _alpha) = Pallet::::do_add_liquidity( -// netuid, -// &OK_COLDKEY_ACCOUNT_ID, -// &OK_HOTKEY_ACCOUNT_ID, -// tick_low, -// tick_high, -// liquidity, -// ) -// .unwrap(); - -// // Swap buy and swap sell -// Pallet::::do_swap( -// netuid, -// GetAlphaForTao::with_amount(liquidity / 10), -// u64::MAX.into(), -// false, -// false, -// ) -// .unwrap(); -// Pallet::::do_swap( -// netuid, -// GetTaoForAlpha::with_amount(liquidity / 10), -// 0_u64.into(), -// false, -// false, -// ) -// .unwrap(); - -// // Get user position -// let mut position = -// Positions::::get((netuid, OK_COLDKEY_ACCOUNT_ID, position_id)).unwrap(); -// assert_eq!(position.liquidity, liquidity); -// assert_eq!(position.tick_low, tick_low); -// assert_eq!(position.tick_high, tick_high); - -// // Check that 50% of fees were credited to the position -// let fee_rate = FeeRate::::get(NetUid::from(netuid)) as f64 / u16::MAX as f64; -// let (actual_fee_tao, actual_fee_alpha) = position.collect_fees(); -// let expected_fee = (fee_rate * (liquidity / 10) as f64 * 0.5) as u64; - -// assert_abs_diff_eq!(actual_fee_tao, expected_fee, epsilon = 1,); -// assert_abs_diff_eq!(actual_fee_alpha, expected_fee, epsilon = 1,); -// }); -// } - #[test] fn test_rollback_works() { new_test_ext().execute_with(|| { @@ -1524,81 +774,6 @@ fn test_rollback_works() { }) } -// TODO: Revise when user liquidity is available -// Test correctness of swap fees: -// - New LP is not eligible to previously accrued fees -// -// cargo test --package pallet-subtensor-swap --lib -- pallet::tests::test_new_lp_doesnt_get_old_fees --exact --show-output -// #[test] -// fn test_new_lp_doesnt_get_old_fees() { -// new_test_ext().execute_with(|| { -// let min_price = tick_to_price(TickIndex::MIN); -// let max_price = tick_to_price(TickIndex::MAX); -// let netuid = NetUid::from(1); - -// // Provide very spread liquidity at the range from min to max that matches protocol liquidity -// let liquidity = 2_000_000_000_000_u64; // 1x of protocol liquidity - -// assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - -// // Calculate ticks -// let tick_low = price_to_tick(min_price); -// let tick_high = price_to_tick(max_price); - -// // Add user liquidity -// Pallet::::do_add_liquidity( -// netuid, -// &OK_COLDKEY_ACCOUNT_ID, -// &OK_HOTKEY_ACCOUNT_ID, -// tick_low, -// tick_high, -// liquidity, -// ) -// .unwrap(); - -// // Swap buy and swap sell -// Pallet::::do_swap( -// netuid, -// GetAlphaForTao::with_amount(liquidity / 10), -// u64::MAX.into(), -// false, -// false, -// ) -// .unwrap(); -// Pallet::::do_swap( -// netuid, -// GetTaoForAlpha::with_amount(liquidity / 10), -// 0_u64.into(), -// false, -// false, -// ) -// .unwrap(); - -// // Add liquidity from a different user to a new tick -// let (position_id_2, _tao, _alpha) = Pallet::::do_add_liquidity( -// netuid, -// &OK_COLDKEY_ACCOUNT_ID_2, -// &OK_HOTKEY_ACCOUNT_ID_2, -// tick_low.next().unwrap(), -// tick_high.prev().unwrap(), -// liquidity, -// ) -// .unwrap(); - -// // Get user position -// let mut position = -// Positions::::get((netuid, OK_COLDKEY_ACCOUNT_ID_2, position_id_2)).unwrap(); -// assert_eq!(position.liquidity, liquidity); -// assert_eq!(position.tick_low, tick_low.next().unwrap()); -// assert_eq!(position.tick_high, tick_high.prev().unwrap()); - -// // Check that collected fees are 0 -// let (actual_fee_tao, actual_fee_alpha) = position.collect_fees(); -// assert_abs_diff_eq!(actual_fee_tao, 0, epsilon = 1); -// assert_abs_diff_eq!(actual_fee_alpha, 0, epsilon = 1); -// }); -// } - #[allow(dead_code)] fn bbox(t: U64F64, a: U64F64, b: U64F64) -> U64F64 { if t < a { @@ -1616,448 +791,8 @@ fn print_current_price(netuid: NetUid) { log::trace!("Current price: {current_price:.6}"); } -// TODO: Revise when user liquidity is available -// RUST_LOG=pallet_subtensor_swap=trace cargo test --package pallet-subtensor-swap --lib -- pallet::tests::test_wrapping_fees --exact --show-output --nocapture -// #[test] -// fn test_wrapping_fees() { -// new_test_ext().execute_with(|| { -// let netuid = NetUid::from(WRAPPING_FEES_NETUID); -// let position_1_low_price = 0.20; -// let position_1_high_price = 0.255; -// let position_2_low_price = 0.255; -// let position_2_high_price = 0.257; -// assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - -// Pallet::::do_add_liquidity( -// netuid, -// &OK_COLDKEY_ACCOUNT_ID_RICH, -// &OK_COLDKEY_ACCOUNT_ID_RICH, -// price_to_tick(position_1_low_price), -// price_to_tick(position_1_high_price), -// 1_000_000_000_u64, -// ) -// .unwrap(); - -// print_current_price(netuid); - -// let order = GetTaoForAlpha::with_amount(800_000_000); -// let sqrt_limit_price = SqrtPrice::from_num(0.000001); -// Pallet::::do_swap(netuid, order, sqrt_limit_price, false, false).unwrap(); - -// let order = GetAlphaForTao::with_amount(1_850_000_000); -// let sqrt_limit_price = SqrtPrice::from_num(1_000_000.0); - -// print_current_price(netuid); - -// Pallet::::do_swap(netuid, order, sqrt_limit_price, false, false).unwrap(); - -// print_current_price(netuid); - -// let add_liquidity_result = Pallet::::do_add_liquidity( -// netuid, -// &OK_COLDKEY_ACCOUNT_ID_RICH, -// &OK_COLDKEY_ACCOUNT_ID_RICH, -// price_to_tick(position_2_low_price), -// price_to_tick(position_2_high_price), -// 1_000_000_000_u64, -// ) -// .unwrap(); - -// let order = GetTaoForAlpha::with_amount(1_800_000_000); -// let sqrt_limit_price = SqrtPrice::from_num(0.000001); - -// let initial_sqrt_price = AlphaSqrtPrice::::get(netuid); -// Pallet::::do_swap(netuid, order, sqrt_limit_price, false, false).unwrap(); -// let final_sqrt_price = AlphaSqrtPrice::::get(netuid); - -// print_current_price(netuid); - -// let mut position = -// Positions::::get((netuid, &OK_COLDKEY_ACCOUNT_ID_RICH, add_liquidity_result.0)) -// .unwrap(); - -// let initial_box_price = bbox( -// initial_sqrt_price, -// position.tick_low.try_to_sqrt_price().unwrap(), -// position.tick_high.try_to_sqrt_price().unwrap(), -// ); - -// let final_box_price = bbox( -// final_sqrt_price, -// position.tick_low.try_to_sqrt_price().unwrap(), -// position.tick_high.try_to_sqrt_price().unwrap(), -// ); - -// let fee_rate = FeeRate::::get(netuid) as f64 / u16::MAX as f64; - -// log::trace!("fee_rate: {fee_rate:.6}"); -// log::trace!("position.liquidity: {}", position.liquidity); -// log::trace!( -// "initial_box_price: {:.6}", -// initial_box_price.to_num::() -// ); -// log::trace!("final_box_price: {:.6}", final_box_price.to_num::()); - -// let expected_fee_tao = ((fee_rate / (1.0 - fee_rate)) -// * (position.liquidity as f64) -// * (final_box_price.to_num::() - initial_box_price.to_num::())) -// as u64; - -// let expected_fee_alpha = ((fee_rate / (1.0 - fee_rate)) -// * (position.liquidity as f64) -// * ((1.0 / final_box_price.to_num::()) - (1.0 / initial_box_price.to_num::()))) -// as u64; - -// log::trace!("Expected ALPHA fee: {:.6}", expected_fee_alpha as f64); - -// let (fee_tao, fee_alpha) = position.collect_fees(); - -// log::trace!("Collected fees: TAO: {fee_tao}, ALPHA: {fee_alpha}"); - -// assert_abs_diff_eq!(fee_tao, expected_fee_tao, epsilon = 1); -// assert_abs_diff_eq!(fee_alpha, expected_fee_alpha, epsilon = 1); -// }); -// } - -// TODO: Revise when user liquidity is available -// Test that price moves less with more liquidity -// cargo test --package pallet-subtensor-swap --lib -- pallet::tests::test_less_price_movement --exact --show-output -// #[test] -// fn test_less_price_movement() { -// let netuid = NetUid::from(1); -// let mut last_end_price = U64F64::from_num(0); -// let initial_stake_liquidity = 1_000_000_000; -// let swapped_liquidity = 1_000_000; - -// // Test case is (order_type, provided_liquidity) -// // Testing algorithm: -// // - Stake initial_stake_liquidity -// // - Provide liquidity if iteration provides lq -// // - Buy or sell -// // - Save end price if iteration doesn't provide lq -// macro_rules! perform_test { -// ($order_t:ident, $provided_liquidity:expr, $limit_price:expr, $should_price_shrink:expr) => { -// let provided_liquidity = $provided_liquidity; -// let should_price_shrink = $should_price_shrink; -// let limit_price = $limit_price; -// new_test_ext().execute_with(|| { -// // Setup swap -// assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - -// // Buy Alpha -// assert_ok!(Pallet::::do_swap( -// netuid, -// GetAlphaForTao::with_amount(initial_stake_liquidity), -// SqrtPrice::from_num(10_000_000_000_u64), -// false, -// false -// )); - -// // Get current price -// let start_price = Pallet::::current_price(netuid); - -// // Add liquidity if this test iteration provides -// if provided_liquidity > 0 { -// let tick_low = price_to_tick(start_price.to_num::() * 0.5); -// let tick_high = price_to_tick(start_price.to_num::() * 1.5); -// assert_ok!(Pallet::::do_add_liquidity( -// netuid, -// &OK_COLDKEY_ACCOUNT_ID, -// &OK_HOTKEY_ACCOUNT_ID, -// tick_low, -// tick_high, -// provided_liquidity, -// )); -// } - -// // Swap -// let sqrt_limit_price = SqrtPrice::from_num(limit_price); -// assert_ok!(Pallet::::do_swap( -// netuid, -// $order_t::with_amount(swapped_liquidity), -// sqrt_limit_price, -// false, -// false -// )); - -// let end_price = Pallet::::current_price(netuid); - -// // Save end price if iteration doesn't provide or compare with previous end price if -// // it does -// if provided_liquidity > 0 { -// assert_eq!(should_price_shrink, end_price < last_end_price); -// } else { -// last_end_price = end_price; -// } -// }); -// }; -// } - -// for provided_liquidity in [0, 1_000_000_000_000_u64] { -// perform_test!(GetAlphaForTao, provided_liquidity, 1000.0_f64, true); -// } -// for provided_liquidity in [0, 1_000_000_000_000_u64] { -// perform_test!(GetTaoForAlpha, provided_liquidity, 0.001_f64, false); -// } -// } - -// TODO: Revise when user liquidity is available -// #[test] -// fn test_swap_subtoken_disabled() { -// new_test_ext().execute_with(|| { -// let netuid = NetUid::from(SUBTOKEN_DISABLED_NETUID); // Use a netuid not used elsewhere -// let price_low = 0.1; -// let price_high = 0.2; -// let tick_low = price_to_tick(price_low); -// let tick_high = price_to_tick(price_high); -// let liquidity = 1_000_000_u64; - -// assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - -// assert_noop!( -// Pallet::::add_liquidity( -// RuntimeOrigin::signed(OK_COLDKEY_ACCOUNT_ID), -// OK_HOTKEY_ACCOUNT_ID, -// netuid, -// tick_low, -// tick_high, -// liquidity, -// ), -// Error::::SubtokenDisabled -// ); - -// assert_noop!( -// Pallet::::modify_position( -// RuntimeOrigin::signed(OK_COLDKEY_ACCOUNT_ID), -// OK_HOTKEY_ACCOUNT_ID, -// netuid, -// PositionId::from(0), -// liquidity as i64, -// ), -// Error::::SubtokenDisabled -// ); -// }); -// } - -// TODO: Revise when user liquidity is available -// #[test] -// fn test_liquidate_v3_removes_positions_ticks_and_state() { -// new_test_ext().execute_with(|| { -// let netuid = NetUid::from(1); - -// // Initialize V3 (creates protocol position, ticks, price, liquidity) -// assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); -// assert!(PalSwapInitialized::::get(netuid)); - -// // Enable user LP -// assert_ok!(Swap::toggle_user_liquidity( -// RuntimeOrigin::root(), -// netuid.into(), -// true -// )); - -// // Add a user position across the full range to ensure ticks/bitmap are populated. -// let min_price = get_min_price(); -// let max_price = get_max_price(); -// let liquidity = 2_000_000_000_u64; - -// let (_pos_id, _tao, _alpha) = Pallet::::do_add_liquidity( -// netuid, -// &OK_COLDKEY_ACCOUNT_ID, -// &OK_HOTKEY_ACCOUNT_ID, -// liquidity, -// ) -// .expect("add liquidity"); - -// // Accrue some global fees so we can verify fee storage is cleared later. -// let sqrt_limit_price = SqrtPrice::from_num(1_000_000.0); -// assert_ok!(Pallet::::do_swap( -// netuid, -// GetAlphaForTao::with_amount(1_000_000), -// sqrt_limit_price, -// false, -// false -// )); - -// // Sanity: protocol & user positions exist, ticks exist, liquidity > 0 -// let protocol_id = Pallet::::protocol_account_id(); -// let prot_positions = -// Positions::::iter_prefix_values((netuid, protocol_id)).collect::>(); -// assert!(!prot_positions.is_empty()); - -// let user_positions = Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) -// .collect::>(); -// assert_eq!(user_positions.len(), 1); - -// assert!(Ticks::::get(netuid, TickIndex::MIN).is_some()); -// assert!(Ticks::::get(netuid, TickIndex::MAX).is_some()); -// assert!(CurrentLiquidity::::get(netuid) > 0); - -// let had_bitmap_words = TickIndexBitmapWords::::iter_prefix((netuid,)) -// .next() -// .is_some(); -// assert!(had_bitmap_words); - -// // ACT: users-only liquidation then protocol clear -// assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); -// assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); - -// // ASSERT: positions cleared (both user and protocol) -// assert_eq!( -// Pallet::::count_positions(netuid, &OK_COLDKEY_ACCOUNT_ID), -// 0 -// ); -// let prot_positions_after = -// Positions::::iter_prefix_values((netuid, protocol_id)).collect::>(); -// assert!(prot_positions_after.is_empty()); -// let user_positions_after = -// Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) -// .collect::>(); -// assert!(user_positions_after.is_empty()); - -// // ASSERT: ticks cleared -// 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: fee globals cleared -// assert!(!FeeGlobalTao::::contains_key(netuid)); -// assert!(!FeeGlobalAlpha::::contains_key(netuid)); - -// // ASSERT: price/tick/liquidity flags cleared -// assert!(!AlphaSqrtPrice::::contains_key(netuid)); -// assert!(!CurrentTick::::contains_key(netuid)); -// assert!(!CurrentLiquidity::::contains_key(netuid)); -// assert!(!PalSwapInitialized::::contains_key(netuid)); - -// // ASSERT: active tick bitmap cleared -// assert!( -// TickIndexBitmapWords::::iter_prefix((netuid,)) -// .next() -// .is_none() -// ); - -// // ASSERT: knobs removed on dereg -// assert!(!FeeRate::::contains_key(netuid)); -// assert!(!EnabledUserLiquidity::::contains_key(netuid)); -// }); -// } - -// TODO: Revise when user liquidity is available -// V3 path with user liquidity disabled at teardown: -// must still remove positions and clear state (after protocol clear). -// #[test] -// fn test_liquidate_v3_with_user_liquidity_disabled() { -// new_test_ext().execute_with(|| { -// let netuid = NetUid::from(101); - -// assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); -// assert!(PalSwapInitialized::::get(netuid)); - -// // Enable temporarily to add a user position -// assert_ok!(Swap::toggle_user_liquidity( -// RuntimeOrigin::root(), -// netuid.into(), -// true -// )); - -// let min_price = tick_to_price(TickIndex::MIN); -// let max_price = tick_to_price(TickIndex::MAX); -// let tick_low = price_to_tick(min_price); -// let tick_high = price_to_tick(max_price); -// let liquidity = 1_000_000_000_u64; - -// let (_pos_id, _tao, _alpha) = Pallet::::do_add_liquidity( -// netuid, -// &OK_COLDKEY_ACCOUNT_ID, -// &OK_HOTKEY_ACCOUNT_ID, -// tick_low, -// tick_high, -// liquidity, -// ) -// .expect("add liquidity"); - -// // Disable user LP *before* liquidation; removal must ignore this flag. -// assert_ok!(Swap::toggle_user_liquidity( -// RuntimeOrigin::root(), -// netuid.into(), -// false -// )); - -// // Users-only dissolve, then clear protocol liquidity/state. -// assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); -// assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); - -// // ASSERT: positions & ticks gone, state reset -// assert_eq!( -// Pallet::::count_positions(netuid, &OK_COLDKEY_ACCOUNT_ID), -// 0 -// ); -// assert!( -// Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) -// .next() -// .is_none() -// ); -// assert!(Ticks::::iter_prefix(netuid).next().is_none()); -// assert!( -// TickIndexBitmapWords::::iter_prefix((netuid,)) -// .next() -// .is_none() -// ); -// assert!(!PalSwapInitialized::::contains_key(netuid)); -// assert!(!AlphaSqrtPrice::::contains_key(netuid)); -// assert!(!CurrentTick::::contains_key(netuid)); -// assert!(!CurrentLiquidity::::contains_key(netuid)); -// assert!(!FeeGlobalTao::::contains_key(netuid)); -// assert!(!FeeGlobalAlpha::::contains_key(netuid)); - -// // `EnabledUserLiquidity` is removed by protocol clear stage. -// assert!(!EnabledUserLiquidity::::contains_key(netuid)); -// }); -// } - -// TODO: Revise when user liquidity is available -// Non‑palswap path: PalSwap not initialized (no positions, no map values); function -// must still clear any residual storages and succeed. -// #[test] -// fn test_liquidate_pal_uninitialized_ok_and_clears() { -// new_test_ext().execute_with(|| { -// let netuid = NetUid::from(202); - -// // Insert map values -// PalSwapInitialized::::insert(netuid, false); - -// // Sanity: PalSwap is not initialized -// assert!(!PalSwapInitialized::::get(netuid)); -// assert!( -// PositionsV2::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) -// .next() -// .is_none() -// ); - -// // ACT -// assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - -// // ASSERT: Defensive clears leave no residues and do not panic -// assert!( -// PositionsV2::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) -// .next() -// .is_none() -// ); - -// // All single-key maps should not have the key after liquidation -// assert!(!FeeRate::::contains_key(netuid)); -// assert!(!EnabledUserLiquidity::::contains_key(netuid)); -// assert!(!FeesTao::::contains_key(netuid)); -// assert!(!FeesAlpha::::contains_key(netuid)); -// assert!(!PalSwapInitialized::::contains_key(netuid)); -// assert!(!SwapBalancer::::contains_key(netuid)); -// }); -// } - /// Simple palswap path: PalSwap is initialized, but no positions, only protocol; function /// must still clear any residual storages and succeed. -/// TODO: Revise when user liquidity is available #[test] fn test_liquidate_pal_simple_ok_and_clears() { new_test_ext().execute_with(|| { @@ -2065,7 +800,6 @@ fn test_liquidate_pal_simple_ok_and_clears() { // Insert map values FeeRate::::insert(netuid, 1_000); - EnabledUserLiquidity::::insert(netuid, false); FeesTao::::insert(netuid, TaoCurrency::from(1_000)); FeesAlpha::::insert(netuid, AlphaCurrency::from(1_000)); PalSwapInitialized::::insert(netuid, true); @@ -2075,25 +809,12 @@ fn test_liquidate_pal_simple_ok_and_clears() { // Sanity: PalSwap is not initialized assert!(PalSwapInitialized::::get(netuid)); - assert!( - PositionsV2::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) - .next() - .is_none() - ); // ACT assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); - // ASSERT: Defensive clears leave no residues and do not panic - assert!( - PositionsV2::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) - .next() - .is_none() - ); - // All single-key maps should not have the key after liquidation assert!(!FeeRate::::contains_key(netuid)); - assert!(!EnabledUserLiquidity::::contains_key(netuid)); assert!(!FeesTao::::contains_key(netuid)); assert!(!FeesAlpha::::contains_key(netuid)); assert!(!PalSwapInitialized::::contains_key(netuid)); @@ -2101,526 +822,12 @@ fn test_liquidate_pal_simple_ok_and_clears() { }); } -// TODO: Revise when user liquidity is available -// #[test] -// fn test_liquidate_idempotent() { -// // V3 flavor -// new_test_ext().execute_with(|| { -// let netuid = NetUid::from(7); -// assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - -// // Add a small user position -// assert_ok!(Swap::toggle_user_liquidity( -// RuntimeOrigin::root(), -// netuid.into(), -// true -// )); -// let tick_low = price_to_tick(0.2); -// let tick_high = price_to_tick(0.3); -// assert_ok!(Pallet::::do_add_liquidity( -// netuid, -// &OK_COLDKEY_ACCOUNT_ID, -// &OK_HOTKEY_ACCOUNT_ID, -// tick_low, -// tick_high, -// 123_456_789 -// )); - -// // Users-only liquidations are idempotent. -// assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); -// assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - -// // Now clear protocol liquidity/state—also idempotent. -// assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); -// assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); - -// // State remains empty -// assert!( -// Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) -// .next() -// .is_none() -// ); -// assert!(Ticks::::iter_prefix(netuid).next().is_none()); -// assert!( -// TickIndexBitmapWords::::iter_prefix((netuid,)) -// .next() -// .is_none() -// ); -// assert!(!PalSwapInitialized::::contains_key(netuid)); -// }); - -// // Non‑V3 flavor -// new_test_ext().execute_with(|| { -// let netuid = NetUid::from(8); - -// // Never initialize V3; both calls no-op and succeed. -// assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); -// assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - -// assert!( -// Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) -// .next() -// .is_none() -// ); -// assert!(Ticks::::iter_prefix(netuid).next().is_none()); -// assert!( -// TickIndexBitmapWords::::iter_prefix((netuid,)) -// .next() -// .is_none() -// ); -// assert!(!PalSwapInitialized::::contains_key(netuid)); -// }); -// } - -// TODO: Revise when user liquidity is available -// #[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_palswap(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"); -// let alpha_taken = ::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(), alpha_taken); - -// // 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!(!PalSwapInitialized::::contains_key(netuid)); -// }); -// } - -// TODO: Revise when user liquidity is available -// #[test] -// fn refund_alpha_single_provider_exact() { -// new_test_ext().execute_with(|| { -// let netuid = NetUid::from(11); -// let cold = OK_COLDKEY_ACCOUNT_ID; -// let hot = OK_HOTKEY_ACCOUNT_ID; - -// assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - -// // --- Create an alpha‑only position (range entirely above current tick → TAO = 0, ALPHA > 0). -// let ct = CurrentTick::::get(netuid); -// let tick_low = ct.next().expect("current tick should not be MAX in tests"); -// let tick_high = TickIndex::MAX; - -// let liquidity = 1_000_000_u64; -// let (_pos_id, tao_needed, alpha_needed) = -// Pallet::::do_add_liquidity(netuid, &cold, &hot, tick_low, tick_high, liquidity) -// .expect("add alpha-only liquidity"); -// assert_eq!(tao_needed, 0, "alpha-only position must not require TAO"); -// assert!(alpha_needed > 0, "alpha-only position must require ALPHA"); - -// // --- Snapshot BEFORE we withdraw funds (baseline for conservation). -// 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; - -// // --- Mimic extrinsic bookkeeping: withdraw α and record provided reserve. -// let alpha_taken = ::BalanceOps::decrease_stake( -// &cold, -// &hot, -// netuid.into(), -// alpha_needed.into(), -// ) -// .expect("decrease ALPHA"); -// AlphaReserve::increase_provided(netuid.into(), alpha_taken); - -// // --- Act: users‑only dissolve. -// assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - -// // --- Assert: total α conserved to owner (may be staked to validator). -// 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 conserved to the account" -// ); - -// // Clear protocol liquidity and V3 state now. -// assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); - -// // --- State is cleared. -// assert!(Ticks::::iter_prefix(netuid).next().is_none()); -// assert_eq!(Pallet::::count_positions(netuid, &cold), 0); -// assert!(!PalSwapInitialized::::contains_key(netuid)); -// }); -// } - -// TODO: Revise when user liquidity is available -// #[test] -// fn refund_alpha_multiple_providers_proportional_to_principal() { -// new_test_ext().execute_with(|| { -// let netuid = NetUid::from(12); -// let c1 = OK_COLDKEY_ACCOUNT_ID; -// let h1 = OK_HOTKEY_ACCOUNT_ID; -// let c2 = OK_COLDKEY_ACCOUNT_ID_2; -// let h2 = OK_HOTKEY_ACCOUNT_ID_2; - -// assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - -// // Use the same "above current tick" trick for alpha‑only positions. -// let ct = CurrentTick::::get(netuid); -// let tick_low = ct.next().expect("current tick should not be MAX in tests"); -// let tick_high = TickIndex::MAX; - -// // Provider #1 (smaller α) -// let liq1 = 700_000_u64; -// let (_p1, t1, a1) = -// Pallet::::do_add_liquidity(netuid, &c1, &h1, tick_low, tick_high, liq1) -// .expect("add alpha-only liquidity #1"); -// assert_eq!(t1, 0); -// assert!(a1 > 0); - -// // Provider #2 (larger α) -// let liq2 = 2_100_000_u64; -// let (_p2, t2, a2) = -// Pallet::::do_add_liquidity(netuid, &c2, &h2, tick_low, tick_high, liq2) -// .expect("add alpha-only liquidity #2"); -// assert_eq!(t2, 0); -// assert!(a2 > 0); - -// // Baselines BEFORE withdrawing -// let a1_before_hot = ::BalanceOps::alpha_balance(netuid.into(), &c1, &h1); -// let a1_before_owner = ::BalanceOps::alpha_balance(netuid.into(), &c1, &c1); -// let a1_before = a1_before_hot + a1_before_owner; - -// let a2_before_hot = ::BalanceOps::alpha_balance(netuid.into(), &c2, &h2); -// let a2_before_owner = ::BalanceOps::alpha_balance(netuid.into(), &c2, &c2); -// let a2_before = a2_before_hot + a2_before_owner; - -// // Withdraw α and account reserves for each provider. -// let a1_taken = -// ::BalanceOps::decrease_stake(&c1, &h1, netuid.into(), a1.into()) -// .expect("decrease α #1"); -// AlphaReserve::increase_provided(netuid.into(), a1_taken); - -// let a2_taken = -// ::BalanceOps::decrease_stake(&c2, &h2, netuid.into(), a2.into()) -// .expect("decrease α #2"); -// AlphaReserve::increase_provided(netuid.into(), a2_taken); - -// // Act -// assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - -// // Each owner is restored to their exact baseline. -// let a1_after_hot = ::BalanceOps::alpha_balance(netuid.into(), &c1, &h1); -// let a1_after_owner = ::BalanceOps::alpha_balance(netuid.into(), &c1, &c1); -// let a1_after = a1_after_hot + a1_after_owner; -// assert_eq!( -// a1_after, a1_before, -// "owner #1 must receive their α principal back" -// ); - -// let a2_after_hot = ::BalanceOps::alpha_balance(netuid.into(), &c2, &h2); -// let a2_after_owner = ::BalanceOps::alpha_balance(netuid.into(), &c2, &c2); -// let a2_after = a2_after_hot + a2_after_owner; -// assert_eq!( -// a2_after, a2_before, -// "owner #2 must receive their α principal back" -// ); -// }); -// } - -// TODO: Revise when user liquidity is available -// #[test] -// fn refund_alpha_same_cold_multiple_hotkeys_conserved_to_owner() { -// new_test_ext().execute_with(|| { -// let netuid = NetUid::from(13); -// let cold = OK_COLDKEY_ACCOUNT_ID; -// let hot1 = OK_HOTKEY_ACCOUNT_ID; -// let hot2 = OK_HOTKEY_ACCOUNT_ID_2; - -// assert_ok!(Pallet::::maybe_initialize_palswap(netuid)); - -// // Two alpha‑only positions on different hotkeys of the same owner. -// let ct = CurrentTick::::get(netuid); -// let tick_low = ct.next().expect("current tick should not be MAX in tests"); -// let tick_high = TickIndex::MAX; - -// let (_p1, _t1, a1) = -// Pallet::::do_add_liquidity(netuid, &cold, &hot1, tick_low, tick_high, 900_000) -// .expect("add alpha-only pos (hot1)"); -// let (_p2, _t2, a2) = -// Pallet::::do_add_liquidity(netuid, &cold, &hot2, tick_low, tick_high, 1_500_000) -// .expect("add alpha-only pos (hot2)"); -// assert!(a1 > 0 && a2 > 0); - -// // Baseline BEFORE: sum over (cold,hot1) + (cold,hot2) + (cold,cold). -// let before_hot1 = ::BalanceOps::alpha_balance(netuid.into(), &cold, &hot1); -// let before_hot2 = ::BalanceOps::alpha_balance(netuid.into(), &cold, &hot2); -// let before_owner = ::BalanceOps::alpha_balance(netuid.into(), &cold, &cold); -// let before_total = before_hot1 + before_hot2 + before_owner; - -// // Withdraw α from both hotkeys; track provided‑reserve. -// let t1 = -// ::BalanceOps::decrease_stake(&cold, &hot1, netuid.into(), a1.into()) -// .expect("decr α #hot1"); -// AlphaReserve::increase_provided(netuid.into(), t1); - -// let t2 = -// ::BalanceOps::decrease_stake(&cold, &hot2, netuid.into(), a2.into()) -// .expect("decr α #hot2"); -// AlphaReserve::increase_provided(netuid.into(), t2); - -// // Act -// assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - -// // The total α "owned" by the coldkey is conserved (credit may land on (cold,cold)). -// let after_hot1 = ::BalanceOps::alpha_balance(netuid.into(), &cold, &hot1); -// let after_hot2 = ::BalanceOps::alpha_balance(netuid.into(), &cold, &hot2); -// let after_owner = ::BalanceOps::alpha_balance(netuid.into(), &cold, &cold); -// let after_total = after_hot1 + after_hot2 + after_owner; - -// assert_eq!( -// after_total, before_total, -// "owner’s α must be conserved across hot ledgers + (owner,owner)" -// ); -// }); -// } - -// TODO: Revise when user liquidity is available -// #[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_palswap(netuid)); -// assert!(PalSwapInitialized::::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"); -// let alpha_taken = ::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_taken); - -// // --- 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!(!PalSwapInitialized::::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)); -// }); -// } - -// TODO: Revise when user liquidity is available #[test] fn test_clear_protocol_liquidity_green_path() { new_test_ext().execute_with(|| { // --- Arrange --- let netuid = NetUid::from(1); - // Ensure the "user liquidity enabled" flag exists so we can verify it's removed later. - EnabledUserLiquidity::::insert(netuid, true); - // Initialize swap state assert_ok!(Pallet::::maybe_initialize_palswap(netuid, None)); assert!( @@ -2628,31 +835,10 @@ fn test_clear_protocol_liquidity_green_path() { "Swap must be initialized" ); - // Sanity: protocol positions exist before clearing. - // TODO: Revise when user liquidity is available - // let protocol_id = Pallet::::protocol_account_id(); - // let prot_positions_before = - // Positions::::iter_prefix_values((netuid, protocol_id)).collect::>(); - // assert!( - // !prot_positions_before.is_empty(), - // "protocol positions should exist after V3 init" - // ); - // --- Act --- // Green path: just clear protocol liquidity and wipe all V3 state. assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); - // --- Assert: all protocol positions removed --- - // TODO: Revise when user liquidity is available - // let prot_positions_after = - // Positions::::iter_prefix_values((netuid, protocol_id)).collect::>(); - // assert!( - // prot_positions_after.is_empty(), - // "protocol positions must be removed by do_clear_protocol_liquidity" - // ); - - // --- Assert: Swap data wiped (idempotent even if some maps were empty) --- - // Fee globals assert!(!FeesTao::::contains_key(netuid)); assert!(!FeesAlpha::::contains_key(netuid)); @@ -2662,15 +848,9 @@ fn test_clear_protocol_liquidity_green_path() { // Knobs removed assert!(!FeeRate::::contains_key(netuid)); - assert!(!EnabledUserLiquidity::::contains_key(netuid)); // --- And it's idempotent --- assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); - // assert!( - // PositionsV2::::iter_prefix_values((netuid, protocol_id)) - // .next() - // .is_none() - // ); assert!(!PalSwapInitialized::::contains_key(netuid)); }); } diff --git a/pallets/swap/src/position.rs b/pallets/swap/src/position.rs deleted file mode 100644 index 864d63a2ab..0000000000 --- a/pallets/swap/src/position.rs +++ /dev/null @@ -1,100 +0,0 @@ -use core::marker::PhantomData; - -use codec::{Decode, Encode, MaxEncodedLen}; -use frame_support::pallet_prelude::*; -// use safe_math::*; -use substrate_fixed::types::U64F64; -use subtensor_macros::freeze_struct; -use subtensor_runtime_common::NetUid; - -use crate::pallet::{Config, Error, LastPositionId}; - -/// Position designates one liquidity position. -#[freeze_struct("6d7ff015e0a73860")] -#[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen, Default)] -#[scale_info(skip_type_params(T))] -pub struct Position { - /// Unique ID of the position - pub id: PositionId, - /// Network identifier - pub netuid: NetUid, - // /// Position liquidity - // pub liquidity: u64, - // /// Fees accrued by the position in quote currency (TAO) relative to global fees - // pub fees_tao: I64F64, - // /// Fees accrued by the position in base currency (Alpha) relative to global fees - // pub fees_alpha: I64F64, - /// Phantom marker for generic Config type - pub _phantom: PhantomData, -} - -impl Position { - pub fn new( - id: PositionId, - netuid: NetUid, - // liquidity: u64, - ) -> Self { - Position { - id, - netuid, - // liquidity, - // fees_tao: I64F64::saturating_from_num(0), - // fees_alpha: I64F64::saturating_from_num(0), - _phantom: PhantomData, - } - } - - /// Converts position to token amounts - /// - /// returns tuple of (TAO, Alpha) - /// - pub fn to_token_amounts(&self, _price_curr: U64F64) -> Result<(u64, u64), Error> { - // TODO: Revise when user liquidity is available - Ok((0, 0)) - } - - /// Collect fees for a position - /// Updates the position - pub fn collect_fees(&mut self) -> (u64, u64) { - // TODO: Revise when user liquidity is available - (0, 0) - } -} - -#[freeze_struct("8501fa251c9d74c")] -#[derive( - Clone, - Copy, - Decode, - DecodeWithMemTracking, - Default, - Encode, - Eq, - MaxEncodedLen, - PartialEq, - RuntimeDebug, - TypeInfo, -)] -pub struct PositionId(u128); - -impl PositionId { - /// Create a new position ID - pub fn new() -> Self { - let new = LastPositionId::::get().saturating_add(1); - LastPositionId::::put(new); - - Self(new) - } -} - -impl From for PositionId { - fn from(value: u128) -> Self { - Self(value) - } -} - -impl From for u128 { - fn from(value: PositionId) -> Self { - value.0 - } -} diff --git a/pallets/swap/src/weights.rs b/pallets/swap/src/weights.rs index 2bbbb8dbdf..210bf1dc6d 100644 --- a/pallets/swap/src/weights.rs +++ b/pallets/swap/src/weights.rs @@ -15,10 +15,6 @@ use sp_std::marker::PhantomData; /// Weight functions needed for pallet_subtensor_swap. pub trait WeightInfo { fn set_fee_rate() -> Weight; - fn add_liquidity() -> Weight; - fn remove_liquidity() -> Weight; - fn modify_position() -> Weight; - fn toggle_user_liquidity() -> Weight; } /// Default weights for pallet_subtensor_swap. @@ -30,34 +26,6 @@ impl WeightInfo for DefaultWeight { .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - - fn add_liquidity() -> Weight { - // Conservative weight estimate for add_liquidity - Weight::from_parts(50_000_000, 0) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(4)) - } - - fn remove_liquidity() -> Weight { - // Conservative weight estimate for remove_liquidity - Weight::from_parts(50_000_000, 0) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(4)) - } - - fn modify_position() -> Weight { - // Conservative weight estimate for modify_position - Weight::from_parts(50_000_000, 0) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(4)) - } - - fn toggle_user_liquidity() -> Weight { - // Conservative weight estimate: one read and one write - Weight::from_parts(10_000_000, 0) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) - } } // For backwards compatibility and tests @@ -67,28 +35,4 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(1)) .saturating_add(RocksDbWeight::get().writes(1)) } - - fn add_liquidity() -> Weight { - Weight::from_parts(50_000_000, 0) - .saturating_add(RocksDbWeight::get().reads(5)) - .saturating_add(RocksDbWeight::get().writes(4)) - } - - fn remove_liquidity() -> Weight { - Weight::from_parts(50_000_000, 0) - .saturating_add(RocksDbWeight::get().reads(4)) - .saturating_add(RocksDbWeight::get().writes(4)) - } - - fn modify_position() -> Weight { - Weight::from_parts(50_000_000, 0) - .saturating_add(RocksDbWeight::get().reads(4)) - .saturating_add(RocksDbWeight::get().writes(4)) - } - - fn toggle_user_liquidity() -> Weight { - Weight::from_parts(10_000_000, 0) - .saturating_add(RocksDbWeight::get().reads(1)) - .saturating_add(RocksDbWeight::get().writes(1)) - } } From 7e8dc3a676dcfcdc5177e92f4c1495f117eec9f5 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Tue, 3 Feb 2026 17:44:31 -0500 Subject: [PATCH 209/240] Cleanup test_claim_root_coinbase_distribution test --- pallets/subtensor/src/tests/claim_root.rs | 97 ----------------------- 1 file changed, 97 deletions(-) diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index 9897030210..9d08c65e3c 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -1098,103 +1098,6 @@ fn test_claim_root_coinbase_distribution() { }); } -// #[test] -// fn test_claim_root_coinbase_distribution() { -// new_test_ext(1).execute_with(|| { -// let owner_coldkey = U256::from(1001); -// let hotkey = U256::from(1002); -// let coldkey = U256::from(1003); -// let netuid = add_dynamic_network(&hotkey, &owner_coldkey); - -// Tempo::::insert(netuid, 1); -// SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 - -// let root_stake = 200_000_000u64; -// let initial_tao = 200_000_000u64; -// SubnetTAO::::insert(NetUid::ROOT, TaoCurrency::from(initial_tao)); - -// SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( -// &hotkey, -// &coldkey, -// NetUid::ROOT, -// root_stake.into(), -// ); - -// let initial_total_hotkey_alpha = 10_000_000u64; -// SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( -// &hotkey, -// &owner_coldkey, -// netuid, -// initial_total_hotkey_alpha.into(), -// ); - -// let initial_alpha_issuance = SubtensorModule::get_alpha_issuance(netuid); -// let alpha_emissions: AlphaCurrency = 1_000_000_000u64.into(); - -// // Set moving price > 1.0 and price > 1.0 -// // So we turn ON root sell -// SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); -// let tao = TaoCurrency::from(10_000_000_000_000_u64); -// let alpha = AlphaCurrency::from(1_000_000_000_000_u64); -// SubnetTAO::::insert(netuid, tao); -// SubnetAlphaIn::::insert(netuid, alpha); -// let current_price = -// ::SwapInterface::current_alpha_price(netuid.into()) -// .saturating_to_num::(); -// assert_eq!(current_price, 10.0f64); -// RootClaimableThreshold::::insert(netuid, I96F32::from_num(0)); - -// // Make sure we are root selling, so we have root alpha divs. -// let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); -// assert!(root_sell_flag, "Root sell flag should be true"); - -// // Set TAOFlow > 0 -// SubnetTaoFlow::::insert(netuid, 2222_i64); - -// // Check total issuance (saved to pending alpha divs) -// run_to_block(2); - -// let alpha_issuance = SubtensorModule::get_alpha_issuance(netuid); -// // We went two blocks so we should have 2x the alpha emissions -// assert_eq!( -// initial_alpha_issuance + alpha_emissions.saturating_mul(2.into()), -// alpha_issuance -// ); - -// let root_prop = initial_tao as f64 / (u64::from(alpha_issuance) + initial_tao) as f64; -// let root_validators_share = 0.5f64; - -// let expected_pending_root_alpha_divs = -// u64::from(alpha_emissions) as f64 * root_prop * root_validators_share; -// assert_abs_diff_eq!( -// u64::from(PendingRootAlphaDivs::::get(netuid)) as f64, -// expected_pending_root_alpha_divs, -// epsilon = 100f64 -// ); - -// // Epoch pending alphas divs is distributed - -// run_to_block(3); - -// assert_eq!(u64::from(PendingRootAlphaDivs::::get(netuid)), 0u64); - -// let claimable = *RootClaimable::::get(hotkey) -// .get(&netuid) -// .expect("claimable must exist at this point"); - -// let validator_take_percent = 0.18f64; -// let calculated_rate = (expected_pending_root_alpha_divs * 2f64) -// * (1f64 - validator_take_percent) -// / (root_stake as f64); - -// assert_abs_diff_eq!( -// claimable.saturating_to_num::(), -// calculated_rate, -// epsilon = 0.001f64, -// ); -// }); -// } - #[test] fn test_sudo_set_num_root_claims() { new_test_ext(1).execute_with(|| { From 4e21da29372b0f04d66a5866e043d7f03851693e Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Tue, 3 Feb 2026 18:27:18 -0500 Subject: [PATCH 210/240] More user liquidity cleanup --- chain-extensions/src/mock.rs | 2 - pallets/admin-utils/src/tests/mock.rs | 2 - pallets/subtensor/src/tests/mock.rs | 2 - pallets/swap/src/mock.rs | 2 - pallets/swap/src/pallet/balancer.rs | 125 ------------------ pallets/swap/src/pallet/impls.rs | 7 +- .../migrations/migrate_swapv3_to_balancer.rs | 1 + pallets/swap/src/pallet/mod.rs | 121 ++++++++++++++--- pallets/swap/src/pallet/tests.rs | 16 +-- pallets/transaction-fee/src/tests/mock.rs | 2 - runtime/src/lib.rs | 2 - 11 files changed, 105 insertions(+), 177 deletions(-) diff --git a/chain-extensions/src/mock.rs b/chain-extensions/src/mock.rs index 27ac5bc06e..69fb9de089 100644 --- a/chain-extensions/src/mock.rs +++ b/chain-extensions/src/mock.rs @@ -417,7 +417,6 @@ impl pallet_subtensor::Config for Test { 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(); } @@ -429,7 +428,6 @@ impl pallet_subtensor_swap::Config for Test { type TaoReserve = TaoCurrencyReserve; type AlphaReserve = AlphaCurrencyReserve; type MaxFeeRate = SwapMaxFeeRate; - type MaxPositions = SwapMaxPositions; type MinimumLiquidity = SwapMinimumLiquidity; type MinimumReserve = SwapMinimumReserve; type WeightInfo = (); diff --git a/pallets/admin-utils/src/tests/mock.rs b/pallets/admin-utils/src/tests/mock.rs index f97e678034..e93029eb5e 100644 --- a/pallets/admin-utils/src/tests/mock.rs +++ b/pallets/admin-utils/src/tests/mock.rs @@ -322,7 +322,6 @@ impl pallet_balances::Config for Test { 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(1_000_000).unwrap(); } @@ -334,7 +333,6 @@ impl pallet_subtensor_swap::Config for Test { type TaoReserve = pallet_subtensor::TaoCurrencyReserve; type AlphaReserve = pallet_subtensor::AlphaCurrencyReserve; type MaxFeeRate = SwapMaxFeeRate; - type MaxPositions = SwapMaxPositions; type MinimumLiquidity = SwapMinimumLiquidity; type MinimumReserve = SwapMinimumReserve; type WeightInfo = (); diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index f0dfd89bdf..b842c97a60 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -308,7 +308,6 @@ impl crate::Config for Test { 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(); } @@ -320,7 +319,6 @@ impl pallet_subtensor_swap::Config for Test { type TaoReserve = TaoCurrencyReserve; type AlphaReserve = AlphaCurrencyReserve; type MaxFeeRate = SwapMaxFeeRate; - type MaxPositions = SwapMaxPositions; type MinimumLiquidity = SwapMinimumLiquidity; type MinimumReserve = SwapMinimumReserve; type WeightInfo = (); diff --git a/pallets/swap/src/mock.rs b/pallets/swap/src/mock.rs index 52ab578ea9..142a59b8df 100644 --- a/pallets/swap/src/mock.rs +++ b/pallets/swap/src/mock.rs @@ -87,7 +87,6 @@ impl system::Config for Test { parameter_types! { pub const SwapProtocolId: PalletId = PalletId(*b"ten/swap"); pub const MaxFeeRate: u16 = 10000; // 15.26% - pub const MaxPositions: u32 = 100; pub const MinimumLiquidity: u64 = 1_000; pub const MinimumReserves: NonZeroU64 = NonZeroU64::new(1).unwrap(); } @@ -304,7 +303,6 @@ impl crate::pallet::Config for Test { type BalanceOps = MockBalanceOps; type ProtocolId = SwapProtocolId; type MaxFeeRate = MaxFeeRate; - type MaxPositions = MaxPositions; type MinimumLiquidity = MinimumLiquidity; type MinimumReserve = MinimumReserves; type WeightInfo = (); diff --git a/pallets/swap/src/pallet/balancer.rs b/pallets/swap/src/pallet/balancer.rs index 14e5623091..1e1386bd41 100644 --- a/pallets/swap/src/pallet/balancer.rs +++ b/pallets/swap/src/pallet/balancer.rs @@ -379,49 +379,6 @@ impl Balancer { } } - /// Calculates current liquidity from alpha and tao reserves using the formula: - /// L = x^w1 * y^w2 - /// where - /// x - alpha reserve - /// y - tao reserve - /// w1 - base weight - /// w2 - quote weight - pub fn calculate_current_liquidity(&self, tao_reserve: u64, alpha_reserve: u64) -> u64 { - let base_numerator_x: u128 = alpha_reserve as u128; - let base_numerator_y: u128 = tao_reserve as u128; - // let base_denominator: u128 = 1_u128; - let w1_fixed: u128 = self.get_base_weight().deconstruct() as u128; - let w2_fixed: u128 = self.get_quote_weight().deconstruct() as u128; - let scale = SafeInt::from(10u128.pow(18)); - - let exp_x = SafeInt::pow_bigint_base( - &SafeInt::from(base_numerator_x), - &SafeInt::from(w1_fixed), - &SafeInt::from(ACCURACY), - 1024, - &scale, - ) - .unwrap_or(SafeInt::from(0)); - let exp_y = SafeInt::pow_bigint_base( - &SafeInt::from(base_numerator_y), - &SafeInt::from(w2_fixed), - &SafeInt::from(ACCURACY), - 1024, - &scale, - ) - .unwrap_or(SafeInt::from(0)); - - // 0.5 scaled for rounding to the nearest integer - // Allow arithmetic side effects here: SafeInt doesn't panic - #[allow(clippy::arithmetic_side_effects)] - let round_nearest_offset = (scale.clone() / SafeInt::from(2)).unwrap_or_default(); - #[allow(clippy::arithmetic_side_effects)] - ((((exp_x * exp_y) / scale.clone()).unwrap_or_default() + round_nearest_offset) / scale) - .unwrap_or_default() - .to_u64() - .unwrap_or(0) - } - /// Calculates amount of Alpha that needs to be sold to get a given amount of TAO pub fn get_base_needed_for_quote( &self, @@ -1070,88 +1027,6 @@ mod tests { assert_abs_diff_eq!(price_f, expected_f, epsilon = 1e-9); } - #[test] - fn test_calculate_current_liquidity() { - // Test case: quote weight (numerator), alpha, tao - // Outer test cases: w_quote - [ - 500_000_000_000_000_000_u64, - 500_000_000_001_000_000, - 499_999_999_999_000_000, - 500_000_000_100_000_000, - 500_000_001_000_000_000, - 500_000_010_000_000_000, - 500_000_100_000_000_000, - 500_001_000_000_000_000, - 500_010_000_000_000_000, - 500_100_000_000_000_000, - 501_000_000_000_000_000, - 510_000_000_000_000_000, - 100_000_000_000_000_000, - 100_000_000_001_000_000, - 200_000_000_000_000_000, - 300_000_000_000_000_000, - 400_000_000_000_000_000, - 600_000_000_000_000_000, - 700_000_000_000_000_000, - 800_000_000_000_000_000, - 899_999_999_999_000_000, - 900_000_000_000_000_000, - 102_337_248_363_782_924, - ] - .into_iter() - .for_each(|w_quote| { - [ - (0_u64, 0_u64), - (1_000_u64, 0_u64), - (0_u64, 1_000_u64), - (1_u64, 1_u64), - (2_u64, 1_u64), - (1_u64, 2_u64), - (1_000_u64, 1_000_u64), - (2_000_u64, 1_000_u64), - (1_000_u64, 2_000_u64), - (1_000_000_u64, 1_000_000_u64), - (2_000_000_u64, 1_000_000_u64), - (1_000_000_u64, 2_000_000_u64), - (1_000_000_000_u64, 1_000_000_000_u64), - (2_000_000_000_u64, 1_000_000_000_u64), - (1_000_000_000_u64, 2_000_000_000_u64), - (1_000_000_000_000_u64, 1_000_u64), - (1_000_u64, 1_000_000_000_000_u64), - (1_000_000_000_000_000_u64, 1_u64), - (1_u64, 1_000_000_000_000_000_u64), - (1_000_000_000_000_000_u64, 1_000_u64), - (1_000_u64, 1_000_000_000_000_000_u64), - (1_000_u64, 21_000_000_000_000_000_u64), - (21_000_000_000_000_000_u64, 1_000_u64), - (1_u64, 21_000_000_000_000_000_u64), - (21_000_000_000_000_000_u64, 1_u64), - (2_u64, 21_000_000_000_000_000_u64), - (21_000_000_000_000_000_u64, 2_u64), - (21_000_000_000_000_000_u64, 21_000_000_000_000_000_u64), - (2, u64::MAX), - (u64::MAX, 2), - (2, u64::MAX - 1), - (u64::MAX - 1, 2), - (u64::MAX, u64::MAX), - ] - .into_iter() - .for_each(|(alpha, tao)| { - let quote = Perquintill::from_rational(w_quote, ACCURACY); - let bal = Balancer::new(quote).unwrap(); - - let actual = bal.calculate_current_liquidity(tao, alpha); - - let w1 = w_quote as f64 / ACCURACY as f64; - let w2 = (ACCURACY - w_quote) as f64 / ACCURACY as f64; - let expected = (((alpha as f64).powf(w2) * (tao as f64).powf(w1)) + 0.5) as u64; - - assert_abs_diff_eq!(actual, expected, epsilon = expected / 1_000_000_000_000); - }); - }); - } - // cargo test --package pallet-subtensor-swap --lib -- pallet::balancer::tests::test_exp_scaled --exact --nocapture #[test] fn test_exp_scaled() { diff --git a/pallets/swap/src/pallet/impls.rs b/pallets/swap/src/pallet/impls.rs index c499e0bb49..8c3fd68209 100644 --- a/pallets/swap/src/pallet/impls.rs +++ b/pallets/swap/src/pallet/impls.rs @@ -81,11 +81,6 @@ impl Pallet { })?; SwapBalancer::::insert(netuid, balancer.clone()); - // Insert current liquidity - let liquidity = - balancer.calculate_current_liquidity(u64::from(tao_reserve), u64::from(alpha_reserve)); - CurrentLiquidity::::insert(netuid, liquidity); - PalSwapInitialized::::insert(netuid, true); Ok(()) @@ -293,7 +288,7 @@ impl Pallet { pub fn do_clear_protocol_liquidity(netuid: NetUid) -> DispatchResult { // let protocol_account = Self::protocol_account_id(); - // 1) Force-close only protocol positions, burning proceeds. + // 1) Force-close protocol liquidity, burning proceeds. let burned_tao = T::TaoReserve::reserve(netuid.into()); let burned_alpha = T::AlphaReserve::reserve(netuid.into()); diff --git a/pallets/swap/src/pallet/migrations/migrate_swapv3_to_balancer.rs b/pallets/swap/src/pallet/migrations/migrate_swapv3_to_balancer.rs index a36e6cc714..147849e5a6 100644 --- a/pallets/swap/src/pallet/migrations/migrate_swapv3_to_balancer.rs +++ b/pallets/swap/src/pallet/migrations/migrate_swapv3_to_balancer.rs @@ -55,6 +55,7 @@ pub fn migrate_swapv3_to_balancer() -> Weight { remove_prefix::("Swap", "EnabledUserLiquidity", &mut weight); remove_prefix::("Swap", "FeeGlobalTao", &mut weight); remove_prefix::("Swap", "FeeGlobalAlpha", &mut weight); + remove_prefix::("Swap", "LastPositionId", &mut weight); // Scrap reservoirs can be just cleaned because they are already included in reserves remove_prefix::("Swap", "ScrapReservoirTao", &mut weight); remove_prefix::("Swap", "ScrapReservoirAlpha", &mut weight); diff --git a/pallets/swap/src/pallet/mod.rs b/pallets/swap/src/pallet/mod.rs index fff070f79b..3c6f815e2a 100644 --- a/pallets/swap/src/pallet/mod.rs +++ b/pallets/swap/src/pallet/mod.rs @@ -57,10 +57,6 @@ mod pallet { #[pallet::constant] type MaxFeeRate: Get; - /// The maximum number of positions a user can have - #[pallet::constant] - type MaxPositions: Get; - /// Minimum liquidity that is safe for rounding and integer math. #[pallet::constant] type MinimumLiquidity: Get; @@ -83,14 +79,6 @@ mod pallet { #[pallet::storage] pub type FeeRate = StorageMap<_, Twox64Concat, NetUid, u16, ValueQuery, DefaultFeeRate>; - /// Storage for the current liquidity amount for each subnet. - #[pallet::storage] - pub type CurrentLiquidity = StorageMap<_, Twox64Concat, NetUid, u64, ValueQuery>; - - /// Position ID counter. - #[pallet::storage] - pub type LastPositionId = StorageValue<_, u128, ValueQuery>; - //////////////////////////////////////////////////// // Balancer (PalSwap) maps and variables @@ -146,18 +134,9 @@ mod pallet { /// The caller does not have enough balance for the operation. InsufficientBalance, - /// Attempted to remove liquidity that does not exist. - LiquidityNotFound, - /// The provided tick range is invalid. InvalidTickRange, - /// Maximum user positions exceeded - MaxPositionsExceeded, - - /// Too many swap steps - TooManySwapSteps, - /// Provided liquidity parameter is invalid (likely too small) InvalidLiquidityValue, @@ -172,6 +151,9 @@ mod pallet { /// Swap reserves are too imbalanced ReservesOutOfBalance, + + /// The extrinsic is deprecated + Deprecated, } #[pallet::call] @@ -201,5 +183,102 @@ mod pallet { Ok(()) } + + /// DEPRECATED + #[pallet::call_index(4)] + #[pallet::weight(Weight::from_parts(15_000_000, 0))] + #[deprecated(note = "Deprecated, user liquidity is permanently disabled")] + pub fn toggle_user_liquidity( + _origin: OriginFor, + _netuid: NetUid, + _enable: bool, + ) -> DispatchResult { + Err(Error::::Deprecated.into()) + } + + /// DEPRECATED + #[pallet::call_index(1)] + #[pallet::weight(Weight::from_parts(15_000_000, 0))] + #[deprecated(note = "Deprecated, user liquidity is permanently disabled")] + pub fn add_liquidity( + _origin: OriginFor, + _hotkey: T::AccountId, + _netuid: NetUid, + _tick_low: TickIndex, + _tick_high: TickIndex, + _liquidity: u64, + ) -> DispatchResult { + Err(Error::::Deprecated.into()) + } + + /// DEPRECATED + #[pallet::call_index(2)] + #[pallet::weight(Weight::from_parts(15_000_000, 0))] + #[deprecated(note = "Deprecated, user liquidity is permanently disabled")] + pub fn remove_liquidity( + _origin: OriginFor, + _hotkey: T::AccountId, + _netuid: NetUid, + _position_id: PositionId, + ) -> DispatchResult { + Err(Error::::Deprecated.into()) + } + + /// DEPRECATED + #[pallet::call_index(3)] + #[pallet::weight(Weight::from_parts(15_000_000, 0))] + #[deprecated(note = "Deprecated, user liquidity is permanently disabled")] + pub fn modify_position( + _origin: OriginFor, + _hotkey: T::AccountId, + _netuid: NetUid, + _position_id: PositionId, + _liquidity_delta: i64, + ) -> DispatchResult { + Err(Error::::Deprecated.into()) + } + + /// DEPRECATED + #[pallet::call_index(5)] + #[pallet::weight(Weight::from_parts(15_000_000, 0))] + #[deprecated(note = "Deprecated, user liquidity is permanently disabled")] + pub fn disable_lp(_origin: OriginFor) -> DispatchResult { + Err(Error::::Deprecated.into()) + } } } + +/// Struct representing a tick index, DEPRECATED +#[derive( + Debug, + Default, + Clone, + Copy, + Decode, + Encode, + DecodeWithMemTracking, + TypeInfo, + MaxEncodedLen, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, +)] +pub struct TickIndex(i32); + +/// Struct representing a liquidity position ID, DEPRECATED +#[derive( + Clone, + Copy, + Decode, + DecodeWithMemTracking, + Default, + Encode, + Eq, + MaxEncodedLen, + PartialEq, + RuntimeDebug, + TypeInfo, +)] +pub struct PositionId(u128); diff --git a/pallets/swap/src/pallet/tests.rs b/pallets/swap/src/pallet/tests.rs index a085e8a64f..912c89bbe8 100644 --- a/pallets/swap/src/pallet/tests.rs +++ b/pallets/swap/src/pallet/tests.rs @@ -7,7 +7,7 @@ use approx::assert_abs_diff_eq; use frame_support::{assert_noop, assert_ok}; -use sp_arithmetic::{Perquintill, helpers_128bit}; +use sp_arithmetic::Perquintill; use sp_runtime::DispatchError; use substrate_fixed::types::U64F64; use subtensor_runtime_common::{Currency, NetUid}; @@ -426,16 +426,6 @@ fn test_swap_initialization() { reserve_weight.get_quote_weight(), Perquintill::from_rational(1_u64, 2_u64), ); - - // Current liquidity is initialized - let expected_liquidity = - helpers_128bit::sqrt((tao.to_u64() as u128).saturating_mul(alpha.to_u64() as u128)) - as u64; - assert_abs_diff_eq!( - CurrentLiquidity::::get(netuid), - expected_liquidity, - epsilon = 1 - ); }); } @@ -791,8 +781,8 @@ fn print_current_price(netuid: NetUid) { log::trace!("Current price: {current_price:.6}"); } -/// Simple palswap path: PalSwap is initialized, but no positions, only protocol; function -/// must still clear any residual storages and succeed. +/// Simple palswap path: PalSwap is initialized. +/// Function must still clear any residual storages and succeed. #[test] fn test_liquidate_pal_simple_ok_and_clears() { new_test_ext().execute_with(|| { diff --git a/pallets/transaction-fee/src/tests/mock.rs b/pallets/transaction-fee/src/tests/mock.rs index 7a54f98c5d..56b7b77308 100644 --- a/pallets/transaction-fee/src/tests/mock.rs +++ b/pallets/transaction-fee/src/tests/mock.rs @@ -387,7 +387,6 @@ impl pallet_balances::Config for Test { 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(1_000_000).unwrap(); } @@ -399,7 +398,6 @@ impl pallet_subtensor_swap::Config for Test { type TaoReserve = pallet_subtensor::TaoCurrencyReserve; type AlphaReserve = pallet_subtensor::AlphaCurrencyReserve; type MaxFeeRate = SwapMaxFeeRate; - type MaxPositions = SwapMaxPositions; type MinimumLiquidity = SwapMinimumLiquidity; type MinimumReserve = SwapMinimumReserve; type WeightInfo = (); diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 15a074bc74..ee52a313e8 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -1102,7 +1102,6 @@ impl pallet_subtensor::Config for Runtime { 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 = unsafe { NonZeroU64::new_unchecked(1_000_000) }; } @@ -1114,7 +1113,6 @@ impl pallet_subtensor_swap::Config for Runtime { type TaoReserve = pallet_subtensor::TaoCurrencyReserve; type AlphaReserve = pallet_subtensor::AlphaCurrencyReserve; type MaxFeeRate = SwapMaxFeeRate; - type MaxPositions = SwapMaxPositions; type MinimumLiquidity = SwapMinimumLiquidity; type MinimumReserve = SwapMinimumReserve; // TODO: set measured weights when the pallet been benchmarked and the type is generated From a8579be107abadd2df8bb041317309de1590bbab Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Tue, 3 Feb 2026 18:38:41 -0500 Subject: [PATCH 211/240] Add freeze struct to TickIndex and PositionId --- pallets/swap/src/pallet/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pallets/swap/src/pallet/mod.rs b/pallets/swap/src/pallet/mod.rs index 3c6f815e2a..842ba8697c 100644 --- a/pallets/swap/src/pallet/mod.rs +++ b/pallets/swap/src/pallet/mod.rs @@ -8,8 +8,8 @@ use subtensor_runtime_common::{ }; use crate::{pallet::balancer::Balancer, weights::WeightInfo}; - pub use pallet::*; +use subtensor_macros::freeze_struct; mod balancer; mod hooks; @@ -249,6 +249,7 @@ mod pallet { } /// Struct representing a tick index, DEPRECATED +#[freeze_struct("7c280c2b3bbbb33e")] #[derive( Debug, Default, @@ -268,6 +269,7 @@ mod pallet { pub struct TickIndex(i32); /// Struct representing a liquidity position ID, DEPRECATED +#[freeze_struct("e695cd6455c3f0cb")] #[derive( Clone, Copy, From a4dd2fdebcabcc166966047bc74b2ba58af70c82 Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 4 Feb 2026 08:59:38 +0800 Subject: [PATCH 212/240] add transaction replacement test case --- .../test/transaction.replace.test.ts | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 contract-tests/test/transaction.replace.test.ts diff --git a/contract-tests/test/transaction.replace.test.ts b/contract-tests/test/transaction.replace.test.ts new file mode 100644 index 0000000000..ac5cccba8a --- /dev/null +++ b/contract-tests/test/transaction.replace.test.ts @@ -0,0 +1,78 @@ +import * as assert from "assert"; + +import { getDevnetApi, getRandomSubstrateSigner, } from "../src/substrate" +import { getPublicClient } from "../src/utils"; +import { ETH_LOCAL_URL, IBALANCETRANSFER_ADDRESS, IBalanceTransferABI } from "../src/config"; +import { devnet } from "@polkadot-api/descriptors" +import { PublicClient } from "viem"; +import { TypedApi } from "polkadot-api"; +import { generateRandomEthersWallet } from "../src/utils"; +import { tao, raoToEth } from "../src/balance-math"; +import { toViemAddress, } from "../src/address-utils" +import { getContract } from "../src/eth" +import { forceSetBalanceToEthAddress, } from "../src/subtensor"; + +describe("Transaction replace tests", () => { + // init eth part + const wallet = generateRandomEthersWallet(); + const wallet2 = generateRandomEthersWallet(); + const signer = getRandomSubstrateSigner(); + let publicClient: PublicClient; + let api: TypedApi + + before(async () => { + + publicClient = await getPublicClient(ETH_LOCAL_URL) + api = await getDevnetApi() + await forceSetBalanceToEthAddress(api, wallet.address) + }); + + it("Can replace simple transfer transaction", async () => { + const transferBalance = raoToEth(tao(1)) + + const gasPrice = BigInt(10e9) + const gasLimit = BigInt(1000000) + const nonce = await publicClient.getTransactionCount({ address: toViemAddress(wallet.address) }) + + let txResponse; + + for (let i = 1; i < 10; i++) { + const transfer = { + to: wallet2.address, + value: transferBalance.toString(), + nonce: nonce, + gasPrice: gasPrice * BigInt(i), + gasLimit: gasLimit * BigInt(i) + } + + txResponse = await wallet.sendTransaction(transfer) + await new Promise(resolve => setTimeout(resolve, 100)) + } + assert.ok(txResponse, "Transaction should be created") + await txResponse.wait() + }) + + it("Can replace precompile call transaction", async () => { + const contract = getContract(IBALANCETRANSFER_ADDRESS, IBalanceTransferABI, wallet) + const transferBalance = raoToEth(tao(1)) + + const gasPrice = BigInt(10e9) + const gasLimit = BigInt(1000000) + const nonce = await publicClient.getTransactionCount({ address: toViemAddress(wallet.address) }) + + let txResponse; + + for (let i = 1; i < 10; i++) { + txResponse = await contract.transfer(signer.publicKey, { + value: transferBalance.toString(), + nonce: nonce, + gasPrice: gasPrice * BigInt(i), + gasLimit: gasLimit * BigInt(i) + }) + + await new Promise(resolve => setTimeout(resolve, 100)) + } + assert.ok(txResponse, "Transaction should be created") + await txResponse.wait() + }) +}) \ No newline at end of file From 6faf9ac6b930a147dd34ec7e9876479d7896492e Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 4 Feb 2026 09:03:23 +0800 Subject: [PATCH 213/240] bump version --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 12e99c031e..629298a5ab 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -241,7 +241,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: 375, + spec_version: 376, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 81cb079fbf2607c21614cab980e0b382959c4191 Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 4 Feb 2026 12:58:27 +0800 Subject: [PATCH 214/240] catch error with wrong nonce --- .../test/transaction.replace.test.ts | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/contract-tests/test/transaction.replace.test.ts b/contract-tests/test/transaction.replace.test.ts index ac5cccba8a..929b8e8078 100644 --- a/contract-tests/test/transaction.replace.test.ts +++ b/contract-tests/test/transaction.replace.test.ts @@ -34,8 +34,6 @@ describe("Transaction replace tests", () => { const gasLimit = BigInt(1000000) const nonce = await publicClient.getTransactionCount({ address: toViemAddress(wallet.address) }) - let txResponse; - for (let i = 1; i < 10; i++) { const transfer = { to: wallet2.address, @@ -45,11 +43,16 @@ describe("Transaction replace tests", () => { gasLimit: gasLimit * BigInt(i) } - txResponse = await wallet.sendTransaction(transfer) + try { + await wallet.sendTransaction(transfer) + } catch (error) { + // ignore error, previous transaction could be mined. the nonce is wrong. + } await new Promise(resolve => setTimeout(resolve, 100)) } - assert.ok(txResponse, "Transaction should be created") - await txResponse.wait() + + // check the node not crashed + await forceSetBalanceToEthAddress(api, wallet.address) }) it("Can replace precompile call transaction", async () => { @@ -60,19 +63,21 @@ describe("Transaction replace tests", () => { const gasLimit = BigInt(1000000) const nonce = await publicClient.getTransactionCount({ address: toViemAddress(wallet.address) }) - let txResponse; - for (let i = 1; i < 10; i++) { - txResponse = await contract.transfer(signer.publicKey, { - value: transferBalance.toString(), - nonce: nonce, - gasPrice: gasPrice * BigInt(i), - gasLimit: gasLimit * BigInt(i) - }) + try { + await contract.transfer(signer.publicKey, { + value: transferBalance.toString(), + nonce: nonce, + gasPrice: gasPrice * BigInt(i), + gasLimit: gasLimit * BigInt(i) + }) + } catch (error) { + // ignore error, previous transaction could be mined. the nonce is wrong. + } await new Promise(resolve => setTimeout(resolve, 100)) } - assert.ok(txResponse, "Transaction should be created") - await txResponse.wait() + // check the node not crashed + await forceSetBalanceToEthAddress(api, wallet.address) }) }) \ No newline at end of file From 96a55832bf4d442816055f81106035536d6fc4de Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 4 Feb 2026 12:59:38 +0800 Subject: [PATCH 215/240] less sleep --- contract-tests/test/transaction.replace.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contract-tests/test/transaction.replace.test.ts b/contract-tests/test/transaction.replace.test.ts index 929b8e8078..afb95ed9d5 100644 --- a/contract-tests/test/transaction.replace.test.ts +++ b/contract-tests/test/transaction.replace.test.ts @@ -48,7 +48,7 @@ describe("Transaction replace tests", () => { } catch (error) { // ignore error, previous transaction could be mined. the nonce is wrong. } - await new Promise(resolve => setTimeout(resolve, 100)) + await new Promise(resolve => setTimeout(resolve, 10)) } // check the node not crashed @@ -75,7 +75,7 @@ describe("Transaction replace tests", () => { // ignore error, previous transaction could be mined. the nonce is wrong. } - await new Promise(resolve => setTimeout(resolve, 100)) + await new Promise(resolve => setTimeout(resolve, 10)) } // check the node not crashed await forceSetBalanceToEthAddress(api, wallet.address) From 8a0ea396981e04b4f9851128f374f2bd36caaaf5 Mon Sep 17 00:00:00 2001 From: John Reed <87283488+JohnReedV@users.noreply.github.com> Date: Wed, 4 Feb 2026 07:48:15 -0800 Subject: [PATCH 216/240] raise fee on `set_pending_childkey_cooldown` --- pallets/subtensor/src/macros/dispatches.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 93de861a58..75b0091ba4 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -1954,7 +1954,7 @@ mod dispatches { /// Sets the pending childkey cooldown (in blocks). Root only. #[pallet::call_index(109)] - #[pallet::weight((Weight::from_parts(10_000, 0), DispatchClass::Operational, Pays::Yes))] + #[pallet::weight((Weight::from_parts(1_970_000_000_000, 0), DispatchClass::Operational, Pays::Yes))] pub fn set_pending_childkey_cooldown( origin: OriginFor, cooldown: u64, From ab43e6b80868d8a29f3315ac0cb5684c8924cd2d Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Wed, 4 Feb 2026 10:48:45 -0500 Subject: [PATCH 217/240] Fix build errors --- pallets/subtensor/src/coinbase/block_emission.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/pallets/subtensor/src/coinbase/block_emission.rs b/pallets/subtensor/src/coinbase/block_emission.rs index 14fad1a056..c499fe3117 100644 --- a/pallets/subtensor/src/coinbase/block_emission.rs +++ b/pallets/subtensor/src/coinbase/block_emission.rs @@ -7,7 +7,6 @@ use substrate_fixed::{ }; use subtensor_runtime_common::{NetUid, TaoCurrency}; use subtensor_swap_interface::SwapHandler; -use subtensor_runtime_common::TaoCurrency; impl Pallet { /// Calculates the dynamic TAO emission for a given subnet. From 24d232b09c61c1b87990d298791ba3e81dcd65ea Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Wed, 4 Feb 2026 13:24:03 -0500 Subject: [PATCH 218/240] Fix sudo_set_max_allowed_uids benchmark --- pallets/admin-utils/src/benchmarking.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/admin-utils/src/benchmarking.rs b/pallets/admin-utils/src/benchmarking.rs index e676c40752..9cab4894e9 100644 --- a/pallets/admin-utils/src/benchmarking.rs +++ b/pallets/admin-utils/src/benchmarking.rs @@ -263,7 +263,7 @@ mod benchmarks { ); #[extrinsic_call] - _(RawOrigin::Root, 1u16.into()/*netuid*/, 2048u16/*max_allowed_uids*/)/*sudo_set_max_allowed_uids*/; + _(RawOrigin::Root, 1u16.into()/*netuid*/, 256u16/*max_allowed_uids*/)/*sudo_set_max_allowed_uids*/; } #[benchmark] From db99fd40c2f3694efdf5d4e0befcbba92d1fc4f6 Mon Sep 17 00:00:00 2001 From: Shamil Gadelshin Date: Fri, 6 Feb 2026 13:03:37 +0300 Subject: [PATCH 219/240] Rename subnet_buyback to add_stake_burn --- pallets/subtensor/src/benchmarks.rs | 2 +- pallets/subtensor/src/extensions/subtensor.rs | 6 +- pallets/subtensor/src/lib.rs | 4 +- pallets/subtensor/src/macros/dispatches.rs | 8 +-- pallets/subtensor/src/macros/errors.rs | 4 +- pallets/subtensor/src/macros/events.rs | 4 +- .../subtensor/src/staking/recycle_alpha.rs | 12 ++-- pallets/subtensor/src/tests/recycle_alpha.rs | 62 +++++++++---------- pallets/subtensor/src/utils/rate_limiting.rs | 8 +-- 9 files changed, 55 insertions(+), 55 deletions(-) diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index 8a6f8d757d..b370b6dbe1 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -1723,7 +1723,7 @@ mod pallet_benchmarks { } #[benchmark] - fn subnet_buyback() { + fn add_stake_burn() { let netuid = NetUid::from(1); let tempo: u16 = 1; let seed: u32 = 1; diff --git a/pallets/subtensor/src/extensions/subtensor.rs b/pallets/subtensor/src/extensions/subtensor.rs index 798f30dbe8..a24a54f3fd 100644 --- a/pallets/subtensor/src/extensions/subtensor.rs +++ b/pallets/subtensor/src/extensions/subtensor.rs @@ -20,7 +20,7 @@ use sp_std::vec::Vec; use subtensor_macros::freeze_struct; use subtensor_runtime_common::{NetUid, NetUidStorageIndex}; -const SUBNET_BUYBACK_PRIORITY_BOOST: u64 = 100; +const ADD_STAKE_BURN_PRIORITY_BOOST: u64 = 100; type CallOf = ::RuntimeCall; type OriginOf = ::RuntimeOrigin; @@ -297,12 +297,12 @@ where .map_err(|_| CustomTransactionError::EvmKeyAssociateRateLimitExceeded)?; Ok((Default::default(), (), origin)) } - Some(Call::subnet_buyback { netuid, .. }) => { + Some(Call::add_stake_burn { netuid, .. }) => { Pallet::::ensure_subnet_owner(origin.clone(), *netuid).map_err(|_| { TransactionValidityError::Invalid(InvalidTransaction::BadSigner) })?; - Ok((Self::validity_ok(SUBNET_BUYBACK_PRIORITY_BOOST), (), origin)) + Ok((Self::validity_ok(ADD_STAKE_BURN_PRIORITY_BOOST), (), origin)) } _ => Ok((Default::default(), (), origin)), } diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index ba7e3dcbf6..69645c0419 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -2722,9 +2722,9 @@ pub enum RateLimitKey { // Last tx block delegate key limit per account ID #[codec(index = 5)] LastTxBlockDelegateTake(AccountId), - // Subnet buyback rate limit + // "Add stake and burn" rate limit #[codec(index = 6)] - SubnetBuyback(NetUid), + AddStakeBurn(NetUid), } pub trait ProxyInterface { diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 88b6a3f0ec..7cfb224722 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2580,8 +2580,8 @@ mod dispatches { Self::do_set_voting_power_ema_alpha(netuid, alpha) } - /// --- Subnet buyback: the extrinsic is a combination of add_stake(add_stake_limit) and - /// burn_alpha. We buy alpha token first and immediately burn the acquired amount of alpha. + /// --- The extrinsic is a combination of add_stake(add_stake_limit) and burn_alpha. We buy + /// alpha token first and immediately burn the acquired amount of alpha (aka Subnet buyback). #[pallet::call_index(132)] #[pallet::weight(( Weight::from_parts(368_000_000, 8556) @@ -2590,14 +2590,14 @@ mod dispatches { DispatchClass::Normal, Pays::Yes ))] - pub fn subnet_buyback( + pub fn add_stake_burn( origin: T::RuntimeOrigin, hotkey: T::AccountId, netuid: NetUid, amount: TaoCurrency, limit: Option, ) -> DispatchResult { - Self::do_subnet_buyback(origin, hotkey, netuid, amount, limit) + Self::do_add_stake_burn(origin, hotkey, netuid, amount, limit) } } } diff --git a/pallets/subtensor/src/macros/errors.rs b/pallets/subtensor/src/macros/errors.rs index f74a7657d8..7d50373f19 100644 --- a/pallets/subtensor/src/macros/errors.rs +++ b/pallets/subtensor/src/macros/errors.rs @@ -280,7 +280,7 @@ mod errors { PrecisionLoss, /// Deprecated call. Deprecated, - /// Subnet buyback exceeded the operation rate limit - SubnetBuybackRateLimitExceeded, + /// "Add stake and burn" exceeded the operation rate limit + AddStakeBurnRateLimitExceeded, } } diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index 9f0c2bdfd5..65c33aee87 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -517,8 +517,8 @@ mod events { alpha: AlphaCurrency, }, - /// Subnet buyback event: alpha token was purchased and burned. - SubnetBuyback { + /// "Add stake and burn" event: alpha token was purchased and burned. + AddStakeBurn { /// The subnet ID netuid: NetUid, /// hotky account ID diff --git a/pallets/subtensor/src/staking/recycle_alpha.rs b/pallets/subtensor/src/staking/recycle_alpha.rs index 4dd362028e..b77982fa31 100644 --- a/pallets/subtensor/src/staking/recycle_alpha.rs +++ b/pallets/subtensor/src/staking/recycle_alpha.rs @@ -136,7 +136,7 @@ impl Pallet { Ok(()) } - pub(crate) fn do_subnet_buyback( + pub(crate) fn do_add_stake_burn( origin: T::RuntimeOrigin, hotkey: T::AccountId, netuid: NetUid, @@ -146,12 +146,12 @@ impl Pallet { 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::SubnetBuyback(netuid)); - let rate_limit = TransactionType::SubnetBuyback.rate_limit_on_subnet::(netuid); + 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::::SubnetBuybackRateLimitExceeded + Error::::AddStakeBurnRateLimitExceeded ); let alpha = if let Some(limit) = limit { @@ -162,9 +162,9 @@ impl Pallet { Self::do_burn_alpha(origin, hotkey.clone(), alpha, netuid)?; - Self::set_rate_limited_last_block(&RateLimitKey::SubnetBuyback(netuid), current_block); + Self::set_rate_limited_last_block(&RateLimitKey::AddStakeBurn(netuid), current_block); - Self::deposit_event(Event::SubnetBuyback { + Self::deposit_event(Event::AddStakeBurn { netuid, hotkey, amount, diff --git a/pallets/subtensor/src/tests/recycle_alpha.rs b/pallets/subtensor/src/tests/recycle_alpha.rs index a49a029805..5cf589de97 100644 --- a/pallets/subtensor/src/tests/recycle_alpha.rs +++ b/pallets/subtensor/src/tests/recycle_alpha.rs @@ -620,7 +620,7 @@ fn test_burn_precision_loss() { } #[test] -fn test_subnet_buyback_success() { +fn test_add_stake_burn_success() { new_test_ext(1).execute_with(|| { let hotkey_account_id = U256::from(533453); let coldkey_account_id = U256::from(55453); @@ -642,8 +642,8 @@ fn test_subnet_buyback_success() { TaoCurrency::ZERO ); - // Execute subnet_buyback - this stakes TAO to get Alpha, then burns the Alpha - assert_ok!(SubtensorModule::subnet_buyback( + // Execute add_stake_burn - this stakes TAO to get Alpha, then burns the Alpha + assert_ok!(SubtensorModule::add_stake_burn( RuntimeOrigin::signed(coldkey_account_id), hotkey_account_id, netuid, @@ -651,7 +651,7 @@ fn test_subnet_buyback_success() { None, )); - // After buyback, hotkey should have zero stake since alpha is burned immediately + // After "add stake and burn", hotkey should have zero stake since alpha is burned immediately assert_eq!( SubtensorModule::get_total_stake_for_hotkey(&hotkey_account_id), TaoCurrency::ZERO @@ -672,18 +672,18 @@ fn test_subnet_buyback_success() { ) })); - // Verify SubnetBuyback event was emitted + // Verify AddStakeBurn event was emitted assert!(System::events().iter().any(|e| { matches!( &e.event, - RuntimeEvent::SubtensorModule(Event::SubnetBuyback { .. }) + RuntimeEvent::SubtensorModule(Event::AddStakeBurn { .. }) ) })); }); } #[test] -fn test_subnet_buyback_with_limit_success() { +fn test_add_stake_burn_with_limit_success() { new_test_ext(1).execute_with(|| { let hotkey_account_id = U256::from(533453); let coldkey_account_id = U256::from(55453); @@ -711,8 +711,8 @@ fn test_subnet_buyback_with_limit_success() { // With 100 TAO into 1000/1000 pool, price moves from 1.0 to ~1.21 let limit_price = TaoCurrency::from(2_000_000_000); // 2.0 TAO per Alpha - // Execute subnet_buyback with limit - assert_ok!(SubtensorModule::subnet_buyback( + // Execute add_stake_burn with limit + assert_ok!(SubtensorModule::add_stake_burn( RuntimeOrigin::signed(coldkey_account_id), hotkey_account_id, netuid, @@ -720,7 +720,7 @@ fn test_subnet_buyback_with_limit_success() { Some(limit_price), )); - // After buyback, hotkey should have zero stake since alpha is burned immediately + // After "add stake and burn", hotkey should have zero stake since alpha is burned immediately assert_eq!( SubtensorModule::get_total_stake_for_hotkey(&hotkey_account_id), TaoCurrency::ZERO @@ -753,7 +753,7 @@ fn test_subnet_buyback_with_limit_success() { } #[test] -fn test_subnet_buyback_non_owner_fails() { +fn test_add_stake_burn_non_owner_fails() { new_test_ext(1).execute_with(|| { let hotkey_account_id = U256::from(1); let coldkey_account_id = U256::from(2); @@ -772,9 +772,9 @@ fn test_subnet_buyback_non_owner_fails() { // Give non-owner some balance SubtensorModule::add_balance_to_coldkey_account(&non_owner_coldkey, amount); - // Non-owner trying to call subnet_buyback should fail with BadOrigin + // Non-owner trying to call add_stake_burn should fail with BadOrigin assert_noop!( - SubtensorModule::subnet_buyback( + SubtensorModule::add_stake_burn( RuntimeOrigin::signed(non_owner_coldkey), hotkey_account_id, netuid, @@ -787,7 +787,7 @@ fn test_subnet_buyback_non_owner_fails() { } #[test] -fn test_subnet_buyback_nonexistent_subnet_fails() { +fn test_add_stake_burn_nonexistent_subnet_fails() { new_test_ext(1).execute_with(|| { let hotkey_account_id = U256::from(1); let coldkey_account_id = U256::from(2); @@ -796,10 +796,10 @@ fn test_subnet_buyback_nonexistent_subnet_fails() { // Give some balance SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount); - // Try to call subnet_buyback on non-existent subnet + // Try to call add_stake_burn on non-existent subnet let nonexistent_netuid = NetUid::from(999); assert_noop!( - SubtensorModule::subnet_buyback( + SubtensorModule::add_stake_burn( RuntimeOrigin::signed(coldkey_account_id), hotkey_account_id, nonexistent_netuid, @@ -812,7 +812,7 @@ fn test_subnet_buyback_nonexistent_subnet_fails() { } #[test] -fn test_subnet_buyback_insufficient_balance_fails() { +fn test_add_stake_burn_insufficient_balance_fails() { new_test_ext(1).execute_with(|| { let hotkey_account_id = U256::from(1); let coldkey_account_id = U256::from(2); @@ -827,9 +827,9 @@ fn test_subnet_buyback_insufficient_balance_fails() { (amount * 10_000_000).into(), ); - // Try to call subnet_buyback without sufficient balance + // Try to call add_stake_burn without sufficient balance assert_noop!( - SubtensorModule::subnet_buyback( + SubtensorModule::add_stake_burn( RuntimeOrigin::signed(coldkey_account_id), hotkey_account_id, netuid, @@ -842,7 +842,7 @@ fn test_subnet_buyback_insufficient_balance_fails() { } #[test] -fn test_subnet_buyback_rate_limit_exceeded() { +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); @@ -856,16 +856,16 @@ fn test_subnet_buyback_rate_limit_exceeded() { let alpha_in = AlphaCurrency::from(1_000_000_000_000); mock::setup_reserves(netuid, tao_reserve, alpha_in); - // Give coldkey sufficient balance for multiple buybacks + // Give coldkey sufficient balance for multiple "add stake and burn" operations. SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount * 10); assert_eq!( - SubtensorModule::get_rate_limited_last_block(&RateLimitKey::SubnetBuyback(netuid)), + SubtensorModule::get_rate_limited_last_block(&RateLimitKey::AddStakeBurn(netuid)), 0 ); - // First buyback should succeed - assert_ok!(SubtensorModule::subnet_buyback( + // First "add stake and burn" should succeed + assert_ok!(SubtensorModule::add_stake_burn( RuntimeOrigin::signed(coldkey_account_id), hotkey_account_id, netuid, @@ -874,27 +874,27 @@ fn test_subnet_buyback_rate_limit_exceeded() { )); assert_eq!( - SubtensorModule::get_rate_limited_last_block(&RateLimitKey::SubnetBuyback(netuid)), + SubtensorModule::get_rate_limited_last_block(&RateLimitKey::AddStakeBurn(netuid)), SubtensorModule::get_current_block_as_u64() ); - // Second buyback immediately after should fail due to rate limit + // Second "add stake and burn" immediately after should fail due to rate limit assert_noop!( - SubtensorModule::subnet_buyback( + SubtensorModule::add_stake_burn( RuntimeOrigin::signed(coldkey_account_id), hotkey_account_id, netuid, amount.into(), None, ), - Error::::SubnetBuybackRateLimitExceeded + Error::::AddStakeBurnRateLimitExceeded ); - // After stepping past the rate limit, buyback should succeed again - let rate_limit = TransactionType::SubnetBuyback.rate_limit_on_subnet::(netuid); + // 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::subnet_buyback( + assert_ok!(SubtensorModule::add_stake_burn( RuntimeOrigin::signed(coldkey_account_id), hotkey_account_id, netuid, diff --git a/pallets/subtensor/src/utils/rate_limiting.rs b/pallets/subtensor/src/utils/rate_limiting.rs index a5de73c417..0649b3b9e3 100644 --- a/pallets/subtensor/src/utils/rate_limiting.rs +++ b/pallets/subtensor/src/utils/rate_limiting.rs @@ -16,7 +16,7 @@ pub enum TransactionType { MechanismCountUpdate, MechanismEmission, MaxUidsTrimming, - SubnetBuyback, + AddStakeBurn, } impl TransactionType { @@ -45,7 +45,7 @@ impl TransactionType { (Tempo::::get(netuid) as u64).saturating_mul(epochs) } Self::SetSNOwnerHotkey => DefaultSetSNOwnerHotkeyRateLimit::::get(), - Self::SubnetBuyback => Tempo::::get(netuid) as u64, + Self::AddStakeBurn => Tempo::::get(netuid) as u64, _ => self.rate_limit::(), } @@ -143,7 +143,7 @@ impl From for u16 { TransactionType::MechanismCountUpdate => 7, TransactionType::MechanismEmission => 8, TransactionType::MaxUidsTrimming => 9, - TransactionType::SubnetBuyback => 10, + TransactionType::AddStakeBurn => 10, } } } @@ -161,7 +161,7 @@ impl From for TransactionType { 7 => TransactionType::MechanismCountUpdate, 8 => TransactionType::MechanismEmission, 9 => TransactionType::MaxUidsTrimming, - 10 => TransactionType::SubnetBuyback, + 10 => TransactionType::AddStakeBurn, _ => TransactionType::Unknown, } } From 8a9212911d90270b66c36bd6686686f21fbf21ff Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 6 Feb 2026 12:18:41 +0000 Subject: [PATCH 220/240] auto-update benchmark weights --- pallets/admin-utils/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 8d1d735052..2209ea7295 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -512,7 +512,7 @@ pub mod pallet { /// The extrinsic will call the Subtensor pallet to set the maximum allowed UIDs for a subnet. #[pallet::call_index(15)] #[pallet::weight(Weight::from_parts(32_140_000, 0) - .saturating_add(::DbWeight::get().reads(5_u64)) + .saturating_add(::DbWeight::get().reads(6_u64)) .saturating_add(::DbWeight::get().writes(1_u64)))] pub fn sudo_set_max_allowed_uids( origin: OriginFor, From c8b352f42373be715eb2b0fd6fc344869cfa8667 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Fri, 6 Feb 2026 12:20:49 -0500 Subject: [PATCH 221/240] Fix subnet_buyback benchmark --- pallets/subtensor/src/benchmarks.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index 4da7d9ee4e..8410578a6a 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -1754,7 +1754,7 @@ mod pallet_benchmarks { Subtensor::::init_new_network(netuid, tempo); SubtokenEnabled::::insert(netuid, true); - Subtensor::::set_burn(netuid, 1.into()); + Subtensor::::set_burn(netuid, 1000.into()); Subtensor::::set_network_registration_allowed(netuid, true); Subtensor::::set_max_allowed_uids(netuid, 4096); From 46350eb22b764b6648e62ece24f5548abbdb5fb2 Mon Sep 17 00:00:00 2001 From: Aliaksandr Tsurko Date: Wed, 11 Feb 2026 18:52:02 +0100 Subject: [PATCH 222/240] Add precompile integration tests --- Cargo.lock | 37 +++++ precompiles/src/balance_transfer.rs | 2 +- precompiles/src/ed25519.rs | 2 +- precompiles/src/extensions.rs | 2 +- precompiles/src/lib.rs | 35 ++--- precompiles/src/sr25519.rs | 2 +- precompiles/src/staking.rs | 4 +- precompiles/src/storage_query.rs | 2 +- precompiles/src/uid_lookup.rs | 2 +- runtime/Cargo.toml | 1 + runtime/tests/precompiles.rs | 219 ++++++++++++++++++++++++++++ 11 files changed, 283 insertions(+), 25 deletions(-) create mode 100644 runtime/tests/precompiles.rs diff --git a/Cargo.lock b/Cargo.lock index 980b8dede8..69d6dc449a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1917,6 +1917,17 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "bstr" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" +dependencies = [ + "memchr", + "regex-automata 0.4.11", + "serde", +] + [[package]] name = "build-helper" version = "0.1.1" @@ -3760,6 +3771,7 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.106", + "unicode-xid", ] [[package]] @@ -13120,18 +13132,23 @@ name = "precompile-utils" version = "0.1.0" source = "git+https://github.com/opentensor/frontier?rev=6dc7c0400cfee2a1acb62ae17149c4d3a983e58d#6dc7c0400cfee2a1acb62ae17149c4d3a983e58d" dependencies = [ + "derive_more 1.0.0", "environmental", "evm", "fp-evm", "frame-support", "frame-system", "hex", + "hex-literal", "impl-trait-for-tuples", "log", "num_enum", "pallet-evm", "parity-scale-codec", "precompile-utils-macro", + "scale-info", + "serde", + "similar-asserts", "sp-core", "sp-io", "sp-runtime", @@ -16357,6 +16374,26 @@ dependencies = [ "wide", ] +[[package]] +name = "similar" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" +dependencies = [ + "bstr", + "unicode-segmentation", +] + +[[package]] +name = "similar-asserts" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b441962c817e33508847a22bd82f03a30cff43642dc2fae8b050566121eb9a" +dependencies = [ + "console", + "similar", +] + [[package]] name = "simple-dns" version = "0.9.3" diff --git a/precompiles/src/balance_transfer.rs b/precompiles/src/balance_transfer.rs index f4d5fb3025..d8d10970a3 100644 --- a/precompiles/src/balance_transfer.rs +++ b/precompiles/src/balance_transfer.rs @@ -10,7 +10,7 @@ use sp_runtime::traits::{AsSystemOriginSigner, Dispatchable, StaticLookup, Uniqu use crate::{PrecompileExt, PrecompileHandleExt}; -pub(crate) struct BalanceTransferPrecompile(PhantomData); +pub struct BalanceTransferPrecompile(PhantomData); impl PrecompileExt for BalanceTransferPrecompile where diff --git a/precompiles/src/ed25519.rs b/precompiles/src/ed25519.rs index 1ea581bd35..dbfe032cdf 100644 --- a/precompiles/src/ed25519.rs +++ b/precompiles/src/ed25519.rs @@ -8,7 +8,7 @@ use fp_evm::{ExitError, ExitSucceed, LinearCostPrecompile, PrecompileFailure}; use crate::{PrecompileExt, parse_slice}; -pub(crate) struct Ed25519Verify(PhantomData); +pub struct Ed25519Verify(PhantomData); impl PrecompileExt for Ed25519Verify where diff --git a/precompiles/src/extensions.rs b/precompiles/src/extensions.rs index 6daa6b6aae..51287a1b88 100644 --- a/precompiles/src/extensions.rs +++ b/precompiles/src/extensions.rs @@ -186,7 +186,7 @@ fn extension_error(err: TransactionValidityError) -> PrecompileFailure { impl PrecompileHandleExt for T where T: PrecompileHandle {} -pub(crate) trait PrecompileExt>: Precompile { +pub trait PrecompileExt>: Precompile { const INDEX: u64; // ss58 public key i.e., the contract sends funds it received to the destination address from diff --git a/precompiles/src/lib.rs b/precompiles/src/lib.rs index ca86d3f9b3..a824ac39d4 100644 --- a/precompiles/src/lib.rs +++ b/precompiles/src/lib.rs @@ -10,6 +10,7 @@ use frame_support::{ dispatch::{DispatchInfo, GetDispatchInfo, PostDispatchInfo}, pallet_prelude::Decode, }; +use pallet_admin_utils::PrecompileEnum; use pallet_evm::{ AddressMapping, IsPrecompileResult, Precompile, PrecompileHandle, PrecompileResult, PrecompileSet, @@ -24,24 +25,24 @@ use sp_core::{H160, U256, crypto::ByteArray}; use sp_runtime::traits::{AsSystemOriginSigner, Dispatchable, StaticLookup}; use subtensor_runtime_common::ProxyType; -use pallet_admin_utils::PrecompileEnum; - -use crate::address_mapping::*; -use crate::alpha::*; -use crate::balance_transfer::*; -use crate::crowdloan::*; -use crate::ed25519::*; use crate::extensions::*; -use crate::leasing::*; -use crate::metagraph::*; -use crate::neuron::*; -use crate::proxy::*; -use crate::sr25519::*; -use crate::staking::*; -use crate::storage_query::*; -use crate::subnet::*; -use crate::uid_lookup::*; -use crate::voting_power::*; + +pub use address_mapping::AddressMappingPrecompile; +pub use alpha::AlphaPrecompile; +pub use balance_transfer::BalanceTransferPrecompile; +pub use crowdloan::CrowdloanPrecompile; +pub use ed25519::Ed25519Verify; +pub use extensions::PrecompileExt; +pub use leasing::LeasingPrecompile; +pub use metagraph::MetagraphPrecompile; +pub use neuron::NeuronPrecompile; +pub use proxy::ProxyPrecompile; +pub use sr25519::Sr25519Verify; +pub use staking::{StakingPrecompile, StakingPrecompileV2}; +pub use storage_query::StorageQueryPrecompile; +pub use subnet::SubnetPrecompile; +pub use uid_lookup::UidLookupPrecompile; +pub use voting_power::VotingPowerPrecompile; mod address_mapping; mod alpha; diff --git a/precompiles/src/sr25519.rs b/precompiles/src/sr25519.rs index 6c6245d8f0..054948d524 100644 --- a/precompiles/src/sr25519.rs +++ b/precompiles/src/sr25519.rs @@ -9,7 +9,7 @@ use fp_evm::{ExitError, ExitSucceed, LinearCostPrecompile, PrecompileFailure}; use crate::{PrecompileExt, parse_slice}; -pub(crate) struct Sr25519Verify(PhantomData); +pub struct Sr25519Verify(PhantomData); impl PrecompileExt for Sr25519Verify where diff --git a/precompiles/src/staking.rs b/precompiles/src/staking.rs index 2ed8891a5c..c276c32e60 100644 --- a/precompiles/src/staking.rs +++ b/precompiles/src/staking.rs @@ -48,7 +48,7 @@ use crate::{PrecompileExt, PrecompileHandleExt}; // to stop supporting both precompiles. // // All the future extensions should happen in StakingPrecompileV2. -pub(crate) struct StakingPrecompileV2(PhantomData); +pub struct StakingPrecompileV2(PhantomData); impl PrecompileExt for StakingPrecompileV2 where @@ -450,7 +450,7 @@ where } // Deprecated, exists for backward compatibility. -pub(crate) struct StakingPrecompile(PhantomData); +pub struct StakingPrecompile(PhantomData); impl PrecompileExt for StakingPrecompile where diff --git a/precompiles/src/storage_query.rs b/precompiles/src/storage_query.rs index b9455ae708..493e4949b0 100644 --- a/precompiles/src/storage_query.rs +++ b/precompiles/src/storage_query.rs @@ -7,7 +7,7 @@ use sp_std::vec::Vec; use crate::PrecompileExt; -pub(crate) struct StorageQueryPrecompile(PhantomData); +pub struct StorageQueryPrecompile(PhantomData); impl PrecompileExt for StorageQueryPrecompile where diff --git a/precompiles/src/uid_lookup.rs b/precompiles/src/uid_lookup.rs index be8c803b45..b791b96786 100644 --- a/precompiles/src/uid_lookup.rs +++ b/precompiles/src/uid_lookup.rs @@ -8,7 +8,7 @@ use sp_std::vec::Vec; use crate::PrecompileExt; -pub(crate) struct UidLookupPrecompile(PhantomData); +pub struct UidLookupPrecompile(PhantomData); impl PrecompileExt for UidLookupPrecompile where diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index b3aced2160..623846de12 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -158,6 +158,7 @@ ethereum.workspace = true frame-metadata.workspace = true sp-io.workspace = true sp-tracing.workspace = true +precompile-utils = { workspace = true, features = ["testing"] } [build-dependencies] substrate-wasm-builder = { workspace = true, optional = true } diff --git a/runtime/tests/precompiles.rs b/runtime/tests/precompiles.rs new file mode 100644 index 0000000000..5565aa8f7f --- /dev/null +++ b/runtime/tests/precompiles.rs @@ -0,0 +1,219 @@ +#![allow(clippy::unwrap_used)] +#![allow(clippy::expect_used)] + +use core::iter::IntoIterator; +use std::collections::BTreeSet; + +use fp_evm::{Context, ExitError, PrecompileFailure, PrecompileResult}; +use node_subtensor_runtime::{BuildStorage, Runtime, RuntimeGenesisConfig, System}; +use pallet_evm::{AddressMapping, BalanceConverter, PrecompileSet}; +use precompile_utils::testing::{MockHandle, PrecompileTesterExt}; +use sp_core::{H160, H256, U256}; +use sp_runtime::traits::Hash; +use subtensor_precompiles::{ + AddressMappingPrecompile, BalanceTransferPrecompile, PrecompileExt, Precompiles, +}; + +type AccountId = ::AccountId; + +fn new_test_ext() -> sp_io::TestExternalities { + let mut ext: sp_io::TestExternalities = RuntimeGenesisConfig::default() + .build_storage() + .unwrap() + .into(); + ext.execute_with(|| System::set_block_number(1)); + ext +} + +fn execute_precompile( + precompiles: &Precompiles, + precompile_address: H160, + caller: H160, + input: Vec, + apparent_value: U256, +) -> Option { + let mut handle = MockHandle::new( + precompile_address, + Context { + address: precompile_address, + caller, + apparent_value, + }, + ); + handle.input = input; + precompiles.execute(&mut handle) +} + +fn evm_apparent_value_from_substrate(amount: u64) -> U256 { + ::BalanceConverter::into_evm_balance(amount.into()) + .expect("runtime balance conversion should work for test amount") + .into() +} + +fn addr_from_index(index: u64) -> H160 { + H160::from_low_u64_be(index) +} + +#[test] +fn precompile_registry_addresses_are_unique() { + new_test_ext().execute_with(|| { + let addresses = Precompiles::::used_addresses(); + let unique: BTreeSet<_> = IntoIterator::into_iter(addresses).collect(); + assert_eq!(unique.len(), addresses.len()); + }); +} + +mod address_mapping { + use super::*; + + fn address_mapping_call_data(target: H160) -> Vec { + // Solidity selector for addressMapping(address). + let selector = sp_io::hashing::keccak_256(b"addressMapping(address)"); + let mut input = Vec::with_capacity(4 + 32); + // First 4 bytes of keccak256(function_signature): ABI function selector. + input.extend_from_slice(&selector[..4]); + // Left-pad the 20-byte address argument to a 32-byte ABI word. + input.extend_from_slice(&[0u8; 12]); + // The 20-byte address payload (right-aligned in the 32-byte ABI word). + input.extend_from_slice(target.as_bytes()); + input + } + + #[test] + fn address_mapping_precompile_returns_runtime_address_mapping() { + new_test_ext().execute_with(|| { + let precompiles = Precompiles::::new(); + + let caller = addr_from_index(1); + let target_address = addr_from_index(0x1234); + let input = address_mapping_call_data(target_address); + + let mapped_account = + ::AddressMapping::into_account_id(target_address); + let expected_output: [u8; 32] = mapped_account.into(); + + let precompile_addr = addr_from_index(AddressMappingPrecompile::::INDEX); + precompiles + .prepare_test(caller, precompile_addr, input) + .with_static_call(true) + .execute_returns_raw(expected_output.to_vec()); + }); + } +} + +mod balance_transfer { + use super::*; + + fn balance_transfer_call_data(target: H256) -> Vec { + // Solidity selector for transfer(bytes32). + let selector = sp_io::hashing::keccak_256(b"transfer(bytes32)"); + let mut input = Vec::with_capacity(4 + 32); + input.extend_from_slice(&selector[..4]); + input.extend_from_slice(target.as_bytes()); + input + } + + #[test] + fn balance_transfer_precompile_transfers_balance() { + new_test_ext().execute_with(|| { + let precompiles = Precompiles::::new(); + let precompile_addr = addr_from_index(BalanceTransferPrecompile::::INDEX); + let dispatch_account: AccountId = BalanceTransferPrecompile::::account_id(); + let destination_raw = H256::repeat_byte(7); + 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(), + ); + + let source_balance_before = + pallet_balances::Pallet::::free_balance(&dispatch_account); + let destination_balance_before = + pallet_balances::Pallet::::free_balance(&destination_account); + + let result = execute_precompile( + &precompiles, + precompile_addr, + addr_from_index(1), + balance_transfer_call_data(destination_raw), + evm_apparent_value_from_substrate(amount), + ); + let precompile_result = + result.expect("expected precompile transfer call to be routed to a precompile"); + precompile_result.expect("expected successful precompile transfer dispatch"); + + let source_balance_after = + pallet_balances::Pallet::::free_balance(&dispatch_account); + let destination_balance_after = + pallet_balances::Pallet::::free_balance(&destination_account); + + assert_eq!(source_balance_after, source_balance_before - amount); + assert_eq!( + destination_balance_after, + destination_balance_before + amount + ); + }); + } + + #[test] + fn balance_transfer_precompile_respects_subtensor_extension_policy() { + new_test_ext().execute_with(|| { + let precompiles = Precompiles::::new(); + let precompile_addr = addr_from_index(BalanceTransferPrecompile::::INDEX); + let dispatch_account: AccountId = BalanceTransferPrecompile::::account_id(); + let destination_raw = H256::repeat_byte(8); + 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(), + ); + + // Activate coldkey-swap guard for precompile dispatch account. + let replacement_coldkey = AccountId::from([9u8; 32]); + let replacement_hash = + ::Hashing::hash_of(&replacement_coldkey); + pallet_subtensor::ColdkeySwapAnnouncements::::insert( + &dispatch_account, + (System::block_number(), replacement_hash), + ); + + let source_balance_before = + pallet_balances::Pallet::::free_balance(&dispatch_account); + let destination_balance_before = + pallet_balances::Pallet::::free_balance(&destination_account); + + let result = execute_precompile( + &precompiles, + precompile_addr, + addr_from_index(1), + balance_transfer_call_data(destination_raw), + evm_apparent_value_from_substrate(amount), + ); + let precompile_result = + result.expect("expected precompile transfer call to be routed to a precompile"); + let failure = precompile_result + .expect_err("expected transaction extension rejection on precompile dispatch"); + let message = match failure { + PrecompileFailure::Error { + exit_status: ExitError::Other(message), + } => message, + other => panic!("unexpected precompile failure: {other:?}"), + }; + assert!( + message.contains("transaction extension rejected"), + "unexpected precompile failure: {message}" + ); + + let source_balance_after = + pallet_balances::Pallet::::free_balance(&dispatch_account); + let destination_balance_after = + pallet_balances::Pallet::::free_balance(&destination_account); + assert_eq!(source_balance_after, source_balance_before); + assert_eq!(destination_balance_after, destination_balance_before); + }); + } +} From 88f37e6f4454bb9454e5a05a1c3574d87f28342f Mon Sep 17 00:00:00 2001 From: Aliaksandr Tsurko Date: Wed, 11 Feb 2026 19:17:41 +0100 Subject: [PATCH 223/240] Bump spec version --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 629298a5ab..486943730c 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -241,7 +241,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: 376, + spec_version: 377, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 4035d8697064b9f378661316d8bd051faf236f32 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 12 Feb 2026 13:16:44 +0000 Subject: [PATCH 224/240] auto-update benchmark weights --- pallets/admin-utils/src/lib.rs | 2 +- pallets/subtensor/src/macros/dispatches.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 8d1d735052..2209ea7295 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -512,7 +512,7 @@ pub mod pallet { /// The extrinsic will call the Subtensor pallet to set the maximum allowed UIDs for a subnet. #[pallet::call_index(15)] #[pallet::weight(Weight::from_parts(32_140_000, 0) - .saturating_add(::DbWeight::get().reads(5_u64)) + .saturating_add(::DbWeight::get().reads(6_u64)) .saturating_add(::DbWeight::get().writes(1_u64)))] pub fn sudo_set_max_allowed_uids( origin: OriginFor, diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 88b6a3f0ec..2e897b9832 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -1434,7 +1434,7 @@ mod dispatches { /// User register a new subnetwork #[pallet::call_index(79)] - #[pallet::weight((Weight::from_parts(396_000_000, 0) + #[pallet::weight((Weight::from_parts(231_000_000, 0) .saturating_add(T::DbWeight::get().reads(35_u64)) .saturating_add(T::DbWeight::get().writes(51_u64)), DispatchClass::Normal, Pays::Yes))] pub fn register_network_with_identity( From c09f6a6ea51ef08cd072d274f526605f5f21af60 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Thu, 12 Feb 2026 13:22:38 -0500 Subject: [PATCH 225/240] Spec bump --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index aa55164f4f..89735b1011 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -243,7 +243,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: 376, + spec_version: 377, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From e859f2f20243bda46609e265752c7dfbc13f9305 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Thu, 12 Feb 2026 13:33:42 -0500 Subject: [PATCH 226/240] Update subtensor pallet weights --- pallets/subtensor/src/macros/dispatches.rs | 70 +++++++++++----------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 7cfb224722..60c37de2fe 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -710,9 +710,9 @@ mod dispatches { /// - Errors stemming from transaction pallet. /// #[pallet::call_index(2)] - #[pallet::weight((Weight::from_parts(340_800_000, 0) - .saturating_add(T::DbWeight::get().reads(25_u64)) - .saturating_add(T::DbWeight::get().writes(16_u64)), DispatchClass::Normal, Pays::Yes))] + #[pallet::weight((Weight::from_parts(523_200_000, 0) + .saturating_add(T::DbWeight::get().reads(19_u64)) + .saturating_add(T::DbWeight::get().writes(13_u64)), DispatchClass::Normal, Pays::Yes))] pub fn add_stake( origin: OriginFor, hotkey: T::AccountId, @@ -1040,9 +1040,9 @@ mod dispatches { /// User register a new subnetwork via burning token #[pallet::call_index(7)] - #[pallet::weight((Weight::from_parts(354_200_000, 0) - .saturating_add(T::DbWeight::get().reads(47_u64)) - .saturating_add(T::DbWeight::get().writes(40_u64)), DispatchClass::Normal, Pays::Yes))] + #[pallet::weight((Weight::from_parts(315_200_000, 0) + .saturating_add(T::DbWeight::get().reads(34_u64)) + .saturating_add(T::DbWeight::get().writes(29_u64)), DispatchClass::Normal, Pays::Yes))] pub fn burned_register( origin: OriginFor, netuid: NetUid, @@ -1225,9 +1225,9 @@ mod dispatches { /// User register a new subnetwork #[pallet::call_index(59)] - #[pallet::weight((Weight::from_parts(235_400_000, 0) + #[pallet::weight((Weight::from_parts(238_500_000, 0) .saturating_add(T::DbWeight::get().reads(36_u64)) - .saturating_add(T::DbWeight::get().writes(52_u64)), DispatchClass::Normal, Pays::Yes))] + .saturating_add(T::DbWeight::get().writes(50_u64)), DispatchClass::Normal, Pays::Yes))] pub fn register_network(origin: OriginFor, hotkey: T::AccountId) -> DispatchResult { Self::do_register_network(origin, &hotkey, 1, None) } @@ -1434,9 +1434,9 @@ mod dispatches { /// User register a new subnetwork #[pallet::call_index(79)] - #[pallet::weight((Weight::from_parts(396_000_000, 0) + #[pallet::weight((Weight::from_parts(235_700_000, 0) .saturating_add(T::DbWeight::get().reads(35_u64)) - .saturating_add(T::DbWeight::get().writes(51_u64)), DispatchClass::Normal, Pays::Yes))] + .saturating_add(T::DbWeight::get().writes(49_u64)), DispatchClass::Normal, Pays::Yes))] pub fn register_network_with_identity( origin: OriginFor, hotkey: T::AccountId, @@ -1504,9 +1504,9 @@ mod dispatches { /// * `TxRateLimitExceeded`: /// - Thrown if key has hit transaction rate limit #[pallet::call_index(84)] - #[pallet::weight((Weight::from_parts(358_500_000, 0) - .saturating_add(T::DbWeight::get().reads(41_u64)) - .saturating_add(T::DbWeight::get().writes(26_u64)), DispatchClass::Normal, Pays::Yes))] + #[pallet::weight((Weight::from_parts(486_500_000, 0) + .saturating_add(T::DbWeight::get().reads(34_u64)) + .saturating_add(T::DbWeight::get().writes(22_u64)), DispatchClass::Normal, Pays::Yes))] pub fn unstake_all_alpha(origin: OriginFor, hotkey: T::AccountId) -> DispatchResult { Self::do_unstake_all_alpha(origin, hotkey) } @@ -1533,8 +1533,8 @@ mod dispatches { /// - The alpha stake amount to move. /// #[pallet::call_index(85)] - #[pallet::weight((Weight::from_parts(164_300_000, 0) - .saturating_add(T::DbWeight::get().reads(15_u64)) + #[pallet::weight((Weight::from_parts(168_200_000, 0) + .saturating_add(T::DbWeight::get().reads(16_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)), DispatchClass::Normal, Pays::Yes))] pub fn move_stake( origin: T::RuntimeOrigin, @@ -1576,8 +1576,8 @@ mod dispatches { /// # Events /// May emit a `StakeTransferred` event on success. #[pallet::call_index(86)] - #[pallet::weight((Weight::from_parts(160_300_000, 0) - .saturating_add(T::DbWeight::get().reads(13_u64)) + #[pallet::weight((Weight::from_parts(163_400_000, 0) + .saturating_add(T::DbWeight::get().reads(14_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)), DispatchClass::Normal, Pays::Yes))] pub fn transfer_stake( origin: T::RuntimeOrigin, @@ -1618,9 +1618,9 @@ mod dispatches { /// May emit a `StakeSwapped` event on success. #[pallet::call_index(87)] #[pallet::weight(( - Weight::from_parts(351_300_000, 0) - .saturating_add(T::DbWeight::get().reads(37_u64)) - .saturating_add(T::DbWeight::get().writes(24_u64)), + Weight::from_parts(453_800_000, 0) + .saturating_add(T::DbWeight::get().reads(30_u64)) + .saturating_add(T::DbWeight::get().writes(20_u64)), DispatchClass::Normal, Pays::Yes ))] @@ -1683,9 +1683,9 @@ mod dispatches { /// - Errors stemming from transaction pallet. /// #[pallet::call_index(88)] - #[pallet::weight((Weight::from_parts(402_900_000, 0) - .saturating_add(T::DbWeight::get().reads(25_u64)) - .saturating_add(T::DbWeight::get().writes(16_u64)), DispatchClass::Normal, Pays::Yes))] + #[pallet::weight((Weight::from_parts(713_200_000, 0) + .saturating_add(T::DbWeight::get().reads(19_u64)) + .saturating_add(T::DbWeight::get().writes(13_u64)), DispatchClass::Normal, Pays::Yes))] pub fn add_stake_limit( origin: OriginFor, hotkey: T::AccountId, @@ -1748,9 +1748,9 @@ mod dispatches { /// - Thrown if there is not enough stake on the hotkey to withdwraw this amount. /// #[pallet::call_index(89)] - #[pallet::weight((Weight::from_parts(377_400_000, 0) - .saturating_add(T::DbWeight::get().reads(29_u64)) - .saturating_add(T::DbWeight::get().writes(15_u64)), DispatchClass::Normal, Pays::Yes))] + #[pallet::weight((Weight::from_parts(611_100_000, 0) + .saturating_add(T::DbWeight::get().reads(23_u64)) + .saturating_add(T::DbWeight::get().writes(12_u64)), DispatchClass::Normal, Pays::Yes))] pub fn remove_stake_limit( origin: OriginFor, hotkey: T::AccountId, @@ -1792,9 +1792,9 @@ mod dispatches { /// May emit a `StakeSwapped` event on success. #[pallet::call_index(90)] #[pallet::weight(( - Weight::from_parts(411_500_000, 0) - .saturating_add(T::DbWeight::get().reads(37_u64)) - .saturating_add(T::DbWeight::get().writes(24_u64)), + Weight::from_parts(661_800_000, 0) + .saturating_add(T::DbWeight::get().reads(30_u64)) + .saturating_add(T::DbWeight::get().writes(20_u64)), DispatchClass::Normal, Pays::Yes ))] @@ -1970,9 +1970,9 @@ mod dispatches { /// at which or better (higher) the staking should execute. /// Without limit_price it remove all the stake similar to `remove_stake` extrinsic #[pallet::call_index(103)] - #[pallet::weight((Weight::from_parts(395_300_000, 10142) - .saturating_add(T::DbWeight::get().reads(29_u64)) - .saturating_add(T::DbWeight::get().writes(15_u64)), DispatchClass::Normal, Pays::Yes))] + #[pallet::weight((Weight::from_parts(615_000_000, 10142) + .saturating_add(T::DbWeight::get().reads(23_u64)) + .saturating_add(T::DbWeight::get().writes(12_u64)), DispatchClass::Normal, Pays::Yes))] pub fn remove_stake_full_limit( origin: T::RuntimeOrigin, hotkey: T::AccountId, @@ -2584,9 +2584,9 @@ mod dispatches { /// alpha token first and immediately burn the acquired amount of alpha (aka Subnet buyback). #[pallet::call_index(132)] #[pallet::weight(( - Weight::from_parts(368_000_000, 8556) - .saturating_add(T::DbWeight::get().reads(28_u64)) - .saturating_add(T::DbWeight::get().writes(17_u64)), + Weight::from_parts(757_700_000, 8556) + .saturating_add(T::DbWeight::get().reads(22_u64)) + .saturating_add(T::DbWeight::get().writes(14_u64)), DispatchClass::Normal, Pays::Yes ))] From f30e0396708e6e8ebeba02d48de67a25b77da99b Mon Sep 17 00:00:00 2001 From: Aliaksandr Tsurko Date: Fri, 13 Feb 2026 11:38:19 +0100 Subject: [PATCH 227/240] Bump spec version --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 89735b1011..664c1304f8 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -243,7 +243,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: 377, + spec_version: 378, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 2208d29d8076472c2d3685c46aa354afca065f53 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Fri, 13 Feb 2026 16:12:32 -0500 Subject: [PATCH 228/240] Increaase tx fees 10x, increase swap fees to 1%, forward tx fees to block author --- chain-extensions/src/mock.rs | 9 ++++ pallets/admin-utils/src/tests/mock.rs | 9 ++++ pallets/subtensor/src/tests/mock.rs | 9 ++++ pallets/subtensor/src/tests/move_stake.rs | 2 +- pallets/subtensor/src/tests/networks.rs | 3 +- pallets/subtensor/src/tests/staking.rs | 63 ++++++++++++----------- pallets/swap/src/mock.rs | 10 +++- pallets/swap/src/pallet/mod.rs | 12 ++++- pallets/transaction-fee/src/lib.rs | 43 +++++++++++++++- pallets/transaction-fee/src/tests/mock.rs | 22 ++++++-- runtime/src/lib.rs | 34 +++++++++++- 11 files changed, 174 insertions(+), 42 deletions(-) diff --git a/chain-extensions/src/mock.rs b/chain-extensions/src/mock.rs index 69fb9de089..7d20ab2b45 100644 --- a/chain-extensions/src/mock.rs +++ b/chain-extensions/src/mock.rs @@ -413,6 +413,14 @@ impl pallet_subtensor::Config for Test { type EvmKeyAssociateRateLimit = EvmKeyAssociateRateLimit; } +pub struct MockAuthorshipProvider; + +impl pallet_subtensor_swap::AuthorshipProvider for MockAuthorshipProvider { + fn author() -> Option { + Some(U256::from(1u64)) + } +} + // Swap-related parameter types parameter_types! { pub const SwapProtocolId: PalletId = PalletId(*b"ten/swap"); @@ -431,6 +439,7 @@ impl pallet_subtensor_swap::Config for Test { type MinimumLiquidity = SwapMinimumLiquidity; type MinimumReserve = SwapMinimumReserve; type WeightInfo = (); + type AuthorshipProvider = MockAuthorshipProvider; } pub struct OriginPrivilegeCmp; diff --git a/pallets/admin-utils/src/tests/mock.rs b/pallets/admin-utils/src/tests/mock.rs index c28755802f..e3445b3ce9 100644 --- a/pallets/admin-utils/src/tests/mock.rs +++ b/pallets/admin-utils/src/tests/mock.rs @@ -318,6 +318,14 @@ impl pallet_balances::Config for Test { type RuntimeHoldReason = (); } +pub struct MockAuthorshipProvider; + +impl pallet_subtensor_swap::AuthorshipProvider for MockAuthorshipProvider { + fn author() -> Option { + Some(U256::from(1u64)) + } +} + // Swap-related parameter types parameter_types! { pub const SwapProtocolId: PalletId = PalletId(*b"ten/swap"); @@ -336,6 +344,7 @@ impl pallet_subtensor_swap::Config for Test { type MinimumLiquidity = SwapMinimumLiquidity; type MinimumReserve = SwapMinimumReserve; type WeightInfo = (); + type AuthorshipProvider = MockAuthorshipProvider; } pub struct OriginPrivilegeCmp; diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index 62f11ad4d0..56d78f5304 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -304,6 +304,14 @@ impl crate::Config for Test { type EvmKeyAssociateRateLimit = EvmKeyAssociateRateLimit; } +pub struct MockAuthorshipProvider; + +impl pallet_subtensor_swap::AuthorshipProvider for MockAuthorshipProvider { + fn author() -> Option { + Some(U256::from(1u64)) + } +} + // Swap-related parameter types parameter_types! { pub const SwapProtocolId: PalletId = PalletId(*b"ten/swap"); @@ -322,6 +330,7 @@ impl pallet_subtensor_swap::Config for Test { type MinimumLiquidity = SwapMinimumLiquidity; type MinimumReserve = SwapMinimumReserve; type WeightInfo = (); + type AuthorshipProvider = MockAuthorshipProvider; } pub struct OriginPrivilegeCmp; diff --git a/pallets/subtensor/src/tests/move_stake.rs b/pallets/subtensor/src/tests/move_stake.rs index 77012e6817..9c9f92d8ac 100644 --- a/pallets/subtensor/src/tests/move_stake.rs +++ b/pallets/subtensor/src/tests/move_stake.rs @@ -711,7 +711,7 @@ fn test_do_move_storage_updates() { destination_netuid ), alpha2, - epsilon = 2.into() + epsilon = 50.into() ); }); } diff --git a/pallets/subtensor/src/tests/networks.rs b/pallets/subtensor/src/tests/networks.rs index 5176ae05dc..fe141c040f 100644 --- a/pallets/subtensor/src/tests/networks.rs +++ b/pallets/subtensor/src/tests/networks.rs @@ -829,7 +829,8 @@ fn destroy_alpha_out_many_stakers_complex_distribution() { netuid.into(), min_stake, ); - min_stake.saturating_add(fee) + // Double the fees because fee is calculated for min_stake, not for min_amount + min_stake + fee * 2.into() }; const N: usize = 20; diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index 0dfd687959..fb614b2325 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -2794,7 +2794,7 @@ fn test_max_amount_add_stable() { // cargo test --package pallet-subtensor --lib -- tests::staking::test_max_amount_add_dynamic --exact --show-output #[test] fn test_max_amount_add_dynamic() { - // tao_in, alpha_in, limit_price, expected_max_swappable + // tao_in, alpha_in, limit_price, expected_max_swappable (with 1% fees) [ // Zero handling (no panics) ( @@ -2808,16 +2808,16 @@ fn test_max_amount_add_dynamic() { // Low bounds (100, 100, 1_100_000_000, Ok(4)), (1_000, 1_000, 1_100_000_000, Ok(48)), - (10_000, 10_000, 1_100_000_000, Ok(489)), + (10_000, 10_000, 1_100_000_000, Ok(492)), // Basic math - (1_000_000, 1_000_000, 4_000_000_000, Ok(1_000_000)), - (1_000_000, 1_000_000, 9_000_000_000, Ok(2_000_000)), - (1_000_000, 1_000_000, 16_000_000_000, Ok(3_000_000)), + (1_000_000, 1_000_000, 4_000_000_000, Ok(1_010_000)), + (1_000_000, 1_000_000, 9_000_000_000, Ok(2_020_000)), + (1_000_000, 1_000_000, 16_000_000_000, Ok(3_030_000)), ( 1_000_000_000_000, 1_000_000_000_000, 16_000_000_000, - Ok(3_000_000_000_000), + Ok(3_030_000_000_000), ), // Normal range values with edge cases ( @@ -2865,7 +2865,7 @@ fn test_max_amount_add_dynamic() { 150_000_000_000, 100_000_000_000, 6_000_000_000, - Ok(150_000_000_000), + Ok(151_500_000_000), ), // Miscellaneous overflows and underflows (u64::MAX / 2, u64::MAX, u64::MAX, Ok(u64::MAX)), @@ -2903,7 +2903,7 @@ fn test_max_amount_add_dynamic() { Ok(v) => assert_abs_diff_eq!( SubtensorModule::get_max_amount_add(netuid, limit_price.into()).unwrap(), v, - epsilon = v / 100 + epsilon = v / 10000 ), } }); @@ -2997,7 +2997,7 @@ fn test_max_amount_remove_dynamic() { let subnet_owner_hotkey = U256::from(1002); let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); - // tao_in, alpha_in, limit_price, expected_max_swappable + // tao_in, alpha_in, limit_price, expected_max_swappable (+ 1% fee) [ // Zero handling (no panics) ( @@ -3021,22 +3021,22 @@ fn test_max_amount_remove_dynamic() { // is sharply decreasing when limit price increases) (1_000, 1_000, 0, Ok(u64::MAX)), (1_001, 1_001, 0, Ok(u64::MAX)), - (1_001, 1_001, 1, Ok(17_472)), - (1_001, 1_001, 2, Ok(17_472)), - (1_001, 1_001, 1_001, Ok(17_472)), - (1_001, 1_001, 10_000, Ok(17_472)), - (1_001, 1_001, 100_000, Ok(17_472)), - (1_001, 1_001, 1_000_000, Ok(17_472)), - (1_001, 1_001, 10_000_000, Ok(9_013)), - (1_001, 1_001, 100_000_000, Ok(2_165)), + (1_001, 1_001, 1, Ok(17_646)), + (1_001, 1_001, 2, Ok(17_646)), + (1_001, 1_001, 1_001, Ok(17_646)), + (1_001, 1_001, 10_000, Ok(17_646)), + (1_001, 1_001, 100_000, Ok(17_646)), + (1_001, 1_001, 1_000_000, Ok(17_646)), + (1_001, 1_001, 10_000_000, Ok(9_103)), + (1_001, 1_001, 100_000_000, Ok(2_186)), // Basic math - (1_000_000, 1_000_000, 250_000_000, Ok(1_000_000)), - (1_000_000, 1_000_000, 62_500_000, Ok(3_000_000)), + (1_000_000, 1_000_000, 250_000_000, Ok(1_010_000)), + (1_000_000, 1_000_000, 62_500_000, Ok(3_030_000)), ( 1_000_000_000_000, 1_000_000_000_000, 62_500_000, - Ok(3_000_000_000_000), + Ok(3_030_000_000_000), ), // Normal range values with edge cases and sanity checks (200_000_000_000, 100_000_000_000, 0, Ok(u64::MAX)), @@ -3044,13 +3044,13 @@ fn test_max_amount_remove_dynamic() { 200_000_000_000, 100_000_000_000, 500_000_000, - Ok(100_000_000_000), + Ok(101_000_000_000), ), ( 200_000_000_000, 100_000_000_000, 125_000_000, - Ok(300_000_000_000), + Ok(303_000_000_000), ), ( 200_000_000_000, @@ -3069,15 +3069,15 @@ fn test_max_amount_remove_dynamic() { )), ), (200_000_000_000, 100_000_000_000, 1_999_999_999, Ok(24)), - (200_000_000_000, 100_000_000_000, 1_999_999_990, Ok(252)), + (200_000_000_000, 100_000_000_000, 1_999_999_990, Ok(254)), // Miscellaneous overflows and underflows ( 21_000_000_000_000_000, 1_000_000, 21_000_000_000_000_000, - Ok(17_455_533), + Ok(17_630_088), ), - (21_000_000_000_000_000, 1_000_000, u64::MAX, Ok(67_164)), + (21_000_000_000_000_000, 1_000_000, u64::MAX, Ok(67_835)), ( 21_000_000_000_000_000, 1_000_000_000_000_000_000, @@ -3090,13 +3090,13 @@ fn test_max_amount_remove_dynamic() { 21_000_000_000_000_000, 1_000_000_000_000_000_000, 20_000_000, - Ok(24_800_000_000_000_000), + Ok(25_048_000_000_000_000), ), ( 21_000_000_000_000_000, 21_000_000_000_000_000, 999_999_999, - Ok(10_500_000), + Ok(10_605_000), ), ( 21_000_000_000_000_000, @@ -3435,8 +3435,8 @@ fn test_max_amount_move_dynamic_stable() { assert_abs_diff_eq!( SubtensorModule::get_max_amount_move(dynamic_netuid, stable_netuid, 375_000_000.into()) .unwrap(), - alpha_in, - epsilon = alpha_in / 1000.into(), + alpha_in + alpha_in / 100.into(), // + 1% fee + epsilon = alpha_in / 10_000.into(), ); // Precision test: @@ -5012,7 +5012,7 @@ fn test_remove_stake_full_limit_ok() { ); let new_balance = SubtensorModule::get_coldkey_balance(&coldkey_account_id); - assert_abs_diff_eq!(new_balance, 9_086_000_000, epsilon = 1_000_000); + assert_abs_diff_eq!(new_balance, 9_008_200_000, epsilon = 1_000_000); }); } @@ -5096,9 +5096,10 @@ fn test_remove_stake_full_limit_ok_with_no_limit_price() { ); let new_balance = SubtensorModule::get_coldkey_balance(&coldkey_account_id); - assert_abs_diff_eq!(new_balance, 9_086_000_000, epsilon = 1_000_000); + assert_abs_diff_eq!(new_balance, 9_008_200_000, epsilon = 1_000_000); }); } + /// This test verifies that minimum stake amount is sufficient to move price and apply /// non-zero staking fees #[test] diff --git a/pallets/swap/src/mock.rs b/pallets/swap/src/mock.rs index 142a59b8df..93df41424a 100644 --- a/pallets/swap/src/mock.rs +++ b/pallets/swap/src/mock.rs @@ -15,7 +15,6 @@ use sp_runtime::{ traits::{BlakeTwo256, IdentityLookup}, }; use std::{cell::RefCell, collections::HashMap}; -// use substrate_fixed::types::U64F64; use subtensor_runtime_common::{ AlphaCurrency, BalanceOps, @@ -296,6 +295,14 @@ impl BalanceOps for MockBalanceOps { } } +pub struct MockAuthorshipProvider; + +impl crate::pallet::AuthorshipProvider for MockAuthorshipProvider { + fn author() -> Option { + Some(1u32) + } +} + impl crate::pallet::Config for Test { type SubnetInfo = MockLiquidityProvider; type TaoReserve = TaoReserve; @@ -306,6 +313,7 @@ impl crate::pallet::Config for Test { type MinimumLiquidity = MinimumLiquidity; type MinimumReserve = MinimumReserves; type WeightInfo = (); + type AuthorshipProvider = MockAuthorshipProvider; } // Build genesis storage according to the mock runtime. diff --git a/pallets/swap/src/pallet/mod.rs b/pallets/swap/src/pallet/mod.rs index 842ba8697c..4d8c86b628 100644 --- a/pallets/swap/src/pallet/mod.rs +++ b/pallets/swap/src/pallet/mod.rs @@ -67,12 +67,15 @@ mod pallet { /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; + + /// Provider of current block author + type AuthorshipProvider: AuthorshipProvider; } /// Default fee rate if not set #[pallet::type_value] pub fn DefaultFeeRate() -> u16 { - 33 // ~0.05 % + 655 // ~1 % } /// The fee rate applied to swaps per subnet, normalized value between 0 and u16::MAX @@ -284,3 +287,10 @@ pub struct TickIndex(i32); TypeInfo, )] pub struct PositionId(u128); + + +/// Allow to query the current block author +pub trait AuthorshipProvider { + /// Return the current block author + fn author() -> Option; +} diff --git a/pallets/transaction-fee/src/lib.rs b/pallets/transaction-fee/src/lib.rs index 013903ea8e..9217d2a2bd 100644 --- a/pallets/transaction-fee/src/lib.rs +++ b/pallets/transaction-fee/src/lib.rs @@ -47,7 +47,7 @@ impl WeightToFeePolynomial for LinearWeightToFee { fn polynomial() -> WeightToFeeCoefficients { let coefficient = WeightToFeeCoefficient { coeff_integer: 0, - coeff_frac: Perbill::from_parts(50_000), // 0.05 unit per weight + coeff_frac: Perbill::from_parts(500_000), // 0.5 unit per weight negative: false, degree: 1, }; @@ -95,6 +95,7 @@ where T: frame_system::Config, T: pallet_subtensor::Config, T: pallet_balances::Config, + T: FeeRecipientProvider>, { fn on_nonzero_unbalanced( imbalance: FungibleImbalance< @@ -111,6 +112,42 @@ where } } +// impl +// OnUnbalanced< +// FungibleImbalance< +// u64, +// DecreaseIssuance, pallet_balances::Pallet>, +// IncreaseIssuance, pallet_balances::Pallet>, +// >, +// > for TransactionFeeHandler +// where +// T: frame_system::Config +// + pallet_subtensor::Config +// + pallet_balances::Config +// + pallet_authorship::Config, +// { +// fn on_nonzero_unbalanced( +// imbalance: FungibleImbalance< +// u64, +// DecreaseIssuance, pallet_balances::Pallet>, +// IncreaseIssuance, pallet_balances::Pallet>, +// >, +// ) { +// if let Some(author) = pallet_authorship::Pallet::::author() { +// // Pay author instead of burning. +// // One of these is the right call depending on your exact fungible API: +// // let _ = pallet_balances::Pallet::::resolve(&author, imbalance); +// // or: let _ = pallet_balances::Pallet::::deposit(&author, imbalance.peek(), Precision::BestEffort); +// // +// // Prefer "resolve" (moves the actual imbalance) if available: +// let _ = as Balanced<_>>::resolve(&author, imbalance); +// } else { +// // Fallback: if no author, burn (or just drop). +// drop(imbalance); +// } +// } +// } + /// Handle Alpha fees impl AlphaFeeHandler for TransactionFeeHandler where @@ -417,3 +454,7 @@ where F::minimum_balance() } } + +pub trait FeeRecipientProvider { + fn fee_recipient() -> Option; +} \ No newline at end of file diff --git a/pallets/transaction-fee/src/tests/mock.rs b/pallets/transaction-fee/src/tests/mock.rs index 56b7b77308..6364b24b63 100644 --- a/pallets/transaction-fee/src/tests/mock.rs +++ b/pallets/transaction-fee/src/tests/mock.rs @@ -29,10 +29,7 @@ use pallet_transaction_payment::{ConstFeeMultiplier, Multiplier}; pub const TAO: u64 = 1_000_000_000; -pub type Block = sp_runtime::generic::Block< - sp_runtime::generic::Header, - UncheckedExtrinsic, ->; +type Block = frame_system::mocking::MockBlock; // Configure a mock runtime to test the pallet. frame_support::construct_runtime!( @@ -128,6 +125,12 @@ parameter_types! { pub FeeMultiplier: Multiplier = Multiplier::one(); } +impl crate::FeeRecipientProvider for Test { + fn fee_recipient() -> Option { + Some(U256::from(1u64)) // AccountId(1) will receive tx fees + } +} + impl pallet_transaction_payment::Config for Test { type RuntimeEvent = RuntimeEvent; type OnChargeTransaction = SubtensorTxFeeHandler>; @@ -383,6 +386,14 @@ impl pallet_balances::Config for Test { type RuntimeHoldReason = (); } +pub struct MockAuthorshipProvider; + +impl pallet_subtensor_swap::AuthorshipProvider for MockAuthorshipProvider { + fn author() -> Option { + Some(U256::from(1u64)) + } +} + // Swap-related parameter types parameter_types! { pub const SwapProtocolId: PalletId = PalletId(*b"ten/swap"); @@ -401,6 +412,7 @@ impl pallet_subtensor_swap::Config for Test { type MinimumLiquidity = SwapMinimumLiquidity; type MinimumReserve = SwapMinimumReserve; type WeightInfo = (); + type AuthorshipProvider = MockAuthorshipProvider; } pub struct OriginPrivilegeCmp; @@ -517,7 +529,7 @@ where pallet_transaction_payment::ChargeTransactionPayment::::from(0), ); - Some(UncheckedExtrinsic::new_signed(call, nonce, (), extra)) + Some(UncheckedExtrinsic::new_signed(call, nonce.into(), (), extra)) } } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 664c1304f8..a2f1a71d40 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -44,6 +44,7 @@ use pallet_subtensor::rpc_info::{ use pallet_subtensor::{CommitmentsInterface, ProxyInterface}; use pallet_subtensor_proxy as pallet_proxy; use pallet_subtensor_swap_runtime_api::{SimSwapResult, SubnetPrice}; +use pallet_subtensor_swap::AuthorshipProvider; use pallet_subtensor_utility as pallet_utility; use runtime_common::prod_or_fast; use safe_math::FixedExt; @@ -104,7 +105,7 @@ use scale_info::TypeInfo; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; pub use sp_runtime::{Perbill, Permill}; -use subtensor_transaction_fee::{SubtensorTxFeeHandler, TransactionFeeHandler}; +use subtensor_transaction_fee::{FeeRecipientProvider, SubtensorTxFeeHandler, TransactionFeeHandler}; // Frontier use fp_rpc::TransactionStatus; @@ -452,6 +453,36 @@ impl pallet_balances::Config for Runtime { type DoneSlashHandler = (); } +// Implement FeeRecipientProvider trait for Runtime to satisfy pallet transaction +// fee OnUnbalanced trait bounds +pub struct BlockAuthorFromAura(core::marker::PhantomData); + +impl> BlockAuthorFromAura { + pub fn recipient() -> Option { + let binding = frame_system::Pallet::::digest(); + let digest_logs = binding.logs(); + let author_index = F::find_author(digest_logs.iter().filter_map(|d| d.as_pre_runtime()))?; + let authority_id = pallet_aura::Authorities::::get() + .get(author_index as usize)? + .clone(); + Some(AccountId32::new(authority_id.to_raw_vec().try_into().ok()?)) + } +} + +impl FeeRecipientProvider for Runtime { + fn fee_recipient() -> Option { + BlockAuthorFromAura::::recipient() + } +} + +impl> AuthorshipProvider + for BlockAuthorFromAura +{ + fn author() -> Option { + Self::recipient() + } +} + parameter_types! { pub const OperationalFeeMultiplier: u8 = 5; pub FeeMultiplier: Multiplier = Multiplier::one(); @@ -1117,6 +1148,7 @@ impl pallet_subtensor_swap::Config for Runtime { type MinimumReserve = SwapMinimumReserve; // TODO: set measured weights when the pallet been benchmarked and the type is generated type WeightInfo = pallet_subtensor_swap::weights::DefaultWeight; + type AuthorshipProvider = BlockAuthorFromAura; } use crate::sudo_wrapper::SudoTransactionExtension; From f6e2f43330705be6b0f364cd88875e15ae3ead44 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Fri, 13 Feb 2026 17:48:57 -0500 Subject: [PATCH 229/240] Move AuthorshipProvider to subtensor pallet, forward swap fees to block builders --- chain-extensions/src/mock.rs | 18 ++++++------- pallets/admin-utils/src/tests/mock.rs | 18 ++++++------- pallets/subtensor/src/macros/config.rs | 9 +++++++ pallets/subtensor/src/staking/stake_utils.rs | 28 ++++++++++++++++++-- pallets/subtensor/src/tests/mock.rs | 18 ++++++------- pallets/swap-interface/src/lib.rs | 16 ++++++++++- pallets/swap/src/mock.rs | 9 ------- pallets/swap/src/pallet/impls.rs | 3 +++ pallets/swap/src/pallet/mod.rs | 11 +------- pallets/swap/src/pallet/swap_step.rs | 14 ++++++++-- pallets/transaction-fee/src/tests/mock.rs | 18 ++++++------- runtime/src/lib.rs | 5 ++-- 12 files changed, 104 insertions(+), 63 deletions(-) diff --git a/chain-extensions/src/mock.rs b/chain-extensions/src/mock.rs index 7d20ab2b45..35c9904a57 100644 --- a/chain-extensions/src/mock.rs +++ b/chain-extensions/src/mock.rs @@ -262,6 +262,14 @@ parameter_types! { pub const AnnouncementDepositFactor: Balance = 1; } +pub struct MockAuthorshipProvider; + +impl pallet_subtensor::AuthorshipProvider for MockAuthorshipProvider { + fn author() -> Option { + Some(U256::from(12345u64)) + } +} + parameter_types! { pub const InitialMinAllowedWeights: u16 = 0; pub const InitialEmissionValue: u16 = 0; @@ -411,14 +419,7 @@ impl pallet_subtensor::Config for Test { type MaxImmuneUidsPercentage = MaxImmuneUidsPercentage; type CommitmentsInterface = CommitmentsI; type EvmKeyAssociateRateLimit = EvmKeyAssociateRateLimit; -} - -pub struct MockAuthorshipProvider; - -impl pallet_subtensor_swap::AuthorshipProvider for MockAuthorshipProvider { - fn author() -> Option { - Some(U256::from(1u64)) - } + type AuthorshipProvider = MockAuthorshipProvider; } // Swap-related parameter types @@ -439,7 +440,6 @@ impl pallet_subtensor_swap::Config for Test { type MinimumLiquidity = SwapMinimumLiquidity; type MinimumReserve = SwapMinimumReserve; type WeightInfo = (); - type AuthorshipProvider = MockAuthorshipProvider; } pub struct OriginPrivilegeCmp; diff --git a/pallets/admin-utils/src/tests/mock.rs b/pallets/admin-utils/src/tests/mock.rs index e3445b3ce9..85ee1ae043 100644 --- a/pallets/admin-utils/src/tests/mock.rs +++ b/pallets/admin-utils/src/tests/mock.rs @@ -74,6 +74,14 @@ pub type BlockNumber = u64; pub type TestAuthId = test_crypto::TestAuthId; pub type UncheckedExtrinsic = TestXt; +pub struct MockAuthorshipProvider; + +impl pallet_subtensor::AuthorshipProvider for MockAuthorshipProvider { + fn author() -> Option { + Some(U256::from(12345u64)) + } +} + parameter_types! { pub const InitialMinAllowedWeights: u16 = 0; pub const InitialEmissionValue: u16 = 0; @@ -222,6 +230,7 @@ impl pallet_subtensor::Config for Test { type MaxImmuneUidsPercentage = MaxImmuneUidsPercentage; type CommitmentsInterface = CommitmentsI; type EvmKeyAssociateRateLimit = EvmKeyAssociateRateLimit; + type AuthorshipProvider = MockAuthorshipProvider; } parameter_types! { @@ -318,14 +327,6 @@ impl pallet_balances::Config for Test { type RuntimeHoldReason = (); } -pub struct MockAuthorshipProvider; - -impl pallet_subtensor_swap::AuthorshipProvider for MockAuthorshipProvider { - fn author() -> Option { - Some(U256::from(1u64)) - } -} - // Swap-related parameter types parameter_types! { pub const SwapProtocolId: PalletId = PalletId(*b"ten/swap"); @@ -344,7 +345,6 @@ impl pallet_subtensor_swap::Config for Test { type MinimumLiquidity = SwapMinimumLiquidity; type MinimumReserve = SwapMinimumReserve; type WeightInfo = (); - type AuthorshipProvider = MockAuthorshipProvider; } pub struct OriginPrivilegeCmp; diff --git a/pallets/subtensor/src/macros/config.rs b/pallets/subtensor/src/macros/config.rs index 15a8fe90c8..4e2b5c4488 100644 --- a/pallets/subtensor/src/macros/config.rs +++ b/pallets/subtensor/src/macros/config.rs @@ -10,6 +10,12 @@ mod config { use pallet_commitments::GetCommitments; use subtensor_swap_interface::{SwapEngine, SwapHandler}; + /// Allow to query the current block author + pub trait AuthorshipProvider { + /// Return the current block author + fn author() -> Option; + } + /// Configure the pallet by specifying the parameters and types on which it depends. #[pallet::config] pub trait Config: @@ -58,6 +64,9 @@ mod config { /// Rate limit for associating an EVM key. type EvmKeyAssociateRateLimit: Get; + + /// Provider of current block author + type AuthorshipProvider: AuthorshipProvider; /// ================================= /// ==== Initial Value Constants ==== diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index f7528935b4..a6ac52b6aa 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -590,6 +590,7 @@ impl Pallet { amount_paid_in: tao, amount_paid_out: tao.to_u64().into(), fee_paid: TaoCurrency::ZERO, + fee_to_block_author: TaoCurrency::ZERO, } }; @@ -643,19 +644,20 @@ impl Pallet { amount_paid_in: alpha, amount_paid_out: alpha.to_u64().into(), fee_paid: AlphaCurrency::ZERO, + fee_to_block_author: AlphaCurrency::ZERO, } }; // Increase only the protocol Alpha reserve. We only use the sum of // (SubnetAlphaIn + SubnetAlphaInProvided) in alpha_reserve(), so it is irrelevant // which one to increase. - let alpha_delta = swap_result.paid_in_reserve_delta_i64().unsigned_abs(); + // Decrease by fee_to_block_author because it will be staked. + let alpha_delta = swap_result.paid_in_reserve_delta_i64().unsigned_abs().saturating_sub(swap_result.fee_to_block_author.into()); SubnetAlphaIn::::mutate(netuid, |total| { *total = total.saturating_add(alpha_delta.into()); }); // Decrease Alpha outstanding. - // TODO: Deprecate, not accurate in v3 anymore SubnetAlphaOut::::mutate(netuid, |total| { *total = total.saturating_sub(alpha_delta.into()); }); @@ -706,6 +708,16 @@ impl Pallet { Self::increase_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid, refund); } + // Increase the stake of the block author + // Alpha in/out counters are already updated in swap_alpha_for_tao accordingly + let maybe_block_author_coldkey = T::AuthorshipProvider::author(); + if let Some(block_author_coldkey) = maybe_block_author_coldkey { + Self::increase_stake_for_hotkey_and_coldkey_on_subnet(hotkey, &block_author_coldkey, netuid, swap_result.fee_to_block_author); + } else { + // block author is not found, burn this alpha + Self::burn_subnet_alpha(netuid, swap_result.fee_to_block_author); + } + // If this is a root-stake if netuid == NetUid::ROOT { // Adjust root claimed value for this hotkey and coldkey. @@ -797,6 +809,18 @@ impl Pallet { StakingHotkeys::::insert(coldkey, staking_hotkeys.clone()); } + // 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(&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); + }); + } + // Record TAO inflow Self::record_tao_inflow(netuid, swap_result.amount_paid_in.into()); diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index 56d78f5304..a858f79e8f 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -153,6 +153,14 @@ parameter_types! { pub const SS58Prefix: u8 = 42; } +pub struct MockAuthorshipProvider; + +impl crate::pallet::AuthorshipProvider for MockAuthorshipProvider { + fn author() -> Option { + Some(U256::from(12345u64)) + } +} + parameter_types! { pub const InitialMinAllowedWeights: u16 = 0; pub const InitialEmissionValue: u16 = 0; @@ -302,14 +310,7 @@ impl crate::Config for Test { type MaxImmuneUidsPercentage = MaxImmuneUidsPercentage; type CommitmentsInterface = CommitmentsI; type EvmKeyAssociateRateLimit = EvmKeyAssociateRateLimit; -} - -pub struct MockAuthorshipProvider; - -impl pallet_subtensor_swap::AuthorshipProvider for MockAuthorshipProvider { - fn author() -> Option { - Some(U256::from(1u64)) - } + type AuthorshipProvider = MockAuthorshipProvider; } // Swap-related parameter types @@ -330,7 +331,6 @@ impl pallet_subtensor_swap::Config for Test { type MinimumLiquidity = SwapMinimumLiquidity; type MinimumReserve = SwapMinimumReserve; type WeightInfo = (); - type AuthorshipProvider = MockAuthorshipProvider; } pub struct OriginPrivilegeCmp; diff --git a/pallets/swap-interface/src/lib.rs b/pallets/swap-interface/src/lib.rs index 10804bd2e4..3053fe589d 100644 --- a/pallets/swap-interface/src/lib.rs +++ b/pallets/swap-interface/src/lib.rs @@ -58,9 +58,23 @@ where fn default_price_limit() -> C; } -#[freeze_struct("d3d0b124fe5a97c8")] +#[freeze_struct("97f9be71bd9edd82")] #[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)] pub struct SwapResult +where + PaidIn: Currency, + PaidOut: Currency, +{ + pub amount_paid_in: PaidIn, + pub amount_paid_out: PaidOut, + pub fee_paid: PaidIn, + pub fee_to_block_author: PaidIn, +} + +/// Externally used swap result (for RPC) +#[freeze_struct("c021997f992cfbe4")] +#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)] +pub struct SwapResultInfo where PaidIn: Currency, PaidOut: Currency, diff --git a/pallets/swap/src/mock.rs b/pallets/swap/src/mock.rs index 93df41424a..3c5e424442 100644 --- a/pallets/swap/src/mock.rs +++ b/pallets/swap/src/mock.rs @@ -295,14 +295,6 @@ impl BalanceOps for MockBalanceOps { } } -pub struct MockAuthorshipProvider; - -impl crate::pallet::AuthorshipProvider for MockAuthorshipProvider { - fn author() -> Option { - Some(1u32) - } -} - impl crate::pallet::Config for Test { type SubnetInfo = MockLiquidityProvider; type TaoReserve = TaoReserve; @@ -313,7 +305,6 @@ impl crate::pallet::Config for Test { type MinimumLiquidity = MinimumLiquidity; type MinimumReserve = MinimumReserves; type WeightInfo = (); - type AuthorshipProvider = MockAuthorshipProvider; } // Build genesis storage according to the mock runtime. diff --git a/pallets/swap/src/pallet/impls.rs b/pallets/swap/src/pallet/impls.rs index 8c3fd68209..54efabb19a 100644 --- a/pallets/swap/src/pallet/impls.rs +++ b/pallets/swap/src/pallet/impls.rs @@ -234,12 +234,14 @@ impl Pallet { log::trace!("Delta out: {}", swap_result.delta_out); log::trace!("Fees: {}", swap_result.fee_paid); + log::trace!("Fees for block author: {}", swap_result.fee_to_block_author); log::trace!("======== End Swap ========"); Ok(SwapResult { amount_paid_in: swap_result.delta_in, amount_paid_out: swap_result.delta_out, fee_paid: swap_result.fee_paid, + fee_to_block_author: swap_result.fee_to_block_author, }) } @@ -388,6 +390,7 @@ impl SwapHandler for Pallet { amount_paid_in: actual_amount, amount_paid_out: actual_amount.to_u64().into(), fee_paid: 0.into(), + fee_to_block_author: 0.into(), }) } } diff --git a/pallets/swap/src/pallet/mod.rs b/pallets/swap/src/pallet/mod.rs index 4d8c86b628..969634a46d 100644 --- a/pallets/swap/src/pallet/mod.rs +++ b/pallets/swap/src/pallet/mod.rs @@ -67,15 +67,12 @@ mod pallet { /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; - - /// Provider of current block author - type AuthorshipProvider: AuthorshipProvider; } /// Default fee rate if not set #[pallet::type_value] pub fn DefaultFeeRate() -> u16 { - 655 // ~1 % + 33 // ~0.05 % } /// The fee rate applied to swaps per subnet, normalized value between 0 and u16::MAX @@ -288,9 +285,3 @@ pub struct TickIndex(i32); )] pub struct PositionId(u128); - -/// Allow to query the current block author -pub trait AuthorshipProvider { - /// Return the current block author - fn author() -> Option; -} diff --git a/pallets/swap/src/pallet/swap_step.rs b/pallets/swap/src/pallet/swap_step.rs index ddb6a7bccc..657d50e8bc 100644 --- a/pallets/swap/src/pallet/swap_step.rs +++ b/pallets/swap/src/pallet/swap_step.rs @@ -117,17 +117,26 @@ where // Convert amounts, actual swap happens here let delta_out = Self::convert_deltas(self.netuid, self.delta_in); log::trace!("\tDelta Out : {delta_out}"); + let mut fee_to_block_author = 0.into(); if self.delta_in > 0.into() { ensure!(delta_out > 0.into(), Error::::ReservesTooLow); - // Hold the fees - Self::add_fees(self.netuid, self.fee); + // Forward 100% of fees to the block author + // If case we want to split fees between liquidity pool and validators, it + // can be done this way: + // ``` + // let lp_fee = self.fee.to_u64().saturating_mul(3).safe_div(5).into(); + // Self::add_fees(self.netuid, lp_fee); + // fee_to_block_author = self.fee.saturating_sub(lp_fee); + // ``` + fee_to_block_author = self.fee; } Ok(SwapStepResult { fee_paid: self.fee, delta_in: self.delta_in, delta_out, + fee_to_block_author, }) } } @@ -265,4 +274,5 @@ where pub(crate) fee_paid: PaidIn, pub(crate) delta_in: PaidIn, pub(crate) delta_out: PaidOut, + pub(crate) fee_to_block_author: PaidIn, } diff --git a/pallets/transaction-fee/src/tests/mock.rs b/pallets/transaction-fee/src/tests/mock.rs index 6364b24b63..cc771a1652 100644 --- a/pallets/transaction-fee/src/tests/mock.rs +++ b/pallets/transaction-fee/src/tests/mock.rs @@ -142,6 +142,14 @@ impl pallet_transaction_payment::Config for Test { type WeightInfo = pallet_transaction_payment::weights::SubstrateWeight; } +pub struct MockAuthorshipProvider; + +impl pallet_subtensor::AuthorshipProvider for MockAuthorshipProvider { + fn author() -> Option { + Some(U256::from(12345u64)) + } +} + parameter_types! { pub const InitialMinAllowedWeights: u16 = 0; pub const InitialEmissionValue: u16 = 0; @@ -290,6 +298,7 @@ impl pallet_subtensor::Config for Test { type MaxImmuneUidsPercentage = MaxImmuneUidsPercentage; type CommitmentsInterface = CommitmentsI; type EvmKeyAssociateRateLimit = EvmKeyAssociateRateLimit; + type AuthorshipProvider = MockAuthorshipProvider; } parameter_types! { @@ -386,14 +395,6 @@ impl pallet_balances::Config for Test { type RuntimeHoldReason = (); } -pub struct MockAuthorshipProvider; - -impl pallet_subtensor_swap::AuthorshipProvider for MockAuthorshipProvider { - fn author() -> Option { - Some(U256::from(1u64)) - } -} - // Swap-related parameter types parameter_types! { pub const SwapProtocolId: PalletId = PalletId(*b"ten/swap"); @@ -412,7 +413,6 @@ impl pallet_subtensor_swap::Config for Test { type MinimumLiquidity = SwapMinimumLiquidity; type MinimumReserve = SwapMinimumReserve; type WeightInfo = (); - type AuthorshipProvider = MockAuthorshipProvider; } pub struct OriginPrivilegeCmp; diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index a2f1a71d40..c019c54944 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -41,10 +41,9 @@ use pallet_subtensor::rpc_info::{ stake_info::StakeInfo, subnet_info::{SubnetHyperparams, SubnetHyperparamsV2, SubnetInfo, SubnetInfov2}, }; -use pallet_subtensor::{CommitmentsInterface, ProxyInterface}; +use pallet_subtensor::{AuthorshipProvider, CommitmentsInterface, ProxyInterface}; use pallet_subtensor_proxy as pallet_proxy; use pallet_subtensor_swap_runtime_api::{SimSwapResult, SubnetPrice}; -use pallet_subtensor_swap::AuthorshipProvider; use pallet_subtensor_utility as pallet_utility; use runtime_common::prod_or_fast; use safe_math::FixedExt; @@ -1128,6 +1127,7 @@ impl pallet_subtensor::Config for Runtime { type MaxImmuneUidsPercentage = MaxImmuneUidsPercentage; type CommitmentsInterface = CommitmentsI; type EvmKeyAssociateRateLimit = EvmKeyAssociateRateLimit; + type AuthorshipProvider = BlockAuthorFromAura; } parameter_types! { @@ -1148,7 +1148,6 @@ impl pallet_subtensor_swap::Config for Runtime { type MinimumReserve = SwapMinimumReserve; // TODO: set measured weights when the pallet been benchmarked and the type is generated type WeightInfo = pallet_subtensor_swap::weights::DefaultWeight; - type AuthorshipProvider = BlockAuthorFromAura; } use crate::sudo_wrapper::SudoTransactionExtension; From bd0d98f5496973ad33cd96fa8f74735bb97c281c Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 16 Feb 2026 13:33:10 -0500 Subject: [PATCH 230/240] Add block builder fee test --- chain-extensions/src/mock.rs | 4 +- common/src/lib.rs | 6 ++ pallets/admin-utils/src/tests/mock.rs | 4 +- pallets/subtensor/src/macros/config.rs | 9 +-- pallets/subtensor/src/staking/stake_utils.rs | 2 +- pallets/subtensor/src/tests/mock.rs | 4 +- pallets/swap-interface/src/lib.rs | 16 +---- pallets/swap/src/pallet/swap_step.rs | 12 ++-- pallets/transaction-fee/src/lib.rs | 61 +++++--------------- pallets/transaction-fee/src/tests/mock.rs | 52 ++++++++++++++--- pallets/transaction-fee/src/tests/mod.rs | 53 +++++++++++++++++ runtime/src/lib.rs | 22 +++---- 12 files changed, 145 insertions(+), 100 deletions(-) diff --git a/chain-extensions/src/mock.rs b/chain-extensions/src/mock.rs index 35c9904a57..a1e6334b1b 100644 --- a/chain-extensions/src/mock.rs +++ b/chain-extensions/src/mock.rs @@ -25,7 +25,7 @@ use sp_runtime::{ traits::{BlakeTwo256, Convert, IdentityLookup}, }; use sp_std::{cell::RefCell, cmp::Ordering, sync::OnceLock}; -use subtensor_runtime_common::{AlphaCurrency, NetUid, TaoCurrency}; +use subtensor_runtime_common::{AlphaCurrency, AuthorshipInfo, NetUid, TaoCurrency}; type Block = frame_system::mocking::MockBlock; @@ -264,7 +264,7 @@ parameter_types! { pub struct MockAuthorshipProvider; -impl pallet_subtensor::AuthorshipProvider for MockAuthorshipProvider { +impl AuthorshipInfo for MockAuthorshipProvider { fn author() -> Option { Some(U256::from(12345u64)) } diff --git a/common/src/lib.rs b/common/src/lib.rs index 658f8b2e01..f28ec6d878 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -260,6 +260,12 @@ pub trait BalanceOps { ) -> Result; } +/// Allows to query the current block author +pub trait AuthorshipInfo { + /// Return the current block author + fn author() -> Option; +} + pub mod time { use super::*; diff --git a/pallets/admin-utils/src/tests/mock.rs b/pallets/admin-utils/src/tests/mock.rs index 85ee1ae043..19d2e891cd 100644 --- a/pallets/admin-utils/src/tests/mock.rs +++ b/pallets/admin-utils/src/tests/mock.rs @@ -19,7 +19,7 @@ use sp_runtime::{ }; use sp_std::cmp::Ordering; use sp_weights::Weight; -use subtensor_runtime_common::{NetUid, TaoCurrency}; +use subtensor_runtime_common::{AuthorshipInfo, NetUid, TaoCurrency}; type Block = frame_system::mocking::MockBlock; // Configure a mock runtime to test the pallet. @@ -76,7 +76,7 @@ pub type UncheckedExtrinsic = TestXt; pub struct MockAuthorshipProvider; -impl pallet_subtensor::AuthorshipProvider for MockAuthorshipProvider { +impl AuthorshipInfo for MockAuthorshipProvider { fn author() -> Option { Some(U256::from(12345u64)) } diff --git a/pallets/subtensor/src/macros/config.rs b/pallets/subtensor/src/macros/config.rs index 4e2b5c4488..a57e302f2a 100644 --- a/pallets/subtensor/src/macros/config.rs +++ b/pallets/subtensor/src/macros/config.rs @@ -9,12 +9,7 @@ mod config { use crate::{CommitmentsInterface, GetAlphaForTao, GetTaoForAlpha}; use pallet_commitments::GetCommitments; use subtensor_swap_interface::{SwapEngine, SwapHandler}; - - /// Allow to query the current block author - pub trait AuthorshipProvider { - /// Return the current block author - fn author() -> Option; - } + use subtensor_runtime_common::AuthorshipInfo; /// Configure the pallet by specifying the parameters and types on which it depends. #[pallet::config] @@ -66,7 +61,7 @@ mod config { type EvmKeyAssociateRateLimit: Get; /// Provider of current block author - type AuthorshipProvider: AuthorshipProvider; + type AuthorshipProvider: AuthorshipInfo; /// ================================= /// ==== Initial Value Constants ==== diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index a6ac52b6aa..036a705ba1 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -3,7 +3,7 @@ use safe_math::*; use share_pool::{SharePool, SharePoolDataOperations}; use sp_std::ops::Neg; use substrate_fixed::types::{I64F64, I96F32, U64F64, U96F32}; -use subtensor_runtime_common::{AlphaCurrency, Currency, NetUid, TaoCurrency}; +use subtensor_runtime_common::{AlphaCurrency, AuthorshipInfo, Currency, NetUid, TaoCurrency}; use subtensor_swap_interface::{Order, SwapHandler, SwapResult}; impl Pallet { diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index a858f79e8f..94ae2d240d 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -29,7 +29,7 @@ use sp_runtime::{ }; use sp_std::{cell::RefCell, cmp::Ordering, sync::OnceLock}; use sp_tracing::tracing_subscriber; -use subtensor_runtime_common::{NetUid, TaoCurrency}; +use subtensor_runtime_common::{AuthorshipInfo, NetUid, TaoCurrency}; use subtensor_swap_interface::{Order, SwapHandler}; use tracing_subscriber::{EnvFilter, layer::SubscriberExt, util::SubscriberInitExt}; type Block = frame_system::mocking::MockBlock; @@ -155,7 +155,7 @@ parameter_types! { pub struct MockAuthorshipProvider; -impl crate::pallet::AuthorshipProvider for MockAuthorshipProvider { +impl AuthorshipInfo for MockAuthorshipProvider { fn author() -> Option { Some(U256::from(12345u64)) } diff --git a/pallets/swap-interface/src/lib.rs b/pallets/swap-interface/src/lib.rs index 3053fe589d..6e6ad7101a 100644 --- a/pallets/swap-interface/src/lib.rs +++ b/pallets/swap-interface/src/lib.rs @@ -58,7 +58,8 @@ where fn default_price_limit() -> C; } -#[freeze_struct("97f9be71bd9edd82")] +/// Externally used swap result (for RPC) +#[freeze_struct("58ff42da64adce1a")] #[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)] pub struct SwapResult where @@ -71,19 +72,6 @@ where pub fee_to_block_author: PaidIn, } -/// Externally used swap result (for RPC) -#[freeze_struct("c021997f992cfbe4")] -#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)] -pub struct SwapResultInfo -where - PaidIn: Currency, - PaidOut: Currency, -{ - pub amount_paid_in: PaidIn, - pub amount_paid_out: PaidOut, - pub fee_paid: PaidIn, -} - impl SwapResult where PaidIn: Currency, diff --git a/pallets/swap/src/pallet/swap_step.rs b/pallets/swap/src/pallet/swap_step.rs index 657d50e8bc..dce12e147f 100644 --- a/pallets/swap/src/pallet/swap_step.rs +++ b/pallets/swap/src/pallet/swap_step.rs @@ -121,15 +121,15 @@ where if self.delta_in > 0.into() { ensure!(delta_out > 0.into(), Error::::ReservesTooLow); - // Forward 100% of fees to the block author - // If case we want to split fees between liquidity pool and validators, it + // Split fees 3/5 vs. 2/5 between liquidity pool and validators. + // In case we want just to forward 100% of fees to the block author, it // can be done this way: // ``` - // let lp_fee = self.fee.to_u64().saturating_mul(3).safe_div(5).into(); - // Self::add_fees(self.netuid, lp_fee); - // fee_to_block_author = self.fee.saturating_sub(lp_fee); + // fee_to_block_author = self.fee; // ``` - fee_to_block_author = self.fee; + let lp_fee = self.fee.to_u64().saturating_mul(3).safe_div(5).into(); + Self::add_fees(self.netuid, lp_fee); + fee_to_block_author = self.fee.saturating_sub(lp_fee); } Ok(SwapStepResult { diff --git a/pallets/transaction-fee/src/lib.rs b/pallets/transaction-fee/src/lib.rs index 9217d2a2bd..21e72e8853 100644 --- a/pallets/transaction-fee/src/lib.rs +++ b/pallets/transaction-fee/src/lib.rs @@ -31,7 +31,7 @@ use core::marker::PhantomData; use smallvec::smallvec; use sp_std::vec::Vec; use substrate_fixed::types::U64F64; -use subtensor_runtime_common::{Balance, Currency, NetUid}; +use subtensor_runtime_common::{AuthorshipInfo, Balance, NetUid}; // Tests #[cfg(test)] @@ -95,7 +95,7 @@ where T: frame_system::Config, T: pallet_subtensor::Config, T: pallet_balances::Config, - T: FeeRecipientProvider>, + T: AuthorshipInfo>, { fn on_nonzero_unbalanced( imbalance: FungibleImbalance< @@ -104,50 +104,21 @@ where IncreaseIssuance, pallet_balances::Pallet>, >, ) { - let ti_before = pallet_subtensor::TotalIssuance::::get(); - pallet_subtensor::TotalIssuance::::put( - ti_before.saturating_sub(imbalance.peek().into()), - ); - drop(imbalance); + if let Some(author) = T::author() { + // Pay block author instead of burning. + // One of these is the right call depending on your exact fungible API: + // let _ = pallet_balances::Pallet::::resolve(&author, imbalance); + // or: let _ = pallet_balances::Pallet::::deposit(&author, imbalance.peek(), Precision::BestEffort); + // + // Prefer "resolve" (moves the actual imbalance) if available: + let _ = as Balanced<_>>::resolve(&author, imbalance); + } else { + // Fallback: if no author, burn (or just drop). + drop(imbalance); + } } } -// impl -// OnUnbalanced< -// FungibleImbalance< -// u64, -// DecreaseIssuance, pallet_balances::Pallet>, -// IncreaseIssuance, pallet_balances::Pallet>, -// >, -// > for TransactionFeeHandler -// where -// T: frame_system::Config -// + pallet_subtensor::Config -// + pallet_balances::Config -// + pallet_authorship::Config, -// { -// fn on_nonzero_unbalanced( -// imbalance: FungibleImbalance< -// u64, -// DecreaseIssuance, pallet_balances::Pallet>, -// IncreaseIssuance, pallet_balances::Pallet>, -// >, -// ) { -// if let Some(author) = pallet_authorship::Pallet::::author() { -// // Pay author instead of burning. -// // One of these is the right call depending on your exact fungible API: -// // let _ = pallet_balances::Pallet::::resolve(&author, imbalance); -// // or: let _ = pallet_balances::Pallet::::deposit(&author, imbalance.peek(), Precision::BestEffort); -// // -// // Prefer "resolve" (moves the actual imbalance) if available: -// let _ = as Balanced<_>>::resolve(&author, imbalance); -// } else { -// // Fallback: if no author, burn (or just drop). -// drop(imbalance); -// } -// } -// } - /// Handle Alpha fees impl AlphaFeeHandler for TransactionFeeHandler where @@ -454,7 +425,3 @@ where F::minimum_balance() } } - -pub trait FeeRecipientProvider { - fn fee_recipient() -> Option; -} \ No newline at end of file diff --git a/pallets/transaction-fee/src/tests/mock.rs b/pallets/transaction-fee/src/tests/mock.rs index cc771a1652..ffc5dffda1 100644 --- a/pallets/transaction-fee/src/tests/mock.rs +++ b/pallets/transaction-fee/src/tests/mock.rs @@ -21,7 +21,7 @@ use sp_runtime::{ }; use sp_std::cmp::Ordering; use sp_weights::Weight; -pub use subtensor_runtime_common::{AlphaCurrency, Currency, NetUid, TaoCurrency}; +pub use subtensor_runtime_common::{AlphaCurrency, AuthorshipInfo, Currency, NetUid, TaoCurrency}; use subtensor_swap_interface::{Order, SwapHandler}; use crate::SubtensorTxFeeHandler; @@ -125,12 +125,6 @@ parameter_types! { pub FeeMultiplier: Multiplier = Multiplier::one(); } -impl crate::FeeRecipientProvider for Test { - fn fee_recipient() -> Option { - Some(U256::from(1u64)) // AccountId(1) will receive tx fees - } -} - impl pallet_transaction_payment::Config for Test { type RuntimeEvent = RuntimeEvent; type OnChargeTransaction = SubtensorTxFeeHandler>; @@ -144,9 +138,17 @@ impl pallet_transaction_payment::Config for Test { pub struct MockAuthorshipProvider; -impl pallet_subtensor::AuthorshipProvider for MockAuthorshipProvider { +pub const MOCK_BLOCK_BUILDER: u64 = 12345u64; + +impl AuthorshipInfo for MockAuthorshipProvider { fn author() -> Option { - Some(U256::from(12345u64)) + Some(U256::from(MOCK_BLOCK_BUILDER)) + } +} + +impl AuthorshipInfo for Test { + fn author() -> Option { + Some(U256::from(MOCK_BLOCK_BUILDER)) } } @@ -636,6 +638,38 @@ pub(crate) fn swap_alpha_to_tao(netuid: NetUid, alpha: AlphaCurrency) -> (u64, u swap_alpha_to_tao_ext(netuid, alpha, false) } +pub(crate) fn swap_tao_to_alpha_ext( + netuid: NetUid, + tao: TaoCurrency, + drop_fees: bool, +) -> (u64, u64) { + if netuid.is_root() { + return (tao.into(), 0); + } + + let order = GetAlphaForTao::::with_amount(tao); + let result = ::SwapInterface::swap( + netuid.into(), + order, + ::SwapInterface::max_price(), + drop_fees, + true, + ); + + assert_ok!(&result); + + let result = result.unwrap(); + + // we don't want to have silent 0 comparisons in tests + assert!(!result.amount_paid_out.is_zero()); + + (result.amount_paid_out.to_u64(), result.fee_paid.to_u64()) +} + +pub(crate) fn swap_tao_to_alpha(netuid: NetUid, tao: TaoCurrency) -> (u64, u64) { + swap_tao_to_alpha_ext(netuid, tao, false) +} + #[allow(dead_code)] pub fn add_network(netuid: NetUid, tempo: u16) { SubtensorModule::init_new_network(netuid, tempo); diff --git a/pallets/transaction-fee/src/tests/mod.rs b/pallets/transaction-fee/src/tests/mod.rs index 7212c91077..d7d7985482 100644 --- a/pallets/transaction-fee/src/tests/mod.rs +++ b/pallets/transaction-fee/src/tests/mod.rs @@ -1210,3 +1210,56 @@ fn test_recycle_alpha_fees_alpha() { assert!(actual_alpha_fee > 0.into()); }); } + +// cargo test --package subtensor-transaction-fee --lib -- tests::test_add_stake_fees_go_to_block_builder --exact --show-output +#[test] +fn test_add_stake_fees_go_to_block_builder() { + new_test_ext().execute_with(|| { + // Portion of swap fees that should go to the block builder + let block_builder_fee_portion = 3. / 5.; + + // Get the block builder balance + let block_builder = U256::from(MOCK_BLOCK_BUILDER); + let block_builder_balance_before = Balances::free_balance(block_builder); + + let stake_amount = TAO; + let sn = setup_subnets(1, 1); + + // 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_u64); + remove_stake_rate_limit_for_tests(&sn.hotkeys[0], &sn.coldkey, sn.subnets[0].netuid); + + // Stake + let balance_before = Balances::free_balance(sn.coldkey); + let call = RuntimeCall::SubtensorModule(pallet_subtensor::Call::add_stake { + hotkey: sn.hotkeys[0], + netuid: sn.subnets[0].netuid, + amount_staked: stake_amount.into(), + }); + + // Dispatch the extrinsic with ChargeTransactionPayment extension + let info = call.get_dispatch_info(); + let ext = pallet_transaction_payment::ChargeTransactionPayment::::from(0); + assert_ok!(ext.dispatch_transaction( + RuntimeOrigin::signed(sn.coldkey).into(), + call, + &info, + 0, + 0, + )); + + let final_balance = Balances::free_balance(sn.coldkey); + let actual_tao_fee = balance_before - stake_amount - final_balance; + assert!(actual_tao_fee > 0); + + // Expect that block builder balance has increased by both the swap fee and the transaction fee + let expected_block_builder_swap_reward = swap_fee as f64 * block_builder_fee_portion; + let expected_tx_fee = 0.000136; // Use very low value for less test flakiness + let block_builder_balance_after = Balances::free_balance(block_builder); + let actual_reward = block_builder_balance_after - block_builder_balance_before; + assert!(actual_reward as f64 >= expected_block_builder_swap_reward + expected_tx_fee); + }); +} \ No newline at end of file diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index c019c54944..740449267e 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -41,7 +41,7 @@ use pallet_subtensor::rpc_info::{ stake_info::StakeInfo, subnet_info::{SubnetHyperparams, SubnetHyperparamsV2, SubnetInfo, SubnetInfov2}, }; -use pallet_subtensor::{AuthorshipProvider, CommitmentsInterface, ProxyInterface}; +use pallet_subtensor::{CommitmentsInterface, ProxyInterface}; use pallet_subtensor_proxy as pallet_proxy; use pallet_subtensor_swap_runtime_api::{SimSwapResult, SubnetPrice}; use pallet_subtensor_utility as pallet_utility; @@ -74,7 +74,7 @@ use sp_version::NativeVersion; use sp_version::RuntimeVersion; use substrate_fixed::types::U64F64; use subtensor_precompiles::Precompiles; -use subtensor_runtime_common::{AlphaCurrency, TaoCurrency, time::*, *}; +use subtensor_runtime_common::{AlphaCurrency, AuthorshipInfo, TaoCurrency, time::*, *}; use subtensor_swap_interface::{Order, SwapHandler}; // A few exports that help ease life for downstream crates. @@ -104,7 +104,7 @@ use scale_info::TypeInfo; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; pub use sp_runtime::{Perbill, Permill}; -use subtensor_transaction_fee::{FeeRecipientProvider, SubtensorTxFeeHandler, TransactionFeeHandler}; +use subtensor_transaction_fee::{SubtensorTxFeeHandler, TransactionFeeHandler}; // Frontier use fp_rpc::TransactionStatus; @@ -452,12 +452,12 @@ impl pallet_balances::Config for Runtime { type DoneSlashHandler = (); } -// Implement FeeRecipientProvider trait for Runtime to satisfy pallet transaction +// Implement AuthorshipInfo trait for Runtime to satisfy pallet transaction // fee OnUnbalanced trait bounds pub struct BlockAuthorFromAura(core::marker::PhantomData); impl> BlockAuthorFromAura { - pub fn recipient() -> Option { + pub fn get_block_author() -> Option { let binding = frame_system::Pallet::::digest(); let digest_logs = binding.logs(); let author_index = F::find_author(digest_logs.iter().filter_map(|d| d.as_pre_runtime()))?; @@ -468,17 +468,17 @@ impl> BlockAuthorFromAura { } } -impl FeeRecipientProvider for Runtime { - fn fee_recipient() -> Option { - BlockAuthorFromAura::::recipient() +impl AuthorshipInfo for Runtime { + fn author() -> Option { + BlockAuthorFromAura::::get_block_author() } } -impl> AuthorshipProvider +impl> AuthorshipInfo for BlockAuthorFromAura { fn author() -> Option { - Self::recipient() + Self::get_block_author() } } @@ -2509,6 +2509,7 @@ impl_runtime_apis! { let price = pallet_subtensor_swap::Pallet::::current_price(netuid.into()); let no_slippage_alpha = U64F64::saturating_from_num(u64::from(tao)).safe_div(price).saturating_to_num::(); let order = pallet_subtensor::GetAlphaForTao::::with_amount(tao); + // fee_to_block_author is included in sr.fee_paid, so it is absent in this calculation pallet_subtensor_swap::Pallet::::sim_swap( netuid.into(), order, @@ -2537,6 +2538,7 @@ impl_runtime_apis! { let price = pallet_subtensor_swap::Pallet::::current_price(netuid.into()); let no_slippage_tao = U64F64::saturating_from_num(u64::from(alpha)).saturating_mul(price).saturating_to_num::(); let order = pallet_subtensor::GetTaoForAlpha::::with_amount(alpha); + // fee_to_block_author is included in sr.fee_paid, so it is absent in this calculation pallet_subtensor_swap::Pallet::::sim_swap( netuid.into(), order, From e33246ab6b1ea195bf2d6f11419860e07bdae18e Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 16 Feb 2026 14:05:19 -0500 Subject: [PATCH 231/240] Fix broken staking tests --- pallets/subtensor/src/tests/staking.rs | 47 ++++++++++++++++++-------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index fb614b2325..7e5b016bdf 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -686,6 +686,9 @@ fn test_remove_stake_total_balance_no_change() { let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); register_ok_neuron(netuid, hotkey_account_id, coldkey_account_id, 192213123); + // Set fee rate to 0 so that alpha fee is not moved to block producer + pallet_subtensor_swap::FeeRate::::insert(netuid, 0); + // Some basic assertions assert_eq!( SubtensorModule::get_total_stake(), @@ -906,6 +909,9 @@ fn test_remove_stake_total_issuance_no_change() { let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); register_ok_neuron(netuid, hotkey_account_id, coldkey_account_id, 192213123); + // Set fee rate to 0 so that alpha fee is not moved to block producer + pallet_subtensor_swap::FeeRate::::insert(netuid, 0); + // Give it some $$$ in his coldkey balance SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount); @@ -971,7 +977,7 @@ fn test_remove_stake_total_issuance_no_change() { assert_abs_diff_eq!( SubtensorModule::get_total_stake(), SubtensorModule::get_network_min_lock() + total_fee.into(), - epsilon = TaoCurrency::from(fee) / 1000.into() + epsilon = TaoCurrency::from(fee) / 1000.into() + 1.into() ); // Check if total issuance is equal to the added stake, even after remove stake (no fee, @@ -1633,6 +1639,9 @@ fn test_clear_small_nominations() { let fee = DefaultMinStake::::get().to_u64(); let init_balance = amount + fee + ExistentialDeposit::get(); + // Set fee rate to 0 so that alpha fee is not moved to block producer + pallet_subtensor_swap::FeeRate::::insert(netuid, 0); + // Register hot1. register_ok_neuron(netuid, hot1, cold1, 0); Delegates::::insert(hot1, SubtensorModule::get_min_delegate_take()); @@ -2794,7 +2803,7 @@ fn test_max_amount_add_stable() { // cargo test --package pallet-subtensor --lib -- tests::staking::test_max_amount_add_dynamic --exact --show-output #[test] fn test_max_amount_add_dynamic() { - // tao_in, alpha_in, limit_price, expected_max_swappable (with 1% fees) + // tao_in, alpha_in, limit_price, expected_max_swappable (with 0.05% fees) [ // Zero handling (no panics) ( @@ -2808,16 +2817,16 @@ fn test_max_amount_add_dynamic() { // Low bounds (100, 100, 1_100_000_000, Ok(4)), (1_000, 1_000, 1_100_000_000, Ok(48)), - (10_000, 10_000, 1_100_000_000, Ok(492)), + (10_000, 10_000, 1_100_000_000, Ok(488)), // Basic math - (1_000_000, 1_000_000, 4_000_000_000, Ok(1_010_000)), - (1_000_000, 1_000_000, 9_000_000_000, Ok(2_020_000)), - (1_000_000, 1_000_000, 16_000_000_000, Ok(3_030_000)), + (1_000_000, 1_000_000, 4_000_000_000, Ok(1_000_500)), + (1_000_000, 1_000_000, 9_000_000_000, Ok(2_001_000)), + (1_000_000, 1_000_000, 16_000_000_000, Ok(3_001_500)), ( 1_000_000_000_000, 1_000_000_000_000, 16_000_000_000, - Ok(3_030_000_000_000), + Ok(3_001_500_000_000), ), // Normal range values with edge cases ( @@ -2865,7 +2874,7 @@ fn test_max_amount_add_dynamic() { 150_000_000_000, 100_000_000_000, 6_000_000_000, - Ok(151_500_000_000), + Ok(150_075_000_000), ), // Miscellaneous overflows and underflows (u64::MAX / 2, u64::MAX, u64::MAX, Ok(u64::MAX)), @@ -2997,7 +3006,7 @@ fn test_max_amount_remove_dynamic() { let subnet_owner_hotkey = U256::from(1002); let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); - // tao_in, alpha_in, limit_price, expected_max_swappable (+ 1% fee) + // tao_in, alpha_in, limit_price, expected_max_swappable (+ 0.05% fee) [ // Zero handling (no panics) ( @@ -3069,7 +3078,7 @@ fn test_max_amount_remove_dynamic() { )), ), (200_000_000_000, 100_000_000_000, 1_999_999_999, Ok(24)), - (200_000_000_000, 100_000_000_000, 1_999_999_990, Ok(254)), + (200_000_000_000, 100_000_000_000, 1_999_999_990, Ok(250)), // Miscellaneous overflows and underflows ( 21_000_000_000_000_000, @@ -3077,7 +3086,7 @@ fn test_max_amount_remove_dynamic() { 21_000_000_000_000_000, Ok(17_630_088), ), - (21_000_000_000_000_000, 1_000_000, u64::MAX, Ok(67_835)), + (21_000_000_000_000_000, 1_000_000, u64::MAX, Ok(67_000)), ( 21_000_000_000_000_000, 1_000_000_000_000_000_000, @@ -3090,7 +3099,7 @@ fn test_max_amount_remove_dynamic() { 21_000_000_000_000_000, 1_000_000_000_000_000_000, 20_000_000, - Ok(25_048_000_000_000_000), + Ok(24_700_000_000_000_000), ), ( 21_000_000_000_000_000, @@ -3435,7 +3444,7 @@ fn test_max_amount_move_dynamic_stable() { assert_abs_diff_eq!( SubtensorModule::get_max_amount_move(dynamic_netuid, stable_netuid, 375_000_000.into()) .unwrap(), - alpha_in + alpha_in / 100.into(), // + 1% fee + alpha_in + alpha_in / 2000.into(), // + 0.05% fee epsilon = alpha_in / 10_000.into(), ); @@ -4088,6 +4097,10 @@ fn test_remove_99_9991_per_cent_stake_removes_all() { let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); register_ok_neuron(netuid, hotkey_account_id, coldkey_account_id, 192213123); + // Set fee rate to 0 so that alpha fee is not moved to block producer + // and the hotkey stake does drop to 0 + pallet_subtensor_swap::FeeRate::::insert(netuid, 0); + // Give it some $$$ in his coldkey balance SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount); @@ -4149,6 +4162,10 @@ fn test_remove_99_9989_per_cent_stake_leaves_a_little() { let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); register_ok_neuron(netuid, hotkey_account_id, coldkey_account_id, 192213123); + // Set fee rate to 0 so that alpha fee is not moved to block producer + // to avoid false success in this test + pallet_subtensor_swap::FeeRate::::insert(netuid, 0); + // Give it some $$$ in his coldkey balance SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount); @@ -5012,7 +5029,7 @@ fn test_remove_stake_full_limit_ok() { ); let new_balance = SubtensorModule::get_coldkey_balance(&coldkey_account_id); - assert_abs_diff_eq!(new_balance, 9_008_200_000, epsilon = 1_000_000); + assert_abs_diff_eq!(new_balance, 9_086_700_000, epsilon = 1_000_000); }); } @@ -5096,7 +5113,7 @@ fn test_remove_stake_full_limit_ok_with_no_limit_price() { ); let new_balance = SubtensorModule::get_coldkey_balance(&coldkey_account_id); - assert_abs_diff_eq!(new_balance, 9_008_200_000, epsilon = 1_000_000); + assert_abs_diff_eq!(new_balance, 9_086_700_000, epsilon = 1_000_000); }); } From 3300b1ffed5fd560a8773cb8cf31efdbd02bcb43 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 16 Feb 2026 14:06:24 -0500 Subject: [PATCH 232/240] fmt --- pallets/subtensor/src/macros/config.rs | 4 ++-- pallets/subtensor/src/staking/stake_utils.rs | 17 ++++++++++++++--- pallets/subtensor/src/tests/staking.rs | 12 ++++++------ pallets/swap/src/pallet/mod.rs | 1 - pallets/swap/src/pallet/swap_step.rs | 2 +- pallets/transaction-fee/src/tests/mock.rs | 7 ++++++- pallets/transaction-fee/src/tests/mod.rs | 5 ++--- runtime/src/lib.rs | 6 ++---- 8 files changed, 33 insertions(+), 21 deletions(-) diff --git a/pallets/subtensor/src/macros/config.rs b/pallets/subtensor/src/macros/config.rs index a57e302f2a..528e5c9fd5 100644 --- a/pallets/subtensor/src/macros/config.rs +++ b/pallets/subtensor/src/macros/config.rs @@ -8,8 +8,8 @@ mod config { use crate::{CommitmentsInterface, GetAlphaForTao, GetTaoForAlpha}; use pallet_commitments::GetCommitments; - use subtensor_swap_interface::{SwapEngine, SwapHandler}; use subtensor_runtime_common::AuthorshipInfo; + use subtensor_swap_interface::{SwapEngine, SwapHandler}; /// Configure the pallet by specifying the parameters and types on which it depends. #[pallet::config] @@ -59,7 +59,7 @@ mod config { /// Rate limit for associating an EVM key. type EvmKeyAssociateRateLimit: Get; - + /// Provider of current block author type AuthorshipProvider: AuthorshipInfo; diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index 036a705ba1..182fe27232 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -652,7 +652,10 @@ impl Pallet { // (SubnetAlphaIn + SubnetAlphaInProvided) in alpha_reserve(), so it is irrelevant // which one to increase. // Decrease by fee_to_block_author because it will be staked. - let alpha_delta = swap_result.paid_in_reserve_delta_i64().unsigned_abs().saturating_sub(swap_result.fee_to_block_author.into()); + let alpha_delta = swap_result + .paid_in_reserve_delta_i64() + .unsigned_abs() + .saturating_sub(swap_result.fee_to_block_author.into()); SubnetAlphaIn::::mutate(netuid, |total| { *total = total.saturating_add(alpha_delta.into()); }); @@ -712,7 +715,12 @@ impl Pallet { // Alpha in/out counters are already updated in swap_alpha_for_tao accordingly let maybe_block_author_coldkey = T::AuthorshipProvider::author(); if let Some(block_author_coldkey) = maybe_block_author_coldkey { - Self::increase_stake_for_hotkey_and_coldkey_on_subnet(hotkey, &block_author_coldkey, netuid, swap_result.fee_to_block_author); + Self::increase_stake_for_hotkey_and_coldkey_on_subnet( + hotkey, + &block_author_coldkey, + netuid, + swap_result.fee_to_block_author, + ); } else { // block author is not found, burn this alpha Self::burn_subnet_alpha(netuid, swap_result.fee_to_block_author); @@ -812,7 +820,10 @@ 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(&block_author_coldkey, swap_result.fee_to_block_author.into()); + Self::add_balance_to_coldkey_account( + &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 diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index 7e5b016bdf..04b84c662b 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -686,9 +686,9 @@ fn test_remove_stake_total_balance_no_change() { let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); register_ok_neuron(netuid, hotkey_account_id, coldkey_account_id, 192213123); - // Set fee rate to 0 so that alpha fee is not moved to block producer + // Set fee rate to 0 so that alpha fee is not moved to block producer pallet_subtensor_swap::FeeRate::::insert(netuid, 0); - + // Some basic assertions assert_eq!( SubtensorModule::get_total_stake(), @@ -909,7 +909,7 @@ fn test_remove_stake_total_issuance_no_change() { let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); register_ok_neuron(netuid, hotkey_account_id, coldkey_account_id, 192213123); - // Set fee rate to 0 so that alpha fee is not moved to block producer + // Set fee rate to 0 so that alpha fee is not moved to block producer pallet_subtensor_swap::FeeRate::::insert(netuid, 0); // Give it some $$$ in his coldkey balance @@ -1639,7 +1639,7 @@ fn test_clear_small_nominations() { let fee = DefaultMinStake::::get().to_u64(); let init_balance = amount + fee + ExistentialDeposit::get(); - // Set fee rate to 0 so that alpha fee is not moved to block producer + // Set fee rate to 0 so that alpha fee is not moved to block producer pallet_subtensor_swap::FeeRate::::insert(netuid, 0); // Register hot1. @@ -4097,7 +4097,7 @@ fn test_remove_99_9991_per_cent_stake_removes_all() { let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); register_ok_neuron(netuid, hotkey_account_id, coldkey_account_id, 192213123); - // Set fee rate to 0 so that alpha fee is not moved to block producer + // Set fee rate to 0 so that alpha fee is not moved to block producer // and the hotkey stake does drop to 0 pallet_subtensor_swap::FeeRate::::insert(netuid, 0); @@ -4162,7 +4162,7 @@ fn test_remove_99_9989_per_cent_stake_leaves_a_little() { let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); register_ok_neuron(netuid, hotkey_account_id, coldkey_account_id, 192213123); - // Set fee rate to 0 so that alpha fee is not moved to block producer + // Set fee rate to 0 so that alpha fee is not moved to block producer // to avoid false success in this test pallet_subtensor_swap::FeeRate::::insert(netuid, 0); diff --git a/pallets/swap/src/pallet/mod.rs b/pallets/swap/src/pallet/mod.rs index 969634a46d..842ba8697c 100644 --- a/pallets/swap/src/pallet/mod.rs +++ b/pallets/swap/src/pallet/mod.rs @@ -284,4 +284,3 @@ pub struct TickIndex(i32); TypeInfo, )] pub struct PositionId(u128); - diff --git a/pallets/swap/src/pallet/swap_step.rs b/pallets/swap/src/pallet/swap_step.rs index dce12e147f..8675a3b90e 100644 --- a/pallets/swap/src/pallet/swap_step.rs +++ b/pallets/swap/src/pallet/swap_step.rs @@ -122,7 +122,7 @@ where ensure!(delta_out > 0.into(), Error::::ReservesTooLow); // Split fees 3/5 vs. 2/5 between liquidity pool and validators. - // In case we want just to forward 100% of fees to the block author, it + // In case we want just to forward 100% of fees to the block author, it // can be done this way: // ``` // fee_to_block_author = self.fee; diff --git a/pallets/transaction-fee/src/tests/mock.rs b/pallets/transaction-fee/src/tests/mock.rs index ffc5dffda1..e6452a0bcd 100644 --- a/pallets/transaction-fee/src/tests/mock.rs +++ b/pallets/transaction-fee/src/tests/mock.rs @@ -531,7 +531,12 @@ where pallet_transaction_payment::ChargeTransactionPayment::::from(0), ); - Some(UncheckedExtrinsic::new_signed(call, nonce.into(), (), extra)) + Some(UncheckedExtrinsic::new_signed( + call, + nonce.into(), + (), + extra, + )) } } diff --git a/pallets/transaction-fee/src/tests/mod.rs b/pallets/transaction-fee/src/tests/mod.rs index d7d7985482..80cfab5fd3 100644 --- a/pallets/transaction-fee/src/tests/mod.rs +++ b/pallets/transaction-fee/src/tests/mod.rs @@ -1226,8 +1226,7 @@ fn test_add_stake_fees_go_to_block_builder() { let sn = setup_subnets(1, 1); // Simulate add stake to get the expected TAO fee - let (_, swap_fee) = - mock::swap_tao_to_alpha(sn.subnets[0].netuid, stake_amount.into()); + 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_u64); remove_stake_rate_limit_for_tests(&sn.hotkeys[0], &sn.coldkey, sn.subnets[0].netuid); @@ -1262,4 +1261,4 @@ fn test_add_stake_fees_go_to_block_builder() { let actual_reward = block_builder_balance_after - block_builder_balance_before; assert!(actual_reward as f64 >= expected_block_builder_swap_reward + expected_tx_fee); }); -} \ No newline at end of file +} diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 740449267e..e2461372ec 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -452,7 +452,7 @@ impl pallet_balances::Config for Runtime { type DoneSlashHandler = (); } -// Implement AuthorshipInfo trait for Runtime to satisfy pallet transaction +// Implement AuthorshipInfo trait for Runtime to satisfy pallet transaction // fee OnUnbalanced trait bounds pub struct BlockAuthorFromAura(core::marker::PhantomData); @@ -474,9 +474,7 @@ impl AuthorshipInfo for Runtime { } } -impl> AuthorshipInfo - for BlockAuthorFromAura -{ +impl> AuthorshipInfo for BlockAuthorFromAura { fn author() -> Option { Self::get_block_author() } From 280a8801fe88fac28b4b9c9dc164170e7b7ce17a Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Tue, 17 Feb 2026 11:03:33 -0500 Subject: [PATCH 233/240] Fix tx weights --- pallets/subtensor/src/macros/dispatches.rs | 24 +++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 60c37de2fe..cec3bdb9fc 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -711,8 +711,8 @@ mod dispatches { /// #[pallet::call_index(2)] #[pallet::weight((Weight::from_parts(523_200_000, 0) - .saturating_add(T::DbWeight::get().reads(19_u64)) - .saturating_add(T::DbWeight::get().writes(13_u64)), DispatchClass::Normal, Pays::Yes))] + .saturating_add(T::DbWeight::get().reads(20_u64)) + .saturating_add(T::DbWeight::get().writes(14_u64)), DispatchClass::Normal, Pays::Yes))] pub fn add_stake( origin: OriginFor, hotkey: T::AccountId, @@ -1505,8 +1505,8 @@ mod dispatches { /// - Thrown if key has hit transaction rate limit #[pallet::call_index(84)] #[pallet::weight((Weight::from_parts(486_500_000, 0) - .saturating_add(T::DbWeight::get().reads(34_u64)) - .saturating_add(T::DbWeight::get().writes(22_u64)), DispatchClass::Normal, Pays::Yes))] + .saturating_add(T::DbWeight::get().reads(35_u64)) + .saturating_add(T::DbWeight::get().writes(23_u64)), DispatchClass::Normal, Pays::Yes))] pub fn unstake_all_alpha(origin: OriginFor, hotkey: T::AccountId) -> DispatchResult { Self::do_unstake_all_alpha(origin, hotkey) } @@ -1619,8 +1619,8 @@ mod dispatches { #[pallet::call_index(87)] #[pallet::weight(( Weight::from_parts(453_800_000, 0) - .saturating_add(T::DbWeight::get().reads(30_u64)) - .saturating_add(T::DbWeight::get().writes(20_u64)), + .saturating_add(T::DbWeight::get().reads(31_u64)) + .saturating_add(T::DbWeight::get().writes(21_u64)), DispatchClass::Normal, Pays::Yes ))] @@ -1684,8 +1684,8 @@ mod dispatches { /// #[pallet::call_index(88)] #[pallet::weight((Weight::from_parts(713_200_000, 0) - .saturating_add(T::DbWeight::get().reads(19_u64)) - .saturating_add(T::DbWeight::get().writes(13_u64)), DispatchClass::Normal, Pays::Yes))] + .saturating_add(T::DbWeight::get().reads(20_u64)) + .saturating_add(T::DbWeight::get().writes(14_u64)), DispatchClass::Normal, Pays::Yes))] pub fn add_stake_limit( origin: OriginFor, hotkey: T::AccountId, @@ -1793,8 +1793,8 @@ mod dispatches { #[pallet::call_index(90)] #[pallet::weight(( Weight::from_parts(661_800_000, 0) - .saturating_add(T::DbWeight::get().reads(30_u64)) - .saturating_add(T::DbWeight::get().writes(20_u64)), + .saturating_add(T::DbWeight::get().reads(31_u64)) + .saturating_add(T::DbWeight::get().writes(21_u64)), DispatchClass::Normal, Pays::Yes ))] @@ -2585,8 +2585,8 @@ mod dispatches { #[pallet::call_index(132)] #[pallet::weight(( Weight::from_parts(757_700_000, 8556) - .saturating_add(T::DbWeight::get().reads(22_u64)) - .saturating_add(T::DbWeight::get().writes(14_u64)), + .saturating_add(T::DbWeight::get().reads(23_u64)) + .saturating_add(T::DbWeight::get().writes(15_u64)), DispatchClass::Normal, Pays::Yes ))] From e9baff00ea3f03672d422fcf7b0c9ea00d6d25c5 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Tue, 17 Feb 2026 14:12:58 -0300 Subject: [PATCH 234/240] bump spec version --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 664c1304f8..082cf124f0 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -243,7 +243,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: 378, + spec_version: 379, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From b942cf024539253f1d8d9d593a1653eb917c467a Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Tue, 17 Feb 2026 18:04:10 -0300 Subject: [PATCH 235/240] fix precompile test --- contract-tests/package-lock.json | 347 ++++++++++- .../test/runtime.call.precompile.test.ts | 2 +- contract-tests/yarn.lock | 571 +++++++----------- 3 files changed, 529 insertions(+), 391 deletions(-) diff --git a/contract-tests/package-lock.json b/contract-tests/package-lock.json index bf5d2f89d9..06c722a6de 100644 --- a/contract-tests/package-lock.json +++ b/contract-tests/package-lock.json @@ -6,13 +6,13 @@ "": { "license": "ISC", "dependencies": { - "@noble/hashes": "^2.0.1", "@polkadot-api/descriptors": "file:.papi/descriptors", "@polkadot-api/ink-contracts": "^0.4.1", "@polkadot-api/sdk-ink": "^0.5.1", "@polkadot-labs/hdkd": "^0.0.25", "@polkadot-labs/hdkd-helpers": "^0.0.25", "@polkadot/api": "^16.4.6", + "@polkadot/util-crypto": "^14.0.1", "@types/mocha": "^10.0.10", "dotenv": "17.2.1", "ethers": "^6.13.5", @@ -35,7 +35,7 @@ }, ".papi/descriptors": { "name": "@polkadot-api/descriptors", - "version": "0.1.0-autogenerated.9476216756280928360", + "version": "0.1.0-autogenerated.5063582544821983772", "peerDependencies": { "polkadot-api": ">=1.21.0" } @@ -808,7 +808,6 @@ "resolved": "https://registry.npmjs.org/@polkadot-api/ink-contracts/-/ink-contracts-0.4.3.tgz", "integrity": "sha512-Wl+4Dxjt0GAl+rADZEgrrqEesqX/xygTpX18TmzmspcKhb9QIZf9FJI8A5Sgtq0TKAOwsd1d/hbHVX3LgbXFXg==", "license": "MIT", - "peer": true, "dependencies": { "@polkadot-api/metadata-builders": "0.13.7", "@polkadot-api/substrate-bindings": "0.16.5", @@ -1125,7 +1124,6 @@ "resolved": "https://registry.npmjs.org/@polkadot-api/smoldot/-/smoldot-0.3.14.tgz", "integrity": "sha512-eWqO0xFQaKzqY5mRYxYuZcj1IiaLcQP+J38UQyuJgEorm+9yHVEQ/XBWoM83P+Y8TwE5IWTICp1LCVeiFQTGPQ==", "license": "MIT", - "peer": true, "dependencies": { "@types/node": "^24.5.2", "smoldot": "2.0.39" @@ -1377,6 +1375,78 @@ "node": ">=18" } }, + "node_modules/@polkadot/api-derive/node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@polkadot/api-derive/node_modules/@polkadot/util-crypto": { + "version": "13.5.9", + "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-13.5.9.tgz", + "integrity": "sha512-foUesMhxkTk8CZ0/XEcfvHk6I0O+aICqqVJllhOpyp/ZVnrTBKBf59T6RpsXx2pCtBlMsLRvg/6Mw7RND1HqDg==", + "license": "Apache-2.0", + "dependencies": { + "@noble/curves": "^1.3.0", + "@noble/hashes": "^1.3.3", + "@polkadot/networks": "13.5.9", + "@polkadot/util": "13.5.9", + "@polkadot/wasm-crypto": "^7.5.3", + "@polkadot/wasm-util": "^7.5.3", + "@polkadot/x-bigint": "13.5.9", + "@polkadot/x-randomvalues": "13.5.9", + "@scure/base": "^1.1.7", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "13.5.9" + } + }, + "node_modules/@polkadot/api/node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@polkadot/api/node_modules/@polkadot/util-crypto": { + "version": "13.5.9", + "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-13.5.9.tgz", + "integrity": "sha512-foUesMhxkTk8CZ0/XEcfvHk6I0O+aICqqVJllhOpyp/ZVnrTBKBf59T6RpsXx2pCtBlMsLRvg/6Mw7RND1HqDg==", + "license": "Apache-2.0", + "dependencies": { + "@noble/curves": "^1.3.0", + "@noble/hashes": "^1.3.3", + "@polkadot/networks": "13.5.9", + "@polkadot/util": "13.5.9", + "@polkadot/wasm-crypto": "^7.5.3", + "@polkadot/wasm-util": "^7.5.3", + "@polkadot/x-bigint": "13.5.9", + "@polkadot/x-randomvalues": "13.5.9", + "@scure/base": "^1.1.7", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "13.5.9" + } + }, "node_modules/@polkadot/keyring": { "version": "13.5.9", "resolved": "https://registry.npmjs.org/@polkadot/keyring/-/keyring-13.5.9.tgz", @@ -1395,6 +1465,42 @@ "@polkadot/util-crypto": "13.5.9" } }, + "node_modules/@polkadot/keyring/node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@polkadot/keyring/node_modules/@polkadot/util-crypto": { + "version": "13.5.9", + "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-13.5.9.tgz", + "integrity": "sha512-foUesMhxkTk8CZ0/XEcfvHk6I0O+aICqqVJllhOpyp/ZVnrTBKBf59T6RpsXx2pCtBlMsLRvg/6Mw7RND1HqDg==", + "license": "Apache-2.0", + "dependencies": { + "@noble/curves": "^1.3.0", + "@noble/hashes": "^1.3.3", + "@polkadot/networks": "13.5.9", + "@polkadot/util": "13.5.9", + "@polkadot/wasm-crypto": "^7.5.3", + "@polkadot/wasm-util": "^7.5.3", + "@polkadot/x-bigint": "13.5.9", + "@polkadot/x-randomvalues": "13.5.9", + "@scure/base": "^1.1.7", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "13.5.9" + } + }, "node_modules/@polkadot/networks": { "version": "13.5.9", "resolved": "https://registry.npmjs.org/@polkadot/networks/-/networks-13.5.9.tgz", @@ -1468,6 +1574,42 @@ "@substrate/connect": "0.8.11" } }, + "node_modules/@polkadot/rpc-provider/node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@polkadot/rpc-provider/node_modules/@polkadot/util-crypto": { + "version": "13.5.9", + "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-13.5.9.tgz", + "integrity": "sha512-foUesMhxkTk8CZ0/XEcfvHk6I0O+aICqqVJllhOpyp/ZVnrTBKBf59T6RpsXx2pCtBlMsLRvg/6Mw7RND1HqDg==", + "license": "Apache-2.0", + "dependencies": { + "@noble/curves": "^1.3.0", + "@noble/hashes": "^1.3.3", + "@polkadot/networks": "13.5.9", + "@polkadot/util": "13.5.9", + "@polkadot/wasm-crypto": "^7.5.3", + "@polkadot/wasm-util": "^7.5.3", + "@polkadot/x-bigint": "13.5.9", + "@polkadot/x-randomvalues": "13.5.9", + "@scure/base": "^1.1.7", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "13.5.9" + } + }, "node_modules/@polkadot/types": { "version": "16.5.3", "resolved": "https://registry.npmjs.org/@polkadot/types/-/types-16.5.3.tgz", @@ -1560,12 +1702,47 @@ "node": ">=18" } }, + "node_modules/@polkadot/types/node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@polkadot/types/node_modules/@polkadot/util-crypto": { + "version": "13.5.9", + "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-13.5.9.tgz", + "integrity": "sha512-foUesMhxkTk8CZ0/XEcfvHk6I0O+aICqqVJllhOpyp/ZVnrTBKBf59T6RpsXx2pCtBlMsLRvg/6Mw7RND1HqDg==", + "license": "Apache-2.0", + "dependencies": { + "@noble/curves": "^1.3.0", + "@noble/hashes": "^1.3.3", + "@polkadot/networks": "13.5.9", + "@polkadot/util": "13.5.9", + "@polkadot/wasm-crypto": "^7.5.3", + "@polkadot/wasm-util": "^7.5.3", + "@polkadot/x-bigint": "13.5.9", + "@polkadot/x-randomvalues": "13.5.9", + "@scure/base": "^1.1.7", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "13.5.9" + } + }, "node_modules/@polkadot/util": { "version": "13.5.9", "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-13.5.9.tgz", "integrity": "sha512-pIK3XYXo7DKeFRkEBNYhf3GbCHg6dKQisSvdzZwuyzA6m7YxQq4DFw4IE464ve4Z7WsJFt3a6C9uII36hl9EWw==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@polkadot/x-bigint": "13.5.9", "@polkadot/x-global": "13.5.9", @@ -1580,27 +1757,28 @@ } }, "node_modules/@polkadot/util-crypto": { - "version": "13.5.9", - "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-13.5.9.tgz", - "integrity": "sha512-foUesMhxkTk8CZ0/XEcfvHk6I0O+aICqqVJllhOpyp/ZVnrTBKBf59T6RpsXx2pCtBlMsLRvg/6Mw7RND1HqDg==", + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-14.0.1.tgz", + "integrity": "sha512-Cu7AKUzBTsUkbOtyuNzXcTpDjR9QW0fVR56o3gBmzfUCmvO1vlsuGzmmPzqpHymQQ3rrfqV78CPs62EGhw0R+A==", "license": "Apache-2.0", "dependencies": { "@noble/curves": "^1.3.0", "@noble/hashes": "^1.3.3", - "@polkadot/networks": "13.5.9", - "@polkadot/util": "13.5.9", + "@polkadot/networks": "14.0.1", + "@polkadot/util": "14.0.1", "@polkadot/wasm-crypto": "^7.5.3", "@polkadot/wasm-util": "^7.5.3", - "@polkadot/x-bigint": "13.5.9", - "@polkadot/x-randomvalues": "13.5.9", + "@polkadot/x-bigint": "14.0.1", + "@polkadot/x-randomvalues": "14.0.1", "@scure/base": "^1.1.7", + "@scure/sr25519": "^0.2.0", "tslib": "^2.8.0" }, "engines": { "node": ">=18" }, "peerDependencies": { - "@polkadot/util": "13.5.9" + "@polkadot/util": "14.0.1" } }, "node_modules/@polkadot/util-crypto/node_modules/@noble/hashes": { @@ -1615,6 +1793,119 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/@polkadot/util-crypto/node_modules/@polkadot/networks": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/@polkadot/networks/-/networks-14.0.1.tgz", + "integrity": "sha512-wGlBtXDkusRAj4P7uxfPz80gLO1+j99MLBaQi3bEym2xrFrFhgIWVHOZlBit/1PfaBjhX2Z8XjRxaM2w1p7w2w==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/util": "14.0.1", + "@substrate/ss58-registry": "^1.51.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/util-crypto/node_modules/@polkadot/util": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-14.0.1.tgz", + "integrity": "sha512-764HhxkPV3x5rM0/p6QdynC2dw26n+SaE+jisjx556ViCd4E28Ke4xSPef6C0Spy4aoXf2gt0PuLEcBvd6fVZg==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-bigint": "14.0.1", + "@polkadot/x-global": "14.0.1", + "@polkadot/x-textdecoder": "14.0.1", + "@polkadot/x-textencoder": "14.0.1", + "@types/bn.js": "^5.1.6", + "bn.js": "^5.2.1", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/util-crypto/node_modules/@polkadot/x-bigint": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-bigint/-/x-bigint-14.0.1.tgz", + "integrity": "sha512-gfozjGnebr2rqURs31KtaWumbW4rRZpbiluhlmai6luCNrf5u8pB+oLA35kPEntrsLk9PnIG9OsC/n4hEtx4OQ==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "14.0.1", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/util-crypto/node_modules/@polkadot/x-global": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-global/-/x-global-14.0.1.tgz", + "integrity": "sha512-aCI44DJU4fU0XXqrrSGIpi7JrZXK2kpe0jaQ2p6oDVXOOYEnZYXnMhTTmBE1lF/xtxzX50MnZrrU87jziU0qbA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/util-crypto/node_modules/@polkadot/x-randomvalues": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-randomvalues/-/x-randomvalues-14.0.1.tgz", + "integrity": "sha512-/XkQcvshzJLHITuPrN3zmQKuFIPdKWoaiHhhVLD6rQWV60lTXA3ajw3ocju8ZN7xRxnweMS9Ce0kMPYa0NhRMg==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "14.0.1", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "14.0.1", + "@polkadot/wasm-util": "*" + } + }, + "node_modules/@polkadot/util-crypto/node_modules/@polkadot/x-textdecoder": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-14.0.1.tgz", + "integrity": "sha512-CcWiPCuPVJsNk4Vq43lgFHqLRBQHb4r9RD7ZIYgmwoebES8TNm4g2ew9ToCzakFKSpzKu6I07Ne9wv/dt5zLuw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "14.0.1", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/util-crypto/node_modules/@polkadot/x-textencoder": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-14.0.1.tgz", + "integrity": "sha512-VY51SpQmF1ccmAGLfxhYnAe95Spfz049WZ/+kK4NfsGF9WejxVdU53Im5C80l45r8qHuYQsCWU3+t0FNunh2Kg==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "14.0.1", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/util-crypto/node_modules/@scure/sr25519": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@scure/sr25519/-/sr25519-0.2.0.tgz", + "integrity": "sha512-uUuLP7Z126XdSizKtrCGqYyR3b3hYtJ6Fg/XFUXmc2//k2aXHDLqZwFeXxL97gg4XydPROPVnuaHGF2+xriSKg==", + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.9.2", + "@noble/hashes": "~1.8.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@polkadot/wasm-bridge": { "version": "7.5.3", "resolved": "https://registry.npmjs.org/@polkadot/wasm-bridge/-/wasm-bridge-7.5.3.tgz", @@ -1763,7 +2054,6 @@ "resolved": "https://registry.npmjs.org/@polkadot/x-randomvalues/-/x-randomvalues-13.5.9.tgz", "integrity": "sha512-Uuuz3oubf1JCCK97fsnVUnHvk4BGp/W91mQWJlgl5TIOUSSTIRr+lb5GurCfl4kgnQq53Zi5fJV+qR9YumbnZw==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@polkadot/x-global": "13.5.9", "tslib": "^2.8.0" @@ -2333,6 +2623,17 @@ "scale-ts": "^1.6.0" } }, + "node_modules/@substrate/light-client-extension-helpers/node_modules/@polkadot-api/substrate-client": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-client/-/substrate-client-0.1.4.tgz", + "integrity": "sha512-MljrPobN0ZWTpn++da9vOvt+Ex+NlqTlr/XT7zi9sqPtDJiQcYl+d29hFAgpaeTqbeQKZwz3WDE9xcEfLE8c5A==", + "license": "MIT", + "optional": true, + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.0.1", + "@polkadot-api/utils": "0.1.0" + } + }, "node_modules/@substrate/light-client-extension-helpers/node_modules/@polkadot-api/utils": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/@polkadot-api/utils/-/utils-0.1.0.tgz", @@ -2434,7 +2735,6 @@ "integrity": "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -2834,7 +3134,6 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=20" } @@ -3058,7 +3357,6 @@ "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", "hasInstallScript": true, "license": "MIT", - "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -4544,7 +4842,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -4577,7 +4874,6 @@ "resolved": "https://registry.npmjs.org/polkadot-api/-/polkadot-api-1.22.0.tgz", "integrity": "sha512-uREBLroPbnJxBBQ+qSkKLF493qukX4PAg32iThlELrZdxfNNgro6nvWRdVmBv73tFHvf+nyWWHKTx1c57nbixg==", "license": "MIT", - "peer": true, "dependencies": { "@polkadot-api/cli": "0.16.3", "@polkadot-api/ink-contracts": "0.4.3", @@ -4844,7 +5140,6 @@ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.1.0" } @@ -4965,6 +5260,16 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/smoldot": { + "version": "2.0.26", + "resolved": "https://registry.npmjs.org/smoldot/-/smoldot-2.0.26.tgz", + "integrity": "sha512-F+qYmH4z2s2FK+CxGj8moYcd1ekSIKH8ywkdqlOz88Dat35iB1DIYL11aILN46YSGMzQW/lbJNS307zBSDN5Ig==", + "license": "GPL-3.0-or-later WITH Classpath-exception-2.0", + "optional": true, + "dependencies": { + "ws": "^8.8.1" + } + }, "node_modules/sort-keys": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-5.1.0.tgz", @@ -5370,7 +5675,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -5804,7 +6108,6 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "license": "MIT", - "peer": true, "engines": { "node": ">=10.0.0" }, diff --git a/contract-tests/test/runtime.call.precompile.test.ts b/contract-tests/test/runtime.call.precompile.test.ts index c409b25c8b..7bacc947fd 100644 --- a/contract-tests/test/runtime.call.precompile.test.ts +++ b/contract-tests/test/runtime.call.precompile.test.ts @@ -66,7 +66,7 @@ describe("Test the dispatch precompile", () => { it("Storage query only allow some pallets prefixed storage", async () => { const authorizedKeys = [ await api.query.SubtensorModule.TotalNetworks.getKey(), - await api.query.Swap.AlphaSqrtPrice.getKey(), + await api.query.Swap.FeeRate.getKey(), await api.query.Balances.TotalIssuance.getKey(), await api.query.Proxy.Announcements.getKey(), await api.query.Scheduler.Agenda.getKey(), diff --git a/contract-tests/yarn.lock b/contract-tests/yarn.lock index 33177a2104..080ecb1325 100644 --- a/contract-tests/yarn.lock +++ b/contract-tests/yarn.lock @@ -2,16 +2,16 @@ # yarn lockfile v1 -"@adraffy/ens-normalize@1.10.1": - version "1.10.1" - resolved "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz" - integrity sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw== - "@adraffy/ens-normalize@^1.10.1", "@adraffy/ens-normalize@^1.11.0": version "1.11.1" resolved "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.11.1.tgz" integrity sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ== +"@adraffy/ens-normalize@1.10.1": + version "1.10.1" + resolved "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz" + integrity sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw== + "@babel/code-frame@^7.26.2": version "7.27.1" resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz" @@ -38,136 +38,11 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@esbuild/aix-ppc64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz#80fcbe36130e58b7670511e888b8e88a259ed76c" - integrity sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA== - -"@esbuild/android-arm64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz#8aa4965f8d0a7982dc21734bf6601323a66da752" - integrity sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg== - -"@esbuild/android-arm@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.12.tgz#300712101f7f50f1d2627a162e6e09b109b6767a" - integrity sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg== - -"@esbuild/android-x64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.12.tgz#87dfb27161202bdc958ef48bb61b09c758faee16" - integrity sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg== - "@esbuild/darwin-arm64@0.25.12": version "0.25.12" resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz" integrity sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg== -"@esbuild/darwin-x64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz#146400a8562133f45c4d2eadcf37ddd09718079e" - integrity sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA== - -"@esbuild/freebsd-arm64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz#1c5f9ba7206e158fd2b24c59fa2d2c8bb47ca0fe" - integrity sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg== - -"@esbuild/freebsd-x64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz#ea631f4a36beaac4b9279fa0fcc6ca29eaeeb2b3" - integrity sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ== - -"@esbuild/linux-arm64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz#e1066bce58394f1b1141deec8557a5f0a22f5977" - integrity sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ== - -"@esbuild/linux-arm@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz#452cd66b20932d08bdc53a8b61c0e30baf4348b9" - integrity sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw== - -"@esbuild/linux-ia32@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz#b24f8acc45bcf54192c7f2f3be1b53e6551eafe0" - integrity sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA== - -"@esbuild/linux-loong64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz#f9cfffa7fc8322571fbc4c8b3268caf15bd81ad0" - integrity sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng== - -"@esbuild/linux-mips64el@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz#575a14bd74644ffab891adc7d7e60d275296f2cd" - integrity sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw== - -"@esbuild/linux-ppc64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz#75b99c70a95fbd5f7739d7692befe60601591869" - integrity sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA== - -"@esbuild/linux-riscv64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz#2e3259440321a44e79ddf7535c325057da875cd6" - integrity sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w== - -"@esbuild/linux-s390x@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz#17676cabbfe5928da5b2a0d6df5d58cd08db2663" - integrity sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg== - -"@esbuild/linux-x64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz#0583775685ca82066d04c3507f09524d3cd7a306" - integrity sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw== - -"@esbuild/netbsd-arm64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz#f04c4049cb2e252fe96b16fed90f70746b13f4a4" - integrity sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg== - -"@esbuild/netbsd-x64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz#77da0d0a0d826d7c921eea3d40292548b258a076" - integrity sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ== - -"@esbuild/openbsd-arm64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz#6296f5867aedef28a81b22ab2009c786a952dccd" - integrity sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A== - -"@esbuild/openbsd-x64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz#f8d23303360e27b16cf065b23bbff43c14142679" - integrity sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw== - -"@esbuild/openharmony-arm64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz#49e0b768744a3924be0d7fd97dd6ce9b2923d88d" - integrity sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg== - -"@esbuild/sunos-x64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz#a6ed7d6778d67e528c81fb165b23f4911b9b13d6" - integrity sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w== - -"@esbuild/win32-arm64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz#9ac14c378e1b653af17d08e7d3ce34caef587323" - integrity sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg== - -"@esbuild/win32-ia32@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz#918942dcbbb35cc14fca39afb91b5e6a3d127267" - integrity sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ== - -"@esbuild/win32-x64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz#9bdad8176be7811ad148d1f8772359041f46c6c5" - integrity sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA== - "@ethereumjs/rlp@^10.0.0": version "10.1.0" resolved "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-10.1.0.tgz" @@ -203,14 +78,6 @@ resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz" integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== -"@jridgewell/trace-mapping@0.3.9": - version "0.3.9" - resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz" - integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== - dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping@^0.3.24": version "0.3.31" resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz" @@ -219,11 +86,54 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@noble/ciphers@^1.3.0": version "1.3.0" resolved "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz" integrity sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw== +"@noble/curves@^1.3.0", "@noble/curves@^1.6.0", "@noble/curves@~1.9.0", "@noble/curves@~1.9.2": + version "1.9.7" + resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz" + integrity sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw== + dependencies: + "@noble/hashes" "1.8.0" + +"@noble/curves@^2.0.0": + version "2.0.1" + resolved "https://registry.npmjs.org/@noble/curves/-/curves-2.0.1.tgz" + integrity sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw== + dependencies: + "@noble/hashes" "2.0.1" + +"@noble/curves@^2.0.1": + version "2.0.1" + resolved "https://registry.npmjs.org/@noble/curves/-/curves-2.0.1.tgz" + integrity sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw== + dependencies: + "@noble/hashes" "2.0.1" + +"@noble/curves@~1.8.1": + version "1.8.2" + resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.8.2.tgz" + integrity sha512-vnI7V6lFNe0tLAuJMu+2sX+FcL14TaCWy1qiczg1VwRmPrpQCdq5ESXQMqUc2tluRNf6irBXrWbl1mGN8uaU/g== + dependencies: + "@noble/hashes" "1.7.2" + +"@noble/curves@~2.0.0": + version "2.0.1" + resolved "https://registry.npmjs.org/@noble/curves/-/curves-2.0.1.tgz" + integrity sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw== + dependencies: + "@noble/hashes" "2.0.1" + "@noble/curves@1.2.0": version "1.2.0" resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz" @@ -245,52 +155,51 @@ dependencies: "@noble/hashes" "1.8.0" -"@noble/curves@^1.3.0", "@noble/curves@^1.6.0", "@noble/curves@~1.9.0", "@noble/curves@~1.9.2": - version "1.9.7" - resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz" - integrity sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw== - dependencies: - "@noble/hashes" "1.8.0" +"@noble/hashes@^1.3.1": + version "1.8.0" + resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz" + integrity sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A== + +"@noble/hashes@^1.3.3", "@noble/hashes@~1.8.0": + version "1.8.0" + resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz" + integrity sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A== + +"@noble/hashes@^1.5.0": + version "1.8.0" + resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz" + integrity sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A== + +"@noble/hashes@^1.8.0", "@noble/hashes@1.8.0": + version "1.8.0" + resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz" + integrity sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A== -"@noble/curves@^2.0.0", "@noble/curves@^2.0.1", "@noble/curves@~2.0.0": +"@noble/hashes@^2.0.0", "@noble/hashes@^2.0.1", "@noble/hashes@~2.0.0", "@noble/hashes@2.0.1": version "2.0.1" - resolved "https://registry.npmjs.org/@noble/curves/-/curves-2.0.1.tgz" - integrity sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw== - dependencies: - "@noble/hashes" "2.0.1" + resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-2.0.1.tgz" + integrity sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw== -"@noble/curves@~1.8.1": - version "1.8.2" - resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.8.2.tgz" - integrity sha512-vnI7V6lFNe0tLAuJMu+2sX+FcL14TaCWy1qiczg1VwRmPrpQCdq5ESXQMqUc2tluRNf6irBXrWbl1mGN8uaU/g== - dependencies: - "@noble/hashes" "1.7.2" +"@noble/hashes@~1.7.1", "@noble/hashes@1.7.1": + version "1.7.1" + resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz" + integrity sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ== + +"@noble/hashes@~1.8.0": + version "1.8.0" + resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz" + integrity sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A== "@noble/hashes@1.3.2": version "1.3.2" resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz" integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== -"@noble/hashes@1.7.1", "@noble/hashes@~1.7.1": - version "1.7.1" - resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz" - integrity sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ== - "@noble/hashes@1.7.2": version "1.7.2" resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.2.tgz" integrity sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ== -"@noble/hashes@1.8.0", "@noble/hashes@^1.3.1", "@noble/hashes@^1.3.3", "@noble/hashes@^1.5.0", "@noble/hashes@^1.8.0", "@noble/hashes@~1.8.0": - version "1.8.0" - resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz" - integrity sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A== - -"@noble/hashes@2.0.1", "@noble/hashes@^2.0.0", "@noble/hashes@^2.0.1", "@noble/hashes@~2.0.0": - version "2.0.1" - resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-2.0.1.tgz" - integrity sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw== - "@pkgjs/parseargs@^0.11.0": version "0.11.0" resolved "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz" @@ -346,9 +255,10 @@ integrity sha512-cgA9fh8dfBai9b46XaaQmj9vwzyHStQjc/xrAvQksgF6SqvZ0yAfxVqLvGrsz/Xi3dsAdKLg09PybC7MUAMv9w== "@polkadot-api/descriptors@file:.papi/descriptors": - version "0.1.0-autogenerated.14746733976505338329" + version "0.1.0-autogenerated.5063582544821983772" + resolved "file:.papi/descriptors" -"@polkadot-api/ink-contracts@0.4.3", "@polkadot-api/ink-contracts@^0.4.1": +"@polkadot-api/ink-contracts@^0.4.1", "@polkadot-api/ink-contracts@>=0.4.0", "@polkadot-api/ink-contracts@0.4.3": version "0.4.3" resolved "https://registry.npmjs.org/@polkadot-api/ink-contracts/-/ink-contracts-0.4.3.tgz" integrity sha512-Wl+4Dxjt0GAl+rADZEgrrqEesqX/xygTpX18TmzmspcKhb9QIZf9FJI8A5Sgtq0TKAOwsd1d/hbHVX3LgbXFXg== @@ -357,17 +267,17 @@ "@polkadot-api/substrate-bindings" "0.16.5" "@polkadot-api/utils" "0.2.0" -"@polkadot-api/json-rpc-provider-proxy@0.2.7": - version "0.2.7" - resolved "https://registry.npmjs.org/@polkadot-api/json-rpc-provider-proxy/-/json-rpc-provider-proxy-0.2.7.tgz" - integrity sha512-+HM4JQXzO2GPUD2++4GOLsmFL6LO8RoLvig0HgCLuypDgfdZMlwd8KnyGHjRnVEHA5X+kvXbk84TDcAXVxTazQ== - "@polkadot-api/json-rpc-provider-proxy@^0.1.0": version "0.1.0" resolved "https://registry.npmjs.org/@polkadot-api/json-rpc-provider-proxy/-/json-rpc-provider-proxy-0.1.0.tgz" integrity sha512-8GSFE5+EF73MCuLQm8tjrbCqlgclcHBSRaswvXziJ0ZW7iw3UEMsKkkKvELayWyBuOPa2T5i1nj6gFOeIsqvrg== -"@polkadot-api/json-rpc-provider@0.0.1", "@polkadot-api/json-rpc-provider@^0.0.1": +"@polkadot-api/json-rpc-provider-proxy@0.2.7": + version "0.2.7" + resolved "https://registry.npmjs.org/@polkadot-api/json-rpc-provider-proxy/-/json-rpc-provider-proxy-0.2.7.tgz" + integrity sha512-+HM4JQXzO2GPUD2++4GOLsmFL6LO8RoLvig0HgCLuypDgfdZMlwd8KnyGHjRnVEHA5X+kvXbk84TDcAXVxTazQ== + +"@polkadot-api/json-rpc-provider@^0.0.1", "@polkadot-api/json-rpc-provider@0.0.1": version "0.0.1" resolved "https://registry.npmjs.org/@polkadot-api/json-rpc-provider/-/json-rpc-provider-0.0.1.tgz" integrity sha512-/SMC/l7foRjpykLTUTacIH05H3mr9ip8b5xxfwXlVezXrNVLp3Cv0GX6uItkKd+ZjzVPf3PFrDF2B2/HLSNESA== @@ -432,6 +342,15 @@ "@polkadot-api/metadata-builders" "0.13.7" "@polkadot-api/substrate-bindings" "0.16.5" +"@polkadot-api/observable-client@^0.3.0": + version "0.3.2" + resolved "https://registry.npmjs.org/@polkadot-api/observable-client/-/observable-client-0.3.2.tgz" + integrity sha512-HGgqWgEutVyOBXoGOPp4+IAq6CNdK/3MfQJmhCJb8YaJiaK4W6aRGrdQuQSTPHfERHCARt9BrOmEvTXAT257Ug== + dependencies: + "@polkadot-api/metadata-builders" "0.3.2" + "@polkadot-api/substrate-bindings" "0.6.0" + "@polkadot-api/utils" "0.1.0" + "@polkadot-api/observable-client@0.17.0": version "0.17.0" resolved "https://registry.npmjs.org/@polkadot-api/observable-client/-/observable-client-0.17.0.tgz" @@ -442,15 +361,6 @@ "@polkadot-api/substrate-client" "0.4.7" "@polkadot-api/utils" "0.2.0" -"@polkadot-api/observable-client@^0.3.0": - version "0.3.2" - resolved "https://registry.npmjs.org/@polkadot-api/observable-client/-/observable-client-0.3.2.tgz" - integrity sha512-HGgqWgEutVyOBXoGOPp4+IAq6CNdK/3MfQJmhCJb8YaJiaK4W6aRGrdQuQSTPHfERHCARt9BrOmEvTXAT257Ug== - dependencies: - "@polkadot-api/metadata-builders" "0.3.2" - "@polkadot-api/substrate-bindings" "0.6.0" - "@polkadot-api/utils" "0.1.0" - "@polkadot-api/pjs-signer@0.6.17": version "0.6.17" resolved "https://registry.npmjs.org/@polkadot-api/pjs-signer/-/pjs-signer-0.6.17.tgz" @@ -522,7 +432,7 @@ "@polkadot-api/json-rpc-provider" "0.0.4" "@polkadot-api/json-rpc-provider-proxy" "0.2.7" -"@polkadot-api/smoldot@0.3.14": +"@polkadot-api/smoldot@>=0.3", "@polkadot-api/smoldot@0.3.14": version "0.3.14" resolved "https://registry.npmjs.org/@polkadot-api/smoldot/-/smoldot-0.3.14.tgz" integrity sha512-eWqO0xFQaKzqY5mRYxYuZcj1IiaLcQP+J38UQyuJgEorm+9yHVEQ/XBWoM83P+Y8TwE5IWTICp1LCVeiFQTGPQ== @@ -530,7 +440,7 @@ "@types/node" "^24.5.2" smoldot "2.0.39" -"@polkadot-api/substrate-bindings@0.16.5", "@polkadot-api/substrate-bindings@^0.16.3": +"@polkadot-api/substrate-bindings@^0.16.3", "@polkadot-api/substrate-bindings@0.16.5": version "0.16.5" resolved "https://registry.npmjs.org/@polkadot-api/substrate-bindings/-/substrate-bindings-0.16.5.tgz" integrity sha512-QFgNlBmtLtiUGTCTurxcE6UZrbI2DaQ5/gyIiC2FYfEhStL8tl20b09FRYHcSjY+lxN42Rcf9HVX+MCFWLYlpQ== @@ -550,6 +460,14 @@ "@scure/base" "^1.1.1" scale-ts "^1.6.0" +"@polkadot-api/substrate-client@^0.1.2", "@polkadot-api/substrate-client@0.1.4": + version "0.1.4" + resolved "https://registry.npmjs.org/@polkadot-api/substrate-client/-/substrate-client-0.1.4.tgz" + integrity sha512-MljrPobN0ZWTpn++da9vOvt+Ex+NlqTlr/XT7zi9sqPtDJiQcYl+d29hFAgpaeTqbeQKZwz3WDE9xcEfLE8c5A== + dependencies: + "@polkadot-api/json-rpc-provider" "0.0.1" + "@polkadot-api/utils" "0.1.0" + "@polkadot-api/substrate-client@0.4.7": version "0.4.7" resolved "https://registry.npmjs.org/@polkadot-api/substrate-client/-/substrate-client-0.4.7.tgz" @@ -559,14 +477,6 @@ "@polkadot-api/raw-client" "0.1.1" "@polkadot-api/utils" "0.2.0" -"@polkadot-api/substrate-client@^0.1.2": - version "0.1.4" - resolved "https://registry.yarnpkg.com/@polkadot-api/substrate-client/-/substrate-client-0.1.4.tgz#7a808e5cb85ecb9fa2b3a43945090a6c807430ce" - integrity sha512-MljrPobN0ZWTpn++da9vOvt+Ex+NlqTlr/XT7zi9sqPtDJiQcYl+d29hFAgpaeTqbeQKZwz3WDE9xcEfLE8c5A== - dependencies: - "@polkadot-api/json-rpc-provider" "0.0.1" - "@polkadot-api/utils" "0.1.0" - "@polkadot-api/utils@0.1.0": version "0.1.0" resolved "https://registry.npmjs.org/@polkadot-api/utils/-/utils-0.1.0.tgz" @@ -661,7 +571,7 @@ rxjs "^7.8.1" tslib "^2.8.1" -"@polkadot/api@16.5.3", "@polkadot/api@^16.4.6": +"@polkadot/api@^16.4.6", "@polkadot/api@16.5.3": version "16.5.3" resolved "https://registry.npmjs.org/@polkadot/api/-/api-16.5.3.tgz" integrity sha512-Ptwo0f5Qonmus7KIklsbFcGTdHtNjbTAwl5GGI8Mp0dmBc7Y/ISJpIJX49UrG6FhW6COMa0ItsU87XIWMRwI/Q== @@ -693,7 +603,7 @@ "@polkadot/util-crypto" "13.5.9" tslib "^2.8.0" -"@polkadot/networks@13.5.9", "@polkadot/networks@^13.5.9": +"@polkadot/networks@^13.5.9", "@polkadot/networks@13.5.9": version "13.5.9" resolved "https://registry.npmjs.org/@polkadot/networks/-/networks-13.5.9.tgz" integrity sha512-nmKUKJjiLgcih0MkdlJNMnhEYdwEml2rv/h59ll2+rAvpsVWMTLCb6Cq6q7UC44+8kiWK2UUJMkFU+3PFFxndA== @@ -816,7 +726,7 @@ rxjs "^7.8.1" tslib "^2.8.1" -"@polkadot/util-crypto@13.5.9", "@polkadot/util-crypto@^13.5.9": +"@polkadot/util-crypto@^13.5.9": version "13.5.9" resolved "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-13.5.9.tgz" integrity sha512-foUesMhxkTk8CZ0/XEcfvHk6I0O+aICqqVJllhOpyp/ZVnrTBKBf59T6RpsXx2pCtBlMsLRvg/6Mw7RND1HqDg== @@ -849,7 +759,23 @@ "@scure/sr25519" "^0.2.0" tslib "^2.8.0" -"@polkadot/util@13.5.9", "@polkadot/util@^13.5.9": +"@polkadot/util-crypto@13.5.9": + version "13.5.9" + resolved "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-13.5.9.tgz" + integrity sha512-foUesMhxkTk8CZ0/XEcfvHk6I0O+aICqqVJllhOpyp/ZVnrTBKBf59T6RpsXx2pCtBlMsLRvg/6Mw7RND1HqDg== + dependencies: + "@noble/curves" "^1.3.0" + "@noble/hashes" "^1.3.3" + "@polkadot/networks" "13.5.9" + "@polkadot/util" "13.5.9" + "@polkadot/wasm-crypto" "^7.5.3" + "@polkadot/wasm-util" "^7.5.3" + "@polkadot/x-bigint" "13.5.9" + "@polkadot/x-randomvalues" "13.5.9" + "@scure/base" "^1.1.7" + tslib "^2.8.0" + +"@polkadot/util@*", "@polkadot/util@^13.5.9", "@polkadot/util@13.5.9": version "13.5.9" resolved "https://registry.npmjs.org/@polkadot/util/-/util-13.5.9.tgz" integrity sha512-pIK3XYXo7DKeFRkEBNYhf3GbCHg6dKQisSvdzZwuyzA6m7YxQq4DFw4IE464ve4Z7WsJFt3a6C9uII36hl9EWw== @@ -921,14 +847,14 @@ "@polkadot/wasm-util" "7.5.3" tslib "^2.7.0" -"@polkadot/wasm-util@7.5.3", "@polkadot/wasm-util@^7.5.3": +"@polkadot/wasm-util@*", "@polkadot/wasm-util@^7.5.3", "@polkadot/wasm-util@7.5.3": version "7.5.3" resolved "https://registry.npmjs.org/@polkadot/wasm-util/-/wasm-util-7.5.3.tgz" integrity sha512-hBr9bbjS+Yr7DrDUSkIIuvlTSoAlI8WXuo9YEB4C76j130u/cl+zyq6Iy/WnaTE6QH+8i9DhM8QTety6TqYnUQ== dependencies: tslib "^2.7.0" -"@polkadot/x-bigint@13.5.9", "@polkadot/x-bigint@^13.5.9": +"@polkadot/x-bigint@^13.5.9", "@polkadot/x-bigint@13.5.9": version "13.5.9" resolved "https://registry.npmjs.org/@polkadot/x-bigint/-/x-bigint-13.5.9.tgz" integrity sha512-JVW6vw3e8fkcRyN9eoc6JIl63MRxNQCP/tuLdHWZts1tcAYao0hpWUzteqJY93AgvmQ91KPsC1Kf3iuuZCi74g== @@ -953,7 +879,7 @@ node-fetch "^3.3.2" tslib "^2.8.0" -"@polkadot/x-global@13.5.9", "@polkadot/x-global@^13.5.9": +"@polkadot/x-global@^13.5.9", "@polkadot/x-global@13.5.9": version "13.5.9" resolved "https://registry.npmjs.org/@polkadot/x-global/-/x-global-13.5.9.tgz" integrity sha512-zSRWvELHd3Q+bFkkI1h2cWIqLo1ETm+MxkNXLec3lB56iyq/MjWBxfXnAFFYFayvlEVneo7CLHcp+YTFd9aVSA== @@ -967,7 +893,7 @@ dependencies: tslib "^2.8.0" -"@polkadot/x-randomvalues@13.5.9": +"@polkadot/x-randomvalues@*", "@polkadot/x-randomvalues@13.5.9": version "13.5.9" resolved "https://registry.npmjs.org/@polkadot/x-randomvalues/-/x-randomvalues-13.5.9.tgz" integrity sha512-Uuuz3oubf1JCCK97fsnVUnHvk4BGp/W91mQWJlgl5TIOUSSTIRr+lb5GurCfl4kgnQq53Zi5fJV+qR9YumbnZw== @@ -1024,116 +950,11 @@ tslib "^2.8.0" ws "^8.18.0" -"@rollup/rollup-android-arm-eabi@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz#7e478b66180c5330429dd161bf84dad66b59c8eb" - integrity sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w== - -"@rollup/rollup-android-arm64@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz#2b025510c53a5e3962d3edade91fba9368c9d71c" - integrity sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w== - "@rollup/rollup-darwin-arm64@4.53.3": version "4.53.3" resolved "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz" integrity sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA== -"@rollup/rollup-darwin-x64@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz#2bf5f2520a1f3b551723d274b9669ba5b75ed69c" - integrity sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ== - -"@rollup/rollup-freebsd-arm64@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz#4bb9cc80252564c158efc0710153c71633f1927c" - integrity sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w== - -"@rollup/rollup-freebsd-x64@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz#2301289094d49415a380cf942219ae9d8b127440" - integrity sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q== - -"@rollup/rollup-linux-arm-gnueabihf@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz#1d03d776f2065e09fc141df7d143476e94acca88" - integrity sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw== - -"@rollup/rollup-linux-arm-musleabihf@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz#8623de0e040b2fd52a541c602688228f51f96701" - integrity sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg== - -"@rollup/rollup-linux-arm64-gnu@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz#ce2d1999bc166277935dde0301cde3dd0417fb6e" - integrity sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w== - -"@rollup/rollup-linux-arm64-musl@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz#88c2523778444da952651a2219026416564a4899" - integrity sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A== - -"@rollup/rollup-linux-loong64-gnu@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz#578ca2220a200ac4226c536c10c8cc6e4f276714" - integrity sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g== - -"@rollup/rollup-linux-ppc64-gnu@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz#aa338d3effd4168a20a5023834a74ba2c3081293" - integrity sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw== - -"@rollup/rollup-linux-riscv64-gnu@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz#16ba582f9f6cff58119aa242782209b1557a1508" - integrity sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g== - -"@rollup/rollup-linux-riscv64-musl@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz#e404a77ebd6378483888b8064c703adb011340ab" - integrity sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A== - -"@rollup/rollup-linux-s390x-gnu@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz#92ad52d306227c56bec43d96ad2164495437ffe6" - integrity sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg== - -"@rollup/rollup-linux-x64-gnu@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz#fd0dea3bb9aa07e7083579f25e1c2285a46cb9fa" - integrity sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w== - -"@rollup/rollup-linux-x64-musl@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz#37a3efb09f18d555f8afc490e1f0444885de8951" - integrity sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q== - -"@rollup/rollup-openharmony-arm64@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz#c489bec9f4f8320d42c9b324cca220c90091c1f7" - integrity sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw== - -"@rollup/rollup-win32-arm64-msvc@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz#152832b5f79dc22d1606fac3db946283601b7080" - integrity sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw== - -"@rollup/rollup-win32-ia32-msvc@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz#54d91b2bb3bf3e9f30d32b72065a4e52b3a172a5" - integrity sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA== - -"@rollup/rollup-win32-x64-gnu@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz#df9df03e61a003873efec8decd2034e7f135c71e" - integrity sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg== - -"@rollup/rollup-win32-x64-msvc@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz#38ae84f4c04226c1d56a3b17296ef1e0460ecdfe" - integrity sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ== - "@rx-state/core@^0.1.4": version "0.1.4" resolved "https://registry.npmjs.org/@rx-state/core/-/core-0.1.4.tgz" @@ -1149,6 +970,15 @@ resolved "https://registry.npmjs.org/@scure/base/-/base-2.0.0.tgz" integrity sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w== +"@scure/bip32@^1.5.0", "@scure/bip32@^1.7.0", "@scure/bip32@1.7.0": + version "1.7.0" + resolved "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz" + integrity sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw== + dependencies: + "@noble/curves" "~1.9.0" + "@noble/hashes" "~1.8.0" + "@scure/base" "~1.2.5" + "@scure/bip32@1.6.2": version "1.6.2" resolved "https://registry.npmjs.org/@scure/bip32/-/bip32-1.6.2.tgz" @@ -1158,12 +988,11 @@ "@noble/hashes" "~1.7.1" "@scure/base" "~1.2.2" -"@scure/bip32@1.7.0", "@scure/bip32@^1.5.0", "@scure/bip32@^1.7.0": - version "1.7.0" - resolved "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz" - integrity sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw== +"@scure/bip39@^1.4.0", "@scure/bip39@^1.6.0", "@scure/bip39@1.6.0": + version "1.6.0" + resolved "https://registry.npmjs.org/@scure/bip39/-/bip39-1.6.0.tgz" + integrity sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A== dependencies: - "@noble/curves" "~1.9.0" "@noble/hashes" "~1.8.0" "@scure/base" "~1.2.5" @@ -1175,14 +1004,6 @@ "@noble/hashes" "~1.7.1" "@scure/base" "~1.2.4" -"@scure/bip39@1.6.0", "@scure/bip39@^1.4.0", "@scure/bip39@^1.6.0": - version "1.6.0" - resolved "https://registry.npmjs.org/@scure/bip39/-/bip39-1.6.0.tgz" - integrity sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A== - dependencies: - "@noble/hashes" "~1.8.0" - "@scure/base" "~1.2.5" - "@scure/sr25519@^0.2.0": version "0.2.0" resolved "https://registry.npmjs.org/@scure/sr25519/-/sr25519-0.2.0.tgz" @@ -1304,20 +1125,27 @@ dependencies: undici-types "~6.21.0" -"@types/node@22.7.5": - version "22.7.5" - resolved "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz" - integrity sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ== +"@types/node@^24.10.1": + version "24.10.1" + resolved "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz" + integrity sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ== dependencies: - undici-types "~6.19.2" + undici-types "~7.16.0" -"@types/node@^24.10.1", "@types/node@^24.5.2": +"@types/node@^24.5.2": version "24.10.1" resolved "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz" integrity sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ== dependencies: undici-types "~7.16.0" +"@types/node@22.7.5": + version "22.7.5" + resolved "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz" + integrity sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ== + dependencies: + undici-types "~6.19.2" + "@types/normalize-package-data@^2.4.3", "@types/normalize-package-data@^2.4.4": version "2.4.4" resolved "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz" @@ -1330,6 +1158,11 @@ dependencies: "@types/node" "*" +abitype@^1.0.6, abitype@^1.0.9, abitype@^1.1.1: + version "1.2.0" + resolved "https://registry.npmjs.org/abitype/-/abitype-1.2.0.tgz" + integrity sha512-fD3ROjckUrWsybaSor2AdWxzA0e/DSyV2dA4aYd7bd8orHsoJjl09fOgKfUkTDfk0BsDGBf4NBgu/c7JoS2Npw== + abitype@1.0.8: version "1.0.8" resolved "https://registry.npmjs.org/abitype/-/abitype-1.0.8.tgz" @@ -1340,11 +1173,6 @@ abitype@1.1.0: resolved "https://registry.npmjs.org/abitype/-/abitype-1.1.0.tgz" integrity sha512-6Vh4HcRxNMLA0puzPjM5GBgT4aAcFGKZzSgAXvuZ27shJP6NEpielTuqbBmZILR5/xd0PizkBGy5hReKz9jl5A== -abitype@^1.0.6, abitype@^1.0.9, abitype@^1.1.1: - version "1.2.0" - resolved "https://registry.npmjs.org/abitype/-/abitype-1.2.0.tgz" - integrity sha512-fD3ROjckUrWsybaSor2AdWxzA0e/DSyV2dA4aYd7bd8orHsoJjl09fOgKfUkTDfk0BsDGBf4NBgu/c7JoS2Npw== - acorn-walk@^8.1.1: version "8.3.4" resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz" @@ -1545,7 +1373,7 @@ color-name@~1.1.4: resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -commander@^14.0.2: +commander@^14.0.2, commander@~14.0.0: version "14.0.2" resolved "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz" integrity sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ== @@ -1680,7 +1508,7 @@ es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: dependencies: es-errors "^1.3.0" -esbuild@^0.25.0: +esbuild@^0.25.0, esbuild@>=0.18: version "0.25.12" resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz" integrity sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg== @@ -1735,7 +1563,7 @@ ethers@^6.13.5: tslib "2.7.0" ws "8.17.1" -eventemitter3@5.0.1, eventemitter3@^5.0.1: +eventemitter3@^5.0.1, eventemitter3@5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz" integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== @@ -2443,7 +2271,7 @@ picocolors@^1.1.1: resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz" integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== -picomatch@^4.0.3: +"picomatch@^3 || ^4", picomatch@^4.0.3: version "4.0.3" resolved "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz" integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q== @@ -2462,7 +2290,7 @@ pkg-types@^1.3.1: mlly "^1.7.4" pathe "^2.0.1" -polkadot-api@^1.22.0: +polkadot-api@^1.22.0, polkadot-api@^1.8.1, polkadot-api@>=1.19.0, polkadot-api@>=1.21.0: version "1.22.0" resolved "https://registry.npmjs.org/polkadot-api/-/polkadot-api-1.22.0.tgz" integrity sha512-uREBLroPbnJxBBQ+qSkKLF493qukX4PAg32iThlELrZdxfNNgro6nvWRdVmBv73tFHvf+nyWWHKTx1c57nbixg== @@ -2604,7 +2432,7 @@ rollup@^4.34.8: "@rollup/rollup-win32-x64-msvc" "4.53.3" fsevents "~2.3.2" -rxjs@^7.8.1, rxjs@^7.8.2: +rxjs@^7.8.1, rxjs@^7.8.2, rxjs@>=7, rxjs@>=7.8.0, rxjs@>=7.8.1: version "7.8.2" resolved "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz" integrity sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA== @@ -2671,9 +2499,9 @@ signal-exit@^4.0.1, signal-exit@^4.1.0: resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== -smoldot@2.0.26: +smoldot@2.0.26, smoldot@2.x: version "2.0.26" - resolved "https://registry.yarnpkg.com/smoldot/-/smoldot-2.0.26.tgz#0e64c7fcd26240fbe4c8d6b6e4b9a9aca77e00f6" + resolved "https://registry.npmjs.org/smoldot/-/smoldot-2.0.26.tgz" integrity sha512-F+qYmH4z2s2FK+CxGj8moYcd1ekSIKH8ywkdqlOz88Dat35iB1DIYL11aILN46YSGMzQW/lbJNS307zBSDN5Ig== dependencies: ws "^8.8.1" @@ -2779,7 +2607,14 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" -strip-ansi@^7.0.1, strip-ansi@^7.1.0, strip-ansi@^7.1.2: +strip-ansi@^7.0.1: + version "7.1.2" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz" + integrity sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA== + dependencies: + ansi-regex "^6.0.1" + +strip-ansi@^7.1.0, strip-ansi@^7.1.2: version "7.1.2" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz" integrity sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA== @@ -2896,16 +2731,16 @@ tsc-prog@^2.3.0: resolved "https://registry.npmjs.org/tsc-prog/-/tsc-prog-2.3.0.tgz" integrity sha512-ycET2d75EgcX7y8EmG4KiZkLAwUzbY4xRhA6NU0uVbHkY4ZjrAAuzTMxXI85kOwATqPnBI5C/7y7rlpY0xdqHA== -tslib@2.7.0: - version "2.7.0" - resolved "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz" - integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA== - tslib@^2.1.0, tslib@^2.7.0, tslib@^2.8.0, tslib@^2.8.1: version "2.8.1" resolved "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== +tslib@2.7.0: + version "2.7.0" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz" + integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA== + tsup@8.5.0: version "8.5.0" resolved "https://registry.npmjs.org/tsup/-/tsup-8.5.0.tgz" @@ -2941,7 +2776,7 @@ type-fest@^5.2.0: dependencies: tagged-tag "^1.0.0" -typescript@^5.7.2, typescript@^5.9.3: +typescript@^5.7.2, typescript@^5.9.3, typescript@>=2.7, typescript@>=4, typescript@>=4.5.0, typescript@>=5.0.4, typescript@>=5.4.0: version "5.9.3" resolved "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz" integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== @@ -3000,20 +2835,6 @@ validate-npm-package-license@^3.0.4: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" -viem@2.23.4: - version "2.23.4" - resolved "https://registry.npmjs.org/viem/-/viem-2.23.4.tgz" - integrity sha512-UQquuolKlS1w5H5e0Fd1KKoUlIPJryIEBzY5AUhGyV1ka+9O6+3uYVhUzj6RbvGK0PtsMKn2ddwPZFwjNDVU/A== - dependencies: - "@noble/curves" "1.8.1" - "@noble/hashes" "1.7.1" - "@scure/bip32" "1.6.2" - "@scure/bip39" "1.5.4" - abitype "1.0.8" - isows "1.0.6" - ox "0.6.7" - ws "8.18.0" - viem@^2.37.9: version "2.41.2" resolved "https://registry.npmjs.org/viem/-/viem-2.41.2.tgz" @@ -3028,6 +2849,20 @@ viem@^2.37.9: ox "0.9.6" ws "8.18.3" +viem@2.23.4: + version "2.23.4" + resolved "https://registry.npmjs.org/viem/-/viem-2.23.4.tgz" + integrity sha512-UQquuolKlS1w5H5e0Fd1KKoUlIPJryIEBzY5AUhGyV1ka+9O6+3uYVhUzj6RbvGK0PtsMKn2ddwPZFwjNDVU/A== + dependencies: + "@noble/curves" "1.8.1" + "@noble/hashes" "1.7.1" + "@scure/bip32" "1.6.2" + "@scure/bip39" "1.5.4" + abitype "1.0.8" + isows "1.0.6" + ox "0.6.7" + ws "8.18.0" + web-streams-polyfill@^3.0.3: version "3.3.3" resolved "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz" @@ -3128,6 +2963,11 @@ write-package@^7.2.0: type-fest "^4.23.0" write-json-file "^6.0.0" +ws@*, ws@^8.18.0, ws@^8.18.2, ws@^8.18.3, ws@^8.8.1, ws@8.18.3: + version "8.18.3" + resolved "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz" + integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== + ws@8.17.1: version "8.17.1" resolved "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz" @@ -3138,11 +2978,6 @@ ws@8.18.0: resolved "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz" integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== -ws@8.18.3, ws@^8.18.0, ws@^8.18.2, ws@^8.18.3, ws@^8.8.1: - version "8.18.3" - resolved "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz" - integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== - y18n@^5.0.5: version "5.0.8" resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" From 0b346d27483e6722befd277910ed3a8788648500 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Tue, 17 Feb 2026 16:06:05 -0500 Subject: [PATCH 236/240] Unstake block builders alpha --- pallets/subtensor/src/staking/stake_utils.rs | 32 +++++++++++--------- pallets/subtensor/src/tests/staking.rs | 32 ++++++++++++++------ pallets/swap/src/pallet/mod.rs | 9 +++++- pallets/swap/src/pallet/swap_step.rs | 10 +++--- 4 files changed, 55 insertions(+), 28 deletions(-) diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index 182fe27232..d030ee2d76 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -648,14 +648,8 @@ impl Pallet { } }; - // Increase only the protocol Alpha reserve. We only use the sum of - // (SubnetAlphaIn + SubnetAlphaInProvided) in alpha_reserve(), so it is irrelevant - // which one to increase. - // Decrease by fee_to_block_author because it will be staked. - let alpha_delta = swap_result - .paid_in_reserve_delta_i64() - .unsigned_abs() - .saturating_sub(swap_result.fee_to_block_author.into()); + // Increase only the protocol Alpha reserve + let alpha_delta = swap_result.paid_in_reserve_delta_i64().unsigned_abs(); SubnetAlphaIn::::mutate(netuid, |total| { *total = total.saturating_add(alpha_delta.into()); }); @@ -711,16 +705,21 @@ impl Pallet { Self::increase_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid, refund); } - // Increase the stake of the block author - // Alpha in/out counters are already updated in swap_alpha_for_tao accordingly + // 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(); if let Some(block_author_coldkey) = maybe_block_author_coldkey { - Self::increase_stake_for_hotkey_and_coldkey_on_subnet( - hotkey, - &block_author_coldkey, + let bb_swap_result = Self::swap_alpha_for_tao( netuid, swap_result.fee_to_block_author, + T::SwapInterface::min_price::(), + true, + )?; + Self::add_balance_to_coldkey_account( + &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 Self::burn_subnet_alpha(netuid, swap_result.fee_to_block_author); @@ -741,7 +740,12 @@ impl Pallet { // } // Record TAO outflow - Self::record_tao_outflow(netuid, swap_result.amount_paid_out.into()); + Self::record_tao_outflow( + netuid, + swap_result + .amount_paid_out + .saturating_add(fee_outflow.into()), + ); LastColdkeyHotkeyStakeBlock::::insert(coldkey, hotkey, Self::get_current_block_as_u64()); diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index 04b84c662b..475027d53d 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -4863,6 +4863,7 @@ fn test_swap_fees_tao_correctness() { let owner_hotkey = U256::from(1); let owner_coldkey = U256::from(2); let coldkey = U256::from(4); + let block_builder = U256::from(12345u64); let amount = 1_000_000_000; let owner_balance_before = amount * 10; let user_balance_before = amount * 100; @@ -4871,8 +4872,6 @@ fn test_swap_fees_tao_correctness() { 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); - let fee_rate = pallet_subtensor_swap::FeeRate::::get(NetUid::from(netuid)) as f64 - / u16::MAX as f64; // Forse-set alpha in and tao reserve to make price equal 0.25 let tao_reserve = TaoCurrency::from(100_000_000_000); @@ -4880,8 +4879,11 @@ fn test_swap_fees_tao_correctness() { mock::setup_reserves(netuid, tao_reserve, alpha_in); // Check starting "total TAO" - let total_tao_before = - user_balance_before + owner_balance_before + SubnetTAO::::get(netuid).to_u64(); + let block_builder_balance_before = SubtensorModule::get_coldkey_balance(&block_builder); + let total_tao_before = user_balance_before + + owner_balance_before + + SubnetTAO::::get(netuid).to_u64() + + block_builder_balance_before; // Get alpha for owner assert_ok!(SubtensorModule::add_stake( @@ -4890,7 +4892,6 @@ fn test_swap_fees_tao_correctness() { netuid, amount.into(), )); - let mut fees = (fee_rate * amount as f64) as u64; // Add owner coldkey Alpha as concentrated liquidity // between current price current price + 0.01 @@ -4909,7 +4910,6 @@ fn test_swap_fees_tao_correctness() { ((limit_price * u64::MAX as f64) as u64).into(), true )); - fees += (fee_rate * amount as f64) as u64; let user_alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &owner_hotkey, @@ -4923,15 +4923,25 @@ fn test_swap_fees_tao_correctness() { netuid, user_alpha, )); - // Do not add fees because selling fees are in alpha + + // Cause tao fees to propagate to SubnetTAO + let (claimed_tao_fees, _) = + ::SwapInterface::adjust_protocol_liquidity( + netuid, + 0.into(), + 0.into(), + ); + SubnetTAO::::mutate(netuid, |tao| *tao += claimed_tao_fees); // Check ending "total TAO" let owner_balance_after = SubtensorModule::get_coldkey_balance(&owner_coldkey); let user_balance_after = SubtensorModule::get_coldkey_balance(&coldkey); + let block_builder_balance_after = SubtensorModule::get_coldkey_balance(&block_builder); + let total_tao_after = user_balance_after + owner_balance_after + SubnetTAO::::get(netuid).to_u64() - + fees; + + block_builder_balance_after; // Total TAO does not change, leave some epsilon for rounding assert_abs_diff_eq!(total_tao_before, total_tao_after, epsilon = 2); @@ -5436,7 +5446,11 @@ fn test_staking_records_flow() { )); // Check that outflow has been recorded (less unstaking fees) - let expected_unstake_fee = expected_flow * fee_rate; + // The block builder will receive a fraction of the fees in alpha and will be forced + // to unstake it. So, the addition out-flow is recorded for this. + let unstaked_block_builder_fraction = 2. / 5.; + let expected_unstake_fee = + expected_flow * fee_rate * (1. - unstaked_block_builder_fraction); assert_abs_diff_eq!( SubnetTaoFlow::::get(netuid), expected_unstake_fee as i64, diff --git a/pallets/swap/src/pallet/mod.rs b/pallets/swap/src/pallet/mod.rs index 842ba8697c..42c80170f2 100644 --- a/pallets/swap/src/pallet/mod.rs +++ b/pallets/swap/src/pallet/mod.rs @@ -2,7 +2,7 @@ use core::num::NonZeroU64; use frame_support::{PalletId, pallet_prelude::*, traits::Get}; use frame_system::pallet_prelude::*; -// use safe_math::SafeDiv; +use sp_arithmetic::Perbill; use subtensor_runtime_common::{ AlphaCurrency, BalanceOps, CurrencyReserve, NetUid, SubnetInfo, TaoCurrency, }; @@ -75,6 +75,13 @@ mod pallet { 33 // ~0.05 % } + /// Fee split between pool and block builder. + /// Pool receives the portion returned by this function + #[pallet::type_value] + pub fn DefaultFeeSplit() -> Perbill { + Perbill::from_rational(3u32, 5u32) + } + /// The fee rate applied to swaps per subnet, normalized value between 0 and u16::MAX #[pallet::storage] pub type FeeRate = StorageMap<_, Twox64Concat, NetUid, u16, ValueQuery, DefaultFeeRate>; diff --git a/pallets/swap/src/pallet/swap_step.rs b/pallets/swap/src/pallet/swap_step.rs index 8675a3b90e..a161238300 100644 --- a/pallets/swap/src/pallet/swap_step.rs +++ b/pallets/swap/src/pallet/swap_step.rs @@ -2,6 +2,7 @@ use core::marker::PhantomData; use frame_support::ensure; use safe_math::*; +use sp_core::Get; use substrate_fixed::types::U64F64; use subtensor_runtime_common::{AlphaCurrency, Currency, CurrencyReserve, NetUid, TaoCurrency}; @@ -121,13 +122,14 @@ where if self.delta_in > 0.into() { ensure!(delta_out > 0.into(), Error::::ReservesTooLow); - // Split fees 3/5 vs. 2/5 between liquidity pool and validators. - // In case we want just to forward 100% of fees to the block author, it - // can be done this way: + // Split fees according to DefaultFeeSplit between liquidity pool and + // validators. In case we want just to forward 100% of fees to the block + // author, it can be done this way: // ``` // fee_to_block_author = self.fee; // ``` - let lp_fee = self.fee.to_u64().saturating_mul(3).safe_div(5).into(); + let fee_split = DefaultFeeSplit::get(); + let lp_fee = fee_split.mul_floor(self.fee.to_u64()).into(); Self::add_fees(self.netuid, lp_fee); fee_to_block_author = self.fee.saturating_sub(lp_fee); } From fc31ca57369f56f879ac46d6c3c1bbd72df519d9 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Tue, 17 Feb 2026 17:50:36 -0500 Subject: [PATCH 237/240] Set fee split as 1:4 --- pallets/subtensor/src/tests/staking.rs | 2 +- pallets/swap/src/pallet/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index 475027d53d..cea4a63dac 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -5448,7 +5448,7 @@ fn test_staking_records_flow() { // Check that outflow has been recorded (less unstaking fees) // The block builder will receive a fraction of the fees in alpha and will be forced // to unstake it. So, the addition out-flow is recorded for this. - let unstaked_block_builder_fraction = 2. / 5.; + let unstaked_block_builder_fraction = 4. / 5.; let expected_unstake_fee = expected_flow * fee_rate * (1. - unstaked_block_builder_fraction); assert_abs_diff_eq!( diff --git a/pallets/swap/src/pallet/mod.rs b/pallets/swap/src/pallet/mod.rs index 42c80170f2..d76582ab65 100644 --- a/pallets/swap/src/pallet/mod.rs +++ b/pallets/swap/src/pallet/mod.rs @@ -79,7 +79,7 @@ mod pallet { /// Pool receives the portion returned by this function #[pallet::type_value] pub fn DefaultFeeSplit() -> Perbill { - Perbill::from_rational(3u32, 5u32) + Perbill::from_rational(1u32, 5u32) } /// The fee rate applied to swaps per subnet, normalized value between 0 and u16::MAX From d1bc5cdf60cdc83a54632a2be7d3efd0a3535a8e Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Wed, 18 Feb 2026 09:46:32 -0500 Subject: [PATCH 238/240] Set fee split 100% to block builder --- pallets/subtensor/src/tests/staking.rs | 6 +++--- pallets/swap/src/pallet/mod.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index cea4a63dac..86ba26c2e2 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -5447,14 +5447,14 @@ fn test_staking_records_flow() { // Check that outflow has been recorded (less unstaking fees) // The block builder will receive a fraction of the fees in alpha and will be forced - // to unstake it. So, the addition out-flow is recorded for this. - let unstaked_block_builder_fraction = 4. / 5.; + // to unstake it. So, the additional out-flow is recorded for this. + let unstaked_block_builder_fraction = 1.; let expected_unstake_fee = expected_flow * fee_rate * (1. - unstaked_block_builder_fraction); assert_abs_diff_eq!( SubnetTaoFlow::::get(netuid), expected_unstake_fee as i64, - epsilon = (expected_unstake_fee / 100.0) as i64 + epsilon = ((expected_unstake_fee / 100.0) as i64).max(1) ); }); } diff --git a/pallets/swap/src/pallet/mod.rs b/pallets/swap/src/pallet/mod.rs index d76582ab65..9fc4aca769 100644 --- a/pallets/swap/src/pallet/mod.rs +++ b/pallets/swap/src/pallet/mod.rs @@ -79,7 +79,7 @@ mod pallet { /// Pool receives the portion returned by this function #[pallet::type_value] pub fn DefaultFeeSplit() -> Perbill { - Perbill::from_rational(1u32, 5u32) + Perbill::zero() } /// The fee rate applied to swaps per subnet, normalized value between 0 and u16::MAX From 82be06e5c16c017b7ad59de4365397fc3426dff9 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Wed, 18 Feb 2026 19:36:09 -0300 Subject: [PATCH 239/240] fix operationals --- pallets/admin-utils/src/lib.rs | 20 +++++---------- pallets/shield/src/lib.rs | 10 +++----- pallets/subtensor/src/macros/dispatches.rs | 30 +++++++--------------- 3 files changed, 18 insertions(+), 42 deletions(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index d5ef7cb204..654f7207b8 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -2107,13 +2107,9 @@ pub mod pallet { /// Sets the announcement delay for coldkey swap. #[pallet::call_index(86)] - #[pallet::weight(( - Weight::from_parts(5_000_000, 0) - .saturating_add(T::DbWeight::get().reads(0_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)), - DispatchClass::Operational, - Pays::Yes - ))] + #[pallet::weight(Weight::from_parts(5_000_000, 0) + .saturating_add(::DbWeight::get().reads(0_u64)) + .saturating_add(::DbWeight::get().writes(1_u64)))] pub fn sudo_set_coldkey_swap_announcement_delay( origin: OriginFor, duration: BlockNumberFor, @@ -2126,13 +2122,9 @@ pub mod pallet { /// Sets the coldkey swap reannouncement delay. #[pallet::call_index(87)] - #[pallet::weight(( - Weight::from_parts(5_000_000, 0) - .saturating_add(T::DbWeight::get().reads(0_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)), - DispatchClass::Operational, - Pays::Yes - ))] + #[pallet::weight(Weight::from_parts(5_000_000, 0) + .saturating_add(::DbWeight::get().reads(0_u64)) + .saturating_add(::DbWeight::get().writes(1_u64)))] pub fn sudo_set_coldkey_swap_reannouncement_delay( origin: OriginFor, duration: BlockNumberFor, diff --git a/pallets/shield/src/lib.rs b/pallets/shield/src/lib.rs index 1ee1d6c4d6..e0fc250058 100644 --- a/pallets/shield/src/lib.rs +++ b/pallets/shield/src/lib.rs @@ -244,13 +244,9 @@ pub mod pallet { /// Announce the ML‑KEM public key that will become `CurrentKey` in /// the following block. #[pallet::call_index(0)] - #[pallet::weight(( - Weight::from_parts(20_999_999_999, 0) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)), - DispatchClass::Operational, - Pays::Yes - ))] + #[pallet::weight(Weight::from_parts(20_999_999_999, 0) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)))] #[allow(clippy::useless_conversion)] pub fn announce_next_key( origin: OriginFor, diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index df1e00dd9a..6330e07d1c 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2485,13 +2485,9 @@ mod dispatches { /// * `SubnetNotExist` - If the subnet does not exist. /// * `NotSubnetOwner` - If the caller is not the subnet owner or root. #[pallet::call_index(129)] - #[pallet::weight(( - Weight::from_parts(10_000, 0) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(2)), - DispatchClass::Operational, - Pays::Yes - ))] + #[pallet::weight(Weight::from_parts(10_000, 0) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)))] pub fn enable_voting_power_tracking( origin: OriginFor, netuid: NetUid, @@ -2515,13 +2511,9 @@ mod dispatches { /// * `NotSubnetOwner` - If the caller is not the subnet owner or root. /// * `VotingPowerTrackingNotEnabled` - If voting power tracking is not enabled. #[pallet::call_index(130)] - #[pallet::weight(( - Weight::from_parts(10_000, 0) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)), - DispatchClass::Operational, - Pays::Yes - ))] + #[pallet::weight(Weight::from_parts(10_000, 0) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)))] pub fn disable_voting_power_tracking( origin: OriginFor, netuid: NetUid, @@ -2546,13 +2538,9 @@ mod dispatches { /// * `SubnetNotExist` - If the subnet does not exist. /// * `InvalidVotingPowerEmaAlpha` - If alpha is greater than 10^18 (1.0). #[pallet::call_index(131)] - #[pallet::weight(( - Weight::from_parts(6_000, 0) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)), - DispatchClass::Operational, - Pays::Yes - ))] + #[pallet::weight(Weight::from_parts(6_000, 0) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)))] pub fn sudo_set_voting_power_ema_alpha( origin: OriginFor, netuid: NetUid, From 56feddc3a14fa112f83d1b7f5c94d8214f7a423c Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Wed, 18 Feb 2026 19:38:00 -0300 Subject: [PATCH 240/240] bump spec version --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 7359556be6..460a84e049 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -243,7 +243,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: 379, + spec_version: 380, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1,