Skip to content

Validator Engine

Relevant Source Files

The Validator Engine is the core orchestration component of the SN106 Bittensor subnet validator system. It implements a scheduled execution cycle that collects multi-chain NFT position data, calculates emission weights based on concentrated liquidity positions, and submits normalized weights to the Subtensor chain every 20 minutes.

This document covers the main validator orchestration logic, EMA weight smoothing, and distribution policy enforcement. For details about the actual weight submission mechanism, see Weight Submission System. For multi-chain data collection specifics, see Multi-Chain Integration.

The validator engine operates on a scheduled interval defined by VALIDATOR_INTERVAL_MINUTES and follows a structured pipeline from data collection through weight submission.

graph TD
    START["runValidator()"] --> INIT["subtensorClient.initialize()"]
    INIT --> CHAINS["CONFIG.getEnabledChains()"]
    CHAINS --> HOTKEYS["getHotkeyToUidMap()"]
    HOTKEYS --> POSITIONS["getAllNFTPositions()"]
    POSITIONS --> TICKS["getCurrentTickPerPool()"]
    TICKS --> ALPHAS["getSubnetAlphaPrices()"]
    ALPHAS --> POOLW["calculatePoolWeightsWithReservedPools()"]
    POOLW --> EMISSIONS["calculatePoolwiseNFTEmissions()"]
    EMISSIONS --> AGGREGATE["Aggregate per-miner emissions"]
    AGGREGATE --> POLICY["Distribution policy check"]
    POLICY --> EMA["EMA weight smoothing"]
    EMA --> SUBMIT["setWeightsOnSubtensor()"]
    SUBMIT --> END["Complete run"]
    
    POLICY --> |"hasPositiveEmission"| INRANGE["In-range miners policy"]
    POLICY --> |"All zero emissions"| OUTRANGE["Out-of-range uniform policy"]
    
    INRANGE --> EMA
    OUTRANGE --> SUBMIT

Sources: validator/index.ts:17-163

The validator engine runs continuously using setInterval with configurable timing:

ComponentImplementationConfiguration
Main LoopsetInterval(runValidator, ...)VALIDATOR_INTERVAL_MINUTES
Initial RunrunValidator() immediate callRuns once on startup
Graceful ShutdownSIGINT/SIGTERM handlersCalls subtensorClient.shutdown()

Sources: validator/index.ts:179-181 , validator/index.ts:166-176

The validator engine coordinates data collection across multiple blockchain networks and Bittensor chain state.

graph LR
    HOTKEYS["getHotkeyToUidMap()"] --> FILTER["Filter enabled chains"]
    FILTER --> SOL["getAllNFTPositions()"]
    SOL --> TICKS["getCurrentTickPerPool()"]
    TICKS --> ALPHAS["getSubnetAlphaPrices()"]
    
    SOL --> SOLPOS["Solana NFT positions"]
    SOL --> ETHPOS["Ethereum NFT positions"]
    SOL --> BASEPOS["Base NFT positions"]
    
    TICKS --> SOLTICK["Solana tick data"]
    TICKS --> ETHTICK["Ethereum tick data"]
    TICKS --> BASETICK["Base tick data"]
    
    ALPHAS --> SUBNET_ALPHAS["Subnet alpha prices map"]

Sources: validator/index.ts:62-86 , validator/chains/index.ts

  1. Hotkey Mapping: getHotkeyToUidMap(wsUrl, netuid) retrieves the mapping between miner hotkeys and their UID positions in the subnet
  2. Position Collection: getAllNFTPositions(hotkeys) aggregates NFT positions across all enabled chains
  3. Tick Data: getCurrentTickPerPool() fetches current market tick data for all pools
  4. Alpha Prices: getSubnetAlphaPrices(wsUrl, filteredSubnetIds) retrieves subnet performance metrics from Subtensor

Sources: validator/index.ts:62-86

The validator engine implements Exponential Moving Average (EMA) smoothing to prevent sudden weight fluctuations and provide stability in reward distribution.

graph TD
    GLOBAL["global.__sn106EmaWeights"] --> CHECK{"EMA enabled?"}
    CHECK --> |"CONFIG.VALIDATOR.USE_EMA"| CALCULATE["updateEma()"]
    CHECK --> |"Disabled"| DIRECT["Direct weight assignment"]
    
    CALCULATE --> ALPHA["EMA_ALPHA * current"]
    CALCULATE --> PREV["(1 - EMA_ALPHA) * previous"]
    ALPHA --> COMBINE["Combined EMA value"]
    PREV --> COMBINE
    
    COMBINE --> EPSILON["Apply EMA_EPSILON threshold"]
    EPSILON --> PERSIST["global.__sn106EmaWeights = nextEma"]
    PERSIST --> ASSIGN["Assign eligible weights"]
    
    DIRECT --> ASSIGN

