use super::*; use crate::{Error, system::ensure_signed}; use frame_support::storage::{TransactionOutcome, with_transaction}; use subtensor_runtime_common::{AlphaBalance, NetUid}; impl Pallet { /// Recycles alpha from a cold/hot key pair, reducing AlphaOut on a subnet /// /// # Arguments /// /// * `origin`: The origin of the call (must be signed by the coldkey) /// * `hotkey`: The hotkey account /// * `amount`: The amount of alpha to recycle /// * `netuid`: The subnet ID from which to reduce AlphaOut /// /// # Returns /// /// * `Result`: The actual amount recycled, or error pub fn do_recycle_alpha( origin: OriginFor, hotkey: T::AccountId, amount: AlphaBalance, netuid: NetUid, ) -> Result { let coldkey: T::AccountId = ensure_signed(origin)?; ensure!(Self::if_subnet_exist(netuid), Error::::SubnetNotExists); ensure!( !netuid.is_root(), Error::::CannotBurnOrRecycleOnRootSubnet ); Self::ensure_subtoken_enabled(netuid)?; // Ensure that the hotkey account exists this is only possible through registration. ensure!( Self::hotkey_account_exists(&hotkey), Error::::HotKeyAccountNotExists ); // Ensure that the hotkey has enough stake to withdraw. // Cap the amount at available Alpha because user might be paying transaxtion fees // in Alpha and their total is already reduced by now. let alpha_available = Self::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid); let amount = amount.min(alpha_available); ensure!( SubnetAlphaOut::::get(netuid) >= amount, Error::::InsufficientLiquidity ); // Ensure that recycled amount is not greater than available to unstake (due to locks) Self::ensure_available_to_unstake(&coldkey, netuid, amount)?; // Deduct from the coldkey's stake. Self::decrease_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid, amount); // Recycle means we should decrease the alpha issuance tracker. Self::recycle_subnet_alpha(netuid, amount); Self::deposit_event(Event::AlphaRecycled(coldkey, hotkey, amount, netuid)); Ok(amount) } /// Burns alpha from a cold/hot key pair without reducing AlphaOut /// /// # Arguments /// /// * `origin`: The origin of the call (must be signed by the coldkey) /// * `hotkey`: The hotkey account /// * `amount`: The "up to" amount of alpha to burn /// * `netuid`: The subnet ID /// /// # Returns /// /// * `Result`: The actual amount burned, or error pub fn do_burn_alpha( origin: OriginFor, hotkey: T::AccountId, amount: AlphaBalance, netuid: NetUid, ) -> Result { let coldkey = ensure_signed(origin)?; ensure!(Self::if_subnet_exist(netuid), Error::::SubnetNotExists); ensure!( !netuid.is_root(), Error::::CannotBurnOrRecycleOnRootSubnet ); Self::ensure_subtoken_enabled(netuid)?; // Ensure that the hotkey account exists this is only possible through registration. ensure!( Self::hotkey_account_exists(&hotkey), Error::::HotKeyAccountNotExists ); // Ensure that the hotkey has enough stake to withdraw. // Cap the amount at available Alpha because user might be paying transaxtion fees // in Alpha and their total is already reduced by now. let alpha_available = Self::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid); let amount = amount.min(alpha_available); ensure!( SubnetAlphaOut::::get(netuid) >= amount, Error::::InsufficientLiquidity ); // Ensure that burned amount is not greater than available to unstake (due to locks) Self::ensure_available_to_unstake(&coldkey, netuid, amount)?; // Deduct from the coldkey's stake. Self::decrease_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid, amount); Self::burn_subnet_alpha(netuid, amount); // Deposit event Self::deposit_event(Event::AlphaBurned(coldkey, hotkey, amount, netuid)); Ok(amount) } pub(crate) fn do_add_stake_burn( origin: OriginFor, hotkey: T::AccountId, netuid: NetUid, amount: TaoBalance, limit: Option, ) -> DispatchResult { ensure!(Self::if_subnet_exist(netuid), Error::::SubnetNotExists); with_transaction(|| { let result = (|| { 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.clone(), alpha, netuid)?; Self::deposit_event(Event::AddStakeBurn { netuid, hotkey, amount, alpha, }); Ok(()) })(); match result { Ok(()) => TransactionOutcome::Commit(Ok(())), Err(err) => TransactionOutcome::Rollback(Err(err)), } }) } /// Atomically stakes TAO and recycles the resulting alpha. /// Permissionless counterpart used by the chain extension so that contracts /// can compose the two operations without leaving residual stake if the /// second leg fails. pub fn do_add_stake_recycle( origin: OriginFor, hotkey: T::AccountId, netuid: NetUid, amount: TaoBalance, ) -> Result { with_transaction(|| { let result = (|| { let alpha = Self::do_add_stake(origin.clone(), hotkey.clone(), netuid, amount)?; Self::do_recycle_alpha(origin, hotkey, alpha, netuid) })(); match result { Ok(alpha) => TransactionOutcome::Commit(Ok(alpha)), Err(err) => TransactionOutcome::Rollback(Err(err)), } }) } /// Atomically stakes TAO and burns the resulting alpha. Permissionless /// counterpart to `do_add_stake_burn`: return the amount of alpha burned. /// limit. Used by the chain extension. pub fn do_add_stake_burn_permissionless( origin: OriginFor, hotkey: T::AccountId, netuid: NetUid, amount: TaoBalance, ) -> Result { with_transaction(|| { let result = (|| { let alpha = Self::do_add_stake(origin.clone(), hotkey.clone(), netuid, amount)?; Self::do_burn_alpha(origin, hotkey, alpha, netuid) })(); match result { Ok(alpha) => TransactionOutcome::Commit(Ok(alpha)), Err(err) => TransactionOutcome::Rollback(Err(err)), } }) } }