FortiBlox LogoFortiBlox Docs
NexusRPC NodesRPC Guides

Network Information

Methods for monitoring cluster health, validator information, epoch tracking, and network status

Network Information

Network information methods provide insights into the health, status, and configuration of the X1 Blockchain cluster. These methods are essential for monitoring, validation, and understanding network conditions.

Overview

Network information methods allow you to:

  • Monitor cluster health - Check node status and availability
  • Track epochs and slots - Understand blockchain timing and progression
  • Query validator information - Access validator performance and stakes
  • Get version information - Verify node software versions
  • Monitor network performance - Track throughput and confirmation times

When to Use Network Methods

Use these methods when you need to:

  • Build monitoring dashboards - Display network health metrics
  • Validate before transactions - Ensure network is operational
  • Track validator performance - Monitor stake and voting status
  • Sync applications - Coordinate with epoch and slot timing
  • Troubleshoot issues - Debug network connectivity problems

Core Methods

getHealth

Returns the current health status of the node.

const { Connection } = require('@solana/web3.js');

const connection = new Connection(
  'https://nexus.fortiblox.com/rpc',
  {
    commitment: 'confirmed',
    httpHeaders: {
      'X-API-Key': process.env.FORTIBLOX_API_KEY
    }
  }
);

async function checkNodeHealth() {
  try {
    const health = await connection.getHealth();
    console.log('Node is healthy:', health);
    return true;
  } catch (error) {
    console.error('Node is unhealthy:', error.message);
    return false;
  }
}

// Monitor health continuously
async function monitorHealth(intervalSeconds = 30) {
  console.log('Starting health monitoring...');

  setInterval(async () => {
    const isHealthy = await checkNodeHealth();
    const timestamp = new Date().toISOString();

    if (isHealthy) {
      console.log(`[${timestamp}] ✓ Node healthy`);
    } else {
      console.log(`[${timestamp}] ✗ Node unhealthy - investigate!`);
    }
  }, intervalSeconds * 1000);
}

// Health check with retry
async function waitForHealthyNode(maxRetries = 5, delayMs = 2000) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      await connection.getHealth();
      console.log('Node is healthy');
      return true;
    } catch (error) {
      console.log(`Attempt ${attempt + 1}: Node unhealthy, retrying...`);
      if (attempt < maxRetries - 1) {
        await new Promise(resolve => setTimeout(resolve, delayMs));
      }
    }
  }

  throw new Error('Node remained unhealthy after maximum retries');
}

// Example usage
checkNodeHealth();
from solana.rpc.api import Client
import os
import time

client = Client(
    "https://nexus.fortiblox.com/rpc",
    extra_headers={"X-API-Key": os.environ['FORTIBLOX_API_KEY']}
)

def check_node_health():
    """Check if node is healthy"""
    try:
        response = client.get_health()
        print(f"Node is healthy: {response}")
        return True
    except Exception as error:
        print(f"Node is unhealthy: {error}")
        return False

def monitor_health(interval_seconds=30):
    """Monitor health continuously"""
    print("Starting health monitoring...")

    while True:
        is_healthy = check_node_health()
        timestamp = time.strftime("%Y-%m-%d %H:%M:%S")

        if is_healthy:
            print(f"[{timestamp}] ✓ Node healthy")
        else:
            print(f"[{timestamp}] ✗ Node unhealthy - investigate!")

        time.sleep(interval_seconds)

def wait_for_healthy_node(max_retries=5, delay_ms=2000):
    """Wait for node to become healthy"""
    for attempt in range(max_retries):
        try:
            client.get_health()
            print("Node is healthy")
            return True
        except Exception:
            print(f"Attempt {attempt + 1}: Node unhealthy, retrying...")
            if attempt < max_retries - 1:
                time.sleep(delay_ms / 1000)

    raise Exception("Node remained unhealthy after maximum retries")

# Example usage
check_node_health()
# Simple health check
curl -X POST https://nexus.fortiblox.com/rpc \
  -H "X-API-Key: $FORTIBLOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getHealth"
  }'

