⚠️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-moonriver.n.dwellir.com/YOUR_API_KEY")
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)