Skip to content

Latest commit

 

History

History
327 lines (251 loc) · 8.21 KB

File metadata and controls

327 lines (251 loc) · 8.21 KB

Staking Queries

Documentation for querying stake distributions, delegations, and staking operations on the Bittensor network.

Overview

Staking is central to Bittensor's security and consensus mechanism. These queries provide detailed information about stake distributions, delegations, and validator requirements.

Query Functions

Total Stake Queries

Get Total Stake

use bittensor_rs::queries::stakes;

// Get total stake for a hotkey across all subnets
let total_stake = stakes::get_total_stake(&client, &hotkey).await?;

// Get stake for specific subnet
let subnet_stake = stakes::get_stake(&client, netuid, &hotkey).await?;

Get Stake Distribution

// Get all stakes for a hotkey
let stake_distribution = stakes::get_stake_distribution(&client, &hotkey).await?;

for (coldkey, amount) in stake_distribution {
    println!("Staker: {} - Amount: {} TAO", 
        coldkey.to_ss58check(), 
        amount as f64 / 1e9
    );
}

Delegation Queries

Check Delegation Status

// Check if account is delegating
let is_delegating = stakes::is_delegating(&client, &coldkey, &hotkey).await?;

// Get delegation amount
let delegation = stakes::get_delegation(&client, &coldkey, &hotkey).await?;

Get All Delegations

// Get all delegations from a coldkey
let delegations = stakes::get_delegations_for_coldkey(&client, &coldkey).await?;

for (hotkey, amount) in delegations {
    println!("Delegating to: {} - Amount: {} TAO",
        hotkey.to_ss58check(),
        amount as f64 / 1e9
    );
}

Validator Requirements

Check Validator Eligibility

// Minimum stake requirement
let min_stake = stakes::get_minimum_delegation(&client, netuid).await?;

// Check if hotkey meets requirements
let stake = stakes::get_stake(&client, netuid, &hotkey).await?;
let is_eligible = stake >= min_stake;

Advanced Queries

Stake History

// Get stake changes over time
async fn analyze_stake_history(
    client: &BittensorClient,
    hotkey: &AccountId32,
    blocks: Vec<u64>
) -> Result<Vec<(u64, u128)>> {
    let mut history = Vec::new();
    
    for block in blocks {
        let stake = stakes::get_stake_at_block(
            client, 
            hotkey, 
            Some(block)
        ).await?;
        
        history.push((block, stake));
    }
    
    Ok(history)
}

Top Stakers Analysis

async fn get_top_stakers(
    client: &BittensorClient,
    netuid: u16,
    top_n: usize
) -> Result<Vec<(AccountId32, u128)>> {
    let neurons = neurons::neurons(client, netuid, None).await?;
    
    let mut stakes: Vec<_> = neurons
        .iter()
        .map(|n| (n.hotkey.clone(), n.stake))
        .collect();
    
    stakes.sort_by(|a, b| b.1.cmp(&a.1));
    stakes.truncate(top_n);
    
    Ok(stakes)
}

Usage Examples

Monitor Staking Changes

async fn monitor_stake_changes(
    client: &BittensorClient,
    hotkey: &AccountId32,
    netuid: u16
) -> Result<()> {
    let mut last_stake = 0u128;
    
    loop {
        let current_stake = stakes::get_stake(client, netuid, hotkey).await?
            .unwrap_or(0);
        
        if current_stake != last_stake {
            let change = if current_stake > last_stake {
                format!("+{}", (current_stake - last_stake) as f64 / 1e9)
            } else {
                format!("-{}", (last_stake - current_stake) as f64 / 1e9)
            };
            
            println!("Stake changed: {} TAO (total: {} TAO)",
                change, current_stake as f64 / 1e9
            );
            
            last_stake = current_stake;
        }
        
        tokio::time::sleep(Duration::from_secs(12)).await;
    }
}

Calculate Staking Yields