# Response (healthy):
# {"jsonrpc":"2.0","result":"ok","id":1}

# Response (unhealthy):
# HTTP 500 with error message

Use Cases:

  • Pre-flight checks before critical operations
  • Health monitoring dashboards
  • Automated alerting systems
  • Load balancer health checks

Performance Tips:

  • Extremely lightweight (1 credit)
  • Use as a heartbeat check
  • Implement retries for transient failures
  • Cache health status briefly (5-10 seconds)

getEpochInfo

Returns information about the current epoch.

async function getEpochInfo() {
  const epochInfo = await connection.getEpochInfo();

  console.log('Epoch Information:', {
    epoch: epochInfo.epoch,
    slotIndex: epochInfo.slotIndex,
    slotsInEpoch: epochInfo.slotsInEpoch,
    absoluteSlot: epochInfo.absoluteSlot,
    blockHeight: epochInfo.blockHeight,
    transactionCount: epochInfo.transactionCount
  });

  // Calculate epoch progress
  const progress = (epochInfo.slotIndex / epochInfo.slotsInEpoch) * 100;
  console.log(`Epoch ${epochInfo.epoch} progress: ${progress.toFixed(2)}%`);

  return epochInfo;
}

// Calculate time remaining in epoch
async function getEpochTimeRemaining() {
  const epochInfo = await connection.getEpochInfo();

  const slotsRemaining = epochInfo.slotsInEpoch - epochInfo.slotIndex;
  const avgSlotTime = 0.4; // ~400ms per slot on Solana
  const secondsRemaining = slotsRemaining * avgSlotTime;
  const minutesRemaining = secondsRemaining / 60;
  const hoursRemaining = minutesRemaining / 60;

  console.log(`Time remaining in epoch ${epochInfo.epoch}:`);
  console.log(`  Slots: ${slotsRemaining.toLocaleString()}`);
  console.log(`  Time: ~${hoursRemaining.toFixed(2)} hours`);

  return {
    epoch: epochInfo.epoch,
    slotsRemaining,
    hoursRemaining
  };
}

// Wait for next epoch
async function waitForNextEpoch() {
  const startEpoch = (await connection.getEpochInfo()).epoch;
  console.log(`Current epoch: ${startEpoch}, waiting for next...`);

  while (true) {
    const currentEpoch = (await connection.getEpochInfo()).epoch;

    if (currentEpoch > startEpoch) {
      console.log(`New epoch started: ${currentEpoch}`);
      return currentEpoch;
    }

    // Check every 30 seconds
    await new Promise(resolve => setTimeout(resolve, 30000));
  }
}

// Track epoch transitions
async function trackEpochTransitions(callback) {
  let lastEpoch = (await connection.getEpochInfo()).epoch;

  setInterval(async () => {
    const currentEpoch = (await connection.getEpochInfo()).epoch;

    if (currentEpoch !== lastEpoch) {
      console.log(`Epoch transition: ${lastEpoch} → ${currentEpoch}`);
      callback(lastEpoch, currentEpoch);
      lastEpoch = currentEpoch;
    }
  }, 60000); // Check every minute
}

// Example usage
getEpochInfo();
getEpochTimeRemaining();
def get_epoch_info():
    """Get current epoch information"""
    response = client.get_epoch_info()
    epoch_info = response.value

    print("Epoch Information:")
    print(f"  Epoch: {epoch_info.epoch}")
    print(f"  Slot Index: {epoch_info.slot_index}")
    print(f"  Slots in Epoch: {epoch_info.slots_in_epoch}")
    print(f"  Absolute Slot: {epoch_info.absolute_slot}")
    print(f"  Block Height: {epoch_info.block_height}")
    print(f"  Transaction Count: {epoch_info.transaction_count}")

    # Calculate progress
    progress = (epoch_info.slot_index / epoch_info.slots_in_epoch) * 100
    print(f"Epoch {epoch_info.epoch} progress: {progress:.2f}%")

    return epoch_info

