code/pallets/subtensor/src/rpc_info/delegate_info.rs

delegate_info.rs

197 lines · 7,787 bytes · 19a6485969RawGitHub
use super::*;
use frame_support::IterableStorageMap;
use frame_support::pallet_prelude::{Decode, Encode};
use safe_math::*;
use substrate_fixed::types::U64F64;
extern crate alloc;
use alloc::collections::BTreeMap;
use codec::Compact;
use sp_runtime::PerU16;
use subtensor_runtime_common::{AlphaBalance, NetUid};

#[freeze_struct("bfb7d342e9ede512")]
#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)]
pub struct DelegateInfo<AccountId: TypeInfo + Encode + Decode> {
    pub delegate_ss58: AccountId,
    pub take: Compact<PerU16>,
    pub nominators: Vec<(AccountId, Vec<(Compact<NetUid>, Compact<u64>)>)>, // map of nominator_ss58 to netuid and stake amount
    pub owner_ss58: AccountId,
    pub registrations: Vec<Compact<NetUid>>, // Vec of netuid this delegate is registered on
    pub validator_permits: Vec<Compact<NetUid>>, // Vec of netuid this delegate has validator permit on
    pub return_per_1000: Compact<u64>, // Delegators current daily return per 1000 TAO staked minus take fee
    pub total_daily_return: Compact<u64>, // Delegators current daily return
}

impl<T: Config> Pallet<T> {
    fn return_per_1000_tao(
        take: Compact<PerU16>,
        total_stake: U64F64,
        emissions_per_day: U64F64,
    ) -> U64F64 {
        // Get the take as a percentage and subtract it from 1 for remainder.
        let without_take: U64F64 = U64F64::saturating_from_num(1).saturating_sub(
            U64F64::saturating_from_num(take.0.deconstruct()).safe_div(u16::MAX.into()),
        );

        if total_stake > U64F64::saturating_from_num(0) {
            emissions_per_day
                .saturating_mul(without_take)
                // Divide by 1000 TAO for return per 1k
                .safe_div(total_stake.safe_div(U64F64::saturating_from_num(1000.0 * 1e9)))
        } else {
            U64F64::saturating_from_num(0)
        }
    }

    #[cfg(test)]
    pub fn return_per_1000_tao_test(
        take: Compact<PerU16>,
        total_stake: U64F64,
        emissions_per_day: U64F64,
    ) -> U64F64 {
        Self::return_per_1000_tao(take, total_stake, emissions_per_day)
    }

