Returns the current vote accounts
curl https://rpc.orbitflare.com -X POST -H "Content-Type: application/json" -d '{ "jsonrpc": "2.0", "id": 1, "method": "getVoteAccounts" }'
import { Connection } from '@solana/web3.js'; const connection = new Connection('https://rpc.orbitflare.com'); // 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 } } }; }
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() } }; } }