def get_epoch_time_remaining():
    """Calculate time remaining in epoch"""
    response = client.get_epoch_info()
    epoch_info = response.value

    slots_remaining = epoch_info.slots_in_epoch - epoch_info.slot_index
    avg_slot_time = 0.4  # ~400ms per slot
    seconds_remaining = slots_remaining * avg_slot_time
    hours_remaining = seconds_remaining / 3600

    print(f"Time remaining in epoch {epoch_info.epoch}:")
    print(f"  Slots: {slots_remaining:,}")
    print(f"  Time: ~{hours_remaining:.2f} hours")

    return {
        "epoch": epoch_info.epoch,
        "slots_remaining": slots_remaining,
        "hours_remaining": hours_remaining
    }

def wait_for_next_epoch():
    """Wait for the next epoch to start"""
    start_epoch = client.get_epoch_info().value.epoch
    print(f"Current epoch: {start_epoch}, waiting for next...")

    while True:
        current_epoch = client.get_epoch_info().value.epoch

        if current_epoch > start_epoch:
            print(f"New epoch started: {current_epoch}")
            return current_epoch

        time.sleep(30)

# Example usage
get_epoch_info()
get_epoch_time_remaining()
curl -X POST https://nexus.fortiblox.com/rpc \
  -H "X-API-Key: $FORTIBLOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getEpochInfo",
    "params": [
      {
        "commitment": "confirmed"
      }
    ]
  }'

# Response:
# {
#   "jsonrpc": "2.0",
#   "result": {
#     "epoch": 361,
#     "slotIndex": 123456,
#     "slotsInEpoch": 432000,
#     "absoluteSlot": 123456789,
#     "blockHeight": 123456000,
#     "transactionCount": 987654321
#   },
#   "id": 1
# }

Use Cases:

  • Epoch-based scheduling (staking, rewards)
  • Network timing synchronization
  • Validator performance tracking
  • Transaction rate monitoring

Performance Tips:

  • Lightweight method (1 credit)
  • Cache for 1-5 seconds in high-frequency apps
  • Useful for understanding blockchain timing

getVoteAccounts

Returns information about all voting accounts and their stake.

async function getVoteAccounts() {
  const voteAccounts = await connection.getVoteAccounts();

  console.log('Current Vote Accounts:');
  console.log(`  Current: ${voteAccounts.current.length} validators`);
  console.log(`  Delinquent: ${voteAccounts.delinquent.length} validators`);

  // Calculate total stake
  const totalCurrentStake = voteAccounts.current.reduce(
    (sum, account) => sum + account.activatedStake,
    0
  );

  const totalDelinquentStake = voteAccounts.delinquent.reduce(
    (sum, account) => sum + account.activatedStake,
    0
  );

  console.log(`\nStake Distribution:`);
  console.log(`  Current: ${(totalCurrentStake / 1e9).toFixed(2)} SOL`);
  console.log(`  Delinquent: ${(totalDelinquentStake / 1e9).toFixed(2)} SOL`);

  return voteAccounts;
}

// Get top validators by stake
async function getTopValidators(count = 10) {
  const voteAccounts = await connection.getVoteAccounts();

  const sorted = [...voteAccounts.current].sort(
    (a, b) => b.activatedStake - a.activatedStake
  );

  const top = sorted.slice(0, count);

  console.log(`\nTop ${count} Validators by Stake:`);
  top.forEach((validator, index) => {
    console.log(`${index + 1}. ${validator.nodePubkey}`);
    console.log(`   Vote: ${validator.votePubkey}`);
    console.log(`   Stake: ${(validator.activatedStake / 1e9).toFixed(2)} SOL`);
    console.log(`   Commission: ${validator.commission}%`);
    console.log(`   Last Vote: ${validator.lastVote}`);
  });

  return top;
}

