chain_subscribeFinalizedHeads - JSON-RPC Method
Description
Creates a subscription to receive notifications about finalized block headers. This JSON-RPC method only sends headers for blocks that have been finalized by GRANDPA consensus, ensuring they will never be reverted. Available only via WebSocket connections.
Parameters
This method does not require any parameters.
Returns
Field | Type | Description |
---|---|---|
subscription | string | Subscription ID for managing the subscription |
Subscription Updates
Each notification contains a finalized block header with:
Field | Type | Description |
---|---|---|
parentHash | string | Hash of the parent block |
number | string | Block number (hex-encoded) |
stateRoot | string | Root of the state trie |
extrinsicsRoot | string | Root of the extrinsics trie |
digest | object | Digest items including consensus messages |
Request Example
{
"jsonrpc": "2.0",
"method": "chain_subscribeFinalizedHeads",
"params": [],
"id": 1
}
Response Example
{
"jsonrpc": "2.0",
"result": "0xabcdef1234567890",
"id": 1
}
Subscription Update Example
{
"jsonrpc": "2.0",
"method": "chain_finalizedHead",
"params": {
"subscription": "0xabcdef1234567890",
"result": {
"parentHash": "0x4a84d1c5fc5c725b26a3c8e126d8f65e1c2d2dc5b3fcbc91e67e7637fa136789",
"number": "0x123450",
"stateRoot": "0x8f7a82e2e0f3e73f5e232e68de18144f6e7a52a6db39bb5e5e3d9ca6e1f72845",
"extrinsicsRoot": "0x2e6f9a7d2c4b3e1a8f5c6d9e4b1a7c3f8e5d2a9b6c4e1f8a5d3c2b9e7f6a4d38",
"digest": {
"logs": [
"0x0642414245b501013c0000009d2ef70f00000000"
]
}
}
}
}
Code Examples
JavaScript (WebSocket)
const WebSocket = require('ws');
const subscribeFinalizedHeads = () => {
const ws = new WebSocket('wss://api-polkadot.n.dwellir.com');
let subscriptionId = null;
ws.on('open', () => {
// Subscribe to finalized heads
ws.send(JSON.stringify({
jsonrpc: '2.0',
method: 'chain_subscribeFinalizedHeads',
params: [],
id: 1
}));
});
ws.on('message', (data) => {
const response = JSON.parse(data);
// Handle subscription confirmation
if (response.id === 1) {
subscriptionId = response.result;
console.log('Subscribed to finalized heads:', subscriptionId);
}
// Handle finalized block headers
if (response.method === 'chain_finalizedHead') {
const header = response.params.result;
const blockNumber = parseInt(header.number, 16);
console.log(`Block #${blockNumber} finalized`);
console.log(` Hash: ${header.parentHash}`);
console.log(` State Root: ${header.stateRoot}`);
// Process finalized block
processFinalizedBlock(header);
}
});
return ws;
};
const processFinalizedBlock = async (header) => {
const blockNumber = parseInt(header.number, 16);
// Safe to process transactions as finalized
console.log(`Processing finalized block ${blockNumber}`);
// Example: Update database with finalized state
// await updateDatabase(blockNumber, header);
// Example: Trigger post-finalization actions
// await notifyServices(blockNumber);
};
Python (websockets)
import asyncio
import json
import websockets
from datetime import datetime
class FinalityMonitor:
def __init__(self, ws_url):
self.ws_url = ws_url
self.finalized_blocks = []
self.last_finalized = None
async def subscribe(self):
async with websockets.connect(self.ws_url) as websocket:
# Subscribe to finalized heads
subscribe_msg = json.dumps({
"jsonrpc": "2.0",
"method": "chain_subscribeFinalizedHeads",
"params": [],
"id": 1
})
await websocket.send(subscribe_msg)
subscription_id = None
async for message in websocket:
data = json.loads(message)
# Handle subscription confirmation
if data.get("id") == 1:
subscription_id = data["result"]
print(f"Monitoring finality - Subscription: {subscription_id}")
# Handle finalized headers
elif data.get("method") == "chain_finalizedHead":
header = data["params"]["result"]
await self.process_finalized(header)
async def process_finalized(self, header):
block_number = int(header["number"], 16)
timestamp = datetime.now()
# Track finalization lag
if self.last_finalized:
blocks_finalized = block_number - self.last_finalized["number"]
time_elapsed = (timestamp - self.last_finalized["timestamp"]).total_seconds()
print(f"Finalized block {block_number}")
print(f" Blocks finalized: {blocks_finalized}")
print(f" Time elapsed: {time_elapsed:.2f}s")
print(f" Finalization rate: {blocks_finalized/time_elapsed:.2f} blocks/s")
self.last_finalized = {
"number": block_number,
"timestamp": timestamp,
"hash": header["parentHash"]
}
self.finalized_blocks.append(block_number)
# Run the monitor
monitor = FinalityMonitor("wss://api-polkadot.n.dwellir.com")
asyncio.run(monitor.subscribe())
TypeScript (@polkadot/api)
import { ApiPromise, WsProvider } from '@polkadot/api';
interface FinalityStats {
lastFinalized: number;
averageLag: number;
finalizedCount: number;
}
class FinalityTracker {
private api: ApiPromise;
private stats: FinalityStats;
private unsubscribe: (() => void) | null = null;
constructor() {
this.stats = {
lastFinalized: 0,
averageLag: 0,
finalizedCount: 0
};
}
async connect(wsUrl: string) {
const provider = new WsProvider(wsUrl);
this.api = await ApiPromise.create({ provider });
// Get current head for comparison
const currentHead = await this.api.rpc.chain.getHeader();
const currentNumber = currentHead.number.toNumber();
// Subscribe to finalized heads
this.unsubscribe = await this.api.rpc.chain.subscribeFinalizedHeads(
(header) => {
const finalizedNumber = header.number.toNumber();
const lag = currentNumber - finalizedNumber;
// Update statistics
this.stats.finalizedCount++;
this.stats.lastFinalized = finalizedNumber;
this.stats.averageLag =
(this.stats.averageLag * (this.stats.finalizedCount - 1) + lag) /
this.stats.finalizedCount;
console.log(`Finalized: #${finalizedNumber}`);
console.log(` Current lag: ${lag} blocks`);
console.log(` Average lag: ${this.stats.averageLag.toFixed(1)} blocks`);
// Trigger finalized block processing
this.onFinalized(header);
}
);
}
private onFinalized(header: any) {
// Implement your finalized block logic here
// This is safe for irreversible operations
}
async disconnect() {
if (this.unsubscribe) {
this.unsubscribe();
}
await this.api.disconnect();
}
getStats(): FinalityStats {
return this.stats;
}
}
// Usage
const tracker = new FinalityTracker();
await tracker.connect('wss://api-polkadot.n.dwellir.com');
// Get stats after some time
setTimeout(() => {
const stats = tracker.getStats();
console.log('Finality Statistics:', stats);
}, 60000);
Finality Lag Monitoring
class FinalityLagMonitor {
constructor(wsUrl) {
this.wsUrl = wsUrl;
this.latestBlock = 0;
this.finalizedBlock = 0;
}
async start() {
const ws = new WebSocket(this.wsUrl);
ws.on('open', () => {
// Subscribe to both new heads and finalized heads
ws.send(JSON.stringify({
jsonrpc: '2.0',
method: 'chain_subscribeNewHeads',
params: [],
id: 1
}));
ws.send(JSON.stringify({
jsonrpc: '2.0',
method: 'chain_subscribeFinalizedHeads',
params: [],
id: 2
}));
});
ws.on('message', (data) => {
const response = JSON.parse(data);
if (response.method === 'chain_newHead') {
const blockNumber = parseInt(response.params.result.number, 16);
this.latestBlock = blockNumber;
this.updateLag();
} else if (response.method === 'chain_finalizedHead') {
const blockNumber = parseInt(response.params.result.number, 16);
this.finalizedBlock = blockNumber;
this.updateLag();
}
});
}
updateLag() {
const lag = this.latestBlock - this.finalizedBlock;
console.log(`Finality lag: ${lag} blocks (Latest: ${this.latestBlock}, Finalized: ${this.finalizedBlock})`);
// Alert if lag is too high
if (lag > 10) {
console.warn(`High finality lag detected: ${lag} blocks`);
}
}
}
Use Cases
- Transaction Confirmation: Wait for finalization before confirming payments
- Database Updates: Only store finalized state to avoid reorgs
- Cross-chain Bridges: Process transfers after finalization
- Exchange Integration: Credit deposits after finalization
- Analytics: Track network finality performance
Notes
- Finalized blocks are irreversible and safe for permanent storage
- Finalization typically lags 2-3 blocks behind the head
- GRANDPA provides deterministic finality
- Use this for operations requiring absolute certainty
- WebSocket connection required (not available over HTTP)
Related Methods
chain_getFinalizedHead
- Get current finalized headchain_subscribeNewHeads
- Subscribe to all new headersgrandpa_roundState
- Monitor GRANDPA finality