Documentation Index
Fetch the complete documentation index at: https://voltaire.tevm.sh/llms.txt
Use this file to discover all available pages before exploring further.
ChainHead
Current chain head information representing the latest block at the tip of the blockchain.
Overview
ChainHead captures essential information about the latest block: number, hash, timestamp, and difficulty. Typically obtained from eth_getBlockByNumber(“latest”).
Type Definition
type ChainHeadType = {
readonly number: BlockNumber;
readonly hash: BlockHash;
readonly timestamp: Uint256; // Unix seconds
readonly difficulty?: Uint256; // 0 post-merge
readonly totalDifficulty?: Uint256; // Cumulative from genesis
};
Usage
Getting Chain Head
import { ChainHead } from '@tevm/voltaire/primitives';
// From RPC response
const block = await rpc.eth_getBlockByNumber('latest', false);
const head = ChainHead.from({
number: block.number,
hash: block.hash,
timestamp: block.timestamp
});
console.log(`Block ${head.number}`);
console.log(`Hash: ${BlockHash.toHex(head.hash)}`);
console.log(`Timestamp: ${head.timestamp}`);
Post-Merge (PoS)
Post-merge blocks have zero difficulty:
const head = ChainHead.from({
number: 18000000n,
hash: blockHash,
timestamp: 1699000000n,
difficulty: 0n // Proof of Stake
});
console.log('Post-merge block (PoS)');
Pre-Merge (PoW)
Pre-merge blocks have non-zero difficulty:
const head = ChainHead.from({
number: 15000000n,
hash: blockHash,
timestamp: 1650000000n,
difficulty: 12000000000000000n, // Block difficulty
totalDifficulty: 58750003716598352816469n // Cumulative
});
console.log(`Difficulty: ${head.difficulty}`);
console.log(`Total difficulty: ${head.totalDifficulty}`);
RPC Integration
eth_getBlockByNumber
Get latest block:
// Get latest block
const block = await rpc.eth_getBlockByNumber('latest', false);
const head = ChainHead.from({
number: BigInt(block.number),
hash: block.hash,
timestamp: BigInt(block.timestamp),
difficulty: block.difficulty ? BigInt(block.difficulty) : undefined,
totalDifficulty: block.totalDifficulty ? BigInt(block.totalDifficulty) : undefined
});
eth_blockNumber
Get just the latest block number:
const blockNumber = await rpc.eth_blockNumber();
console.log(`Current block: ${blockNumber}`);
Consensus Types
Proof of Stake (Post-Merge)
The Merge (September 15, 2022) transitioned Ethereum to PoS:
- Block 15537394+ have
difficulty: 0
- No mining, validators propose blocks
- 12 second slot time
function isPostMerge(head) {
return head.difficulty === 0n;
}
Proof of Work (Pre-Merge)
Pre-merge blocks had mining difficulty:
- Variable block times (~13 seconds average)
- Difficulty adjusted every 2016 blocks
- Total difficulty tracked cumulative work
function isPreMerge(head) {
return head.difficulty && head.difficulty > 0n;
}
Time Analysis
Block Age
Calculate block age:
const now = BigInt(Math.floor(Date.now() / 1000));
const age = now - head.timestamp;
console.log(`Block age: ${age} seconds`);
console.log(`Block age: ${age / 60n} minutes`);
Network Health
Recent blocks indicate healthy network:
const now = BigInt(Math.floor(Date.now() / 1000));
const age = now - head.timestamp;
if (age < 30n) {
console.log('Network healthy: recent block');
} else if (age < 300n) {
console.log('Network normal');
} else {
console.log('Warning: stale block data');
}
Chain Reorganizations
Track reorgs by monitoring chain head changes:
let previousHead = null;
async function checkForReorg() {
const block = await rpc.eth_getBlockByNumber('latest', false);
const currentHead = ChainHead.from(block);
if (previousHead) {
// Same number but different hash = reorg
if (currentHead.number === previousHead.number &&
!BlockHash.equals(currentHead.hash, previousHead.hash)) {
console.log('Chain reorganization detected!');
}
// Number went backwards = deep reorg
if (currentHead.number < previousHead.number) {
console.log('Deep reorganization detected!');
}
}
previousHead = currentHead;
}
setInterval(checkForReorg, 12000); // Check every 12 seconds
Finality
Gasper Finality (PoS)
Blocks finalized after 2 epochs (~12.8 minutes):
async function isFinalized(blockNumber, rpc) {
const latest = await rpc.eth_getBlockByNumber('latest', false);
const currentBlock = BigInt(latest.number);
// ~64 blocks per epoch, 2 epochs for finality
const finalityDistance = 128n;
return currentBlock - blockNumber >= finalityDistance;
}
Safe Block
Safe block is justified (~1 epoch old):
const safeBlock = await rpc.eth_getBlockByNumber('safe', false);
const safeHead = ChainHead.from(safeBlock);
console.log(`Safe block: ${safeHead.number}`);
Common Patterns
Wait for Block
Wait for specific block number:
async function waitForBlock(targetBlock, rpc) {
while (true) {
const block = await rpc.eth_getBlockByNumber('latest', false);
const head = ChainHead.from(block);
if (head.number >= targetBlock) {
return head;
}
await new Promise(resolve => setTimeout(resolve, 12000));
}
}
Block Production Rate
Calculate blocks per second:
async function getBlockRate(rpc, samples = 10) {
const latest = await rpc.eth_getBlockByNumber('latest', false);
const latestHead = ChainHead.from(latest);
const oldBlockNum = latestHead.number - BigInt(samples);
const old = await rpc.eth_getBlockByNumber(`0x${oldBlockNum.toString(16)}`, false);
const oldHead = ChainHead.from(old);
const timeDelta = latestHead.timestamp - oldHead.timestamp;
const blockDelta = latestHead.number - oldHead.number;
const blocksPerSecond = Number(blockDelta) / Number(timeDelta);
return blocksPerSecond;
}
API Reference
Constructors
ChainHead.from({ number, hash, timestamp, difficulty?, totalDifficulty? }) - Create from block data
See Also