// Find specific validator
async function findValidator(validatorPubkey) {
  const voteAccounts = await connection.getVoteAccounts();

  const allValidators = [
    ...voteAccounts.current,
    ...voteAccounts.delinquent
  ];

  const validator = allValidators.find(
    v => v.nodePubkey === validatorPubkey || v.votePubkey === validatorPubkey
  );

  if (!validator) {
    console.log('Validator not found');
    return null;
  }

  const status = voteAccounts.current.includes(validator)
    ? 'Current'
    : 'Delinquent';

  console.log('Validator Information:');
  console.log(`  Node: ${validator.nodePubkey}`);
  console.log(`  Vote Account: ${validator.votePubkey}`);
  console.log(`  Status: ${status}`);
  console.log(`  Stake: ${(validator.activatedStake / 1e9).toFixed(2)} SOL`);
  console.log(`  Commission: ${validator.commission}%`);
  console.log(`  Last Vote: ${validator.lastVote}`);
  console.log(`  Root Slot: ${validator.rootSlot}`);

  return validator;
}

// Monitor validator health
async function monitorValidatorHealth(validatorPubkey) {
  console.log(`Monitoring validator: ${validatorPubkey}`);

  setInterval(async () => {
    const validator = await findValidator(validatorPubkey);

    if (!validator) {
      console.log(`[${new Date().toISOString()}] Validator not found!`);
      return;
    }

    const voteAccounts = await connection.getVoteAccounts();
    const isCurrent = voteAccounts.current.some(
      v => v.votePubkey === validator.votePubkey
    );

    console.log(`[${new Date().toISOString()}] Status: ${isCurrent ? 'Current' : 'Delinquent'}`);
  }, 60000); // Check every minute
}

// Example usage
getVoteAccounts();
getTopValidators(10);
def get_vote_accounts():
    """Get all vote accounts"""
    response = client.get_vote_accounts()
    vote_accounts = response.value

    print("Current Vote Accounts:")
    print(f"  Current: {len(vote_accounts.current)} validators")
    print(f"  Delinquent: {len(vote_accounts.delinquent)} validators")

    # Calculate total stake
    total_current_stake = sum(
        account.activated_stake for account in vote_accounts.current
    )
    total_delinquent_stake = sum(
        account.activated_stake for account in vote_accounts.delinquent
    )

    print("\nStake Distribution:")
    print(f"  Current: {total_current_stake / 1e9:.2f} SOL")
    print(f"  Delinquent: {total_delinquent_stake / 1e9:.2f} SOL")

    return vote_accounts

def get_top_validators(count=10):
    """Get top validators by stake"""
    response = client.get_vote_accounts()
    vote_accounts = response.value

    sorted_validators = sorted(
        vote_accounts.current,
        key=lambda v: v.activated_stake,
        reverse=True
    )

    top = sorted_validators[:count]

    print(f"\nTop {count} Validators by Stake:")
    for index, validator in enumerate(top):
        print(f"{index + 1}. {validator.node_pubkey}")
        print(f"   Vote: {validator.vote_pubkey}")
        print(f"   Stake: {validator.activated_stake / 1e9:.2f} SOL")
        print(f"   Commission: {validator.commission}%")
        print(f"   Last Vote: {validator.last_vote}")

    return top

def find_validator(validator_pubkey):
    """Find specific validator"""
    response = client.get_vote_accounts()
    vote_accounts = response.value

    all_validators = vote_accounts.current + vote_accounts.delinquent

    validator = next(
        (v for v in all_validators
         if v.node_pubkey == validator_pubkey or v.vote_pubkey == validator_pubkey),
        None
    )

    if not validator:
        print("Validator not found")
        return None

    status = "Current" if validator in vote_accounts.current else "Delinquent"

    print("Validator Information:")
    print(f"  Node: {validator.node_pubkey}")
    print(f"  Vote Account: {validator.vote_pubkey}")
    print(f"  Status: {status}")
    print(f"  Stake: {validator.activated_stake / 1e9:.2f} SOL")
    print(f"  Commission: {validator.commission}%")
    print(f"  Last Vote: {validator.last_vote}")
    print(f"  Root Slot: {validator.root_slot}")

    return validator

