⚠️Blast API (blastapi.io) ends Oct 31. Migrate to Dwellir and skip the expensive compute units.
Switch Today →
Skip to main content

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

FieldTypeDescription
subscriptionstringSubscription ID for managing the subscription

Subscription Updates

Each notification contains a finalized block header with:

FieldTypeDescription
parentHashstringHash of the parent block
numberstringBlock number (hex-encoded)
stateRootstringRoot of the state trie
extrinsicsRootstringRoot of the extrinsics trie
digestobjectDigest 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

  1. Transaction Confirmation: Wait for finalization before confirming payments
  2. Database Updates: Only store finalized state to avoid reorgs
  3. Cross-chain Bridges: Process transfers after finalization
  4. Exchange Integration: Credit deposits after finalization
  5. 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)