From f5f836aa25347b8f6e69cf33ad6896c533feaa6b Mon Sep 17 00:00:00 2001 From: Roman Yarlykov Date: Tue, 17 Feb 2026 19:28:27 +0500 Subject: [PATCH 1/2] add: finality page and table --- docs/components/FinalityTable.tsx | 156 ++++++++++++++++++++++ docs/pages/integrate-concero/finality.mdx | 45 +++++++ vocs.config.tsx | 4 + 3 files changed, 205 insertions(+) create mode 100644 docs/components/FinalityTable.tsx create mode 100644 docs/pages/integrate-concero/finality.mdx diff --git a/docs/components/FinalityTable.tsx b/docs/components/FinalityTable.tsx new file mode 100644 index 0000000..40b7aed --- /dev/null +++ b/docs/components/FinalityTable.tsx @@ -0,0 +1,156 @@ +import { useState, useEffect } from 'react' +import { GITHUB_REPOSITORIES } from '../constants/config' + +interface NetworkEntry { + name: string + chainSelector: number + finalityTagEnabled?: boolean + finalityConfirmations?: number + minBlockConfirmations?: number +} + +interface FinalityRow { + name: string + chainSelector: string + finalityMethod: string + minBlockConfirmations: number +} + +function parseFinalityMethod(entry: NetworkEntry): string { + if (entry.finalityTagEnabled === true) { + return 'finalized' + } + if (typeof entry.finalityConfirmations === 'number' && entry.finalityConfirmations > 0) { + return String(entry.finalityConfirmations) + } + return 'none' +} + +function parseRows(data: unknown): FinalityRow[] { + if (typeof data !== 'object' || data === null || Array.isArray(data)) return [] + + const rows: FinalityRow[] = [] + + for (const item of Object.values(data as Record)) { + if (typeof item !== 'object' || item === null) continue + + const entry = item as Record + const name = typeof entry.name === 'string' ? entry.name.trim() : null + if (!name) continue + + const chainSelector = entry.chainSelector != null ? String(entry.chainSelector) : '—' + const minBlockConfirmations = + typeof entry.minBlockConfirmations === 'number' ? entry.minBlockConfirmations : 1 + + rows.push({ + name, + chainSelector, + finalityMethod: parseFinalityMethod(entry as unknown as NetworkEntry), + minBlockConfirmations, + }) + } + + return rows + .filter(row => row.finalityMethod !== 'none') + .sort((a, b) => a.name.localeCompare(b.name)) +} + +export function FinalityTable() { + const [rows, setRows] = useState([]) + const [loading, setLoading] = useState(true) + const [error, setError] = useState(null) + + useEffect(() => { + let isActive = true + const controller = new AbortController() + + const fetchData = async () => { + try { + const response = await fetch(GITHUB_REPOSITORIES.V2_NETWORKS.MAINNET_NETWORKS_URL, { + signal: controller.signal, + }) + + if (!response.ok) { + throw new Error(`Failed to fetch: ${response.status} ${response.statusText}`) + } + + const data = await response.json() + + if (!isActive) return + + setRows(parseRows(data)) + setError(null) + } catch (err) { + if (err instanceof DOMException && err.name === 'AbortError') return + if (isActive) { + setError(err instanceof Error ? err.message : 'An unknown error occurred') + } + } finally { + if (isActive) setLoading(false) + } + } + + fetchData() + + return () => { + isActive = false + controller.abort() + } + }, []) + + if (loading) { + return
Loading finality data...
+ } + + if (error) { + return
Error loading finality data: {error}
+ } + + if (rows.length === 0) { + return
No chain data available.
+ } + + return ( +
+ + + + {['SRC Blockchain', 'Chain selector', 'Finality (tag/block depth)', 'minBlockConfirmations'].map(col => ( + + ))} + + + + {rows.map(row => ( + + + + + + + ))} + +
+ {col} +
{row.name}{row.chainSelector}{row.finalityMethod}{row.minBlockConfirmations}
+
+ ) +} + +const cellStyle: React.CSSProperties = { + padding: '8px 12px', + borderBottom: '1px solid var(--vocs-color_tableBorder)', + fontSize: '0.9rem', + color: 'var(--vocs-color_text)', +} diff --git a/docs/pages/integrate-concero/finality.mdx b/docs/pages/integrate-concero/finality.mdx new file mode 100644 index 0000000..3ddb4fe --- /dev/null +++ b/docs/pages/integrate-concero/finality.mdx @@ -0,0 +1,45 @@ +--- +title: Finality +description: +--- + +import { FinalityTable } from '../../components/FinalityTable' + +# Finality + +// TODO: Add links to sources + +Finality determines how many block confirmations the protocol waits for before forwarding a message to the destination chain. It is controlled via the `srcBlockConfirmations` field in `MessageRequest`. + +## How to enable finality + +Set `srcBlockConfirmations` to `type(uint64).max` (`18446744073709551615`) to request finality-based confirmation instead of a fixed block count. + +```solidity +IConceroRouter.MessageRequest({ + dstChainSelector: dstChainSelector, + srcBlockConfirmations: type(uint64).max, // enable finality + // ... +}); +``` + +When finality is enabled, the Relayer waits until the source block is included in the chain's finalized head (`eth_getBlockByNumber("finalized")`). For chains where the finality tag is unavailable, the protocol falls back to a fixed `finalityConfirmations` count defined in the chain configuration. + +## Min block confirmations + +Each chain defines a minimum number of block confirmations (`minBlockConfirmations`). When `srcBlockConfirmations` is set to 0, the protocol automatically applies this minimum value. If the chain does not define one, the protocol defaults to 1. + +Use 0 when you want the fastest possible delivery without manually tracking per-chain safe minimums. + +Warning +The following configurations lead to silent failure: + - dstChainSelector references a chain not supported by the protocol + - srcBlockConfirmations = type(uint64).max is set for a chain where finality is not supported (isFinalitySupported = false) +†† +Always verify the destination chain selector and finality support before sending a message. + +## Supported chains + +Finality is only available on chains where `isFinalitySupported` or `finalityConfirmations` is set in the protocol's [chain configuration](https://github.com/concero/v2-networks/blob/master/networks/mainnet.json). + + diff --git a/vocs.config.tsx b/vocs.config.tsx index 07cf9b9..87f876b 100644 --- a/vocs.config.tsx +++ b/vocs.config.tsx @@ -152,6 +152,10 @@ export default defineConfig({ text: 'Send & Receive a message', link: '/integrate-concero/send-a-message', }, + { + text: 'Finality', + link: '/integrate-concero/finality', + }, { text: 'Track a message', link: '/integrate-concero/track-a-message', From cca422c1fda16dc17e8ef0d961508de48f8e5294 Mon Sep 17 00:00:00 2001 From: Roman Yarlykov Date: Wed, 18 Feb 2026 11:35:28 +0500 Subject: [PATCH 2/2] refactor: finality page --- docs/components/FinalityTable.tsx | 14 +++--- docs/pages/integrate-concero/finality.mdx | 56 ++++++++++++++++------- 2 files changed, 48 insertions(+), 22 deletions(-) diff --git a/docs/components/FinalityTable.tsx b/docs/components/FinalityTable.tsx index 40b7aed..2c09141 100644 --- a/docs/components/FinalityTable.tsx +++ b/docs/components/FinalityTable.tsx @@ -39,8 +39,7 @@ function parseRows(data: unknown): FinalityRow[] { if (!name) continue const chainSelector = entry.chainSelector != null ? String(entry.chainSelector) : '—' - const minBlockConfirmations = - typeof entry.minBlockConfirmations === 'number' ? entry.minBlockConfirmations : 1 + const minBlockConfirmations = typeof entry.minBlockConfirmations === 'number' ? entry.minBlockConfirmations : 1 rows.push({ name, @@ -50,9 +49,7 @@ function parseRows(data: unknown): FinalityRow[] { }) } - return rows - .filter(row => row.finalityMethod !== 'none') - .sort((a, b) => a.name.localeCompare(b.name)) + return rows.filter(row => row.finalityMethod !== 'none').sort((a, b) => a.name.localeCompare(b.name)) } export function FinalityTable() { @@ -115,7 +112,12 @@ export function FinalityTable() { - {['SRC Blockchain', 'Chain selector', 'Finality (tag/block depth)', 'minBlockConfirmations'].map(col => ( + {[ + 'SRC Blockchain', + 'Chain selector', + 'Finality (tag/finalityConfirmations)', + 'minBlockConfirmations', + ].map(col => (