Параметры
object
Объект конфигурации, содержащий:
string
Уровень подтверждения (processed, confirmed, finalized)
string
Открытый ключ конкретного голосующего счёта для запроса (в кодировке base-58)
boolean
Включать ли нестейкинговые просроченные голосующие счета
number
Максимальное расстояние слота для признания голосующего счёта просроченным
Ответ
object
Объект, содержащий:
array
Массив текущих объектов голосующих счетов, содержащих:
string
Открытый ключ голосующего счёта (в кодировке base-58)
string
Открытый ключ валидатора (в кодировке base-58)
number
Размер активированного стейка в lamports
boolean
Голосовал ли счёт в текущей эпохе
array
number
Слот последнего голосования
number
Корневой слот
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
}
}
};
}
Примечания
- Возвращает текущие голосующие счета
- Ответ включает как текущие, так и просроченные голосующие счета
- Ответ приходит немедленно, так как читается из текущего состояния
- Счета могут изменяться при изменении стейка и голосовании
- Голосующие счета должны быть действительными
Рекомендации
- Используйте подходящий уровень подтверждения в зависимости от ваших потребностей
- Кэшируйте результаты там, где это уместно, для снижения нагрузки на RPC
- Отслеживайте изменения в голосующих счетах
- Рассмотрите использование подписки на websocket для обновлений в реальном времени
- Обрабатывайте сетевые ошибки и выполняйте повторные попытки при необходимости
Распространённые ошибки
| Код | Сообщение | Решение |
|---|---|---|
| -32601 | Method not found | Убедитесь, что вы подключены к узлу Solana RPC |
| -32602 | Invalid params | Проверьте параметры конфигурации |
| -32007 | Vote account not found | Убедитесь, что голосующий счёт существует |
Сценарии использования
-
Анализ голосующих счетов
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 } }; } } -
Мониторинг голосующих счетов
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 }); } }); } } -
Планирование голосующих счетов
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() } }; } }