Перейти к основному содержанию

Параметры

config
object
Объект конфигурации, содержащий:
commitment
string
Уровень подтверждения (processed, confirmed, finalized)
votePubkey
string
Открытый ключ конкретного голосующего счёта для запроса (в кодировке base-58)
keepUnstakedDelinquents
boolean
Включать ли нестейкинговые просроченные голосующие счета
delinquentSlotDistance
number
Максимальное расстояние слота для признания голосующего счёта просроченным

Ответ

result
object
Объект, содержащий:
current
array
Массив текущих объектов голосующих счетов, содержащих:
votePubkey
string
Открытый ключ голосующего счёта (в кодировке base-58)
nodePubkey
string
Открытый ключ валидатора (в кодировке base-58)
activatedStake
number
Размер активированного стейка в lamports
epochVoteAccount
boolean
Голосовал ли счёт в текущей эпохе
epochCredits
array
Массив объектов кредитов эпохи, содержащих:
epoch
number
Номер эпохи
credits
number
Количество заработанных кредитов
previousCredits
number
Количество кредитов в предыдущей эпохе
lastVote
number
Слот последнего голосования
rootSlot
number
Корневой слот
delinquent
array
Массив просроченных голосующих счетов с такой же структурой, как и current

Примеры кода

Базовый запрос

curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "getVoteAccounts"
}'

Использование web3.js

import { Connection } from '@solana/web3.js';

const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');

// Get vote accounts
const voteAccounts = await connection.getVoteAccounts();
console.log('Vote accounts:', voteAccounts);

// Get vote accounts with analysis
async function getVoteAccountsWithAnalysis(
  config: { 
    commitment?: string;
    votePubkey?: string;
    keepUnstakedDelinquents?: boolean;
    delinquentSlotDistance?: number;
  }
) {
  const voteAccounts = await connection.getVoteAccounts(config);
  
  return {
    voteAccounts,
    analysis: {
      current: {
        count: voteAccounts.current.length,
        totalStake: voteAccounts.current.reduce((sum, acc) => sum + acc.activatedStake, 0),
        averageStake: voteAccounts.current.reduce((sum, acc) => sum + acc.activatedStake, 0) / voteAccounts.current.length,
        activeVoters: voteAccounts.current.filter(acc => acc.epochVoteAccount).length
      },
      delinquent: {
        count: voteAccounts.delinquent.length,
        totalStake: voteAccounts.delinquent.reduce((sum, acc) => sum + acc.activatedStake, 0),
        averageStake: voteAccounts.delinquent.reduce((sum, acc) => sum + acc.activatedStake, 0) / voteAccounts.delinquent.length,
        activeVoters: voteAccounts.delinquent.filter(acc => acc.epochVoteAccount).length
      },
      metadata: {
        timestamp: Date.now(),
        commitment: config.commitment
      }
    }
  };
}

Примечания

  1. Возвращает текущие голосующие счета
  2. Ответ включает как текущие, так и просроченные голосующие счета
  3. Ответ приходит немедленно, так как читается из текущего состояния
  4. Счета могут изменяться при изменении стейка и голосовании
  5. Голосующие счета должны быть действительными

Рекомендации

  1. Используйте подходящий уровень подтверждения в зависимости от ваших потребностей
  2. Кэшируйте результаты там, где это уместно, для снижения нагрузки на RPC
  3. Отслеживайте изменения в голосующих счетах
  4. Рассмотрите использование подписки на websocket для обновлений в реальном времени
  5. Обрабатывайте сетевые ошибки и выполняйте повторные попытки при необходимости

Распространённые ошибки

КодСообщениеРешение
-32601Method not foundУбедитесь, что вы подключены к узлу Solana RPC
-32602Invalid paramsПроверьте параметры конфигурации
-32007Vote account not foundУбедитесь, что голосующий счёт существует

