code/pallets/subtensor/src/staking/order_swap.rs

order_swap.rs

188 lines · 7,298 bytes · 19a6485969RawGitHub
use super::*;
use frame_support::transactional;
use substrate_fixed::types::U64F64;
use subtensor_runtime_common::{AlphaBalance, NetUid, TaoBalance};
use subtensor_swap_interface::{Order, OrderSwapInterface, SwapHandler, SwapResult};

impl<T: Config> OrderSwapInterface<T::AccountId> for Pallet<T> {
    #[transactional]
    fn buy_alpha(
        coldkey: &T::AccountId,
        hotkey: &T::AccountId,
        netuid: NetUid,
        tao_amount: TaoBalance,
        limit_price: TaoBalance,
        validate: bool,
    ) -> Result<AlphaBalance, DispatchError> {
        ensure!(Self::if_subnet_exist(netuid), Error::<T>::SubnetNotExists);
        Self::ensure_subtoken_enabled(netuid)?;
        if validate {
            ensure!(
                Self::hotkey_account_exists(hotkey),
                Error::<T>::HotKeyAccountNotExists
            );
            ensure!(
                tao_amount >= DefaultMinStake::<T>::get(),
                Error::<T>::AmountTooLow
            );
            ensure!(
                Self::can_remove_balance_from_coldkey_account(coldkey, tao_amount),
                Error::<T>::NotEnoughBalanceToStake
            );
        }
        // `limit_price` is already in ×10⁹ scale (same as the `current_alpha_price` RPC
        // endpoint), which is also the scale the AMM uses for its price_limit argument.
        // Pass it directly without any scaling.  u64::MAX means "no ceiling".
        let amm_limit = limit_price;
        // Stable subnets (mechanism_id != 1) are always 1:1 and never stop early.
        if SubnetMechanism::<T>::get(netuid) == 1 {
            let sim_order = GetAlphaForTao::<T>::with_amount(tao_amount);
            let sim: SwapResult<TaoBalance, AlphaBalance> =
                T::SwapInterface::swap(netuid.into(), sim_order, amm_limit, false, true)?;
            ensure!(
                sim.amount_paid_in.saturating_add(sim.fee_paid) >= tao_amount,
                Error::<T>::SlippageTooHigh
            );
        }
        let alpha_out =
            Self::stake_into_subnet(hotkey, coldkey, netuid, tao_amount, amm_limit, false)?;
        Ok(alpha_out)
    }

    #[transactional]
    fn sell_alpha(
        coldkey: &T::AccountId,
        hotkey: &T::AccountId,
        netuid: NetUid,
        alpha_amount: AlphaBalance,
        limit_price: TaoBalance,
        validate: bool,
    ) -> Result<TaoBalance, DispatchError> {
        ensure!(Self::if_subnet_exist(netuid), Error::<T>::SubnetNotExists);
        Self::ensure_subtoken_enabled(netuid)?;
        if validate {
            Self::validate_remove_stake(
                coldkey,
                hotkey,
                netuid,
                alpha_amount,
                alpha_amount,
                false,
            )?;
        }
        // `limit_price` is already in ×10⁹ scale (same as the `current_alpha_price` RPC
        // endpoint), which is also the scale the AMM uses for its price_limit argument.
        // Pass it directly without any scaling.  0 means "no floor".
        let amm_limit = limit_price;
        // Stable subnets (mechanism_id != 1) are always 1:1 and never stop early.
        if SubnetMechanism::<T>::get(netuid) == 1 {
            let sim_order = GetTaoForAlpha::<T>::with_amount(alpha_amount);
            let sim: SwapResult<AlphaBalance, TaoBalance> =
                T::SwapInterface::swap(netuid.into(), sim_order, amm_limit, false, true)?;
            ensure!(
                sim.amount_paid_in.saturating_add(sim.fee_paid) >= alpha_amount,
                Error::<T>::SlippageTooHigh
            );
        }
        let tao_out = Self::unstake_from_subnet(
            hotkey,
            coldkey,
            coldkey,
            netuid,
            alpha_amount,
            amm_limit,
            false,
        )?;
        Ok(tao_out)
    }

