Documentation Index
Fetch the complete documentation index at: https://docs.orbitflare.com/llms.txt
Use this file to discover all available pages before exploring further.
Параметры
Объект конфигурации, содержащий:Уровень подтверждения (processed, confirmed, finalized)
Открытый ключ конкретного голосующего счёта для запроса (в кодировке base-58)
Включать ли нестейкинговые просроченные голосующие счета
Максимальное расстояние слота для признания голосующего счёта просроченным
Ответ
Объект, содержащий:Массив текущих объектов голосующих счетов, содержащих:Открытый ключ голосующего счёта (в кодировке base-58)
Открытый ключ валидатора (в кодировке base-58)
Размер активированного стейка в lamports
Голосовал ли счёт в текущей эпохе
Массив объектов кредитов эпохи, содержащих:Количество заработанных кредитов
Количество кредитов в предыдущей эпохе
Слот последнего голосования
Массив просроченных голосующих счетов с такой же структурой, как и 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()
}
};
}
}