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(_); /// 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; /// Tao reserves info. type TaoReserve: TokenReserve; /// Alpha reserves info. type AlphaReserve: TokenReserve; /// Implementor of /// [`BalanceOps`](subtensor_swap_interface::BalanceOps). type BalanceOps: BalanceOps; /// This type is used to derive protocol accoun ID. #[pallet::constant] type ProtocolId: Get; /// The maximum fee rate that can be set #[pallet::constant] type MaxFeeRate: Get; /// Minimum liquidity that is safe for rounding and integer math. #[pallet::constant] type MinimumLiquidity: Get; /// Minimum reserve for tao and alpha #[pallet::constant] type MinimumReserve: Get; /// 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; } /// Benchmark setup helper — the runtime wires this to set state in other pallets. #[cfg(feature = "runtime-benchmarks")] pub trait BenchmarkHelper { fn setup_subnet(netuid: NetUid); fn register_hotkey(hotkey: &AccountId, coldkey: &AccountId); } #[cfg(feature = "runtime-benchmarks")] impl BenchmarkHelper 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 = 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 = StorageMap<_, Twox64Concat, NetUid, Balancer, ValueQuery, DefaultBalancer>; /// Storage to determine whether balancer swap was initialized for a specific subnet. #[pallet::storage] pub type PalSwapInitialized = StorageMap<_, Twox64Concat, NetUid, bool, ValueQuery>; /// TAO protocol liquidity that could not be injected without exceeding balancer weight bounds. #[pallet::storage] pub type BalancerTaoReservoir = StorageMap<_, Twox64Concat, NetUid, TaoBalance, ValueQuery>; /// Alpha protocol liquidity that could not be injected without exceeding balancer weight bounds. #[pallet::storage] pub type BalancerAlphaReservoir = StorageMap<_, Twox64Concat, NetUid, AlphaBalance, ValueQuery>; /// --- Storage for migration run status #[pallet::storage] pub type HasMigrationRun = StorageMap<_, Identity, BoundedVec, bool, ValueQuery>; /// Alpha reservoir for scraps of protocol claimed fees. #[pallet::storage] pub type ScrapReservoirAlpha = StorageMap<_, Twox64Concat, NetUid, AlphaBalance, ValueQuery>; #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { /// Event emitted when the fee rate has been updated for a subnet FeeRateSet { netuid: NetUid, rate: u16 }, } #[pallet::error] #[derive(PartialEq)] pub enum Error { /// 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 Pallet { #![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(::WeightInfo::set_fee_rate())] pub fn set_fee_rate(origin: OriginFor, netuid: NetUid, rate: u16) -> DispatchResult { ensure_root(origin)?; // Ensure that the subnet exists. ensure!( T::SubnetInfo::exists(netuid.into()), Error::::MechanismDoesNotExist ); ensure!(rate <= T::MaxFeeRate::get(), Error::::FeeRateTooHigh); FeeRate::::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, _netuid: NetUid, _enable: bool, ) -> DispatchResult { Err(Error::::Deprecated.into()) } /// DEPRECATED #[pallet::call_index(1)] #[pallet::weight(Weight::from_parts(15_000_000, 0))] 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))] 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 #[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);