# Example usage
get_vote_accounts()
get_top_validators(10)
# Get all vote accounts
curl -X POST https://nexus.fortiblox.com/rpc \
  -H "X-API-Key: $FORTIBLOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getVoteAccounts",
    "params": [
      {
        "commitment": "confirmed"
      }
    ]
  }'

# Get vote accounts for specific validator
curl -X POST https://nexus.fortiblox.com/rpc \
  -H "X-API-Key: $FORTIBLOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getVoteAccounts",
    "params": [
      {
        "votePubkey": "VALIDATOR_VOTE_ACCOUNT_PUBKEY"
      }
    ]
  }'

Use Cases:

  • Staking platform displays
  • Validator selection for delegation
  • Network decentralization metrics
  • Validator performance monitoring

Performance Tips:

  • Standard method (1 credit)
  • Response can be large (hundreds of validators)
  • Cache for 1-5 minutes
  • Filter by specific vote account if possible

getClusterNodes

Returns information about all nodes participating in the cluster.

async function getClusterNodes() {
  const nodes = await connection.getClusterNodes();

  console.log(`Total cluster nodes: ${nodes.length}`);

  // Group by version
  const versionCounts = {};
  nodes.forEach(node => {
    const version = node.version || 'unknown';
    versionCounts[version] = (versionCounts[version] || 0) + 1;
  });

  console.log('\nVersion Distribution:');
  Object.entries(versionCounts)
    .sort((a, b) => b[1] - a[1])
    .forEach(([version, count]) => {
      console.log(`  ${version}: ${count} nodes`);
    });

  return nodes;
}

// Find nodes by location (if available)
async function getNodesByFeatureSet() {
  const nodes = await connection.getClusterNodes();

  const byFeatureSet = {};

  nodes.forEach(node => {
    const featureSet = node.featureSet || 0;
    if (!byFeatureSet[featureSet]) {
      byFeatureSet[featureSet] = [];
    }
    byFeatureSet[featureSet].push(node);
  });

  console.log('Nodes by Feature Set:');
  Object.entries(byFeatureSet).forEach(([featureSet, nodeList]) => {
    console.log(`  Feature Set ${featureSet}: ${nodeList.length} nodes`);
  });

  return byFeatureSet;
}

// Check if specific node is in cluster
async function isNodeInCluster(nodePubkey) {
  const nodes = await connection.getClusterNodes();

  const node = nodes.find(n => n.pubkey === nodePubkey);

  if (node) {
    console.log('Node found:', {
      pubkey: node.pubkey,
      gossip: node.gossip,
      tpu: node.tpu,
      rpc: node.rpc,
      version: node.version
    });
    return true;
  }

  console.log('Node not found in cluster');
  return false;
}

// Example usage
getClusterNodes();
def get_cluster_nodes():
    """Get all cluster nodes"""
    response = client.get_cluster_nodes()
    nodes = response.value

    print(f"Total cluster nodes: {len(nodes)}")

    # Group by version
    version_counts = {}
    for node in nodes:
        version = node.version if node.version else "unknown"
        version_counts[version] = version_counts.get(version, 0) + 1

    print("\nVersion Distribution:")
    for version, count in sorted(version_counts.items(), key=lambda x: x[1], reverse=True):
        print(f"  {version}: {count} nodes")

    return nodes

def get_nodes_by_feature_set():
    """Group nodes by feature set"""
    response = client.get_cluster_nodes()
    nodes = response.value

    by_feature_set = {}

    for node in nodes:
        feature_set = node.feature_set if node.feature_set else 0
        if feature_set not in by_feature_set:
            by_feature_set[feature_set] = []
        by_feature_set[feature_set].append(node)

    print("Nodes by Feature Set:")
    for feature_set, node_list in by_feature_set.items():
        print(f"  Feature Set {feature_set}: {len(node_list)} nodes")

    return by_feature_set

