Skip to content

System Architecture

Relevant Source Files

This document explains the high-level architecture of the SN106 Bittensor subnet validator system, including its core components, data flow patterns, and multi-chain integration mechanisms. It covers the validator engine orchestration, emission calculation algorithms, and weight submission processes.

For detailed implementation specifics and code examples, see Technical Implementation. For user setup instructions, see Configuration and Docker Deployment.

SN106 operates as a distributed validator system that incentivizes concentrated liquidity provision across multiple blockchain networks. The system evaluates NFT liquidity positions, calculates rewards based on position quality and subnet performance, then submits weight distributions to the Bittensor Subtensor chain every 20 minutes.

graph TB
    subgraph "External Dependencies"
        SUBTENSOR["Subtensor Chain<br/>Weight Submission Target"]
        SOLANA["Solana Network<br/>Raydium CLMM"]
        ETHEREUM["Ethereum Network<br/>Uniswap V3<br/>(Coming Soon)"]
        BASE["Base Network<br/>Uniswap V3<br/>(Coming Soon)"]
    end
    
    subgraph "Validator Core"
        ENGINE["ValidatorEngine<br/>validator/index.ts<br/>runValidatorLoop()"]
        TIMER["20-Minute Timer<br/>setInterval()"]
        EMA["EMA Weight Manager<br/>updateEma()"]
    end
    
    subgraph "Data Collection Layer"
        CHAINS["Chain Manager<br/>validator/chains/index.ts<br/>getEnabledChains()"]
        POSITIONS["Position Fetcher<br/>fetchNFTPositionsFromAllChains()"]
        TICKS["Tick Data Fetcher<br/>fetchCurrentTickDataForAllPools()"]
        ALPHAS["Subnet Alpha Prices<br/>getSubnetAlphaPrices()"]
    end
    
    subgraph "Processing Engine"
        POOLW["Pool Weight Calculator<br/>utils/poolWeights.ts<br/>calculatePoolWeights()"]
        EMISSIONS["Emissions Calculator<br/>validator/calculations/emissions.ts<br/>calculatePoolwiseNFTEmissions()"]
        AGGREGATOR["Weight Aggregator<br/>aggregateMinerWeights()"]
    end
    
    subgraph "Submission System"
        POLICY["Distribution Policy<br/>hasPositiveEmission logic"]
        WEIGHTS["Weight Preparation<br/>prepareWeightsForSubmission()"]
        SUBMIT["Subtensor Submission<br/>utils/setWeights.ts<br/>setWeightsOnSubtensor()"]
    end
    
    TIMER --> ENGINE
    ENGINE --> CHAINS
    CHAINS --> POSITIONS
    CHAINS --> TICKS
    ENGINE --> ALPHAS
    
    POSITIONS --> POOLW
    TICKS --> POOLW
    ALPHAS --> POOLW
    
    POOLW --> EMISSIONS
    POSITIONS --> EMISSIONS
    TICKS --> EMISSIONS
    
    EMISSIONS --> AGGREGATOR
    AGGREGATOR --> POLICY
    POLICY --> WEIGHTS
    EMA --> WEIGHTS
    WEIGHTS --> SUBMIT
    
    SUBMIT --> SUBTENSOR
    POSITIONS --> SOLANA
    POSITIONS --> ETHEREUM
    POSITIONS --> BASE

Sources: validator/index.ts:1-300 , validator/chains/index.ts:1-50 , utils/poolWeights.ts:1-100 , validator/calculations/emissions.ts:1-200 , utils/setWeights.ts:1-150

The ValidatorEngine in validator/index.ts orchestrates the entire validation cycle through runValidatorLoop(). It manages the 20-minute execution timer and coordinates all data collection, processing, and submission phases.

ComponentFunctionFile Location
Main LooprunValidatorLoop() validator/index.ts:50-100
EMA ManagementupdateEma() validator/index.ts:200-250
Error HandlingTry-catch blocks with logging validator/index.ts:150-200

