code/precompiles/src/alpha.rs

alpha.rs

616 lines · 23,218 bytes · 19a6485969RawGitHub
use core::marker::PhantomData;

use crate::PrecompileExt;
use fp_evm::{ExitError, PrecompileFailure};
use pallet_evm::{BalanceConverter, PrecompileHandle, SubstrateBalance};
use precompile_utils::EvmResult;
use sp_runtime::{SaturatedConversion, Vec};

use crate::PrecompileHandleExt;
use sp_core::U256;
use substrate_fixed::types::U64F64;
use subtensor_runtime_common::{NetUid, Token};
use subtensor_swap_interface::{Order, SwapHandler};
pub struct AlphaPrecompile<R>(PhantomData<R>);

impl<R> PrecompileExt<R::AccountId> for AlphaPrecompile<R>
where
    R: frame_system::Config
        + pallet_subtensor::Config
        + pallet_subtensor_swap::Config
        + pallet_evm::Config,
    R::AccountId: From<[u8; 32]>,
{
    const INDEX: u64 = 2056;
}

#[precompile_utils::precompile]
impl<R> AlphaPrecompile<R>
where
    R: frame_system::Config
        + pallet_subtensor::Config
        + pallet_subtensor_swap::Config
        + pallet_evm::Config,
{
    #[precompile::public("getAlphaPrice(uint16)")]
    #[precompile::view]
    fn get_alpha_price(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<U256> {
        // SubnetMechanism + SubnetAlphaIn + SubnetTAO + SwapBalancer reads
        handle.record_db_reads::<R>(4)?;
        let current_alpha_price =
            <pallet_subtensor_swap::Pallet<R> as SwapHandler>::current_alpha_price(netuid.into());
        let price = current_alpha_price.saturating_mul(U64F64::from_num(1_000_000_000));
        let price: SubstrateBalance = price.saturating_to_num::<u64>().into();
        let price_eth = <R as pallet_evm::Config>::BalanceConverter::into_evm_balance(price)
            .map(|amount| amount.into_u256())
            .ok_or(ExitError::InvalidRange)?;

        Ok(price_eth)
    }

    #[precompile::public("getMovingAlphaPrice(uint16)")]
    #[precompile::view]
    fn get_moving_alpha_price(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<U256> {
        // SubnetMechanism + SubnetMovingPrice reads
        handle.record_db_reads::<R>(2)?;
        let moving_alpha_price: U64F64 =
            pallet_subtensor::Pallet::<R>::get_moving_alpha_price(netuid.into());
        let price = moving_alpha_price.saturating_mul(U64F64::from_num(1_000_000_000));
        let price: SubstrateBalance = price.saturating_to_num::<u64>().into();
        let price_eth = <R as pallet_evm::Config>::BalanceConverter::into_evm_balance(price)
            .map(|amount| amount.into_u256())
            .ok_or(ExitError::InvalidRange)?;

        Ok(price_eth)
    }

    #[precompile::public("getTaoInPool(uint16)")]
    #[precompile::view]
    fn get_tao_in_pool(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<u64> {
        handle.record_db_reads::<R>(1)?;
        Ok(pallet_subtensor::SubnetTAO::<R>::get(NetUid::from(netuid)).to_u64())
    }

    #[precompile::public("getAlphaInPool(uint16)")]
    #[precompile::view]
    fn get_alpha_in_pool(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<u64> {
        handle.record_db_reads::<R>(1)?;
        Ok(pallet_subtensor::SubnetAlphaIn::<R>::get(NetUid::from(netuid)).into())
    }

    #[precompile::public("getAlphaOutPool(uint16)")]
    #[precompile::view]
    fn get_alpha_out_pool(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<u64> {
        handle.record_db_reads::<R>(1)?;
        Ok(pallet_subtensor::SubnetAlphaOut::<R>::get(NetUid::from(netuid)).into())
    }

    #[precompile::public("getAlphaIssuance(uint16)")]
    #[precompile::view]
    fn get_alpha_issuance(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<u64> {
        // SubnetAlphaIn + SubnetAlphaOut reads
        handle.record_db_reads::<R>(2)?;
        Ok(pallet_subtensor::Pallet::<R>::get_alpha_issuance(netuid.into()).into())
    }

    #[precompile::public("getTaoWeight()")]
    #[precompile::view]
    fn get_tao_weight(handle: &mut impl PrecompileHandle) -> EvmResult<U256> {
        handle.record_db_reads::<R>(1)?;
        let tao_weight = pallet_subtensor::TaoWeight::<R>::get();
        Ok(U256::from(tao_weight))
    }

    #[precompile::public("getCKBurn()")]
    #[precompile::view]
    fn get_ck_burn(handle: &mut impl PrecompileHandle) -> EvmResult<U256> {
        handle.record_db_reads::<R>(1)?;
        let ck_burn = pallet_subtensor::CKBurn::<R>::get();
        Ok(U256::from(ck_burn))
    }

    #[precompile::public("simSwapTaoForAlpha(uint16,uint64)")]
    #[precompile::view]
    fn sim_swap_tao_for_alpha(
        handle: &mut impl PrecompileHandle,
        netuid: u16,
        tao: u64,
    ) -> EvmResult<U256> {
        // SubnetMechanism + swap simulation reads
        handle.record_db_reads::<R>(9)?;

        let order = pallet_subtensor::GetAlphaForTao::<R>::with_amount(tao);
        let swap_result =
            <pallet_subtensor_swap::Pallet<R> as SwapHandler>::sim_swap(netuid.into(), order)
                .map_err(|e| PrecompileFailure::Error {
                    exit_status: ExitError::Other(Into::<&'static str>::into(e).into()),
                })?;
        Ok(U256::from(swap_result.amount_paid_out.to_u64()))
    }

    #[precompile::public("simSwapAlphaForTao(uint16,uint64)")]
    #[precompile::view]
    fn sim_swap_alpha_for_tao(
        handle: &mut impl PrecompileHandle,
        netuid: u16,
        alpha: u64,
    ) -> EvmResult<U256> {
        // SubnetMechanism + swap simulation reads
        handle.record_db_reads::<R>(9)?;

        let order = pallet_subtensor::GetTaoForAlpha::<R>::with_amount(alpha);
        let swap_result =
            <pallet_subtensor_swap::Pallet<R> as SwapHandler>::sim_swap(netuid.into(), order)
                .map_err(|e| PrecompileFailure::Error {
                    exit_status: ExitError::Other(Into::<&'static str>::into(e).into()),
                })?;
        Ok(U256::from(swap_result.amount_paid_out.to_u64()))
    }

    #[precompile::public("getSubnetMechanism(uint16)")]
    #[precompile::view]
    fn get_subnet_mechanism(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<u16> {
        handle.record_db_reads::<R>(1)?;
        Ok(pallet_subtensor::SubnetMechanism::<R>::get(NetUid::from(
            netuid,
        )))
    }

    #[precompile::public("getRootNetuid()")]
    #[precompile::view]
    fn get_root_netuid(_handle: &mut impl PrecompileHandle) -> EvmResult<u16> {
        Ok(NetUid::ROOT.into())
    }

    #[precompile::public("getEMAPriceHalvingBlocks(uint16)")]
    #[precompile::view]
    fn get_ema_price_halving_blocks(
        handle: &mut impl PrecompileHandle,
        netuid: u16,
    ) -> EvmResult<u64> {
        handle.record_db_reads::<R>(1)?;
        Ok(pallet_subtensor::EMAPriceHalvingBlocks::<R>::get(
            NetUid::from(netuid),
        ))
    }

    #[precompile::public("getSubnetVolume(uint16)")]
    #[precompile::view]
    fn get_subnet_volume(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<U256> {
        handle.record_db_reads::<R>(1)?;
        Ok(U256::from(pallet_subtensor::SubnetVolume::<R>::get(
            NetUid::from(netuid),
        )))
    }

    #[precompile::public("getTaoInEmission(uint16)")]
    #[precompile::view]
    fn get_tao_in_emission(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<U256> {
        handle.record_db_reads::<R>(1)?;
        Ok(U256::from(
            pallet_subtensor::SubnetTaoInEmission::<R>::get(NetUid::from(netuid)).to_u64(),
        ))
    }

    #[precompile::public("getAlphaInEmission(uint16)")]
    #[precompile::view]
    fn get_alpha_in_emission(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<U256> {
        handle.record_db_reads::<R>(1)?;
        Ok(U256::from(
            pallet_subtensor::SubnetAlphaInEmission::<R>::get(NetUid::from(netuid)).to_u64(),
        ))
    }

    #[precompile::public("getAlphaOutEmission(uint16)")]
    #[precompile::view]
    fn get_alpha_out_emission(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<U256> {
        handle.record_db_reads::<R>(1)?;
        Ok(U256::from(
            pallet_subtensor::SubnetAlphaOutEmission::<R>::get(NetUid::from(netuid)).to_u64(),
        ))
    }

    #[precompile::public("getSumAlphaPrice()")]
    #[precompile::view]
    fn get_sum_alpha_price(handle: &mut impl PrecompileHandle) -> EvmResult<U256> {
        // NetworksAdded iteration + current_alpha_price reads
        handle.record_db_reads::<R>(1)?;
        let subnet_limit = pallet_subtensor::SubnetLimit::<R>::get().saturated_into::<u64>();

        handle.record_db_reads::<R>(subnet_limit)?;

        let mut sum_alpha_price: U64F64 = U64F64::from_num(0);
        let netuids = pallet_subtensor::NetworksAdded::<R>::iter()
            .filter(|(netuid, _)| *netuid != NetUid::ROOT)
            .map(|(netuid, _)| netuid)
            .collect::<Vec<_>>();

        // NetworksAdded entry + current_alpha_price reads
        handle.record_db_reads::<R>(
            netuids
                .len()
                .saturated_into::<u64>()
                .saturating_mul(5)
                .saturating_sub(subnet_limit),
        )?;

        for netuid in netuids.iter() {
            let price =
                <pallet_subtensor_swap::Pallet<R> as SwapHandler>::current_alpha_price(*netuid);

            if price < U64F64::from_num(1) {
                sum_alpha_price = sum_alpha_price.saturating_add(price);
            }
        }

        let price = sum_alpha_price.saturating_mul(U64F64::from_num(1_000_000_000));
        let price: SubstrateBalance = price.saturating_to_num::<u64>().into();
        let price_eth = <R as pallet_evm::Config>::BalanceConverter::into_evm_balance(price)
            .map(|amount| amount.into_u256())
            .ok_or(ExitError::InvalidRange)?;

        Ok(price_eth)
    }
}

#[cfg(test)]
mod tests {
    #![allow(clippy::expect_used)]

    use super::*;
    use crate::PrecompileExt;
    use crate::mock::{
        Runtime, addr_from_index, alpha_price_to_evm, assert_static_call, new_test_ext,
        precompiles, selector_u32,
    };
    use precompile_utils::solidity::encode_with_selector;
    use substrate_fixed::types::I96F32;
    use subtensor_runtime_common::{AlphaBalance, TaoBalance};

    const DYNAMIC_NETUID_U16: u16 = 1;
    const SUM_PRICE_NETUID_U16: u16 = 2;
    const TAO_WEIGHT: u64 = 444;
    const CK_BURN: u64 = 555;
    const EMA_HALVING_BLOCKS: u64 = 777;
    const SUBNET_VOLUME: u128 = 888;
    const TAO_IN_EMISSION: u64 = 111;
    const ALPHA_IN_EMISSION: u64 = 222;
    const ALPHA_OUT_EMISSION: u64 = 333;

    fn seed_alpha_test_state() {
        let dynamic_netuid = NetUid::from(DYNAMIC_NETUID_U16);
        let sum_price_netuid = NetUid::from(SUM_PRICE_NETUID_U16);

        pallet_subtensor::TaoWeight::<Runtime>::put(TAO_WEIGHT);
        pallet_subtensor::CKBurn::<Runtime>::put(CK_BURN);

        pallet_subtensor::NetworksAdded::<Runtime>::insert(dynamic_netuid, true);
        pallet_subtensor::SubnetMechanism::<Runtime>::insert(dynamic_netuid, 1);
        pallet_subtensor::SubnetTAO::<Runtime>::insert(
            dynamic_netuid,
            TaoBalance::from(20_000_000_000_u64),
        );
        pallet_subtensor::SubnetAlphaIn::<Runtime>::insert(
            dynamic_netuid,
            AlphaBalance::from(10_000_000_000_u64),
        );
        pallet_subtensor::SubnetAlphaOut::<Runtime>::insert(
            dynamic_netuid,
            AlphaBalance::from(3_000_000_000_u64),
        );
        pallet_subtensor::SubnetTaoInEmission::<Runtime>::insert(
            dynamic_netuid,
            TaoBalance::from(TAO_IN_EMISSION),
        );
        pallet_subtensor::SubnetAlphaInEmission::<Runtime>::insert(
            dynamic_netuid,
            AlphaBalance::from(ALPHA_IN_EMISSION),
        );
        pallet_subtensor::SubnetAlphaOutEmission::<Runtime>::insert(
            dynamic_netuid,
            AlphaBalance::from(ALPHA_OUT_EMISSION),
        );
        pallet_subtensor::SubnetVolume::<Runtime>::insert(dynamic_netuid, SUBNET_VOLUME);
        pallet_subtensor::EMAPriceHalvingBlocks::<Runtime>::insert(
            dynamic_netuid,
            EMA_HALVING_BLOCKS,
        );
        pallet_subtensor::SubnetMovingPrice::<Runtime>::insert(
            dynamic_netuid,
            I96F32::from_num(3.0 / 2.0),
        );

        pallet_subtensor::NetworksAdded::<Runtime>::insert(sum_price_netuid, true);
        pallet_subtensor::SubnetMechanism::<Runtime>::insert(sum_price_netuid, 1);
        pallet_subtensor::SubnetTAO::<Runtime>::insert(
            sum_price_netuid,
            TaoBalance::from(5_000_000_000_u64),
        );
        pallet_subtensor::SubnetAlphaIn::<Runtime>::insert(
            sum_price_netuid,
            AlphaBalance::from(10_000_000_000_u64),
        );
    }

    #[test]
    fn alpha_precompile_matches_runtime_values_for_dynamic_subnet() {
        new_test_ext().execute_with(|| {
            seed_alpha_test_state();

            let precompiles = precompiles::<AlphaPrecompile<Runtime>>();
            let caller = addr_from_index(1);
            let precompile_addr = addr_from_index(AlphaPrecompile::<Runtime>::INDEX);

            let dynamic_netuid = NetUid::from(DYNAMIC_NETUID_U16);
            let alpha_price =
                <pallet_subtensor_swap::Pallet<Runtime> as SwapHandler>::current_alpha_price(
                    dynamic_netuid,
                );
            let moving_alpha_price =
                pallet_subtensor::Pallet::<Runtime>::get_moving_alpha_price(dynamic_netuid);

            assert!(alpha_price > U64F64::from_num(1));
            assert!(moving_alpha_price > U64F64::from_num(1));

            assert_static_call(
                &precompiles,
                caller,
                precompile_addr,
                encode_with_selector(selector_u32("getAlphaPrice(uint16)"), (DYNAMIC_NETUID_U16,)),
                alpha_price_to_evm(alpha_price),
            );
            assert_static_call(
                &precompiles,
                caller,
                precompile_addr,
                encode_with_selector(
                    selector_u32("getMovingAlphaPrice(uint16)"),
                    (DYNAMIC_NETUID_U16,),
                ),
                alpha_price_to_evm(moving_alpha_price),
            );
            assert_static_call(
                &precompiles,
                caller,
                precompile_addr,
                encode_with_selector(selector_u32("getTaoInPool(uint16)"), (DYNAMIC_NETUID_U16,)),
                pallet_subtensor::SubnetTAO::<Runtime>::get(dynamic_netuid)
                    .to_u64()
                    .into(),
            );
            assert_static_call(
                &precompiles,
                caller,
                precompile_addr,
                encode_with_selector(
                    selector_u32("getAlphaInPool(uint16)"),
                    (DYNAMIC_NETUID_U16,),
                ),
                u64::from(pallet_subtensor::SubnetAlphaIn::<Runtime>::get(
                    dynamic_netuid,
                ))
                .into(),
            );
            assert_static_call(
                &precompiles,
                caller,
                precompile_addr,
                encode_with_selector(
                    selector_u32("getAlphaOutPool(uint16)"),
                    (DYNAMIC_NETUID_U16,),
                ),
                u64::from(pallet_subtensor::SubnetAlphaOut::<Runtime>::get(
                    dynamic_netuid,
                ))
                .into(),
            );
            assert_static_call(
                &precompiles,
                caller,
                precompile_addr,
                encode_with_selector(
                    selector_u32("getAlphaIssuance(uint16)"),
                    (DYNAMIC_NETUID_U16,),
                ),
                u64::from(pallet_subtensor::Pallet::<Runtime>::get_alpha_issuance(
                    dynamic_netuid,
                ))
                .into(),
            );
            assert_static_call(
                &precompiles,
                caller,
                precompile_addr,
                encode_with_selector(
                    selector_u32("getSubnetMechanism(uint16)"),
                    (DYNAMIC_NETUID_U16,),
                ),
                pallet_subtensor::SubnetMechanism::<Runtime>::get(dynamic_netuid).into(),
            );
            assert_static_call(
                &precompiles,
                caller,
                precompile_addr,
                encode_with_selector(
                    selector_u32("getEMAPriceHalvingBlocks(uint16)"),
                    (DYNAMIC_NETUID_U16,),
                ),
                pallet_subtensor::EMAPriceHalvingBlocks::<Runtime>::get(dynamic_netuid).into(),
            );
            assert_static_call(
                &precompiles,
                caller,
                precompile_addr,
                encode_with_selector(
                    selector_u32("getSubnetVolume(uint16)"),
                    (DYNAMIC_NETUID_U16,),
                ),
                pallet_subtensor::SubnetVolume::<Runtime>::get(dynamic_netuid).into(),
            );
            assert_static_call(
                &precompiles,
                caller,
                precompile_addr,
                encode_with_selector(
                    selector_u32("getTaoInEmission(uint16)"),
                    (DYNAMIC_NETUID_U16,),
                ),
                pallet_subtensor::SubnetTaoInEmission::<Runtime>::get(dynamic_netuid)
                    .to_u64()
                    .into(),
            );
            assert_static_call(
                &precompiles,
                caller,
                precompile_addr,
                encode_with_selector(
                    selector_u32("getAlphaInEmission(uint16)"),
                    (DYNAMIC_NETUID_U16,),
                ),
                pallet_subtensor::SubnetAlphaInEmission::<Runtime>::get(dynamic_netuid)
                    .to_u64()
                    .into(),
            );
            assert_static_call(
                &precompiles,
                caller,
                precompile_addr,
                encode_with_selector(
                    selector_u32("getAlphaOutEmission(uint16)"),
                    (DYNAMIC_NETUID_U16,),
                ),
                pallet_subtensor::SubnetAlphaOutEmission::<Runtime>::get(dynamic_netuid)
                    .to_u64()
                    .into(),
            );
        });
    }

    #[test]
    fn alpha_precompile_matches_runtime_global_values() {
        new_test_ext().execute_with(|| {
            seed_alpha_test_state();

            let precompiles = precompiles::<AlphaPrecompile<Runtime>>();
            let caller = addr_from_index(1);
            let precompile_addr = addr_from_index(AlphaPrecompile::<Runtime>::INDEX);

            let mut sum_alpha_price = U64F64::from_num(0);
            for (netuid, _) in pallet_subtensor::NetworksAdded::<Runtime>::iter() {
                if netuid.is_root() {
                    continue;
                }
                let price =
                    <pallet_subtensor_swap::Pallet<Runtime> as SwapHandler>::current_alpha_price(
                        netuid,
                    );
                if price < U64F64::from_num(1) {
                    sum_alpha_price += price;
                }
            }

            assert!(sum_alpha_price > U64F64::from_num(0));

            assert_static_call(
                &precompiles,
                caller,
                precompile_addr,
                selector_u32("getCKBurn()").to_be_bytes().to_vec(),
                pallet_subtensor::CKBurn::<Runtime>::get().into(),
            );
            assert_static_call(
                &precompiles,
                caller,
                precompile_addr,
                selector_u32("getTaoWeight()").to_be_bytes().to_vec(),
                pallet_subtensor::TaoWeight::<Runtime>::get().into(),
            );
            assert_static_call(
                &precompiles,
                caller,
                precompile_addr,
                selector_u32("getRootNetuid()").to_be_bytes().to_vec(),
                u16::from(NetUid::ROOT).into(),
            );
            assert_static_call(
                &precompiles,
                caller,
                precompile_addr,
                selector_u32("getSumAlphaPrice()").to_be_bytes().to_vec(),
                alpha_price_to_evm(sum_alpha_price),
            );
        });
    }

    #[test]
    fn alpha_precompile_matches_runtime_swap_simulations() {
        new_test_ext().execute_with(|| {
            seed_alpha_test_state();

            let precompiles = precompiles::<AlphaPrecompile<Runtime>>();
            let caller = addr_from_index(1);
            let precompile_addr = addr_from_index(AlphaPrecompile::<Runtime>::INDEX);

            let tao_amount = 1_000_000_000_u64;
            let alpha_amount = 1_000_000_000_u64;
            let expected_alpha = <pallet_subtensor_swap::Pallet<Runtime> as SwapHandler>::sim_swap(
                NetUid::from(DYNAMIC_NETUID_U16),
                pallet_subtensor::GetAlphaForTao::<Runtime>::with_amount(tao_amount),
            )
            .expect("tao-for-alpha simulation should succeed")
            .amount_paid_out
            .to_u64();
            let expected_tao = <pallet_subtensor_swap::Pallet<Runtime> as SwapHandler>::sim_swap(
                NetUid::from(DYNAMIC_NETUID_U16),
                pallet_subtensor::GetTaoForAlpha::<Runtime>::with_amount(alpha_amount),
            )
            .expect("alpha-for-tao simulation should succeed")
            .amount_paid_out
            .to_u64();

            assert!(expected_alpha > 0);
            assert!(expected_tao > 0);

            assert_static_call(
                &precompiles,
                caller,
                precompile_addr,
                encode_with_selector(
                    selector_u32("simSwapTaoForAlpha(uint16,uint64)"),
                    (DYNAMIC_NETUID_U16, tao_amount),
                ),
                expected_alpha.into(),
            );
            assert_static_call(
                &precompiles,
                caller,
                precompile_addr,
                encode_with_selector(
                    selector_u32("simSwapAlphaForTao(uint16,uint64)"),
                    (DYNAMIC_NETUID_U16, alpha_amount),
                ),
                expected_tao.into(),
            );
            assert_static_call(
                &precompiles,
                caller,
                precompile_addr,
                encode_with_selector(
                    selector_u32("simSwapTaoForAlpha(uint16,uint64)"),
                    (DYNAMIC_NETUID_U16, 0_u64),
                ),
                U256::zero(),
            );
            assert_static_call(
                &precompiles,
                caller,
                precompile_addr,
                encode_with_selector(
                    selector_u32("simSwapAlphaForTao(uint16,uint64)"),
                    (DYNAMIC_NETUID_U16, 0_u64),
                ),
                U256::zero(),
            );
        });
    }
}