def is_node_in_cluster(node_pubkey):
    """Check if node is in cluster"""
    response = client.get_cluster_nodes()
    nodes = response.value

    node = next((n for n in nodes if n.pubkey == node_pubkey), None)

    if node:
        print("Node found:")
        print(f"  Pubkey: {node.pubkey}")
        print(f"  Gossip: {node.gossip}")
        print(f"  TPU: {node.tpu}")
        print(f"  RPC: {node.rpc}")
        print(f"  Version: {node.version}")
        return True

    print("Node not found in cluster")
    return False

# Example usage
get_cluster_nodes()
curl -X POST https://nexus.fortiblox.com/rpc \
  -H "X-API-Key: $FORTIBLOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getClusterNodes"
  }'

Use Cases:

  • Network topology analysis
  • Version upgrade monitoring
  • Node discovery
  • Cluster health assessment

Performance Tips:

  • Standard method (1 credit)
  • Returns detailed node information
  • Cache for several minutes
  • Response can be large

getVersion

Returns the current Solana version running on the node.

async function getNodeVersion() {
  const version = await connection.getVersion();

  console.log('Node Version Information:');
  console.log(`  Solana Core: ${version['solana-core']}`);
  console.log(`  Feature Set: ${version['feature-set']}`);

  return version;
}

// Check if node meets minimum version
async function checkMinimumVersion(minVersion) {
  const version = await connection.getVersion();
  const current = version['solana-core'];

  console.log(`Current version: ${current}`);
  console.log(`Required version: ${minVersion}`);

  // Simple version comparison (for demo purposes)
  const isCompatible = current >= minVersion;

  if (isCompatible) {
    console.log('✓ Version requirement met');
  } else {
    console.log('✗ Version requirement not met');
  }

  return isCompatible;
}

// Example usage
getNodeVersion();
def get_node_version():
    """Get node version information"""
    response = client.get_version()
    version = response.value

    print("Node Version Information:")
    print(f"  Solana Core: {version['solana-core']}")
    print(f"  Feature Set: {version.get('feature-set', 'N/A')}")

    return version

def check_minimum_version(min_version):
    """Check if node meets minimum version"""
    response = client.get_version()
    version = response.value
    current = version['solana-core']

    print(f"Current version: {current}")
    print(f"Required version: {min_version}")

    is_compatible = current >= min_version

    if is_compatible:
        print("✓ Version requirement met")
    else:
        print("✗ Version requirement not met")

    return is_compatible

# Example usage
get_node_version()
curl -X POST https://nexus.fortiblox.com/rpc \
  -H "X-API-Key: $FORTIBLOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getVersion"
  }'

# Response:
# {
#   "jsonrpc": "2.0",
#   "result": {
#     "solana-core": "1.18.0",
#     "feature-set": 1234567890
#   },
#   "id": 1
# }

Use Cases:

  • Compatibility checks
  • Feature availability verification
  • Debugging version-specific issues
  • Monitoring node upgrades

Performance Tips:

  • Extremely lightweight (1 credit)
  • Version rarely changes
  • Cache indefinitely or for hours

Real-World Use Cases

Network Health Dashboard

class NetworkMonitor {
  constructor(connection) {
    this.connection = connection;
    this.metrics = {
      health: null,
      epoch: null,
      validators: null,
      nodes: null,
      version: null,
      lastUpdate: null
    };
  }

  async updateMetrics() {
    try {
      // Gather all metrics in parallel
      const [health, epoch, validators, nodes, version] = await Promise.all([
        this.connection.getHealth().then(() => true).catch(() => false),
        this.connection.getEpochInfo(),
        this.connection.getVoteAccounts(),
        this.connection.getClusterNodes(),
        this.connection.getVersion()
      ]);

      this.metrics = {
        health,
        epoch,
        validators,
        nodes,
        version,
        lastUpdate: new Date()
      };

      return this.metrics;
    } catch (error) {
      console.error('Error updating metrics:', error);
      return null;
    }
  }