Сценарии использования

  1. Анализ голосующих счетов
    interface VoteAccountAnalysis {
      accounts: {
        current: Array<{
          votePubkey: string;
          nodePubkey: string;
          activatedStake: number;
          epochVoteAccount: boolean;
          lastVote: number;
          rootSlot?: number;
        }>;
        delinquent: Array<{
          votePubkey: string;
          nodePubkey: string;
          activatedStake: number;
          epochVoteAccount: boolean;
          lastVote: number;
          rootSlot?: number;
        }>;
      };
      metrics: {
        current: {
          count: number;
          totalStake: number;
          averageStake: number;
          activeVoters: number;
          participationRate: number;
        };
        delinquent: {
          count: number;
          totalStake: number;
          averageStake: number;
          activeVoters: number;
          participationRate: number;
        };
      };
      metadata: {
        timestamp: number;
        commitment?: string;
      };
    }
    
    class VoteAccountAnalyzer {
      async analyzeVoteAccounts(
        config: { 
          commitment?: string;
          votePubkey?: string;
          keepUnstakedDelinquents?: boolean;
          delinquentSlotDistance?: number;
        }
      ): Promise<VoteAccountAnalysis> {
        const voteAccounts = await connection.getVoteAccounts(config);
        
        return {
          accounts: {
            current: voteAccounts.current.map(acc => ({
              votePubkey: acc.votePubkey,
              nodePubkey: acc.nodePubkey,
              activatedStake: acc.activatedStake,
              epochVoteAccount: acc.epochVoteAccount,
              lastVote: acc.lastVote,
              rootSlot: acc.rootSlot
            })),
            delinquent: voteAccounts.delinquent.map(acc => ({
              votePubkey: acc.votePubkey,
              nodePubkey: acc.nodePubkey,
              activatedStake: acc.activatedStake,
              epochVoteAccount: acc.epochVoteAccount,
              lastVote: acc.lastVote,
              rootSlot: acc.rootSlot
            }))
          },
          metrics: {
            current: {
              count: voteAccounts.current.length,
              totalStake: voteAccounts.current.reduce((sum, acc) => sum + acc.activatedStake, 0),
              averageStake: voteAccounts.current.reduce((sum, acc) => sum + acc.activatedStake, 0) / voteAccounts.current.length,
              activeVoters: voteAccounts.current.filter(acc => acc.epochVoteAccount).length,
              participationRate: voteAccounts.current.filter(acc => acc.epochVoteAccount).length / voteAccounts.current.length
            },
            delinquent: {
              count: voteAccounts.delinquent.length,
              totalStake: voteAccounts.delinquent.reduce((sum, acc) => sum + acc.activatedStake, 0),
              averageStake: voteAccounts.delinquent.reduce((sum, acc) => sum + acc.activatedStake, 0) / voteAccounts.delinquent.length,
              activeVoters: voteAccounts.delinquent.filter(acc => acc.epochVoteAccount).length,
              participationRate: voteAccounts.delinquent.filter(acc => acc.epochVoteAccount).length / voteAccounts.delinquent.length
            }
          },
          metadata: {
            timestamp: Date.now(),
            commitment: config.commitment
          }
        };
      }
    }
    
  2. Мониторинг голосующих счетов
    interface VoteAccountChange {
      changes: {
        current: Array<{
          type: 'added' | 'removed' | 'modified';
          votePubkey: string;
          previousStake?: number;
          currentStake?: number;
          previousVote?: boolean;
          currentVote?: boolean;
        }>;
        delinquent: Array<{
          type: 'added' | 'removed' | 'modified';
          votePubkey: string;
          previousStake?: number;
          currentStake?: number;
          previousVote?: boolean;
          currentVote?: boolean;
        }>;
      };
      metadata: {
        timestamp: number;
      };
    }
    
    class VoteAccountMonitor {
      private previousAccounts: {
        current: Map<string, {
          stake: number;
          vote: boolean;
        }>;
        delinquent: Map<string, {
          stake: number;
          vote: boolean;
        }>;
      } = {
        current: new Map(),
        delinquent: new Map()
      };
      
      async monitorVoteAccounts(
        config: { 
          commitment?: string;
          votePubkey?: string;
          keepUnstakedDelinquents?: boolean;
          delinquentSlotDistance?: number;
        }
      ): Promise<VoteAccountChange | null> {
        const voteAccounts = await connection.getVoteAccounts(config);
        
        const changes: {
          current: Array<{
            type: 'added' | 'removed' | 'modified';
            votePubkey: string;
            previousStake?: number;
            currentStake?: number;
            previousVote?: boolean;
            currentVote?: boolean;
          }>;
          delinquent: Array<{
            type: 'added' | 'removed' | 'modified';
            votePubkey: string;
            previousStake?: number;
            currentStake?: number;
            previousVote?: boolean;
            currentVote?: boolean;
          }>;
        } = {
          current: [],
          delinquent: []
        };
        
        // Check current accounts
        this.checkAccountChanges(
          voteAccounts.current,
          this.previousAccounts.current,
          changes.current
        );
        
        // Check delinquent accounts
        this.checkAccountChanges(
          voteAccounts.delinquent,
          this.previousAccounts.delinquent,
          changes.delinquent
        );
        
        // Update previous accounts
        this.previousAccounts.current = new Map(
          voteAccounts.current.map(acc => [
            acc.votePubkey,
            {
              stake: acc.activatedStake,
              vote: acc.epochVoteAccount
            }
          ])
        );
        
        this.previousAccounts.delinquent = new Map(
          voteAccounts.delinquent.map(acc => [
            acc.votePubkey,
            {
              stake: acc.activatedStake,
              vote: acc.epochVoteAccount
            }
          ])
        );
        
        if (changes.current.length > 0 || changes.delinquent.length > 0) {
          return {
            changes,
            metadata: {
              timestamp: Date.now()
            }
          };
        }
        
        return null;
      }
      
      private checkAccountChanges(
        currentAccounts: Array<{
          votePubkey: string;
          activatedStake: number;
          epochVoteAccount: boolean;
        }>,
        previousAccounts: Map<string, {
          stake: number;
          vote: boolean;
        }>,
        changes: Array<{
          type: 'added' | 'removed' | 'modified';
          votePubkey: string;
          previousStake?: number;
          currentStake?: number;
          previousVote?: boolean;
          currentVote?: boolean;
        }>
      ) {
        // Check for removed or modified accounts
        previousAccounts.forEach((data, votePubkey) => {
          const current = currentAccounts.find(acc => acc.votePubkey === votePubkey);
          
          if (!current) {
            changes.push({
              type: 'removed',
              votePubkey,
              previousStake: data.stake,
              previousVote: data.vote
            });
          } else if (
            current.activatedStake !== data.stake ||
            current.epochVoteAccount !== data.vote
          ) {
            changes.push({
              type: 'modified',
              votePubkey,
              previousStake: data.stake,
              currentStake: current.activatedStake,
              previousVote: data.vote,
              currentVote: current.epochVoteAccount
            });
          }
        });
        
        // Check for added accounts
        currentAccounts.forEach(account => {
          if (!previousAccounts.has(account.votePubkey)) {
            changes.push({
              type: 'added',
              votePubkey: account.votePubkey,
              currentStake: account.activatedStake,
              currentVote: account.epochVoteAccount
            });
          }
        });
      }
    }
    
  3. Планирование голосующих счетов
    interface VoteAccountPlan {
      accounts: {
        current: Array<{
          votePubkey: string;
          nodePubkey: string;
          activatedStake: number;
          epochVoteAccount: boolean;
        }>;
        delinquent: Array<{
          votePubkey: string;
          nodePubkey: string;
          activatedStake: number;
          epochVoteAccount: boolean;
        }>;
      };
      recommendations: Array<{
        type: 'stake' | 'unstake' | 'monitor';
        votePubkey: string;
        reason: string;
      }>;
      metadata: {
        timestamp: number;
      };
    }
    
    class VoteAccountPlanner {
      private readonly minStake = 1000000000; // 1 SOL
      private readonly maxDelinquentVotes = 3;
      
      async planVoteAccounts(
        config: { 
          commitment?: string;
          votePubkey?: string;
          keepUnstakedDelinquents?: boolean;
          delinquentSlotDistance?: number;
        }
      ): Promise<VoteAccountPlan> {
        const voteAccounts = await connection.getVoteAccounts(config);
        
        const recommendations: Array<{
          type: 'stake' | 'unstake' | 'monitor';
          votePubkey: string;
          reason: string;
        }> = [];
        
        // Check current accounts
        voteAccounts.current.forEach(account => {
          if (account.activatedStake < this.minStake) {
            recommendations.push({
              type: 'stake',
              votePubkey: account.votePubkey,
              reason: `Account has low stake (${account.activatedStake})`
            });
          }
          
          if (!account.epochVoteAccount) {
            recommendations.push({
              type: 'monitor',
              votePubkey: account.votePubkey,
              reason: 'Account not voting in current epoch'
            });
          }
        });
        
        // Check delinquent accounts
        voteAccounts.delinquent.forEach(account => {
          if (account.activatedStake > 0) {
            recommendations.push({
              type: 'unstake',
              votePubkey: account.votePubkey,
              reason: 'Account is delinquent with stake'
            });
          }
        });
        
        return {
          accounts: {
            current: voteAccounts.current.map(acc => ({
              votePubkey: acc.votePubkey,
              nodePubkey: acc.nodePubkey,
              activatedStake: acc.activatedStake,
              epochVoteAccount: acc.epochVoteAccount
            })),
            delinquent: voteAccounts.delinquent.map(acc => ({
              votePubkey: acc.votePubkey,
              nodePubkey: acc.nodePubkey,
              activatedStake: acc.activatedStake,
              epochVoteAccount: acc.epochVoteAccount
            }))
          },
          recommendations,
          metadata: {
            timestamp: Date.now()
          }
        };
      }
    }