code/precompiles/src/alpha.rs

alpha.rs

1,012 lines · 40,169 bytes · 71136ad109RawGitHub
use core::marker::PhantomData;

use crate::PrecompileExt;
use fp_evm::{ExitError, PrecompileFailure};
use frame_support::{
    BoundedVec,
    dispatch::{DispatchInfo, GetDispatchInfo, PostDispatchInfo},
    traits::{ConstU32, IsSubType},
};
use frame_system::RawOrigin;
use pallet_evm::{AddressMapping, BalanceConverter, PrecompileHandle, SubstrateBalance};
use precompile_utils::{EvmResult, prelude::BoundedBytes};
use sp_runtime::{
    SaturatedConversion, Vec,
    traits::{AsSystemOriginSigner, Dispatchable},
};

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
        + pallet_admin_utils::Config
        + pallet_balances::Config
        + pallet_shield::Config
        + pallet_subtensor_proxy::Config
        + Send
        + Sync
        + scale_info::TypeInfo,
    R::AccountId: From<[u8; 32]>,
    <R as frame_system::Config>::RuntimeOrigin: AsSystemOriginSigner<R::AccountId> + Clone,
    <R as frame_system::Config>::RuntimeCall: From<pallet_admin_utils::Call<R>>
        + GetDispatchInfo
        + Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>
        + IsSubType<pallet_balances::Call<R>>
        + IsSubType<pallet_subtensor::Call<R>>
        + IsSubType<pallet_shield::Call<R>>
        + IsSubType<pallet_subtensor_proxy::Call<R>>,
    <R as pallet_evm::Config>::AddressMapping: AddressMapping<R::AccountId>,
{
    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
        + pallet_admin_utils::Config
        + pallet_balances::Config
        + pallet_shield::Config
        + pallet_subtensor_proxy::Config
        + Send
        + Sync
        + scale_info::TypeInfo,
    R::AccountId: From<[u8; 32]>,
    <R as frame_system::Config>::RuntimeOrigin: AsSystemOriginSigner<R::AccountId> + Clone,
    <R as frame_system::Config>::RuntimeCall: From<pallet_admin_utils::Call<R>>
        + GetDispatchInfo
        + Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>
        + IsSubType<pallet_balances::Call<R>>
        + IsSubType<pallet_subtensor::Call<R>>
        + IsSubType<pallet_shield::Call<R>>
        + IsSubType<pallet_subtensor_proxy::Call<R>>,
    <R as pallet_evm::Config>::AddressMapping: AddressMapping<R::AccountId>,
{
    #[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)
    }

    #[precompile::public("setRecycleOrBurn(uint16,uint8)")]
    fn set_recycle_or_burn(
        handle: &mut impl PrecompileHandle,
        netuid: u16,
        mode: u8,
    ) -> EvmResult<()> {
        let recycle_or_burn = match mode {
            0 => pallet_subtensor::RecycleOrBurnEnum::Burn,
            1 => pallet_subtensor::RecycleOrBurnEnum::Recycle,
            _ => {
                return Err(PrecompileFailure::Error {
                    exit_status: ExitError::Other("invalid recycle-or-burn mode".into()),
                });
            }
        };
        let caller = handle.caller_account_id::<R>();
        let call = pallet_admin_utils::Call::<R>::sudo_set_recycle_or_burn {
            netuid: NetUid::from(netuid),
            recycle_or_burn,
        };
        handle.try_dispatch_runtime_call::<R, _>(call, RawOrigin::Signed(caller))
    }

    #[precompile::public("setBurnHalfLife(uint16,uint16)")]
    fn set_burn_half_life(
        handle: &mut impl PrecompileHandle,
        netuid: u16,
        burn_half_life: u16,
    ) -> EvmResult<()> {
        let caller = handle.caller_account_id::<R>();
        let call = pallet_admin_utils::Call::<R>::sudo_set_burn_half_life {
            netuid: NetUid::from(netuid),
            burn_half_life,
        };
        handle.try_dispatch_runtime_call::<R, _>(call, RawOrigin::Signed(caller))
    }

    #[precompile::public("setBurnIncreaseMultiplier(uint16,uint128)")]
    fn set_burn_increase_multiplier(
        handle: &mut impl PrecompileHandle,
        netuid: u16,
        raw_multiplier: u128,
    ) -> EvmResult<()> {
        let caller = handle.caller_account_id::<R>();
        let call = pallet_admin_utils::Call::<R>::sudo_set_burn_increase_mult {
            netuid: NetUid::from(netuid),
            burn_increase_mult: U64F64::from_bits(raw_multiplier),
        };
        handle.try_dispatch_runtime_call::<R, _>(call, RawOrigin::Signed(caller))
    }

    #[precompile::public("getEmissionAccounting(uint16,bytes32)")]
    #[precompile::view]
    fn get_emission_accounting(
        handle: &mut impl PrecompileHandle,
        netuid: u16,
        hotkey: sp_core::H256,
    ) -> EvmResult<(u64, u64, u64, u64, u64, u64, u64, u128, u64)> {
        handle.record_db_reads::<R>(9)?;
        let netuid = NetUid::from(netuid);
        let hotkey = R::AccountId::from(hotkey.0);
        Ok((
            pallet_subtensor::AlphaDividendsPerSubnet::<R>::get(netuid, &hotkey).to_u64(),
            pallet_subtensor::RootAlphaDividendsPerSubnet::<R>::get(netuid, &hotkey).to_u64(),
            pallet_subtensor::LastHotkeyEmissionOnNetuid::<R>::get(&hotkey, netuid).to_u64(),
            pallet_subtensor::PendingServerEmission::<R>::get(netuid).to_u64(),
            pallet_subtensor::PendingValidatorEmission::<R>::get(netuid).to_u64(),
            pallet_subtensor::PendingRootAlphaDivs::<R>::get(netuid).to_u64(),
            pallet_subtensor::PendingOwnerCut::<R>::get(netuid).to_u64(),
            pallet_subtensor::MinerBurned::<R>::get(netuid).to_bits(),
            pallet_subtensor::RAORecycledForRegistration::<R>::get(netuid).to_u64(),
        ))
    }

    #[precompile::public("getSubnetEconomicState(uint16)")]
    #[precompile::view]
    fn get_subnet_economic_state(
        handle: &mut impl PrecompileHandle,
        netuid: u16,
    ) -> EvmResult<(bool, u128, u64, u64, u64)> {
        handle.record_db_reads::<R>(5)?;
        let netuid = NetUid::from(netuid);
        Ok((
            pallet_subtensor::SubnetEmissionEnabled::<R>::get(netuid),
            pallet_subtensor::RootProp::<R>::get(netuid).to_bits(),
            pallet_subtensor::SubnetExcessTao::<R>::get(netuid).to_u64(),
            pallet_subtensor::SubnetRootSellTao::<R>::get(netuid).to_u64(),
            pallet_subtensor::SubnetProtocolAlpha::<R>::get(netuid).to_u64(),
        ))
    }

    #[precompile::public("getSubnetFlowState(uint16)")]
    #[precompile::view]
    fn get_subnet_flow_state(
        handle: &mut impl PrecompileHandle,
        netuid: u16,
    ) -> EvmResult<(U256, bool, u64, U256, U256, bool, u64, U256)> {
        handle.record_db_reads::<R>(4)?;
        let netuid = NetUid::from(netuid);
        let tao_ema = pallet_subtensor::SubnetEmaTaoFlow::<R>::get(netuid);
        let protocol_ema = pallet_subtensor::SubnetEmaProtocolFlow::<R>::get(netuid);
        Ok((
            signed_i64_word(pallet_subtensor::SubnetTaoFlow::<R>::get(netuid)),
            tao_ema.is_some(),
            tao_ema.map(|(block, _)| block).unwrap_or(0),
            signed_i128_word(tao_ema.map(|(_, value)| value.to_bits()).unwrap_or(0)),
            signed_i64_word(pallet_subtensor::SubnetProtocolFlow::<R>::get(netuid)),
            protocol_ema.is_some(),
            protocol_ema.map(|(block, _)| block).unwrap_or(0),
            signed_i128_word(protocol_ema.map(|(_, value)| value.to_bits()).unwrap_or(0)),
        ))
    }

    #[precompile::public("getEmissionGateConfig()")]
    #[precompile::view]
    fn get_emission_gate_config(
        handle: &mut impl PrecompileHandle,
    ) -> EvmResult<(u64, U256, bool, U256, u128, u128, u128, u128, u64)> {
        handle.record_db_reads::<R>(9)?;
        let block_emission = pallet_subtensor::Pallet::<R>::calculate_block_emission()
            .map_err(|error| PrecompileFailure::Error {
                exit_status: ExitError::Other(error.into()),
            })?
            .to_u64();
        Ok((
            block_emission,
            signed_i128_word(pallet_subtensor::SubnetMovingAlpha::<R>::get().to_bits()),
            pallet_subtensor::NetTaoFlowEnabled::<R>::get(),
            signed_i128_word(pallet_subtensor::TaoFlowCutoff::<R>::get().to_bits()),
            pallet_subtensor::FlowNormExponent::<R>::get().to_bits(),
            pallet_subtensor::EmissionBarQuantile::<R>::get().to_bits(),
            pallet_subtensor::EmissionGateExponent::<R>::get().to_bits(),
            pallet_subtensor::EmissionGateBar::<R>::get().to_bits(),
            pallet_subtensor::FlowEmaSmoothingFactor::<R>::get(),
        ))
    }

    #[precompile::public("getSwapState(uint16)")]
    #[precompile::view]
    fn get_swap_state(
        handle: &mut impl PrecompileHandle,
        netuid: u16,
    ) -> EvmResult<(u16, bool, u64, u64, u64)> {
        handle.record_db_reads::<R>(5)?;
        let netuid = NetUid::from(netuid);
        Ok((
            pallet_subtensor_swap::FeeRate::<R>::get(netuid),
            pallet_subtensor_swap::PalSwapInitialized::<R>::get(netuid),
            pallet_subtensor_swap::SwapBalancer::<R>::get(netuid)
                .get_quote_weight()
                .deconstruct(),
            pallet_subtensor_swap::BalancerTaoReservoir::<R>::get(netuid).to_u64(),
            pallet_subtensor_swap::BalancerAlphaReservoir::<R>::get(netuid).to_u64(),
        ))
    }

    #[precompile::public("hasSwapMigrationRun(bytes)")]
    #[precompile::view]
    fn has_swap_migration_run(
        handle: &mut impl PrecompileHandle,
        migration_name: BoundedBytes<ConstU32<128>>,
    ) -> EvmResult<bool> {
        handle.record_db_reads::<R>(1)?;
        let migration_name = BoundedVec::<u8, ConstU32<128>>::truncate_from(migration_name.into());
        Ok(pallet_subtensor_swap::HasMigrationRun::<R>::get(
            migration_name,
        ))
    }
}