The chain management system supports multiple blockchain networks through a unified interface defined in validator/chains/index.ts .

ChainStatusProgram/ContractData Source
SolanaActiveSN106_SVM_PROGRAM_IDRaydium CLMM
EthereumComing SoonSN106_CONTRACT_ADDRESSUniswap V3
BaseComing SoonSN106_CONTRACT_ADDRESSUniswap V3
graph TD
    subgraph "Chain Management"
        CONFIG["config/environment.ts<br/>ENABLED_CHAINS"]
        FILTER["getEnabledChains()<br/>validator/chains/index.ts"]
    end
    
    subgraph "Solana Integration"
        SOLANA_FETCH["fetchSolanaNFTPositions()<br/>validator/chains/solana.ts"]
        SOLANA_TICKS["fetchSolanaCurrentTicks()<br/>validator/chains/solana.ts"]
        RAYDIUM["Raydium CLMM Program<br/>RAYDIUM_CLMM_PROGRAM_ID"]
    end
    
    subgraph "Ethereum Integration"
        ETH_FETCH["fetchEthereumNFTPositions()<br/>validator/chains/ethereum.ts"]
        ETH_TICKS["fetchEthereumCurrentTicks()<br/>validator/chains/ethereum.ts"]
        UNISWAP["Uniswap V3 Contracts<br/>UNISWAP_V3_FACTORY_ADDRESS"]
    end
    
    subgraph "Data Aggregation"
        POSITIONS["NFTPosition[]<br/>Unified Position Array"]
        TICKDATA["PoolTickData<br/>Record<string, PoolTickData>"]
    end
    
    CONFIG --> FILTER
    FILTER --> SOLANA_FETCH
    FILTER --> ETH_FETCH
    
    SOLANA_FETCH --> RAYDIUM
    SOLANA_TICKS --> RAYDIUM
    ETH_FETCH --> UNISWAP
    ETH_TICKS --> UNISWAP
    
    SOLANA_FETCH --> POSITIONS
    ETH_FETCH --> POSITIONS
    SOLANA_TICKS --> TICKDATA
    ETH_TICKS --> TICKDATA

Sources: validator/chains/index.ts:10-40 , validator/chains/solana.ts:1-200 , config/environment.ts:20-60

The emissions calculator in validator/calculations/emissions.ts implements a sophisticated scoring system that evaluates NFT positions based on three factors:

  1. Position Width: Calculated as tickUpper - tickLower
  2. Distance from Current Tick: Absolute difference between position center and current market tick
  3. Liquidity Amount: The actual liquidity value in the position
graph TD
    subgraph "NFT Position Input"
        POSITION["NFTPosition<br/>tickLower, tickUpper, liquidity"]
        CURRENT["currentTick<br/>from PoolTickData"]
    end
    
    subgraph "Scoring Calculations"
        WIDTH["widthCalculation<br/>tickUpper - tickLower"]
        CENTER["centerCalculation<br/>(tickLower + tickUpper) / 2"]
        DISTANCE["distanceCalculation<br/>Math.abs(center - currentTick)"]
    end
    
    subgraph "Score Components"
        WIDTH_PENALTY["widthPenalty<br/>1 / Math.pow(width, 1.2)"]
        CENTER_WEIGHT["centerWeight<br/>1 / (1 + distanceFromCenter)"]
        BASE_SCORE["baseScore<br/>widthPenalty * centerWeight"]
    end
    
    subgraph "Final Score"
        FINAL["finalScore<br/>baseScore * position.liquidity"]
    end
    
    POSITION --> WIDTH
    POSITION --> CENTER
    CURRENT --> DISTANCE
    CENTER --> DISTANCE
    
    WIDTH --> WIDTH_PENALTY
    DISTANCE --> CENTER_WEIGHT
    WIDTH_PENALTY --> BASE_SCORE
    CENTER_WEIGHT --> BASE_SCORE
    
    BASE_SCORE --> FINAL
    POSITION --> FINAL

