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(PhantomData); impl PrecompileExt for AlphaPrecompile 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]>, ::RuntimeOrigin: AsSystemOriginSigner + Clone, ::RuntimeCall: From> + GetDispatchInfo + Dispatchable + IsSubType> + IsSubType> + IsSubType> + IsSubType>, ::AddressMapping: AddressMapping, { const INDEX: u64 = 2056; } #[precompile_utils::precompile] impl AlphaPrecompile 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]>, ::RuntimeOrigin: AsSystemOriginSigner + Clone, ::RuntimeCall: From> + GetDispatchInfo + Dispatchable + IsSubType> + IsSubType> + IsSubType> + IsSubType>, ::AddressMapping: AddressMapping, { #[precompile::public("getAlphaPrice(uint16)")] #[precompile::view] fn get_alpha_price(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { // SubnetMechanism + SubnetAlphaIn + SubnetTAO + SwapBalancer reads handle.record_db_reads::(4)?; let current_alpha_price = 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::().into(); let price_eth = ::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 { // SubnetMechanism + SubnetMovingPrice reads handle.record_db_reads::(2)?; let moving_alpha_price: U64F64 = pallet_subtensor::Pallet::::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::().into(); let price_eth = ::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 { handle.record_db_reads::(1)?; Ok(pallet_subtensor::SubnetTAO::::get(NetUid::from(netuid)).to_u64()) } #[precompile::public("getAlphaInPool(uint16)")] #[precompile::view] fn get_alpha_in_pool(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { handle.record_db_reads::(1)?; Ok(pallet_subtensor::SubnetAlphaIn::::get(NetUid::from(netuid)).into()) } #[precompile::public("getAlphaOutPool(uint16)")] #[precompile::view] fn get_alpha_out_pool(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { handle.record_db_reads::(1)?; Ok(pallet_subtensor::SubnetAlphaOut::::get(NetUid::from(netuid)).into()) } #[precompile::public("getAlphaIssuance(uint16)")] #[precompile::view] fn get_alpha_issuance(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { // SubnetAlphaIn + SubnetAlphaOut reads handle.record_db_reads::(2)?; Ok(pallet_subtensor::Pallet::::get_alpha_issuance(netuid.into()).into()) } #[precompile::public("getTaoWeight()")] #[precompile::view] fn get_tao_weight(handle: &mut impl PrecompileHandle) -> EvmResult { handle.record_db_reads::(1)?; let tao_weight = pallet_subtensor::TaoWeight::::get(); Ok(U256::from(tao_weight)) } #[precompile::public("getCKBurn()")] #[precompile::view] fn get_ck_burn(handle: &mut impl PrecompileHandle) -> EvmResult { handle.record_db_reads::(1)?; let ck_burn = pallet_subtensor::CKBurn::::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 { // SubnetMechanism + swap simulation reads handle.record_db_reads::(9)?; let order = pallet_subtensor::GetAlphaForTao::::with_amount(tao); let swap_result = 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 { // SubnetMechanism + swap simulation reads handle.record_db_reads::(9)?; let order = pallet_subtensor::GetTaoForAlpha::::with_amount(alpha); let swap_result = 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 { handle.record_db_reads::(1)?; Ok(pallet_subtensor::SubnetMechanism::::get(NetUid::from( netuid, ))) } #[precompile::public("getRootNetuid()")] #[precompile::view] fn get_root_netuid(_handle: &mut impl PrecompileHandle) -> EvmResult { Ok(NetUid::ROOT.into()) } #[precompile::public("getEMAPriceHalvingBlocks(uint16)")] #[precompile::view] fn get_ema_price_halving_blocks( handle: &mut impl PrecompileHandle, netuid: u16, ) -> EvmResult { handle.record_db_reads::(1)?; Ok(pallet_subtensor::EMAPriceHalvingBlocks::::get( NetUid::from(netuid), )) } #[precompile::public("getSubnetVolume(uint16)")] #[precompile::view] fn get_subnet_volume(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { handle.record_db_reads::(1)?; Ok(U256::from(pallet_subtensor::SubnetVolume::::get( NetUid::from(netuid), ))) } #[precompile::public("getTaoInEmission(uint16)")] #[precompile::view] fn get_tao_in_emission(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { handle.record_db_reads::(1)?; Ok(U256::from( pallet_subtensor::SubnetTaoInEmission::::get(NetUid::from(netuid)).to_u64(), )) } #[precompile::public("getAlphaInEmission(uint16)")] #[precompile::view] fn get_alpha_in_emission(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { handle.record_db_reads::(1)?; Ok(U256::from( pallet_subtensor::SubnetAlphaInEmission::::get(NetUid::from(netuid)).to_u64(), )) } #[precompile::public("getAlphaOutEmission(uint16)")] #[precompile::view] fn get_alpha_out_emission(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { handle.record_db_reads::(1)?; Ok(U256::from( pallet_subtensor::SubnetAlphaOutEmission::::get(NetUid::from(netuid)).to_u64(), )) } #[precompile::public("getSumAlphaPrice()")] #[precompile::view] fn get_sum_alpha_price(handle: &mut impl PrecompileHandle) -> EvmResult { // NetworksAdded iteration + current_alpha_price reads handle.record_db_reads::(1)?; let subnet_limit = pallet_subtensor::SubnetLimit::::get().saturated_into::(); handle.record_db_reads::(subnet_limit)?; let mut sum_alpha_price: U64F64 = U64F64::from_num(0); let netuids = pallet_subtensor::NetworksAdded::::iter() .filter(|(netuid, _)| *netuid != NetUid::ROOT) .map(|(netuid, _)| netuid) .collect::>(); // NetworksAdded entry + current_alpha_price reads handle.record_db_reads::( netuids .len() .saturated_into::() .saturating_mul(5) .saturating_sub(subnet_limit), )?; for netuid in netuids.iter() { let price = 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::().into(); let price_eth = ::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::(); let call = pallet_admin_utils::Call::::sudo_set_recycle_or_burn { netuid: NetUid::from(netuid), recycle_or_burn, }; handle.try_dispatch_runtime_call::(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::(); let call = pallet_admin_utils::Call::::sudo_set_burn_half_life { netuid: NetUid::from(netuid), burn_half_life, }; handle.try_dispatch_runtime_call::(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::(); let call = pallet_admin_utils::Call::::sudo_set_burn_increase_mult { netuid: NetUid::from(netuid), burn_increase_mult: U64F64::from_bits(raw_multiplier), }; handle.try_dispatch_runtime_call::(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::(9)?; let netuid = NetUid::from(netuid); let hotkey = R::AccountId::from(hotkey.0); Ok(( pallet_subtensor::AlphaDividendsPerSubnet::::get(netuid, &hotkey).to_u64(), pallet_subtensor::RootAlphaDividendsPerSubnet::::get(netuid, &hotkey).to_u64(), pallet_subtensor::LastHotkeyEmissionOnNetuid::::get(&hotkey, netuid).to_u64(), pallet_subtensor::PendingServerEmission::::get(netuid).to_u64(), pallet_subtensor::PendingValidatorEmission::::get(netuid).to_u64(), pallet_subtensor::PendingRootAlphaDivs::::get(netuid).to_u64(), pallet_subtensor::PendingOwnerCut::::get(netuid).to_u64(), pallet_subtensor::MinerBurned::::get(netuid).to_bits(), pallet_subtensor::RAORecycledForRegistration::::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::(5)?; let netuid = NetUid::from(netuid); Ok(( pallet_subtensor::SubnetEmissionEnabled::::get(netuid), pallet_subtensor::RootProp::::get(netuid).to_bits(), pallet_subtensor::SubnetExcessTao::::get(netuid).to_u64(), pallet_subtensor::SubnetRootSellTao::::get(netuid).to_u64(), pallet_subtensor::SubnetProtocolAlpha::::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::(4)?; let netuid = NetUid::from(netuid); let tao_ema = pallet_subtensor::SubnetEmaTaoFlow::::get(netuid); let protocol_ema = pallet_subtensor::SubnetEmaProtocolFlow::::get(netuid); Ok(( signed_i64_word(pallet_subtensor::SubnetTaoFlow::::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::::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::(9)?; let block_emission = pallet_subtensor::Pallet::::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::::get().to_bits()), pallet_subtensor::NetTaoFlowEnabled::::get(), signed_i128_word(pallet_subtensor::TaoFlowCutoff::::get().to_bits()), pallet_subtensor::FlowNormExponent::::get().to_bits(), pallet_subtensor::EmissionBarQuantile::::get().to_bits(), pallet_subtensor::EmissionGateExponent::::get().to_bits(), pallet_subtensor::EmissionGateBar::::get().to_bits(), pallet_subtensor::FlowEmaSmoothingFactor::::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::(5)?; let netuid = NetUid::from(netuid); Ok(( pallet_subtensor_swap::FeeRate::::get(netuid), pallet_subtensor_swap::PalSwapInitialized::::get(netuid), pallet_subtensor_swap::SwapBalancer::::get(netuid) .get_quote_weight() .deconstruct(), pallet_subtensor_swap::BalancerTaoReservoir::::get(netuid).to_u64(), pallet_subtensor_swap::BalancerAlphaReservoir::::get(netuid).to_u64(), )) } #[precompile::public("hasSwapMigrationRun(bytes)")] #[precompile::view] fn has_swap_migration_run( handle: &mut impl PrecompileHandle, migration_name: BoundedBytes>, ) -> EvmResult { handle.record_db_reads::(1)?; let migration_name = BoundedVec::>::truncate_from(migration_name.into()); Ok(pallet_subtensor_swap::HasMigrationRun::::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::::put(TAO_WEIGHT); pallet_subtensor::CKBurn::::put(CK_BURN); pallet_subtensor::NetworksAdded::::insert(dynamic_netuid, true); pallet_subtensor::SubnetMechanism::::insert(dynamic_netuid, 1); pallet_subtensor::SubnetTAO::::insert( dynamic_netuid, TaoBalance::from(20_000_000_000_u64), ); pallet_subtensor::SubnetAlphaIn::::insert( dynamic_netuid, AlphaBalance::from(10_000_000_000_u64), ); pallet_subtensor::SubnetAlphaOut::::insert( dynamic_netuid, AlphaBalance::from(3_000_000_000_u64), ); pallet_subtensor::SubnetTaoInEmission::::insert( dynamic_netuid, TaoBalance::from(TAO_IN_EMISSION), ); pallet_subtensor::SubnetAlphaInEmission::::insert( dynamic_netuid, AlphaBalance::from(ALPHA_IN_EMISSION), ); pallet_subtensor::SubnetAlphaOutEmission::::insert( dynamic_netuid, AlphaBalance::from(ALPHA_OUT_EMISSION), ); pallet_subtensor::SubnetVolume::::insert(dynamic_netuid, SUBNET_VOLUME); pallet_subtensor::EMAPriceHalvingBlocks::::insert( dynamic_netuid, EMA_HALVING_BLOCKS, ); pallet_subtensor::SubnetMovingPrice::::insert( dynamic_netuid, I96F32::from_num(3.0 / 2.0), ); pallet_subtensor::NetworksAdded::::insert(sum_price_netuid, true); pallet_subtensor::SubnetMechanism::::insert(sum_price_netuid, 1); pallet_subtensor::SubnetTAO::::insert( sum_price_netuid, TaoBalance::from(5_000_000_000_u64), ); pallet_subtensor::SubnetAlphaIn::::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::>(); let caller = addr_from_index(1); let precompile_addr = addr_from_index(AlphaPrecompile::::INDEX); let dynamic_netuid = NetUid::from(DYNAMIC_NETUID_U16); let alpha_price = as SwapHandler>::current_alpha_price( dynamic_netuid, ); let moving_alpha_price = pallet_subtensor::Pallet::::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::::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::::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::::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::::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::::get(dynamic_netuid).into(), ); assert_static_call( &precompiles, caller, precompile_addr, encode_with_selector( selector_u32("getEMAPriceHalvingBlocks(uint16)"), (DYNAMIC_NETUID_U16,), ), pallet_subtensor::EMAPriceHalvingBlocks::::get(dynamic_netuid).into(), ); assert_static_call( &precompiles, caller, precompile_addr, encode_with_selector( selector_u32("getSubnetVolume(uint16)"), (DYNAMIC_NETUID_U16,), ), pallet_subtensor::SubnetVolume::::get(dynamic_netuid).into(), ); assert_static_call( &precompiles, caller, precompile_addr, encode_with_selector( selector_u32("getTaoInEmission(uint16)"), (DYNAMIC_NETUID_U16,), ), pallet_subtensor::SubnetTaoInEmission::::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::::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::::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::>(); let caller = addr_from_index(1); let precompile_addr = addr_from_index(AlphaPrecompile::::INDEX); let mut sum_alpha_price = U64F64::from_num(0); for (netuid, _) in pallet_subtensor::NetworksAdded::::iter() { if netuid.is_root() { continue; } let price = 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::::get().into(), ); assert_static_call( &precompiles, caller, precompile_addr, selector_u32("getTaoWeight()").to_be_bytes().to_vec(), pallet_subtensor::TaoWeight::::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::>(); let caller = addr_from_index(1); let precompile_addr = addr_from_index(AlphaPrecompile::::INDEX); let tao_amount = 1_000_000_000_u64; let alpha_amount = 1_000_000_000_u64; let expected_alpha = as SwapHandler>::sim_swap( NetUid::from(DYNAMIC_NETUID_U16), pallet_subtensor::GetAlphaForTao::::with_amount(tao_amount), ) .expect("tao-for-alpha simulation should succeed") .amount_paid_out .to_u64(); let expected_tao = as SwapHandler>::sim_swap( NetUid::from(DYNAMIC_NETUID_U16), pallet_subtensor::GetTaoForAlpha::::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::>(); let caller = addr_from_index(1); let address = addr_from_index(AlphaPrecompile::::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::::get(netuid), pallet_subtensor::RootProp::::get(netuid).to_bits(), pallet_subtensor::SubnetExcessTao::::get(netuid).to_u64(), pallet_subtensor::SubnetRootSellTao::::get(netuid).to_u64(), pallet_subtensor::SubnetProtocolAlpha::::get(netuid).to_u64(), ), ); let tao_ema = pallet_subtensor::SubnetEmaTaoFlow::::get(netuid); let protocol_ema = pallet_subtensor::SubnetEmaProtocolFlow::::get(netuid); assert_view( &precompiles, caller, address, "getSubnetFlowState(uint16)", (DYNAMIC_NETUID_U16,), ( signed_i64_word(pallet_subtensor::SubnetTaoFlow::::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::::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::::calculate_block_emission() .expect("block emission calculation should succeed") .to_u64(), signed_i128_word(pallet_subtensor::SubnetMovingAlpha::::get().to_bits()), pallet_subtensor::NetTaoFlowEnabled::::get(), signed_i128_word(pallet_subtensor::TaoFlowCutoff::::get().to_bits()), pallet_subtensor::FlowNormExponent::::get().to_bits(), pallet_subtensor::EmissionBarQuantile::::get().to_bits(), pallet_subtensor::EmissionGateExponent::::get().to_bits(), pallet_subtensor::EmissionGateBar::::get().to_bits(), pallet_subtensor::FlowEmaSmoothingFactor::::get(), ); assert_view( &precompiles, caller, address, "getEmissionGateConfig()", (), emission_gate, ); let balancer = pallet_subtensor_swap::SwapBalancer::::get(netuid); assert_view( &precompiles, caller, address, "getSwapState(uint16)", (DYNAMIC_NETUID_U16,), ( pallet_subtensor_swap::FeeRate::::get(netuid), pallet_subtensor_swap::PalSwapInitialized::::get(netuid), balancer.get_quote_weight().deconstruct(), pallet_subtensor_swap::BalancerTaoReservoir::::get(netuid).to_u64(), pallet_subtensor_swap::BalancerAlphaReservoir::::get(netuid).to_u64(), ), ); let migration_name = b"reader-test".to_vec(); pallet_subtensor_swap::HasMigrationRun::::insert( BoundedVec::truncate_from(migration_name.clone()), true, ); assert_view( &precompiles, caller, address, "hasSwapMigrationRun(bytes)", (BoundedBytes::>::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::::put(TaoBalance::from( FIRST_HALVING_ISSUANCE, )); #[allow(deprecated)] pallet_subtensor::BlockEmission::::put(123_u64); let expected_block_emission = pallet_subtensor::Pallet::::calculate_block_emission() .expect("halving-boundary emission calculation should succeed") .to_u64(); assert_eq!(expected_block_emission, 500_000_000); let precompiles = precompiles::>(); assert_view( &precompiles, addr_from_index(1), addr_from_index(AlphaPrecompile::::INDEX), "getEmissionGateConfig()", (), ( expected_block_emission, signed_i128_word( pallet_subtensor::SubnetMovingAlpha::::get().to_bits(), ), pallet_subtensor::NetTaoFlowEnabled::::get(), signed_i128_word(pallet_subtensor::TaoFlowCutoff::::get().to_bits()), pallet_subtensor::FlowNormExponent::::get().to_bits(), pallet_subtensor::EmissionBarQuantile::::get().to_bits(), pallet_subtensor::EmissionGateExponent::::get().to_bits(), pallet_subtensor::EmissionGateBar::::get().to_bits(), pallet_subtensor::FlowEmaSmoothingFactor::::get(), ), ); }); } fn assert_view( 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); } }