async fn calculate_staking_yield(
    client: &BittensorClient,
    hotkey: &AccountId32,
    netuid: u16,
    period_blocks: u64
) -> Result<f64> {
    let current_block = client.get_block_number().await?;
    let start_block = current_block.saturating_sub(period_blocks);
    
    // Get neuron info
    let neurons = neurons::neurons(client, netuid, None).await?;
    let neuron = neurons.iter()
        .find(|n| n.hotkey == *hotkey)
        .ok_or_else(|| anyhow::anyhow!("Neuron not found"))?;
    
    // Calculate yield from emissions
    let emission_per_block = neuron.emission;
    let total_emissions = emission_per_block * period_blocks as f64;
    let stake = neuron.stake as f64 / 1e9;
    
    if stake > 0.0 {
        let apy = (total_emissions / stake) * (365.0 * 24.0 * 3600.0 / 12.0) / period_blocks as f64;
        Ok(apy * 100.0)
    } else {
        Ok(0.0)
    }
}

Delegation Analysis

async fn analyze_delegation_distribution(
    client: &BittensorClient,
    delegate: &AccountId32
) -> Result<()> {
    let delegations = stakes::get_stake_distribution(client, delegate).await?;
    
    let total: u128 = delegations.values().sum();
    let count = delegations.len();
    let average = total / count as u128;
    
    println!("Delegation Analysis for {}", delegate.to_ss58check());
    println!("Total delegated: {} TAO", total as f64 / 1e9);
    println!("Number of delegators: {}", count);
    println!("Average delegation: {} TAO", average as f64 / 1e9);
    
    // Find concentration
    let mut sorted: Vec<_> = delegations.values().copied().collect();
    sorted.sort_by(|a, b| b.cmp(a));
    
    let top_10_sum: u128 = sorted.iter().take(10).sum();
    let concentration = (top_10_sum as f64 / total as f64) * 100.0;
    
    println!("Top 10 delegator concentration: {:.2}%", concentration);
    
    Ok(())
}

Stake Migration Tracking

async fn track_stake_migrations(
    client: &BittensorClient,
    addresses: Vec<AccountId32>,
    interval: Duration
) -> Result<()> {
    let mut last_stakes: HashMap<AccountId32, u128> = HashMap::new();
    
    // Initialize
    for addr in &addresses {
        let stake = stakes::get_total_stake(client, addr).await?.unwrap_or(0);
        last_stakes.insert(addr.clone(), stake);
    }
    
    loop {
        tokio::time::sleep(interval).await;
        
        for addr in &addresses {
            let current = stakes::get_total_stake(client, addr).await?.unwrap_or(0);
            let last = last_stakes.get(addr).copied().unwrap_or(0);
            
            if current != last {
                let change = current as i128 - last as i128;
                println!("{}: {} TAO", 
                    addr.to_ss58check(),
                    if change > 0 { 
                        format!("+{}", change as f64 / 1e9) 
                    } else { 
                        format!("{}", change as f64 / 1e9) 
                    }
                );
                
                last_stakes.insert(addr.clone(), current);
            }
        }
    }
}

Performance Optimization

Caching Strategies

use std::time::{Duration, Instant};

struct StakeCache {
    cache: HashMap<(u16, AccountId32), (u128, Instant)>,
    ttl: Duration,
}

impl StakeCache {
    fn new(ttl: Duration) -> Self {
        Self {
            cache: HashMap::new(),
            ttl,
        }
    }
    
    async fn get_stake(
        &mut self,
        client: &BittensorClient,
        netuid: u16,
        hotkey: &AccountId32
    ) -> Result<u128> {
        let key = (netuid, hotkey.clone());
        
        if let Some((stake, timestamp)) = self.cache.get(&key) {
            if timestamp.elapsed() < self.ttl {
                return Ok(*stake);
            }
        }
        
        let stake = stakes::get_stake(client, netuid, hotkey).await?
            .unwrap_or(0);
        
        self.cache.insert(key, (stake, Instant::now()));
        Ok(stake)
    }
}

Error Handling

use bittensor_rs::Error;

match stakes::get_stake(&client, netuid, &hotkey).await {
    Ok(Some(stake)) => println!("Stake: {} TAO", stake as f64 / 1e9),
    Ok(None) => println!("No stake found"),
    Err(Error::StorageNotFound) => println!("Storage not initialized"),
    Err(e) => println!("Query error: {}", e),
}

Related Documentation