GetEpoch - Query Epoch Information
Retrieve Sui epoch information via gRPC including validators, stake distribution, and timing. Essential for staking applications and network monitoring with Dwellir.
Query Epoch Information and Validator Details
The GetEpoch method retrieves comprehensive information about a specific Sui epoch, including validator set, stake distribution, epoch timing, and network parameters. This is essential for staking applications, validator monitoring, and understanding network state transitions.
Overview
Sui operates on an epoch-based consensus model where the network progresses through discrete time periods (epochs), each lasting approximately 24 hours. At each epoch boundary, the validator set is reconfigured, staking rewards are distributed, and gas price references are updated. The GetEpoch method provides full visibility into this lifecycle, making it indispensable for staking dashboards, validator analytics, and governance tooling.
Unlike traditional blockchains where validator sets can change at any block, Sui's epoch model provides predictable validator rotation windows. This means applications can cache validator data for the duration of an epoch and only refresh at epoch boundaries, reducing query overhead significantly.
Key Capabilities
- Epoch Timing: Determine when the current epoch started, when it ends, and how much time remains
- Validator Set: Retrieve the full list of active validators including their voting power and metadata
- Gas Price Reference: Access the reference gas price set for the epoch by validator consensus
- Transaction Volume: Query total transactions processed within a given epoch
- Historical Analysis: Fetch data for any past epoch to build trend analytics
Method Signature
Service: sui.rpc.v2.LedgerService
Method: GetEpoch
Type: Unary RPC
Use Cases
Epoch Timing Calculator
interface EpochTiming {
epochNumber: number;
startTime: Date;
endTime: Date;
duration: number;
timeRemaining: number;
percentComplete: number;
}
async function getEpochTiming(): Promise<EpochTiming> {
const epoch = await getEpoch();
const startTime = new Date(parseInt(epoch.epoch_start_timestamp));
const endTime = new Date(parseInt(epoch.epoch_end_timestamp));
const now = new Date();
const duration = endTime.getTime() - startTime.getTime();
const elapsed = now.getTime() - startTime.getTime();
const timeRemaining = endTime.getTime() - now.getTime();
const percentComplete = (elapsed / duration) * 100;
return {
epochNumber: parseInt(epoch.epoch),
startTime,
endTime,
duration,
timeRemaining,
percentComplete
};
}
// Usage
const timing = await getEpochTiming();
console.log(`Epoch ${timing.epochNumber}`);
console.log(`Progress: ${timing.percentComplete.toFixed(2)}%`);
console.log(`Time remaining: ${Math.floor(timing.timeRemaining / 3600000)} hours`);Validator Analysis
interface ValidatorStats {
totalValidators: number;
totalVotingPower: bigint;
topValidators: Array<{
address: string;
name: string;
votingPower: bigint;
votingPowerPercent: number;
}>;
averageVotingPower: number;
}
async function analyzeValidators(epochId?: number): Promise<ValidatorStats> {
const epoch = await getEpoch(epochId);
const validators = epoch.validators;
const totalVotingPower = validators.reduce(
(sum: bigint, v: any) => sum + BigInt(v.voting_power),
0n
);
const validatorsWithPercent = validators.map((v: any) => {
const power = BigInt(v.voting_power);
const percent = (Number(power) / Number(totalVotingPower)) * 100;
return {
address: v.sui_address,
name: v.name,
votingPower: power,
votingPowerPercent: percent
};
});
// Sort by voting power
validatorsWithPercent.sort((a, b) =>
b.votingPower > a.votingPower ? 1 : -1
);
const topValidators = validatorsWithPercent.slice(0, 10);
return {
totalValidators: validators.length,
totalVotingPower,
topValidators,
averageVotingPower: Number(totalVotingPower) / validators.length
};
}
// Usage
const stats = await analyzeValidators();
console.log(`Total validators: ${stats.totalValidators}`);
console.log(`Total voting power: ${stats.totalVotingPower}`);
console.log('\nTop 10 validators:');
stats.topValidators.forEach((v, i) => {
console.log(`${i + 1}. ${v.name}: ${v.votingPowerPercent.toFixed(2)}%`);
});Find Validator by Address
async function findValidator(
validatorAddress: string,
epochId?: number
): Promise<any | null> {
const epoch = await getEpoch(epochId);
const validator = epoch.validators.find(
(v: any) => v.sui_address === validatorAddress
);
if (!validator) {
console.warn(`Validator ${validatorAddress} not found in epoch ${epoch.epoch}`);
return null;
}
return {
address: validator.sui_address,
name: validator.name,
description: validator.description,
votingPower: BigInt(validator.voting_power),
imageUrl: validator.image_url,
projectUrl: validator.project_url
};
}
// Usage
const validator = await findValidator('0x...');
if (validator) {
console.log('Validator:', validator.name);
console.log('Voting power:', validator.votingPower);
console.log('Website:', validator.projectUrl);
}Epoch History Tracker
interface EpochComparison {
epochNumber: number;
transactions: number;
gasPrice: bigint;
validatorCount: number;
duration: number;
}
async function compareEpochs(
startEpoch: number,
endEpoch: number
): Promise<EpochComparison[]> {
const epochPromises: Promise<any>[] = [];
for (let i = startEpoch; i <= endEpoch; i++) {
epochPromises.push(getEpoch(i));
}
const epochs = await Promise.all(epochPromises);
return epochs.map(epoch => {
const duration =
parseInt(epoch.epoch_end_timestamp) -
parseInt(epoch.epoch_start_timestamp);
return {
epochNumber: parseInt(epoch.epoch),
transactions: parseInt(epoch.epoch_total_transactions),
gasPrice: BigInt(epoch.reference_gas_price),
validatorCount: epoch.validators.length,
duration
};
});
}
// Usage - Compare last 10 epochs
const currentEpoch = await getEpoch();
const currentEpochNumber = parseInt(currentEpoch.epoch);
const history = await compareEpochs(
currentEpochNumber - 9,
currentEpochNumber
);
console.log('Epoch History:');
history.forEach(epoch => {
console.log(`Epoch ${epoch.epochNumber}:`);
console.log(` Transactions: ${epoch.transactions}`);
console.log(` Gas Price: ${epoch.gasPrice}`);
console.log(` Validators: ${epoch.validatorCount}`);
});Staking Dashboard Data
interface StakingDashboard {
currentEpoch: number;
epochProgress: number;
timeUntilNextEpoch: number;
referenceGasPrice: bigint;
totalValidators: number;
totalStake: bigint;
networkActivity: {
totalTransactions: number;
transactionsPerSecond: number;
};
}
async function getStakingDashboard(): Promise<StakingDashboard> {
const epoch = await getEpoch();
const startMs = parseInt(epoch.epoch_start_timestamp);
const endMs = parseInt(epoch.epoch_end_timestamp);
const nowMs = Date.now();
const duration = endMs - startMs;
const elapsed = nowMs - startMs;
const epochProgress = (elapsed / duration) * 100;
const timeUntilNextEpoch = endMs - nowMs;
const totalStake = epoch.validators.reduce(
(sum: bigint, v: any) => sum + BigInt(v.voting_power),
0n
);
const transactionsPerSecond =
parseInt(epoch.epoch_total_transactions) / (elapsed / 1000);
return {
currentEpoch: parseInt(epoch.epoch),
epochProgress,
timeUntilNextEpoch,
referenceGasPrice: BigInt(epoch.reference_gas_price),
totalValidators: epoch.validators.length,
totalStake,
networkActivity: {
totalTransactions: parseInt(epoch.epoch_total_transactions),
transactionsPerSecond
}
};
}
// Usage
const dashboard = await getStakingDashboard();
console.log('Sui Staking Dashboard');
console.log('====================');
console.log(`Current Epoch: ${dashboard.currentEpoch}`);
console.log(`Progress: ${dashboard.epochProgress.toFixed(2)}%`);
console.log(`Next epoch in: ${Math.floor(dashboard.timeUntilNextEpoch / 3600000)} hours`);
console.log(`Active Validators: ${dashboard.totalValidators}`);
console.log(`Total Stake: ${dashboard.totalStake}`);
console.log(`Network TPS: ${dashboard.networkActivity.transactionsPerSecond.toFixed(2)}`);Best Practices
Cache Current Epoch with Short TTL
class EpochCache {
private currentEpoch: { data: any; timestamp: number } | null = null;
private ttl = 60000; // 1 minute
async getCurrentEpoch(): Promise<any> {
if (
this.currentEpoch &&
Date.now() - this.currentEpoch.timestamp < this.ttl
) {
return this.currentEpoch.data;
}
const epoch = await getEpoch();
this.currentEpoch = {
data: epoch,
timestamp: Date.now()
};
return epoch;
}
invalidate(): void {
this.currentEpoch = null;
}
}Handle Epoch Transitions
async function waitForNextEpoch(): Promise<number> {
const currentEpoch = await getEpoch();
const currentEpochNumber = parseInt(currentEpoch.epoch);
const epochEndTime = parseInt(currentEpoch.epoch_end_timestamp);
const timeUntilNext = epochEndTime - Date.now();
if (timeUntilNext > 0) {
console.log(`Waiting ${Math.floor(timeUntilNext / 1000)}s for next epoch`);
await new Promise(resolve => setTimeout(resolve, timeUntilNext + 5000));
}
const nextEpoch = await getEpoch();
return parseInt(nextEpoch.epoch);
}Performance Characteristics
| Metric | Value |
|---|---|
| Typical Latency | 30-70ms |
| Response Size | 10-100KB (varies by validator count) |
| Cache Recommended | Yes (1-5 minute TTL for current epoch) |
| Rate Limit Impact | Low |
Common Errors
| Error Code | Scenario | Solution |
|---|---|---|
NOT_FOUND | Epoch doesn't exist yet | Query current or past epochs only |
INVALID_ARGUMENT | Invalid epoch ID | Verify epoch number is valid |
UNAUTHENTICATED | Missing/invalid token | Verify x-api-key header |
Related Methods
- GetCheckpoint - Query checkpoint data
- SubscribeCheckpoints - Monitor new checkpoints
Need help? Contact support@dwellir.com or check the gRPC overview.
GetCheckpoint
Retrieve Sui blockchain checkpoints via gRPC to access finalized state, transaction batches, and validator signatures. Essential for applications requiring guaranteed finality with Dwellir's infrastructure.
GetObject
Retrieve detailed Sui blockchain object information using the gRPC GetObject method. Access object metadata, ownership, version history, and contents with high-performance binary serialization through Dwellir's infrastructure.