code/pallets/swap/src/pallet/mod.rs

mod.rs

308 lines · 9,418 bytes · 19a6485969RawGitHub
use core::num::NonZeroU64;

use frame_support::{PalletId, pallet_prelude::*, traits::Get};
use frame_system::pallet_prelude::*;
use subtensor_runtime_common::{
    AlphaBalance, BalanceOps, NetUid, SubnetInfo, TaoBalance, TokenReserve,
};

use crate::{pallet::balancer::Balancer, weights::WeightInfo};
pub use pallet::*;
use subtensor_macros::freeze_struct;

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)]
mod pallet {
    use super::*;
    use frame_system::ensure_root;

    #[pallet::pallet]
    pub struct Pallet<T>(_);

    /// Configure the pallet by specifying the parameters and types on which it depends.
    #[pallet::config]
    pub trait Config: frame_system::Config {
        /// Implementor of
        /// [`SubnetInfo`](subtensor_swap_interface::SubnetInfo).
        type SubnetInfo: SubnetInfo<Self::AccountId>;

        /// Tao reserves info.
        type TaoReserve: TokenReserve<TaoBalance>;

        /// Alpha reserves info.
        type AlphaReserve: TokenReserve<AlphaBalance>;

        /// Implementor of
        /// [`BalanceOps`](subtensor_swap_interface::BalanceOps).
        type BalanceOps: BalanceOps<Self::AccountId>;

        /// This type is used to derive protocol accoun ID.
        #[pallet::constant]
        type ProtocolId: Get<PalletId>;

        /// The maximum fee rate that can be set
        #[pallet::constant]
        type MaxFeeRate: Get<u16>;

        /// Minimum liquidity that is safe for rounding and integer math.
        #[pallet::constant]
        type MinimumLiquidity: Get<u64>;

        /// Minimum reserve for tao and alpha
        #[pallet::constant]
        type MinimumReserve: Get<NonZeroU64>;

        /// Weight information for extrinsics in this pallet.
        type WeightInfo: WeightInfo;

        /// Helper for setting up cross-pallet state needed by benchmarks.
        #[cfg(feature = "runtime-benchmarks")]
        type BenchmarkHelper: BenchmarkHelper<Self::AccountId>;
    }

    /// Benchmark setup helper — the runtime wires this to set state in other pallets.
    #[cfg(feature = "runtime-benchmarks")]
    pub trait BenchmarkHelper<AccountId> {
        fn setup_subnet(netuid: NetUid);
        fn register_hotkey(hotkey: &AccountId, coldkey: &AccountId);
    }

    #[cfg(feature = "runtime-benchmarks")]
    impl<AccountId> BenchmarkHelper<AccountId> for () {
        fn setup_subnet(_netuid: NetUid) {}
        fn register_hotkey(_hotkey: &AccountId, _coldkey: &AccountId) {}
    }

    /// Default fee rate if not set
    #[pallet::type_value]
    pub fn DefaultFeeRate() -> u16 {
        33 // ~0.05 %
    }

    /// The fee rate applied to swaps per subnet, normalized value between 0 and u16::MAX
    #[pallet::storage]
    pub type FeeRate<T> = StorageMap<_, Twox64Concat, NetUid, u16, ValueQuery, DefaultFeeRate>;

    ////////////////////////////////////////////////////
    // Balancer (PalSwap) maps and variables

    /// Default reserve weight
    #[pallet::type_value]
    pub fn DefaultBalancer() -> Balancer {
        Balancer::default()
    }

    /// u64-normalized reserve weight
    #[pallet::storage]
    pub type SwapBalancer<T> =
        StorageMap<_, Twox64Concat, NetUid, Balancer, ValueQuery, DefaultBalancer>;

    /// Storage to determine whether balancer swap was initialized for a specific subnet.
    #[pallet::storage]
    pub type PalSwapInitialized<T> = StorageMap<_, Twox64Concat, NetUid, bool, ValueQuery>;

    /// TAO protocol liquidity that could not be injected without exceeding balancer weight bounds.
    #[pallet::storage]
    pub type BalancerTaoReservoir<T> = StorageMap<_, Twox64Concat, NetUid, TaoBalance, ValueQuery>;

    /// Alpha protocol liquidity that could not be injected without exceeding balancer weight bounds.
    #[pallet::storage]
    pub type BalancerAlphaReservoir<T> =
        StorageMap<_, Twox64Concat, NetUid, AlphaBalance, ValueQuery>;

    /// --- Storage for migration run status
    #[pallet::storage]
    pub type HasMigrationRun<T: Config> =
        StorageMap<_, Identity, BoundedVec<u8, MigrationKeyMaxLen>, bool, ValueQuery>;