Sources: validator/index.ts:32-59 , validator/index.ts:124-146

The EMA calculation uses a configurable alpha parameter and epsilon threshold:

ParameterDefaultPurpose
EMA_ALPHA0.3Controls smoothing rate (higher = more responsive)
EMA_EPSILON0.001Minimum threshold for non-zero weights

The updateEma function implements the formula: next[k] = EMA_ALPHA * current + (1 - EMA_ALPHA) * previous

Sources: validator/index.ts:47-59

The validator engine enforces a two-tier distribution policy based on whether miners have in-range or out-of-range liquidity positions.

graph TD
    EMISSIONS["minerWeightsRaw"] --> CHECK{"hasPositiveEmission?"}
    CHECK --> |"true"| INRANGE["In-range miners policy"]
    CHECK --> |"false"| OUTRANGE["Out-of-range miners policy"]
    
    INRANGE --> ZERO["Set all UIDs to 0"]
    ZERO --> POSITIVE["Assign weights to positive emissions only"]
    POSITIVE --> EMA_CHECK{"EMA enabled?"}
    EMA_CHECK --> |"true"| EMA_APPLY["Apply EMA to eligible miners"]
    EMA_CHECK --> |"false"| DIRECT_ASSIGN["Direct assignment"]
    
    OUTRANGE --> UNIFORM["Fallback to uniform distribution"]
    UNIFORM --> SETWEIGHTS["setWeightsOnSubtensor()"]
    
    EMA_APPLY --> SETWEIGHTS
    DIRECT_ASSIGN --> SETWEIGHTS

Sources: validator/index.ts:113-157

  1. Positive Emission Check: Object.values(minerWeightsRaw).some(v => isFinite(v) && v > 0)
  2. UID Initialization: All known UIDs from hotkeyToUid are set to 0
  3. Selective Assignment: Only miners with isFinite(w) && w > 0 receive weights
  4. Uniform Fallback: When no positive emissions exist, setWeightsOnSubtensor handles uniform distribution

Sources: validator/index.ts:113-151

The validator engine integrates with multiple system components through well-defined interfaces.

graph TD
    VALIDATOR["validator/index.ts"] --> API["validator/api.ts"]
    VALIDATOR --> CHAINS["validator/chains/index.ts"]
    VALIDATOR --> EMISSIONS["validator/calculations/emissions.ts"]
    VALIDATOR --> UTILS_BT["utils/bittensor.ts"]
    VALIDATOR --> UTILS_POOL["utils/poolWeights.ts"]
    VALIDATOR --> UTILS_WEIGHTS["utils/setWeights.ts"]
    VALIDATOR --> CONFIG["config/environment.ts"]
    VALIDATOR --> LOGGER["utils/logger.ts"]
    
    API --> SUBTENSOR["subtensorClient singleton"]
    CHAINS --> SOL_CHAIN["chains/solana.ts"]
    CHAINS --> ETH_CHAIN["chains/ethereum.ts"]
    CHAINS --> BASE_CHAIN["chains/base.ts"]
    
    EMISSIONS --> POOL_CALC["Pool-wise emission calculation"]
    UTILS_BT --> HOTKEY_MAP["Hotkey-to-UID mapping"]
    UTILS_POOL --> RESERVED_SHARE["Reserved share logic"]
    UTILS_WEIGHTS --> WEIGHT_SUBMISSION["Subtensor weight submission"]

Sources: validator/index.ts:8-15 , docs/ARCHITECTURE.md:25-47

The validator engine depends on typed configuration from the environment system:

Configuration SectionUsageSource
CONFIG.SUBTENSORWebSocket URL, hotkey URI, netuid validator/index.ts:25-27
CONFIG.VALIDATOREMA settings, interval timing validator/index.ts:34-35
CONFIG.getEnabledChains()Multi-chain filtering validator/index.ts:21-22

Sources: validator/index.ts:21-35 , config/environment.ts

The validator engine implements comprehensive error handling to ensure continuous operation despite individual component failures.

  • Chain Failures: Individual chain failures don’t stop the entire validation cycle
  • Data Validation: All numeric values are checked with isFinite() before use
  • Fallback Policies: Out-of-range scenarios trigger uniform weight distribution
  • Singleton Management: subtensorClient provides managed WebSocket connections with proper shutdown

Sources: validator/index.ts:64-66 , validator/index.ts:83-85 , validator/index.ts:166-176