Sources: validator/calculations/emissions.ts:50-100 , validator/calculations/emissions.ts:150-200

The pool weight calculator in utils/poolWeights.ts implements a reserved-share logic that allocates rewards based on subnet performance while ensuring fair distribution within subnets.

Allocation TypePercentageTarget
Reserved Share25%Subnet 0 pools (no-alpha pools)
Proportional Share75%Alpha token pools by subnet performance

The validator implements Exponential Moving Average smoothing to prevent sudden weight fluctuations:

graph LR
    subgraph "EMA Configuration"
        ALPHA["EMA_ALPHA<br/>config value: 0.3"]
        EPSILON["EMA_EPSILON<br/>threshold: 0.001"]
    end
    
    subgraph "Weight Processing"
        CURRENT["currentWeights<br/>Record<string, number>"]
        PREVIOUS["previousWeights<br/>stored from last run"]
        FORMULA["emaFormula<br/>α * current + (1-α) * previous"]
    end
    
    subgraph "Output"
        SMOOTHED["smoothedWeights<br/>applied to submission"]
        THRESHOLD["thresholdFiltering<br/>weights < epsilon set to 0"]
    end
    
    ALPHA --> FORMULA
    CURRENT --> FORMULA
    PREVIOUS --> FORMULA
    FORMULA --> SMOOTHED
    EPSILON --> THRESHOLD
    SMOOTHED --> THRESHOLD

Sources: validator/index.ts:250-300 , config/environment.ts:80-120

The validator executes a complete cycle every 20 minutes through the following sequence:

  1. Initialization: Load configuration and enabled chains
  2. Data Collection: Fetch positions, ticks, and subnet prices
  3. Weight Calculation: Calculate pool weights and NFT emissions
  4. Distribution Policy: Apply in-range vs out-of-range logic
  5. Submission: Submit weights to Subtensor with burn mechanism
graph TD
    subgraph "Emission Analysis"
        EMISSIONS["calculatePoolwiseNFTEmissions()<br/>returns NFTEmissionResult[]"]
        CHECK["hasPositiveEmission<br/>Object.values().some(v => v > 0)"]
    end
    
    subgraph "Policy Decision"
        POSITIVE{"hasPositiveEmission?"}
        INRANGE["In-Range Policy<br/>Set all UIDs to 0<br/>Assign weights only to positive"]
        OUTRANGE["Out-of-Range Policy<br/>Uniform distribution<br/>All UIDs get equal weight"]
    end
    
    subgraph "Weight Assignment"
        ZERO["Initialize all known UIDs to 0"]
        ASSIGN["Assign weights to miners with emissions > 0"]
        UNIFORM["Apply uniform distribution fallback"]
    end
    
    EMISSIONS --> CHECK
    CHECK --> POSITIVE
    POSITIVE -->|"Yes"| INRANGE
    POSITIVE -->|"No"| OUTRANGE
    
    INRANGE --> ZERO
    ZERO --> ASSIGN
    OUTRANGE --> UNIFORM

Sources: validator/index.ts:300-350 , utils/setWeights.ts:50-100

The system uses a typed configuration system in config/environment.ts that validates all environment variables at startup and provides defaults for optional settings.

CategoryEnvironment VariableDefaultPurpose
SubtensorSUBTENSOR_WS_URLRequiredWebSocket endpoint
SubtensorVALIDATOR_HOTKEY_URIRequiredPrivate key/mnemonic/URI
SubtensorNETUID106Subnet identifier
PerformanceUSE_EMAtrueEnable weight smoothing
PerformanceEMA_ALPHA0.3Smoothing factor
ChainsENABLED_CHAINS’SOLANA,ETHEREUM,BASE’Active chains
BurnBURN_PERCENTAGE95Percentage burned to UID 0

Sources: config/environment.ts:1-150 , README.md:104-124