    /// Alpha reservoir for scraps of protocol claimed fees.
    #[pallet::storage]
    pub type ScrapReservoirAlpha<T> = StorageMap<_, Twox64Concat, NetUid, AlphaBalance, ValueQuery>;

    #[pallet::event]
    #[pallet::generate_deposit(pub(super) fn deposit_event)]
    pub enum Event<T: Config> {
        /// Event emitted when the fee rate has been updated for a subnet
        FeeRateSet { netuid: NetUid, rate: u16 },
    }

    #[pallet::error]
    #[derive(PartialEq)]
    pub enum Error<T> {
        /// The fee rate is too high
        FeeRateTooHigh,

        /// The provided amount is insufficient for the swap.
        InsufficientInputAmount,

        /// The provided liquidity is insufficient for the operation.
        InsufficientLiquidity,

        /// The operation would exceed the price limit.
        PriceLimitExceeded,

        /// The caller does not have enough balance for the operation.
        InsufficientBalance,

        /// The provided tick range is invalid.
        InvalidTickRange,

        /// Provided liquidity parameter is invalid (likely too small)
        InvalidLiquidityValue,

        /// Reserves too low for operation.
        ReservesTooLow,

        /// The subnet does not exist.
        MechanismDoesNotExist,

        /// The subnet does not have subtoken enabled
        SubtokenDisabled,

        /// Swap reserves are too imbalanced
        ReservesOutOfBalance,

        /// Swap input is too large relative to input-side liquidity
        SwapInputTooLarge,

        /// The extrinsic is deprecated
        Deprecated,
    }

    #[pallet::call]
    impl<T: Config> Pallet<T> {
        #![deny(clippy::expect_used)]

        /// Set the fee rate for swaps on a specific subnet (normalized value).
        /// For example, 0.3% is approximately 196.
        ///
        /// Only callable by the admin origin
        #[pallet::call_index(0)]
        #[pallet::weight(<T as pallet::Config>::WeightInfo::set_fee_rate())]
        pub fn set_fee_rate(origin: OriginFor<T>, netuid: NetUid, rate: u16) -> DispatchResult {
            ensure_root(origin)?;

            // Ensure that the subnet exists.
            ensure!(
                T::SubnetInfo::exists(netuid.into()),
                Error::<T>::MechanismDoesNotExist
            );

            ensure!(rate <= T::MaxFeeRate::get(), Error::<T>::FeeRateTooHigh);

            FeeRate::<T>::insert(netuid, rate);

            Self::deposit_event(Event::FeeRateSet { netuid, rate });

            Ok(())
        }

        /// DEPRECATED
        #[pallet::call_index(4)]
        #[pallet::weight(Weight::from_parts(15_000_000, 0))]
        pub fn toggle_user_liquidity(
            _origin: OriginFor<T>,
            _netuid: NetUid,
            _enable: bool,
        ) -> DispatchResult {
            Err(Error::<T>::Deprecated.into())
        }

        /// DEPRECATED
        #[pallet::call_index(1)]
        #[pallet::weight(Weight::from_parts(15_000_000, 0))]
        pub fn add_liquidity(
            _origin: OriginFor<T>,
            _hotkey: T::AccountId,
            _netuid: NetUid,
            _tick_low: TickIndex,
            _tick_high: TickIndex,
            _liquidity: u64,
        ) -> DispatchResult {
            Err(Error::<T>::Deprecated.into())
        }

        /// DEPRECATED
        #[pallet::call_index(2)]
        #[pallet::weight(Weight::from_parts(15_000_000, 0))]
        pub fn remove_liquidity(
            _origin: OriginFor<T>,
            _hotkey: T::AccountId,
            _netuid: NetUid,
            _position_id: PositionId,
        ) -> DispatchResult {
            Err(Error::<T>::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<T>,
            _hotkey: T::AccountId,
            _netuid: NetUid,
            _position_id: PositionId,
            _liquidity_delta: i64,
        ) -> DispatchResult {
            Err(Error::<T>::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<T>) -> DispatchResult {
            Err(Error::<T>::Deprecated.into())
        }
    }
}

/// Struct representing a tick index, DEPRECATED
#[freeze_struct("7c280c2b3bbbb33e")]
#[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
#[freeze_struct("e695cd6455c3f0cb")]
#[derive(
    Clone,
    Copy,
    Decode,
    DecodeWithMemTracking,
    Default,
    Encode,
    Eq,
    MaxEncodedLen,
    PartialEq,
    RuntimeDebug,
    TypeInfo,
)]
pub struct PositionId(u128);