code/pallets/subtensor/src/subnets/tao_usd_price.rs

tao_usd_price.rs

55 lines · 2,253 bytes · d967d13588RawGitHub
//! The TAO/USD price that values gamma.
//!
//! One root-set oracle feeds the price. The accepted price falls to the feed at once and
//! rises toward it by at most [`crate::TAO_USD_PRICE_MAX_STEP`] per block.

use super::*;

impl<T: Config> Pallet<T> {
    /// The accepted price at `now`: `state.accepted` raised by `TAO_USD_PRICE_MAX_STEP` of
    /// itself, rounded up, per block since `state.fed_at`, up to the feed.
    pub(crate) fn tao_usd_price_at(state: &TaoUsdPriceState, now: u64) -> u64 {
        let blocks = now.saturating_sub(state.fed_at);
        let step = crate::TAO_USD_PRICE_MAX_STEP.mul_ceil(state.accepted);
        state
            .accepted
            .saturating_add(step.saturating_mul(blocks))
            .min(state.feed)
    }

    /// The TAO/USD price a mint uses at `now`, given a feed at most
    /// `TAO_USD_PRICE_MAX_AGE` old.
    pub fn mint_tao_usd_price(now: u64) -> Result<u64, Error<T>> {
        TaoUsdPrice::<T>::get()
            .filter(|state| now.saturating_sub(state.fed_at) <= crate::TAO_USD_PRICE_MAX_AGE)
            .map(|state| Self::tao_usd_price_at(&state, now))
            .ok_or(Error::<T>::TaoUsdPriceUnavailable)
    }

    /// Stores the oracle's feed, at most one per block. The accepted price settles at the
    /// current block, never above the feed; the first feed is accepted as it is.
    pub fn do_feed_tao_usd_price(origin: OriginFor<T>, price: u64) -> DispatchResult {
        let who = ensure_signed(origin)?;
        ensure!(
            TaoUsdPriceOracle::<T>::get().as_ref() == Some(&who),
            DispatchError::BadOrigin
        );
        ensure!(price != 0, Error::<T>::InvalidValue);
        let now = Self::get_current_block_as_u64();
        let state = TaoUsdPrice::<T>::get();
        ensure!(
            state.as_ref().is_none_or(|state| state.fed_at < now),
            Error::<T>::TxRateLimitExceeded
        );
        let accepted = state
            .map_or(price, |state| Self::tao_usd_price_at(&state, now))
            .min(price);
        TaoUsdPrice::<T>::put(TaoUsdPriceState {
            accepted,
            feed: price,
            fed_at: now,
        });
        Self::deposit_event(Event::TaoUsdPriceFed { price });
        Ok(())
    }
}