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(PhantomData); impl PrecompileExt for AlphaPrecompile 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 AlphaPrecompile 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 { // 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) } } #[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(), ); }); } }