⚠️Blast API (blastapi.io) ends Oct 31. Migrate to Dwellir and skip Alchemy's 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#

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-asset-hub-polkadot.n.dwellir.com")
asyncio.run(monitor.subscribe())

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)