use super::*; use safe_math::*; use share_pool::{SafeFloat, SharePool, SharePoolDataOperations}; use sp_std::{collections::btree_map::BTreeMap, ops::Neg}; use substrate_fixed::types::{I64F64, I96F32, U64F64, U96F32}; use subtensor_runtime_common::{AlphaBalance, AuthorshipInfo, NetUid, TaoBalance, Token}; use subtensor_swap_interface::{Order, SwapHandler, SwapResult}; impl Pallet { /// Retrieves the total alpha issuance for a given subnet. /// /// This function calculates the total alpha issuance by summing the alpha /// values from `SubnetAlphaIn` and `SubnetAlphaOut` for the specified subnet. /// /// # Arguments /// * `netuid`: The unique identifier of the subnet. /// /// # Returns /// * `u64`: The total alpha issuance for the specified subnet. pub fn get_alpha_issuance(netuid: NetUid) -> AlphaBalance { SubnetAlphaIn::::get(netuid) .saturating_add(SubnetAlphaOut::::get(netuid)) .saturating_add(T::SwapInterface::protocol_alpha_reservoir(netuid)) } pub fn get_moving_alpha_price(netuid: NetUid) -> U64F64 { let one = U64F64::saturating_from_num(1.0); if netuid.is_root() { // Root. one } else if SubnetMechanism::::get(netuid) == 0 { // Stable one } else { U64F64::saturating_from_num(SubnetMovingPrice::::get(netuid)) } } pub fn update_moving_price(netuid: NetUid) { let blocks_since_start_call = U64F64::saturating_from_num({ // We expect FirstEmissionBlockNumber to be set earlier, and we take the block when // `start_call` was called (first block before FirstEmissionBlockNumber). let start_call_block = FirstEmissionBlockNumber::::get(netuid) .unwrap_or_default() .saturating_sub(1); Self::get_current_block_as_u64().saturating_sub(start_call_block) }); // Use halving time hyperparameter. The meaning of this parameter can be best explained under // the assumption of a constant price and SubnetMovingAlpha == 0.5: It is how many blocks it // will take in order for the distance between current EMA of price and current price to shorten // by half. let halving_time = EMAPriceHalvingBlocks::::get(netuid); let current_ma_unsigned = U64F64::saturating_from_num(SubnetMovingAlpha::::get()); let alpha: U64F64 = current_ma_unsigned.saturating_mul(blocks_since_start_call.safe_div( blocks_since_start_call.saturating_add(U64F64::saturating_from_num(halving_time)), )); // 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: U64F64 = U64F64::saturating_from_num(1.0).saturating_sub(alpha); let current_price: U64F64 = alpha.saturating_mul(U64F64::saturating_from_num( T::SwapInterface::current_alpha_price(netuid.into()) .min(U64F64::saturating_from_num(1.0)), )); let current_moving: U64F64 = one_minus_alpha.saturating_mul(U64F64::saturating_from_num( Self::get_moving_alpha_price(netuid), )); // Convert batch to signed I96F32 to avoid migration of SubnetMovingPrice for now`` let new_moving: I96F32 = I96F32::saturating_from_num(current_price.saturating_add(current_moving)); SubnetMovingPrice::::insert(netuid, new_moving); } /// Gets the Median Subnet Alpha Price pub fn get_median_subnet_alpha_price() -> U64F64 { let default_price = U64F64::saturating_from_num(1_u64); let zero_price = U64F64::saturating_from_num(0_u64); let two = U64F64::saturating_from_num(2_u64); let mut price_counts: BTreeMap = BTreeMap::new(); let mut total_prices: usize = 0; for (netuid, added) in NetworksAdded::::iter() { if !added || netuid == NetUid::ROOT { continue; } let price = T::SwapInterface::current_alpha_price(netuid); if price <= zero_price { continue; } total_prices = total_prices.saturating_add(1); if let Some(count) = price_counts.get_mut(&price) { *count = count.saturating_add(1); } else { price_counts.insert(price, 1usize); } } if total_prices == 0 { return default_price; } let Some(last_index) = total_prices.checked_sub(1) else { return default_price; }; let Some(lower_target) = last_index.checked_div(2) else { return default_price; }; let Some(upper_target) = total_prices.checked_div(2) else { return default_price; }; let mut cumulative: usize = 0; let mut lower_price: Option = None; let mut upper_price: Option = None; for (price, count) in price_counts.into_iter() { let next_cumulative = cumulative.saturating_add(count); if lower_price.is_none() && lower_target < next_cumulative { lower_price = Some(price); } if upper_price.is_none() && upper_target < next_cumulative { upper_price = Some(price); } if lower_price.is_some() && upper_price.is_some() { break; } cumulative = next_cumulative; } match (lower_price, upper_price) { (Some(_), Some(upper)) if lower_target == upper_target => upper, (Some(lower), Some(upper)) => lower.saturating_add(upper).safe_div(two), _ => default_price, } } /// Retrieves the TAO weight as a normalized value between 0 and 1. /// /// This function performs the following steps: /// 1. Fetches the TAO weight from storage using the TaoWeight storage item. /// 2. Converts the retrieved u64 value to a fixed-point number (U96F32). /// 3. Normalizes the weight by dividing it by the maximum possible u64 value. /// 4. Returns the normalized weight as an U96F32 fixed-point number. /// /// The normalization ensures that the returned value is always between 0 and 1, /// regardless of the actual stored weight value. /// /// # Returns /// * `U96F32`: The normalized TAO weight as a fixed-point number between 0 and 1. /// /// # Note /// This function uses saturating division to prevent potential overflow errors. pub fn get_tao_weight() -> U96F32 { // Step 1: Fetch the TAO weight from storage let stored_weight = TaoWeight::::get(); // Step 2: Convert the u64 weight to U96F32 let weight_fixed = U96F32::saturating_from_num(stored_weight); // Step 3: Normalize the weight by dividing by u64::MAX // This ensures the result is always between 0 and 1 weight_fixed.safe_div(U96F32::saturating_from_num(u64::MAX)) } pub fn get_ck_burn() -> U96F32 { let stored_weight = CKBurn::::get(); let weight_fixed = U96F32::saturating_from_num(stored_weight); weight_fixed.safe_div(U96F32::saturating_from_num(u64::MAX)) } /// Sets the TAO weight in storage. /// /// This function performs the following steps: /// 1. Takes the provided weight value as a u64. /// 2. Updates the TaoWeight storage item with the new value. /// /// # Arguments /// * `weight`: The new TAO weight value to be set, as a u64. /// /// # Effects /// This function modifies the following storage item: /// * `TaoWeight`: Updates it with the new weight value. /// /// # Note /// The weight is stored as a raw u64 value. To get the normalized weight between 0 and 1, /// use the `get_tao_weight()` function. pub fn set_tao_weight(weight: u64) { // Update the TaoWeight storage with the new weight value TaoWeight::::set(weight); } // Set the amount burned on non owned CK pub fn set_ck_burn(weight: u64) { // Update the ck burn value. CKBurn::::set(weight); } /// Calculates the weighted combination of alpha and TAO stake for a single hotkey on a subnet. /// pub fn get_stake_weights_for_hotkey_on_subnet( hotkey: &T::AccountId, netuid: NetUid, ) -> (I64F64, I64F64, I64F64) { // Retrieve the TAO weight. let tao_weight = I64F64::saturating_from_num(Self::get_tao_weight()); log::debug!("tao_weight: {tao_weight:?}"); // Step 1: Get stake of hotkey (neuron) let alpha_stake = I64F64::saturating_from_num(Self::get_inherited_for_hotkey_on_subnet(hotkey, netuid)); log::debug!("alpha_stake: {alpha_stake:?}"); // Step 2: Get the TAO stake for the hotkey let tao_stake = I64F64::saturating_from_num(Self::get_tao_inherited_for_hotkey_on_subnet( hotkey, netuid, )); log::debug!("tao_stake: {tao_stake:?}"); // Step 3: Combine alpha and tao stakes let total_stake = alpha_stake.saturating_add(tao_stake.saturating_mul(tao_weight)); log::debug!("total_stake: {total_stake:?}"); (total_stake, alpha_stake, tao_stake) } /// Calculates the weighted combination of alpha and TAO stake for hotkeys on a subnet. /// pub fn get_stake_weights_for_network( netuid: NetUid, ) -> (Vec, Vec, Vec) { // Retrieve the TAO weight. let tao_weight: I64F64 = I64F64::saturating_from_num(Self::get_tao_weight()); log::debug!("tao_weight: {tao_weight:?}"); // Step 1: Get subnetwork size let n: u16 = Self::get_subnetwork_n(netuid); // Step 2: Get stake of all hotkeys (neurons) ordered by uid let alpha_stake: Vec = (0..n) .map(|uid| { if Keys::::contains_key(netuid, uid) { let hotkey: T::AccountId = Keys::::get(netuid, uid); I64F64::saturating_from_num(Self::get_inherited_for_hotkey_on_subnet( &hotkey, netuid, )) } else { I64F64::saturating_from_num(0) } }) .collect(); log::debug!("alpha_stake: {alpha_stake:?}"); // Step 3: Calculate the TAO stake vector. // Initialize a vector to store TAO stakes for each neuron. let tao_stake: Vec = (0..n) .map(|uid| { if Keys::::contains_key(netuid, uid) { let hotkey: T::AccountId = Keys::::get(netuid, uid); I64F64::saturating_from_num(Self::get_tao_inherited_for_hotkey_on_subnet( &hotkey, netuid, )) } else { I64F64::saturating_from_num(0) } }) .collect(); log::trace!("tao_stake: {tao_stake:?}"); // Step 4: Combine alpha and TAO stakes. // Calculate the weighted average of alpha and TAO stakes for each neuron. let total_stake: Vec = alpha_stake .iter() .zip(tao_stake.iter()) .map(|(alpha_i, tao_i)| alpha_i.saturating_add(tao_i.saturating_mul(tao_weight))) .collect(); log::trace!("total_stake: {total_stake:?}"); (total_stake, alpha_stake, tao_stake) } /// Calculates the total inherited stake (alpha) held by a hotkey on a network, considering child/parent relationships. /// /// This function performs the following steps: /// 1. Retrieves the initial alpha (stake) for the hotkey on the specified subnet. /// 2. Retrieves the list of children and parents for the hotkey on the subnet. /// 3. Calculates the alpha allocated to children: /// a. For each child, computes the proportion of alpha to be allocated. /// b. Accumulates the total alpha allocated to all children. /// 4. Calculates the alpha received from parents: /// a. For each parent, retrieves the parent's stake on the subnet. /// b. Computes the proportion of the parent's stake to be inherited. /// c. Accumulates the total alpha inherited from all parents. /// 5. Computes the final inherited alpha by adjusting the initial alpha: /// a. Subtracts the alpha allocated to children. /// b. Adds the alpha inherited from parents. /// 6. Returns the final inherited alpha value. /// /// # Arguments /// * `hotkey`: AccountId of the hotkey whose total inherited stake is to be calculated. /// * `netuid`: Network unique identifier specifying the subnet context. /// /// # Returns /// * `u64`: The total inherited alpha for the hotkey on the subnet after considering the /// stakes allocated to children and inherited from parents. /// /// # Note /// This function uses saturating arithmetic to prevent overflows. pub fn get_tao_inherited_for_hotkey_on_subnet( hotkey: &T::AccountId, netuid: NetUid, ) -> TaoBalance { let initial_tao: U96F32 = U96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet(hotkey, NetUid::ROOT)); // Initialize variables to track alpha allocated to children and inherited from parents. let mut tao_to_children: U96F32 = U96F32::saturating_from_num(0); let mut tao_from_parents: U96F32 = U96F32::saturating_from_num(0); // Step 2: Retrieve the lists of parents and children for the hotkey on the subnet. let parents: Vec<(u64, T::AccountId)> = Self::get_parents(hotkey, netuid); let children: Vec<(u64, T::AccountId)> = Self::get_children(hotkey, netuid); log::trace!("Parents for hotkey {hotkey:?} on subnet {netuid}: {parents:?}"); log::trace!("Children for hotkey {hotkey:?} on subnet {netuid}: {children:?}"); // Step 3: Calculate the total tao allocated to children. for (proportion, _) in children { // Convert the proportion to a normalized value between 0 and 1. let normalized_proportion: U96F32 = U96F32::saturating_from_num(proportion) .safe_div(U96F32::saturating_from_num(u64::MAX)); log::trace!("Normalized proportion for child: {normalized_proportion:?}"); // Calculate the amount of tao to be allocated to this child. let tao_proportion_to_child: U96F32 = U96F32::saturating_from_num(initial_tao).saturating_mul(normalized_proportion); log::trace!("Tao proportion to child: {tao_proportion_to_child:?}"); // Add this child's allocation to the total tao allocated to children. tao_to_children = tao_to_children.saturating_add(tao_proportion_to_child); } log::trace!("Total tao allocated to children: {tao_to_children:?}"); // Step 4: Calculate the total tao inherited from parents. for (proportion, parent) in parents { // Retrieve the parent's total stake on this subnet. let parent_tao = U96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet( &parent, NetUid::ROOT, )); log::trace!("Parent tao for parent {parent:?} on subnet {netuid}: {parent_tao:?}"); // Convert the proportion to a normalized value between 0 and 1. let normalized_proportion = U96F32::saturating_from_num(proportion) .safe_div(U96F32::saturating_from_num(u64::MAX)); log::trace!("Normalized proportion from parent: {normalized_proportion:?}"); // Calculate the amount of tao to be inherited from this parent. let tao_proportion_from_parent: U96F32 = U96F32::saturating_from_num(parent_tao).saturating_mul(normalized_proportion); log::trace!("Tao proportion from parent: {tao_proportion_from_parent:?}"); // Add this parent's contribution to the total tao inherited from parents. tao_from_parents = tao_from_parents.saturating_add(tao_proportion_from_parent); } log::trace!("Total tao inherited from parents: {tao_from_parents:?}"); // Step 5: Calculate the final inherited tao for the hotkey. let finalized_tao: U96F32 = initial_tao .saturating_sub(tao_to_children) // Subtract tao allocated to children .saturating_add(tao_from_parents); // Add tao inherited from parents log::trace!("Finalized tao for hotkey {hotkey:?} on subnet {netuid}: {finalized_tao:?}"); // Step 6: Return the final inherited tao value. finalized_tao.saturating_to_num::().into() } pub fn get_inherited_for_hotkey_on_subnet( hotkey: &T::AccountId, netuid: NetUid, ) -> AlphaBalance { // Step 1: Retrieve the initial total stake (alpha) for the hotkey on the specified subnet. let initial_alpha: U96F32 = U96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet(hotkey, netuid)); log::debug!("Initial alpha for hotkey {hotkey:?} on subnet {netuid}: {initial_alpha:?}"); if netuid.is_root() { return initial_alpha.saturating_to_num::().into(); } // Initialize variables to track alpha allocated to children and inherited from parents. let mut alpha_to_children: U96F32 = U96F32::saturating_from_num(0); let mut alpha_from_parents: U96F32 = U96F32::saturating_from_num(0); // Step 2: Retrieve the lists of parents and children for the hotkey on the subnet. let parents: Vec<(u64, T::AccountId)> = Self::get_parents(hotkey, netuid); let children: Vec<(u64, T::AccountId)> = Self::get_children(hotkey, netuid); log::debug!("Parents for hotkey {hotkey:?} on subnet {netuid}: {parents:?}"); log::debug!("Children for hotkey {hotkey:?} on subnet {netuid}: {children:?}"); // Step 3: Calculate the total alpha allocated to children. for (proportion, _) in children { // Convert the proportion to a normalized value between 0 and 1. let normalized_proportion: U96F32 = U96F32::saturating_from_num(proportion) .safe_div(U96F32::saturating_from_num(u64::MAX)); log::trace!("Normalized proportion for child: {normalized_proportion:?}"); // Calculate the amount of alpha to be allocated to this child. let alpha_proportion_to_child: U96F32 = U96F32::saturating_from_num(initial_alpha).saturating_mul(normalized_proportion); log::trace!("Alpha proportion to child: {alpha_proportion_to_child:?}"); // Add this child's allocation to the total alpha allocated to children. alpha_to_children = alpha_to_children.saturating_add(alpha_proportion_to_child); } log::debug!("Total alpha allocated to children: {alpha_to_children:?}"); // Step 4: Calculate the total alpha inherited from parents. for (proportion, parent) in parents { // Retrieve the parent's total stake on this subnet. let parent_alpha: U96F32 = U96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet(&parent, netuid)); log::trace!("Parent alpha for parent {parent:?} on subnet {netuid}: {parent_alpha:?}"); // Convert the proportion to a normalized value between 0 and 1. let normalized_proportion: U96F32 = U96F32::saturating_from_num(proportion) .safe_div(U96F32::saturating_from_num(u64::MAX)); log::trace!("Normalized proportion from parent: {normalized_proportion:?}"); // Calculate the amount of alpha to be inherited from this parent. let alpha_proportion_from_parent: U96F32 = U96F32::saturating_from_num(parent_alpha).saturating_mul(normalized_proportion); log::trace!("Alpha proportion from parent: {alpha_proportion_from_parent:?}"); // Add this parent's contribution to the total alpha inherited from parents. alpha_from_parents = alpha_from_parents.saturating_add(alpha_proportion_from_parent); } log::debug!("Total alpha inherited from parents: {alpha_from_parents:?}"); // Step 5: Calculate the final inherited alpha for the hotkey. let finalized_alpha: U96F32 = initial_alpha .saturating_sub(alpha_to_children) // Subtract alpha allocated to children .saturating_add(alpha_from_parents); // Add alpha inherited from parents log::trace!( "Finalized alpha for hotkey {hotkey:?} on subnet {netuid}: {finalized_alpha:?}" ); // Step 6: Return the final inherited alpha value. finalized_alpha.saturating_to_num::().into() } /// Checks if a specific hotkey-coldkey pair has enough stake on a subnet to fulfill a given decrement. /// /// This function performs the following steps: /// 1. Retrieves the current stake for the hotkey-coldkey pair on the specified subnet. /// 2. Compares this stake with the requested decrement amount. /// /// # Arguments /// * `hotkey`: The account ID of the hotkey. /// * `coldkey`: The account ID of the coldkey. /// * `netuid`: The unique identifier of the subnet. /// * `decrement`: The amount of stake to be potentially decremented. /// /// # Returns /// * `bool`: True if the account has enough stake to fulfill the decrement, false otherwise. /// /// # Note /// This function only checks the stake for the specific hotkey-coldkey pair, not the total stake of the hotkey or coldkey individually. pub fn calculate_reduced_stake_on_subnet( hotkey: &T::AccountId, coldkey: &T::AccountId, netuid: NetUid, decrement: AlphaBalance, ) -> Result> { // Retrieve the current stake for this hotkey-coldkey pair on the subnet let current_stake = Self::get_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid); // Compare the current stake with the requested decrement // Return true if the current stake is greater than or equal to the decrement if current_stake >= decrement { Ok(current_stake.saturating_sub(decrement)) } else { Err(Error::::NotEnoughStakeToWithdraw) } } /// Retrieves the alpha (stake) value for a given hotkey and coldkey pair on a specific subnet. /// /// This function performs the following steps: /// 1. Takes the hotkey, coldkey, and subnet ID as input parameters. /// 2. Accesses the Alpha storage map to retrieve the stake value. /// 3. Returns the retrieved stake value as a u64. /// /// # Arguments /// * `hotkey`: The account ID of the hotkey (neuron). /// * `coldkey`: The account ID of the coldkey (owner). /// * `netuid`: The unique identifier of the subnet. /// /// # Returns /// * `u64`: The alpha (stake) value for the specified hotkey-coldkey pair on the given subnet. /// /// # Note /// This function retrieves the stake specific to the hotkey-coldkey pair, not the total stake of the hotkey or coldkey individually. pub fn get_stake_for_hotkey_and_coldkey_on_subnet( hotkey: &T::AccountId, coldkey: &T::AccountId, netuid: NetUid, ) -> AlphaBalance { let alpha_share_pool = Self::get_alpha_share_pool(hotkey.clone(), netuid); alpha_share_pool.try_get_value(coldkey).unwrap_or(0).into() } /// Retrieves the total stake (alpha) for a given hotkey on a specific subnet. /// /// This function performs the following step: /// 1. Retrieves and returns the total alpha value associated with the hotkey on the specified subnet. /// /// # Arguments /// * `hotkey`: The account ID of the hotkey. /// * `netuid`: The unique identifier of the subnet. /// /// # Returns /// * `u64`: The total alpha value for the hotkey on the specified subnet. /// /// # Note /// This function returns the cumulative stake across all coldkeys associated with this hotkey on the subnet. pub fn get_stake_for_hotkey_on_subnet(hotkey: &T::AccountId, netuid: NetUid) -> AlphaBalance { // Retrieve and return the total alpha this hotkey owns on this subnet. // This value represents the sum of stakes from all coldkeys associated with this hotkey. TotalHotkeyAlpha::::get(hotkey, netuid) } /// Increase hotkey stake on a subnet. /// /// The function updates share totals given current prices. /// /// # Arguments /// * `hotkey`: The account ID of the hotkey. /// * `netuid`: The unique identifier of the subnet. /// * `amount`: The amount of alpha to be added. /// pub fn increase_stake_for_hotkey_on_subnet( hotkey: &T::AccountId, netuid: NetUid, amount: AlphaBalance, ) { let mut alpha_share_pool = Self::get_alpha_share_pool(hotkey.clone(), netuid); alpha_share_pool.update_value_for_all(amount.to_u64() as i64); } /// Decrease hotkey stake on a subnet. /// /// The function updates share totals given current prices. /// /// # Arguments /// * `hotkey`: The account ID of the hotkey. /// * `netuid`: The unique identifier of the subnet. /// * `amount`: The amount of alpha to be added. /// pub fn decrease_stake_for_hotkey_on_subnet(hotkey: &T::AccountId, netuid: NetUid, amount: u64) { let mut alpha_share_pool = Self::get_alpha_share_pool(hotkey.clone(), netuid); alpha_share_pool.update_value_for_all((amount as i64).neg()); } /// Buys shares in the hotkey on a given subnet /// /// The function updates share totals given current prices. /// /// # Arguments /// * `hotkey`: The account ID of the hotkey. /// * `coldkey`: The account ID of the coldkey (owner). /// * `netuid`: The unique identifier of the subnet. /// * `amount`: The amount of alpha to be added. /// pub fn increase_stake_for_hotkey_and_coldkey_on_subnet( hotkey: &T::AccountId, coldkey: &T::AccountId, netuid: NetUid, amount: AlphaBalance, ) { if !amount.is_zero() { let mut staking_hotkeys = StakingHotkeys::::get(coldkey); if !staking_hotkeys.contains(hotkey) { staking_hotkeys.push(hotkey.clone()); StakingHotkeys::::insert(coldkey, staking_hotkeys.clone()); } } let mut alpha_share_pool = Self::get_alpha_share_pool(hotkey.clone(), netuid); // We expect to add a positive amount here. let amount = amount.to_u64() as i64; alpha_share_pool.update_value_for_one(coldkey, amount); } pub fn try_increase_stake_for_hotkey_and_coldkey_on_subnet( hotkey: &T::AccountId, netuid: NetUid, amount: AlphaBalance, ) -> bool { let mut alpha_share_pool = Self::get_alpha_share_pool(hotkey.clone(), netuid); let amount = amount.to_u64() as i64; alpha_share_pool.sim_update_value_for_one(amount) } /// Sell shares in the hotkey on a given subnet /// /// The function updates share totals given current prices. /// /// # Arguments /// * `hotkey`: The account ID of the hotkey. /// * `coldkey`: The account ID of the coldkey (owner). /// * `netuid`: The unique identifier of the subnet. /// * `amount`: The amount of alpha to be added. /// pub fn decrease_stake_for_hotkey_and_coldkey_on_subnet( hotkey: &T::AccountId, coldkey: &T::AccountId, netuid: NetUid, amount: AlphaBalance, ) { let mut alpha_share_pool = Self::get_alpha_share_pool(hotkey.clone(), netuid); let amount = amount.to_u64(); // We expect a negative value here if let Ok(value) = alpha_share_pool.try_get_value(coldkey) && value >= amount { alpha_share_pool.update_value_for_one(coldkey, (amount as i64).neg()); } } /// Swaps TAO for the alpha token on the subnet. /// /// Updates TaoIn, AlphaIn, and AlphaOut pub fn swap_tao_for_alpha( netuid: NetUid, tao: TaoBalance, price_limit: TaoBalance, drop_fees: bool, ) -> Result, DispatchError> { // Step 1: Get the mechanism type for the subnet (0 for Stable, 1 for Dynamic) let mechanism_id: u16 = SubnetMechanism::::get(netuid); let swap_result = if mechanism_id == 1 { let order = GetAlphaForTao::::with_amount(tao); T::SwapInterface::swap(netuid.into(), order, price_limit.into(), drop_fees, false)? } else { // Step 3.b.1: Stable mechanism, just return the value 1:1 SwapResult { amount_paid_in: tao, amount_paid_out: tao.to_u64().into(), fee_paid: TaoBalance::ZERO, fee_to_block_author: TaoBalance::ZERO, } }; let alpha_decrease = swap_result.paid_out_reserve_delta_i64().unsigned_abs(); // Decrease Alpha reserves. Self::decrease_provided_alpha_reserve(netuid.into(), alpha_decrease.into()); // Increase Alpha outstanding. SubnetAlphaOut::::mutate(netuid, |total| { *total = total.saturating_add(swap_result.amount_paid_out.into()); }); // Increase the protocol TAO reserve SubnetTAO::::mutate(netuid, |total| { let delta = swap_result.paid_in_reserve_delta_i64().unsigned_abs(); *total = total.saturating_add(delta.into()); }); // Increase Total Tao reserves. TotalStake::::mutate(|total| *total = total.saturating_add(tao)); // Increase total subnet TAO volume. SubnetVolume::::mutate(netuid, |total| { *total = total.saturating_add(tao.to_u64() as u128); }); Ok(swap_result) } /// Swaps a subnet's Alpha token for TAO. /// /// Updates TaoIn, AlphaIn, and AlphaOut pub fn swap_alpha_for_tao( netuid: NetUid, alpha: AlphaBalance, price_limit: TaoBalance, drop_fees: bool, ) -> Result, DispatchError> { // Step 1: Get the mechanism type for the subnet (0 for Stable, 1 for Dynamic) let mechanism_id: u16 = SubnetMechanism::::get(netuid); // Step 2: Swap alpha and attain tao let swap_result = if mechanism_id == 1 { let order = GetTaoForAlpha::::with_amount(alpha); T::SwapInterface::swap(netuid.into(), order, price_limit.into(), drop_fees, false)? } else { // Step 3.b.1: Stable mechanism, just return the value 1:1 SwapResult { amount_paid_in: alpha, amount_paid_out: alpha.to_u64().into(), fee_paid: AlphaBalance::ZERO, fee_to_block_author: AlphaBalance::ZERO, } }; // 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()); }); // Decrease Alpha outstanding. SubnetAlphaOut::::mutate(netuid, |total| { *total = total.saturating_sub(alpha_delta.into()); }); // Decrease tao reserves. let tao_delta = swap_result.paid_out_reserve_delta_i64().unsigned_abs(); Self::decrease_provided_tao_reserve(netuid.into(), tao_delta.into()); // Reduce total TAO reserves. TotalStake::::mutate(|total| *total = total.saturating_sub(swap_result.amount_paid_out)); // Increase total subnet TAO volume. SubnetVolume::::mutate(netuid, |total| { *total = total.saturating_add(swap_result.amount_paid_out.to_u64() as u128) }); // Return the tao received. Ok(swap_result) } /// Unstakes alpha from a subnet for a given hotkey and coldkey pair. /// /// We update the pools associated with a subnet as well as update hotkey alpha shares. /// Credits the unstaked TAO to the beneficiary account pub fn unstake_from_subnet( hotkey: &T::AccountId, coldkey: &T::AccountId, beneficiary: &T::AccountId, netuid: NetUid, alpha: AlphaBalance, price_limit: TaoBalance, drop_fees: bool, ) -> Result { // Decrease alpha on subnet Self::decrease_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid, alpha); // Swap the alpha for TAO. let swap_result = Self::swap_alpha_for_tao(netuid, alpha, price_limit, drop_fees)?; // Refund the unused alpha (in case if limit price is hit) let refund = alpha.saturating_sub( swap_result .amount_paid_in .saturating_add(swap_result.fee_paid) .into(), ); if !refund.is_zero() { Self::increase_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid, refund); } // Transfer unstaked TAO from subnet account to the coldkey. Self::transfer_tao_from_subnet(netuid, beneficiary, swap_result.amount_paid_out.into())?; // Swap (in a fee-less way) the block builder alpha fee let mut fee_outflow = 0_u64; let maybe_block_author_coldkey = T::AuthorshipProvider::author(); if let Some(block_author_coldkey) = maybe_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::transfer_tao_from_subnet( netuid, &block_author_coldkey, bb_swap_result.amount_paid_out.into(), )?; fee_outflow = bb_swap_result.amount_paid_out.into(); } else { // block author is not found, burn this alpha 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. Self::remove_stake_adjust_root_claimed_for_hotkey_and_coldkey(hotkey, coldkey, alpha); // If the coldkey no longer holds any root stake, remove it from the // auto-claim staking-coldkey index so dead entries do not accumulate. if !Self::coldkey_has_root_stake(coldkey) { Self::maybe_remove_coldkey_index(coldkey); } } // Step 3: Update StakingHotkeys if the hotkey's total alpha, across all subnets, is zero // TODO const: fix. // if Self::get_stake(hotkey, coldkey) == 0 { // StakingHotkeys::::mutate(coldkey, |hotkeys| { // hotkeys.retain(|k| k != hotkey); // }); // } // Record TAO outflow Self::record_tao_outflow( netuid, swap_result .amount_paid_out .saturating_add(fee_outflow.into()), ); // Cleanup locks if needed Self::cleanup_lock_if_zero(coldkey, netuid); LastColdkeyHotkeyStakeBlock::::insert(coldkey, hotkey, Self::get_current_block_as_u64()); // Deposit and log the unstaking event. Self::deposit_event(Event::StakeRemoved( coldkey.clone(), hotkey.clone(), swap_result.amount_paid_out.into(), swap_result.amount_paid_in.into(), netuid, swap_result.fee_paid.to_u64(), )); log::debug!( "StakeRemoved( coldkey: {:?}, hotkey:{:?}, tao: {:?}, alpha:{:?}, netuid: {:?}, fee {} )", coldkey.clone(), hotkey.clone(), swap_result.amount_paid_out, swap_result.amount_paid_in, netuid, swap_result.fee_paid ); Ok(swap_result.amount_paid_out.into()) } /// Stakes TAO into a subnet for a given hotkey and coldkey pair. /// /// We update the pools associated with a subnet as well as update hotkey alpha shares. pub(crate) fn stake_into_subnet( hotkey: &T::AccountId, coldkey: &T::AccountId, netuid: NetUid, tao: TaoBalance, price_limit: TaoBalance, drop_fees: bool, ) -> Result { // Transfer TAO from coldkey to the subnet account. // Actual transfered may be different within ED amount. let tao_staked = Self::transfer_tao_to_subnet(netuid, coldkey, tao)?; // Swap the tao to alpha. let swap_result = Self::swap_tao_for_alpha(netuid, tao_staked, price_limit, drop_fees)?; ensure!( !swap_result.amount_paid_out.is_zero(), Error::::AmountTooLow ); ensure!( Self::try_increase_stake_for_hotkey_and_coldkey_on_subnet( hotkey, netuid, swap_result.amount_paid_out.into(), ), Error::::InsufficientLiquidity ); // Increase the alpha on the hotkey account. Self::increase_stake_for_hotkey_and_coldkey_on_subnet( hotkey, coldkey, netuid, swap_result.amount_paid_out.into(), ); // Step 4: Update the list of hotkeys staking for this coldkey let mut staking_hotkeys = StakingHotkeys::::get(coldkey); if !staking_hotkeys.contains(hotkey) { staking_hotkeys.push(hotkey.clone()); 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 { // TAO was transferred to subnet account in the beginning of this fn // swap_tao_for_alpha guarantees that input amount of TAO was split into // reserve delta + fee_to_block_author. // Now transfer the fee from subnet account to block builder. Self::transfer_tao_from_subnet( netuid, &block_author_coldkey, swap_result.fee_to_block_author.into(), )?; } else { // Block author is not found - burn this TAO if let Some(subnet_account_id) = Self::get_subnet_account_id(netuid) { let _ = Self::burn_tao(&subnet_account_id, swap_result.fee_to_block_author.into()); } } // Refund the TAO the AMM could not consume (e.g. when the user-supplied // price limit is hit before the full `tao_staked` is swapped). Without // this, the unswapped remainder is stranded on the subnet PalletId // account. Mirrors the alpha refund in `unstake_from_subnet`. let consumed_tao = swap_result .amount_paid_in .saturating_add(swap_result.fee_paid); let refund_tao = tao_staked.saturating_sub(consumed_tao); if !refund_tao.is_zero() { Self::transfer_tao_from_subnet(netuid, coldkey, refund_tao)?; // `swap_tao_for_alpha` bumped `TotalStake` by the full `tao_staked`; // only `consumed_tao` actually became stake, so back out the refund. TotalStake::::mutate(|total| *total = total.saturating_sub(refund_tao)); } // Record TAO inflow Self::record_tao_inflow(netuid, swap_result.amount_paid_in.into()); // Cleanup locks if needed Self::cleanup_lock_if_zero(coldkey, netuid); LastColdkeyHotkeyStakeBlock::::insert(coldkey, hotkey, Self::get_current_block_as_u64()); // If this is a root-stake if netuid == NetUid::ROOT { // Adjust root claimed for this hotkey and coldkey. let alpha = swap_result.amount_paid_out.into(); Self::add_stake_adjust_root_claimed_for_hotkey_and_coldkey(hotkey, coldkey, alpha); Self::maybe_add_coldkey_index(coldkey); } // Deposit and log the staking event. Self::deposit_event(Event::StakeAdded( coldkey.clone(), hotkey.clone(), tao_staked, swap_result.amount_paid_out.into(), netuid, swap_result.fee_paid.to_u64(), )); log::debug!( "StakeAdded( coldkey: {:?}, hotkey:{:?}, tao: {:?}, alpha:{:?}, netuid: {:?}, fee {} )", coldkey.clone(), hotkey.clone(), tao_staked, swap_result.amount_paid_out, netuid, swap_result.fee_paid, ); Ok(swap_result.amount_paid_out.into()) } /// Transfers stake between coldkeys and/or hotkey within one subnet without running it /// through swap. /// /// Does not incur any swapping nor fees pub fn transfer_stake_within_subnet( origin_coldkey: &T::AccountId, origin_hotkey: &T::AccountId, destination_coldkey: &T::AccountId, destination_hotkey: &T::AccountId, netuid: NetUid, alpha: AlphaBalance, ) -> Result { // Transfer lock (may fail if destination coldkey has a conflicting lock) Self::transfer_lock(origin_coldkey, destination_coldkey, netuid, alpha)?; // Decrease alpha on origin keys Self::decrease_stake_for_hotkey_and_coldkey_on_subnet( origin_hotkey, origin_coldkey, netuid, alpha, ); if netuid == NetUid::ROOT { Self::remove_stake_adjust_root_claimed_for_hotkey_and_coldkey( origin_hotkey, origin_coldkey, alpha, ); } // Increase alpha on destination keys Self::increase_stake_for_hotkey_and_coldkey_on_subnet( destination_hotkey, destination_coldkey, netuid, alpha, ); if netuid == NetUid::ROOT { Self::add_stake_adjust_root_claimed_for_hotkey_and_coldkey( destination_hotkey, destination_coldkey, u64::from(alpha).into(), ); } // Calculate TAO equivalent based on current price (it is accurate because // there's no slippage in this move) let current_price = ::SwapInterface::current_alpha_price(netuid.into()); let tao_equivalent: TaoBalance = current_price .saturating_mul(U64F64::saturating_from_num(alpha)) .saturating_to_num::() .into(); // Ensure tao_equivalent is above DefaultMinStake ensure!( tao_equivalent >= DefaultMinStake::::get(), Error::::AmountTooLow ); // Step 3: Update StakingHotkeys if the hotkey's total alpha, across all subnets, is zero // TODO: fix. // if Self::get_stake(hotkey, coldkey) == 0 { // StakingHotkeys::::mutate(coldkey, |hotkeys| { // hotkeys.retain(|k| k != hotkey); // }); // } LastColdkeyHotkeyStakeBlock::::insert( destination_coldkey, destination_hotkey, Self::get_current_block_as_u64(), ); // Deposit and log the unstaking event. Self::deposit_event(Event::StakeRemoved( origin_coldkey.clone(), origin_hotkey.clone(), tao_equivalent, alpha, netuid, 0_u64, // 0 fee )); Self::deposit_event(Event::StakeAdded( destination_coldkey.clone(), destination_hotkey.clone(), tao_equivalent, alpha, netuid, 0_u64, // 0 fee )); Ok(tao_equivalent) } pub fn get_alpha_share_pool( hotkey: ::AccountId, netuid: NetUid, ) -> SharePool, HotkeyAlphaSharePoolDataOperations> { let ops = HotkeyAlphaSharePoolDataOperations::new(hotkey, netuid); SharePool::, HotkeyAlphaSharePoolDataOperations>::new(ops) } /// Validate add_stake user input pub fn validate_add_stake( coldkey: &T::AccountId, hotkey: &T::AccountId, netuid: NetUid, mut stake_to_be_added: TaoBalance, max_amount: TaoBalance, allow_partial: bool, ) -> Result<(), Error> { // Ensure that the subnet exists. ensure!(Self::if_subnet_exist(netuid), Error::::SubnetNotExists); // Ensure that the subnet is enabled. Self::ensure_subtoken_enabled(netuid)?; // Get the minimum balance (and amount) that satisfies the transaction let min_stake = DefaultMinStake::::get(); let min_amount = { let order = GetAlphaForTao::::with_amount(min_stake); let fee = T::SwapInterface::sim_swap(netuid.into(), order) .map(|res| res.fee_paid) .unwrap_or(T::SwapInterface::approx_fee_amount( netuid.into(), min_stake.into(), )); min_stake.saturating_add(fee.into()) }; // Ensure that the stake_to_be_added is at least the min_amount ensure!(stake_to_be_added >= min_amount, Error::::AmountTooLow); // Ensure that if partial execution is not allowed, the amount will not cause // slippage over desired if !allow_partial { ensure!(stake_to_be_added <= max_amount, Error::::SlippageTooHigh); } else { stake_to_be_added = max_amount.min(stake_to_be_added); } // Ensure the callers coldkey has enough stake to perform the transaction. ensure!( Self::can_remove_balance_from_coldkey_account(coldkey, stake_to_be_added.into()), Error::::NotEnoughBalanceToStake ); // Ensure that the hotkey account exists this is only possible through registration. ensure!( Self::hotkey_account_exists(hotkey), Error::::HotKeyAccountNotExists ); let order = GetAlphaForTao::::with_amount(stake_to_be_added); let swap_result = T::SwapInterface::sim_swap(netuid.into(), order) .map_err(|_| Error::::InsufficientLiquidity)?; // Check that actual withdrawn TAO amount is not lower than the minimum stake ensure!( swap_result.amount_paid_in >= min_stake, Error::::AmountTooLow ); ensure!( !swap_result.amount_paid_out.is_zero(), Error::::InsufficientLiquidity ); // Ensure hotkey pool is precise enough let try_stake_result = Self::try_increase_stake_for_hotkey_and_coldkey_on_subnet( hotkey, netuid, swap_result.amount_paid_out.into(), ); ensure!(try_stake_result, Error::::InsufficientLiquidity); Ok(()) } /// Validate remove_stake user input /// pub fn validate_remove_stake( coldkey: &T::AccountId, hotkey: &T::AccountId, netuid: NetUid, alpha_unstaked: AlphaBalance, max_amount: AlphaBalance, allow_partial: bool, ) -> Result<(), Error> { // Ensure that the subnet exists. ensure!(Self::if_subnet_exist(netuid), Error::::SubnetNotExists); // Ensure that the subnet is enabled. // Self::ensure_subtoken_enabled(netuid)?; // Do not allow zero unstake amount ensure!(!alpha_unstaked.is_zero(), Error::::AmountTooLow); // Ensure that the stake amount to be removed is above the minimum in tao equivalent. // Bypass this check if the user unstakes full amount let remaining_alpha_stake = Self::calculate_reduced_stake_on_subnet(hotkey, coldkey, netuid, alpha_unstaked)?; let order = GetTaoForAlpha::::with_amount(alpha_unstaked); match T::SwapInterface::sim_swap(netuid.into(), order) { Ok(res) => { if !remaining_alpha_stake.is_zero() { ensure!( res.amount_paid_out >= DefaultMinStake::::get(), Error::::AmountTooLow ); } } Err(_) => return Err(Error::::InsufficientLiquidity), } // Ensure that if partial execution is not allowed, the amount will not cause // slippage over desired if !allow_partial { ensure!(alpha_unstaked <= max_amount, Error::::SlippageTooHigh); } // Ensure that the hotkey account exists this is only possible through registration. ensure!( Self::hotkey_account_exists(hotkey), Error::::HotKeyAccountNotExists ); // Ensure that unstaked amount is not greater than available to unstake (due to locks) Self::ensure_available_to_unstake(coldkey, netuid, alpha_unstaked)?; Ok(()) } /// Validate if unstake_all can be executed /// pub fn validate_unstake_all( coldkey: &T::AccountId, hotkey: &T::AccountId, only_alpha: bool, ) -> Result<(), Error> { // Get all netuids (filter out root) let subnets = Self::get_all_subnet_netuids(); // Ensure that the hotkey account exists this is only possible through registration. ensure!( Self::hotkey_account_exists(hotkey), Error::::HotKeyAccountNotExists ); let mut unstaking_any = false; for netuid in subnets.iter() { if only_alpha && netuid.is_root() { continue; } // Get user's stake in this subnet let alpha = Self::get_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, *netuid); // Ensure that unstaked amount is not greater than available to unstake (due to locks) // for this subnet. Self::ensure_available_to_unstake(coldkey, *netuid, alpha)?; if Self::validate_remove_stake(coldkey, hotkey, *netuid, alpha, alpha, false).is_ok() { unstaking_any = true; } } // If no unstaking happens, return error ensure!(unstaking_any, Error::::AmountTooLow); Ok(()) } /// Validate stake transition user input /// That works for move_stake, transfer_stake, and swap_stake /// pub fn validate_stake_transition( origin_coldkey: &T::AccountId, destination_coldkey: &T::AccountId, origin_hotkey: &T::AccountId, destination_hotkey: &T::AccountId, origin_netuid: NetUid, destination_netuid: NetUid, alpha_amount: AlphaBalance, max_amount: AlphaBalance, maybe_allow_partial: Option, check_transfer_toggle: bool, ) -> Result<(), Error> { // Ensure stake transition is actually happening if origin_coldkey == destination_coldkey && origin_hotkey == destination_hotkey { ensure!(origin_netuid != destination_netuid, Error::::SameNetuid); } // Ensure that both subnets exist. ensure!( Self::if_subnet_exist(origin_netuid), Error::::SubnetNotExists ); if origin_netuid != destination_netuid { ensure!( Self::if_subnet_exist(destination_netuid), Error::::SubnetNotExists ); } ensure!( SubtokenEnabled::::get(origin_netuid), Error::::SubtokenDisabled ); ensure!( SubtokenEnabled::::get(destination_netuid), Error::::SubtokenDisabled ); // Ensure that the origin hotkey account exists ensure!( Self::hotkey_account_exists(origin_hotkey), Error::::HotKeyAccountNotExists ); // Ensure that the destination hotkey account exists ensure!( Self::hotkey_account_exists(destination_hotkey), Error::::HotKeyAccountNotExists ); // Ensure there is enough stake in the origin subnet. let origin_alpha = Self::get_stake_for_hotkey_and_coldkey_on_subnet( origin_hotkey, origin_coldkey, origin_netuid, ); ensure!( alpha_amount <= origin_alpha, Error::::NotEnoughStakeToWithdraw ); // If origin and destination netuid are different, do the swap-related checks if origin_netuid != destination_netuid { // Ensure that the stake amount to be removed is above the minimum in tao equivalent. let order = GetTaoForAlpha::::with_amount(alpha_amount); let tao_equivalent = T::SwapInterface::sim_swap(origin_netuid.into(), order) .map(|res| res.amount_paid_out) .map_err(|_| Error::::InsufficientLiquidity)?; ensure!( tao_equivalent > DefaultMinStake::::get(), Error::::AmountTooLow ); // Ensure that if partial execution is not allowed, the amount will not cause // slippage over desired if let Some(allow_partial) = maybe_allow_partial && !allow_partial { ensure!(alpha_amount <= max_amount, Error::::SlippageTooHigh); } } if check_transfer_toggle { // Ensure transfer is toggled. ensure!( TransferToggle::::get(origin_netuid), Error::::TransferDisallowed ); if origin_netuid != destination_netuid { ensure!( TransferToggle::::get(destination_netuid), Error::::TransferDisallowed ); } } // Enforce lock invariant: if the is cross-subnet move, the remaining amount must // cover the lock. if origin_netuid != destination_netuid { Self::ensure_available_to_unstake(origin_coldkey, origin_netuid, alpha_amount)?; } Ok(()) } pub fn increase_provided_tao_reserve(netuid: NetUid, tao: TaoBalance) { if !tao.is_zero() { SubnetTAO::::mutate(netuid, |total| { *total = total.saturating_add(tao); }); } } pub fn decrease_provided_tao_reserve(netuid: NetUid, tao: TaoBalance) { if !tao.is_zero() { SubnetTAO::::mutate(netuid, |total| { *total = total.saturating_sub(tao); }); } } pub fn increase_provided_alpha_reserve(netuid: NetUid, alpha: AlphaBalance) { if !alpha.is_zero() { SubnetAlphaIn::::mutate(netuid, |total| { *total = total.saturating_add(alpha); }); } } pub fn decrease_provided_alpha_reserve(netuid: NetUid, alpha: AlphaBalance) { if !alpha.is_zero() { SubnetAlphaIn::::mutate(netuid, |total| { *total = total.saturating_sub(alpha); }); } } } /////////////////////////////////////////// // Alpha share pool chain data layer #[derive(Debug)] pub struct HotkeyAlphaSharePoolDataOperations { netuid: NetUid, hotkey: ::AccountId, _marker: sp_std::marker::PhantomData, } impl HotkeyAlphaSharePoolDataOperations { fn new(hotkey: ::AccountId, netuid: NetUid) -> Self { HotkeyAlphaSharePoolDataOperations { netuid, hotkey, _marker: sp_std::marker::PhantomData, } } } // Alpha share key is coldkey because the HotkeyAlphaSharePoolDataOperations struct already has hotkey and netuid type AlphaShareKey = ::AccountId; impl SharePoolDataOperations> for HotkeyAlphaSharePoolDataOperations { fn get_shared_value(&self) -> u64 { u64::from(TotalHotkeyAlpha::::get(&self.hotkey, self.netuid)) } fn get_share(&self, key: &AlphaShareKey) -> SafeFloat { // Read the deprecated Alpha map first and, if value is not available, try new AlphaV2 let maybe_share_v1 = Alpha::::try_get((&(self.hotkey), key, self.netuid)); if let Ok(share_v1) = maybe_share_v1 { return SafeFloat::from(share_v1); } AlphaV2::::get((&(self.hotkey), key, self.netuid)) } fn try_get_share(&self, key: &AlphaShareKey) -> Result { // Read the deprecated Alpha map first and, if value is not available, try new AlphaV2 let maybe_share_v1 = Alpha::::try_get((&(self.hotkey), key, self.netuid)); if let Ok(share_v1) = maybe_share_v1 { return Ok(SafeFloat::from(share_v1)); } let maybe_share = AlphaV2::::try_get((&(self.hotkey), key, self.netuid)); if let Ok(share) = maybe_share { Ok(share) } else { Err(()) } } fn get_denominator(&self) -> SafeFloat { // Read the deprecated TotalHotkeyShares map first and, if value is not available, try new TotalHotkeySharesV2 let maybe_denomnator_v1 = TotalHotkeyShares::::try_get(&(self.hotkey), self.netuid); if let Ok(denomnator_v1) = maybe_denomnator_v1 { return SafeFloat::from(denomnator_v1); } TotalHotkeySharesV2::::get(&(self.hotkey), self.netuid) } fn set_shared_value(&mut self, value: u64) { if value != 0 { TotalHotkeyAlpha::::insert(&(self.hotkey), self.netuid, AlphaBalance::from(value)); } else { TotalHotkeyAlpha::::remove(&(self.hotkey), self.netuid); } } fn set_share(&mut self, key: &AlphaShareKey, share: SafeFloat) { // Lazy Alpha -> AlphaV2 migration happens right here // Delete the Alpha entry, insert into AlphaV2 let maybe_share_v1 = Alpha::::try_get((&(self.hotkey), key, self.netuid)); if maybe_share_v1.is_ok() { Alpha::::remove((&self.hotkey, key, self.netuid)); } if !share.is_zero() { AlphaV2::::insert((&self.hotkey, key, self.netuid), share); } else { AlphaV2::::remove((&self.hotkey, key, self.netuid)); } } fn set_denominator(&mut self, update: SafeFloat) { // Lazy TotalHotkeyShares -> TotalHotkeySharesV2 migration happens right here // Delete the TotalHotkeyShares entry, insert into TotalHotkeySharesV2 let maybe_denominator_v1 = TotalHotkeyShares::::try_get(&(self.hotkey), self.netuid); if maybe_denominator_v1.is_ok() { TotalHotkeyShares::::remove(&self.hotkey, self.netuid); } if !update.is_zero() { TotalHotkeySharesV2::::insert(&self.hotkey, self.netuid, update); } else { TotalHotkeySharesV2::::remove(&self.hotkey, self.netuid); } } }