  getHealthStatus() {
    if (!this.metrics.health) return 'Unhealthy';

    const currentValidators = this.metrics.validators.current.length;
    const delinquentValidators = this.metrics.validators.delinquent.length;
    const healthRatio = currentValidators / (currentValidators + delinquentValidators);

    if (healthRatio >= 0.9) return 'Excellent';
    if (healthRatio >= 0.75) return 'Good';
    if (healthRatio >= 0.5) return 'Fair';
    return 'Poor';
  }

  async generateReport() {
    await this.updateMetrics();

    console.log('\n=== Network Health Report ===');
    console.log(`Generated: ${this.metrics.lastUpdate.toISOString()}\n`);

    console.log('Node Health:', this.metrics.health ? '✓ Healthy' : '✗ Unhealthy');
    console.log(`Version: ${this.metrics.version['solana-core']}\n`);

    console.log('Epoch Information:');
    console.log(`  Current Epoch: ${this.metrics.epoch.epoch}`);
    console.log(`  Progress: ${((this.metrics.epoch.slotIndex / this.metrics.epoch.slotsInEpoch) * 100).toFixed(2)}%`);
    console.log(`  Block Height: ${this.metrics.epoch.blockHeight.toLocaleString()}\n`);

    console.log('Validator Status:');
    console.log(`  Current: ${this.metrics.validators.current.length}`);
    console.log(`  Delinquent: ${this.metrics.validators.delinquent.length}`);
    console.log(`  Overall Health: ${this.getHealthStatus()}\n`);

    console.log(`Cluster Nodes: ${this.metrics.nodes.length}`);
    console.log('============================\n');
  }

  startMonitoring(intervalSeconds = 60) {
    console.log('Starting network monitoring...');
    this.generateReport();

    setInterval(() => {
      this.generateReport();
    }, intervalSeconds * 1000);
  }
}

// Usage
const monitor = new NetworkMonitor(connection);
monitor.startMonitoring(60);

Pre-Transaction Network Check

async function performPreTransactionChecks() {
  console.log('Performing pre-transaction checks...\n');

  const checks = {
    nodeHealth: false,
    networkVersion: false,
    validatorHealth: false,
    epochProgress: false
  };

  try {
    // 1. Check node health
    await connection.getHealth();
    checks.nodeHealth = true;
    console.log('✓ Node is healthy');
  } catch (error) {
    console.log('✗ Node is unhealthy');
    return checks;
  }

  // 2. Check network version
  const version = await connection.getVersion();
  const minVersion = '1.16.0';
  if (version['solana-core'] >= minVersion) {
    checks.networkVersion = true;
    console.log(`✓ Node version ${version['solana-core']} meets minimum ${minVersion}`);
  } else {
    console.log(`✗ Node version ${version['solana-core']} below minimum ${minVersion}`);
  }

  // 3. Check validator health
  const voteAccounts = await connection.getVoteAccounts();
  const totalValidators = voteAccounts.current.length + voteAccounts.delinquent.length;
  const healthRatio = voteAccounts.current.length / totalValidators;

  if (healthRatio >= 0.67) {
    checks.validatorHealth = true;
    console.log(`✓ Validator health good (${(healthRatio * 100).toFixed(1)}% current)`);
  } else {
    console.log(`✗ Validator health poor (${(healthRatio * 100).toFixed(1)}% current)`);
  }

  // 4. Check we're not at epoch boundary
  const epochInfo = await connection.getEpochInfo();
  const progress = epochInfo.slotIndex / epochInfo.slotsInEpoch;

  if (progress < 0.98) {
    checks.epochProgress = true;
    console.log(`✓ Epoch progress safe (${(progress * 100).toFixed(1)}%)`);
  } else {
    console.log(`✗ Near epoch boundary (${(progress * 100).toFixed(1)}%)`);
  }

  const allPassed = Object.values(checks).every(check => check);

  console.log(`\n${allPassed ? '✓' : '✗'} Overall: ${allPassed ? 'Safe to proceed' : 'Wait before transacting'}`);

  return checks;
}

Validator Monitoring Service

class ValidatorMonitor {
  constructor(connection, validatorPubkey) {
    this.connection = connection;
    this.validatorPubkey = validatorPubkey;
    this.history = [];
  }