fn signed_i64_word(value: i64) -> U256 {
    let mut encoded = [if value.is_negative() { 0xff } else { 0 }; 32];
    encoded[24..].copy_from_slice(&value.to_be_bytes());
    U256::from_big_endian(&encoded)
}

fn signed_i128_word(value: i128) -> U256 {
    let mut encoded = [if value.is_negative() { 0xff } else { 0 }; 32];
    encoded[16..].copy_from_slice(&value.to_be_bytes());
    U256::from_big_endian(&encoded)
}

#[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(),
            );
        });
    }

    #[test]
    fn alpha_state_views_return_typed_runtime_state() {
        new_test_ext().execute_with(|| {
            let precompiles = precompiles::<AlphaPrecompile<Runtime>>();
            let caller = addr_from_index(1);
            let address = addr_from_index(AlphaPrecompile::<Runtime>::INDEX);
            let netuid = NetUid::from(DYNAMIC_NETUID_U16);
            let hotkey = sp_core::H256::repeat_byte(0x41);

            assert_view(
                &precompiles,
                caller,
                address,
                "getEmissionAccounting(uint16,bytes32)",
                (DYNAMIC_NETUID_U16, hotkey),
                (
                    0_u64, 0_u64, 0_u64, 0_u64, 0_u64, 0_u64, 0_u64, 0_u128, 0_u64,
                ),
            );

            assert_view(
                &precompiles,
                caller,
                address,
                "getSubnetEconomicState(uint16)",
                (DYNAMIC_NETUID_U16,),
                (
                    pallet_subtensor::SubnetEmissionEnabled::<Runtime>::get(netuid),
                    pallet_subtensor::RootProp::<Runtime>::get(netuid).to_bits(),
                    pallet_subtensor::SubnetExcessTao::<Runtime>::get(netuid).to_u64(),
                    pallet_subtensor::SubnetRootSellTao::<Runtime>::get(netuid).to_u64(),
                    pallet_subtensor::SubnetProtocolAlpha::<Runtime>::get(netuid).to_u64(),
                ),
            );

            let tao_ema = pallet_subtensor::SubnetEmaTaoFlow::<Runtime>::get(netuid);
            let protocol_ema = pallet_subtensor::SubnetEmaProtocolFlow::<Runtime>::get(netuid);
            assert_view(
                &precompiles,
                caller,
                address,
                "getSubnetFlowState(uint16)",
                (DYNAMIC_NETUID_U16,),
                (
                    signed_i64_word(pallet_subtensor::SubnetTaoFlow::<Runtime>::get(netuid)),
                    tao_ema.is_some(),
                    tao_ema.map(|(block, _)| block).unwrap_or(0),
                    signed_i128_word(tao_ema.map(|(_, value)| value.to_bits()).unwrap_or(0)),
                    signed_i64_word(pallet_subtensor::SubnetProtocolFlow::<Runtime>::get(netuid)),
                    protocol_ema.is_some(),
                    protocol_ema.map(|(block, _)| block).unwrap_or(0),
                    signed_i128_word(protocol_ema.map(|(_, value)| value.to_bits()).unwrap_or(0)),
                ),
            );

            let emission_gate = (
                pallet_subtensor::Pallet::<Runtime>::calculate_block_emission()
                    .expect("block emission calculation should succeed")
                    .to_u64(),
                signed_i128_word(pallet_subtensor::SubnetMovingAlpha::<Runtime>::get().to_bits()),
                pallet_subtensor::NetTaoFlowEnabled::<Runtime>::get(),
                signed_i128_word(pallet_subtensor::TaoFlowCutoff::<Runtime>::get().to_bits()),
                pallet_subtensor::FlowNormExponent::<Runtime>::get().to_bits(),
                pallet_subtensor::EmissionBarQuantile::<Runtime>::get().to_bits(),
                pallet_subtensor::EmissionGateExponent::<Runtime>::get().to_bits(),
                pallet_subtensor::EmissionGateBar::<Runtime>::get().to_bits(),
                pallet_subtensor::FlowEmaSmoothingFactor::<Runtime>::get(),
            );
            assert_view(
                &precompiles,
                caller,
                address,
                "getEmissionGateConfig()",
                (),
                emission_gate,
            );

            let balancer = pallet_subtensor_swap::SwapBalancer::<Runtime>::get(netuid);
            assert_view(
                &precompiles,
                caller,
                address,
                "getSwapState(uint16)",
                (DYNAMIC_NETUID_U16,),
                (
                    pallet_subtensor_swap::FeeRate::<Runtime>::get(netuid),
                    pallet_subtensor_swap::PalSwapInitialized::<Runtime>::get(netuid),
                    balancer.get_quote_weight().deconstruct(),
                    pallet_subtensor_swap::BalancerTaoReservoir::<Runtime>::get(netuid).to_u64(),
                    pallet_subtensor_swap::BalancerAlphaReservoir::<Runtime>::get(netuid).to_u64(),
                ),
            );

            let migration_name = b"reader-test".to_vec();
            pallet_subtensor_swap::HasMigrationRun::<Runtime>::insert(
                BoundedVec::truncate_from(migration_name.clone()),
                true,
            );
            assert_view(
                &precompiles,
                caller,
                address,
                "hasSwapMigrationRun(bytes)",
                (BoundedBytes::<ConstU32<128>>::from(migration_name),),
                true,
            );
        });
    }

    #[test]
    fn emission_gate_block_emission_matches_runtime_at_halving_boundary() {
        new_test_ext().execute_with(|| {
            const FIRST_HALVING_ISSUANCE: u64 = 10_500_000_000_000_000;

            pallet_subtensor::TotalIssuance::<Runtime>::put(TaoBalance::from(
                FIRST_HALVING_ISSUANCE,
            ));
            #[allow(deprecated)]
            pallet_subtensor::BlockEmission::<Runtime>::put(123_u64);

            let expected_block_emission =
                pallet_subtensor::Pallet::<Runtime>::calculate_block_emission()
                    .expect("halving-boundary emission calculation should succeed")
                    .to_u64();
            assert_eq!(expected_block_emission, 500_000_000);

            let precompiles = precompiles::<AlphaPrecompile<Runtime>>();
            assert_view(
                &precompiles,
                addr_from_index(1),
                addr_from_index(AlphaPrecompile::<Runtime>::INDEX),
                "getEmissionGateConfig()",
                (),
                (
                    expected_block_emission,
                    signed_i128_word(
                        pallet_subtensor::SubnetMovingAlpha::<Runtime>::get().to_bits(),
                    ),
                    pallet_subtensor::NetTaoFlowEnabled::<Runtime>::get(),
                    signed_i128_word(pallet_subtensor::TaoFlowCutoff::<Runtime>::get().to_bits()),
                    pallet_subtensor::FlowNormExponent::<Runtime>::get().to_bits(),
                    pallet_subtensor::EmissionBarQuantile::<Runtime>::get().to_bits(),
                    pallet_subtensor::EmissionGateExponent::<Runtime>::get().to_bits(),
                    pallet_subtensor::EmissionGateBar::<Runtime>::get().to_bits(),
                    pallet_subtensor::FlowEmaSmoothingFactor::<Runtime>::get(),
                ),
            );
        });
    }

    fn assert_view<Args, Output>(
        precompiles: &impl pallet_evm::PrecompileSet,
        caller: sp_core::H160,
        address: sp_core::H160,
        signature: &str,
        args: Args,
        expected: Output,
    ) where
        Args: precompile_utils::solidity::Codec,
        Output: precompile_utils::solidity::Codec,
    {
        use precompile_utils::testing::PrecompileTesterExt;

        precompiles
            .prepare_test(
                caller,
                address,
                encode_with_selector(selector_u32(signature), args),
            )
            .with_static_call(true)
            .execute_returns(expected);
    }
}