    fn get_delegate_by_existing_account(
        delegate: AccountIdOf<T>,
        skip_nominators: bool,
    ) -> DelegateInfo<T::AccountId> {
        let mut nominators = Vec::<(T::AccountId, Vec<(Compact<NetUid>, Compact<u64>)>)>::new();
        let mut nominator_map =
            BTreeMap::<T::AccountId, Vec<(Compact<NetUid>, Compact<u64>)>>::new();

        if !skip_nominators {
            let mut alpha_share_pools = vec![];
            for netuid in Self::get_all_subnet_netuids() {
                let alpha_share_pool = Self::get_alpha_share_pool(delegate.clone(), netuid);
                alpha_share_pools.push(alpha_share_pool);
            }

            for (nominator, netuid, alpha_stake) in Self::alpha_iter_single_prefix(&delegate) {
                if alpha_stake.is_zero() {
                    continue;
                }

                if let Some(alpha_share_pool) = alpha_share_pools.get(u16::from(netuid) as usize) {
                    let coldkey_stake = alpha_share_pool.get_value_from_shares(alpha_stake);

                    nominator_map
                        .entry(nominator.clone())
                        .or_insert(Vec::new())
                        .push((netuid.into(), coldkey_stake.into()));
                }
            }

            for (nominator, stakes) in nominator_map {
                nominators.push((nominator, stakes));
            }
        }

        let registrations = Self::get_registered_networks_for_hotkey(&delegate.clone());
        let mut validator_permits = Vec::<Compact<NetUid>>::new();
        let mut emissions_per_day: U64F64 = U64F64::saturating_from_num(0);

        for netuid in registrations.iter() {
            if let Ok(uid) = Self::get_uid_for_net_and_hotkey(*netuid, &delegate.clone()) {
                let validator_permit = Self::get_validator_permit_for_uid(*netuid, uid);
                if validator_permit {
                    validator_permits.push((*netuid).into());
                }

                let emission: U64F64 = u64::from(Self::get_emission_for_uid(*netuid, uid)).into();
                let tempo: U64F64 = Self::get_tempo(*netuid).into();
                if tempo > U64F64::saturating_from_num(0) {
                    let epochs_per_day: U64F64 = U64F64::saturating_from_num(7200).safe_div(tempo);
                    emissions_per_day =
                        emissions_per_day.saturating_add(emission.saturating_mul(epochs_per_day));
                }
            }
        }

        let owner = Self::get_owning_coldkey_for_hotkey(&delegate.clone());
        let take: Compact<PerU16> = <Delegates<T>>::get(delegate.clone()).into();

        let total_stake: U64F64 = u64::from(Self::get_stake_for_hotkey_on_subnet(
            &delegate.clone(),
            NetUid::ROOT,
        ))
        .into();

        let return_per_1000: U64F64 =
            Self::return_per_1000_tao(take, total_stake, emissions_per_day);

        DelegateInfo {
            delegate_ss58: delegate.clone(),
            take,
            nominators,
            owner_ss58: owner.clone(),
            registrations: registrations.iter().map(|x| x.into()).collect(),
            validator_permits,
            return_per_1000: return_per_1000.saturating_to_num::<u64>().into(),
            total_daily_return: emissions_per_day.saturating_to_num::<u64>().into(),
        }
    }

    pub fn get_delegate(delegate: T::AccountId) -> Option<DelegateInfo<T::AccountId>> {
        // Check delegate exists
        if !<Delegates<T>>::contains_key(delegate.clone()) {
            return None;
        }

        let delegate_info = Self::get_delegate_by_existing_account(delegate.clone(), false);
        Some(delegate_info)
    }

    /// get all delegates info from storage
    ///
    pub fn get_delegates() -> Vec<DelegateInfo<T::AccountId>> {
        let mut delegates = Vec::<DelegateInfo<T::AccountId>>::new();
        for delegate in <Delegates<T> as IterableStorageMap<T::AccountId, PerU16>>::iter_keys() {
            let delegate_info = Self::get_delegate_by_existing_account(delegate.clone(), false);
            delegates.push(delegate_info);
        }

        delegates
    }

    /// get all delegate info and staked token amount for a given delegatee account
    ///
    pub fn get_delegated(
        delegatee: T::AccountId,
    ) -> Vec<(
        DelegateInfo<T::AccountId>,
        (Compact<NetUid>, Compact<AlphaBalance>),
    )> {
        let mut delegates: Vec<(
            DelegateInfo<T::AccountId>,
            (Compact<NetUid>, Compact<AlphaBalance>),
        )> = Vec::new();
        for delegate in <Delegates<T> as IterableStorageMap<T::AccountId, PerU16>>::iter_keys() {
            // Staked to this delegate, so add to list
            for (netuid, _) in Self::alpha_iter_prefix((&delegate, &delegatee)) {
                let delegate_info = Self::get_delegate_by_existing_account(delegate.clone(), true);
                delegates.push((
                    delegate_info,
                    (
                        netuid.into(),
                        Self::get_stake_for_hotkey_and_coldkey_on_subnet(
                            &delegate, &delegatee, netuid,
                        )
                        .into(),
                    ),
                ));
            }
        }

        delegates
    }

    // Helper function to get the coldkey associated with a hotkey
    pub fn get_coldkey_for_hotkey(hotkey: &T::AccountId) -> T::AccountId {
        Owner::<T>::get(hotkey)
    }

    pub fn maybe_coldkey_for_hotkey(hotkey: &T::AccountId) -> Option<T::AccountId> {
        Owner::<T>::try_get(hotkey).ok()
    }
}