    fn current_alpha_price(netuid: NetUid) -> U64F64 {
        T::SwapInterface::current_alpha_price(netuid)
    }

    fn transfer_tao(from: &T::AccountId, to: &T::AccountId, amount: TaoBalance) -> DispatchResult {
        Pallet::<T>::transfer_tao(from, to, amount)
    }

    #[transactional]
    fn transfer_staked_alpha(
        from_coldkey: &T::AccountId,
        from_hotkey: &T::AccountId,
        to_coldkey: &T::AccountId,
        to_hotkey: &T::AccountId,
        netuid: NetUid,
        amount: AlphaBalance,
        validate_sender: bool,
        validate_receiver: bool,
    ) -> DispatchResult {
        ensure!(Self::if_subnet_exist(netuid), Error::<T>::SubnetNotExists);
        Self::ensure_subtoken_enabled(netuid)?;
        if validate_sender {
            ensure!(
                Self::hotkey_account_exists(from_hotkey),
                Error::<T>::HotKeyAccountNotExists
            );
            ensure!(!amount.is_zero(), Error::<T>::AmountTooLow);
            let tao_equiv = T::SwapInterface::current_alpha_price(netuid)
                .saturating_mul(U64F64::saturating_from_num(amount.to_u64()))
                .saturating_to_num::<u64>();
            ensure!(
                TaoBalance::from(tao_equiv) >= DefaultMinStake::<T>::get(),
                Error::<T>::AmountTooLow
            );
            Self::ensure_available_to_unstake(from_coldkey, netuid, amount)?;
        }

        if validate_receiver {
            ensure!(
                Self::hotkey_account_exists(to_hotkey),
                Error::<T>::HotKeyAccountNotExists
            );
        }

        let available =
            Self::get_stake_for_hotkey_and_coldkey_on_subnet(from_hotkey, from_coldkey, netuid);
        ensure!(available >= amount, Error::<T>::NotEnoughStakeToWithdraw);
        Self::decrease_stake_for_hotkey_and_coldkey_on_subnet(
            from_hotkey,
            from_coldkey,
            netuid,
            amount,
        );
        Self::increase_stake_for_hotkey_and_coldkey_on_subnet(
            to_hotkey, to_coldkey, netuid, amount,
        );
        LastColdkeyHotkeyStakeBlock::<T>::insert(
            to_coldkey,
            to_hotkey,
            Self::get_current_block_as_u64(),
        );
        Ok(())
    }

    fn register_pallet_hotkey(coldkey: &T::AccountId, hotkey: &T::AccountId) -> DispatchResult {
        Self::create_account_if_non_existent(coldkey, hotkey)
    }

    fn pallet_hotkey_registered(coldkey: &T::AccountId, hotkey: &T::AccountId) -> bool {
        Self::coldkey_owns_hotkey(coldkey, hotkey)
    }

    #[cfg(feature = "runtime-benchmarks")]
    fn set_up_netuid_for_benchmark(netuid: NetUid) {
        if !Self::if_subnet_exist(netuid) {
            Self::init_new_network(netuid, 100);
        }
        SubtokenEnabled::<T>::insert(netuid, true);
        // Seed pool reserves so the AMM price is well-defined and swaps return non-zero.
        SubnetTAO::<T>::insert(netuid, TaoBalance::from(1_000_000_000_000_u64));
        SubnetAlphaIn::<T>::insert(netuid, AlphaBalance::from(1_000_000_000_000_u64));
    }

    #[cfg(feature = "runtime-benchmarks")]
    fn set_up_acc_for_benchmark(hotkey: &T::AccountId, coldkey: &T::AccountId) {
        let _ = Self::create_account_if_non_existent(coldkey, hotkey);
        let credit = Self::mint_tao(TaoBalance::from(1_000_000_000_000_u64));
        let _ = Self::spend_tao(coldkey, credit, TaoBalance::from(1_000_000_000_000_u64));
    }
}