  async checkStatus() {
    const voteAccounts = await this.connection.getVoteAccounts();

    const validator = [...voteAccounts.current, ...voteAccounts.delinquent].find(
      v => v.nodePubkey === this.validatorPubkey || v.votePubkey === this.validatorPubkey
    );

    if (!validator) {
      return { found: false };
    }

    const isCurrent = voteAccounts.current.some(
      v => v.votePubkey === validator.votePubkey
    );

    const status = {
      found: true,
      timestamp: new Date(),
      isCurrent,
      activatedStake: validator.activatedStake,
      commission: validator.commission,
      lastVote: validator.lastVote,
      rootSlot: validator.rootSlot
    };

    this.history.push(status);

    // Keep last 100 records
    if (this.history.length > 100) {
      this.history.shift();
    }

    return status;
  }

  async detectStatusChange(previousStatus, currentStatus) {
    if (!previousStatus.found || !currentStatus.found) return;

    if (previousStatus.isCurrent && !currentStatus.isCurrent) {
      console.log(`⚠️ Validator became DELINQUENT at ${currentStatus.timestamp.toISOString()}`);
      // Send alert
    }

    if (!previousStatus.isCurrent && currentStatus.isCurrent) {
      console.log(`✓ Validator became CURRENT at ${currentStatus.timestamp.toISOString()}`);
      // Send notification
    }
  }

  async monitor(intervalSeconds = 60) {
    console.log(`Monitoring validator: ${this.validatorPubkey}\n`);

    let previousStatus = await this.checkStatus();

    setInterval(async () => {
      const currentStatus = await this.checkStatus();

      if (!currentStatus.found) {
        console.log(`[${new Date().toISOString()}] ✗ Validator not found`);
        return;
      }

      console.log(`[${new Date().toISOString()}] ${currentStatus.isCurrent ? '✓ Current' : '✗ Delinquent'}`);
      console.log(`  Stake: ${(currentStatus.activatedStake / 1e9).toFixed(2)} SOL`);
      console.log(`  Last Vote: ${currentStatus.lastVote}`);

      await this.detectStatusChange(previousStatus, currentStatus);

      previousStatus = currentStatus;
    }, intervalSeconds * 1000);
  }

  getUptimePercentage() {
    if (this.history.length === 0) return 0;

    const currentCount = this.history.filter(s => s.isCurrent).length;
    return (currentCount / this.history.length) * 100;
  }
}

// Usage
const validatorMonitor = new ValidatorMonitor(
  connection,
  'VALIDATOR_PUBKEY_HERE'
);
validatorMonitor.monitor(60);

Performance Optimization

Batch Network Queries

async function getBatchNetworkInfo() {
  // Fetch multiple metrics in parallel
  const [health, epoch, slot, blockHeight, version] = await Promise.all([
    connection.getHealth().then(() => true).catch(() => false),
    connection.getEpochInfo(),
    connection.getSlot(),
    connection.getBlockHeight(),
    connection.getVersion()
  ]);

  return {
    health,
    epoch,
    slot,
    blockHeight,
    version,
    timestamp: new Date()
  };
}

Smart Caching

class CachedNetworkInfo {
  constructor(connection) {
    this.connection = connection;
    this.cache = new Map();
  }

  async getEpochInfo(maxAge = 5000) {
    return this.getCached('epochInfo', maxAge, () =>
      this.connection.getEpochInfo()
    );
  }

  async getVersion(maxAge = 3600000) {
    return this.getCached('version', maxAge, () =>
      this.connection.getVersion()
    );
  }

  async getCached(key, maxAge, fetcher) {
    const cached = this.cache.get(key);

    if (cached && Date.now() - cached.timestamp < maxAge) {
      return cached.data;
    }

    const data = await fetcher();
    this.cache.set(key, { data, timestamp: Date.now() });

    return data;
  }

  clear() {
    this.cache.clear();
  }
}

const cachedInfo = new CachedNetworkInfo(connection);

Additional Resources