diff --git a/src/components/CCIP/ChainHero/ChainHero.css b/src/components/CCIP/ChainHero/ChainHero.css index f87f696a869..98d1d58b11b 100644 --- a/src/components/CCIP/ChainHero/ChainHero.css +++ b/src/components/CCIP/ChainHero/ChainHero.css @@ -1,7 +1,6 @@ .ccip-chain-hero { background: var(--Page-Background-Alt); border-bottom: 1px solid var(--gray-200); - min-height: 241px; } .ccip-chain-hero__heading { diff --git a/src/components/CCIP/ChainHero/ChainHero.tsx b/src/components/CCIP/ChainHero/ChainHero.tsx index 07bd086223b..1b0550380ee 100644 --- a/src/components/CCIP/ChainHero/ChainHero.tsx +++ b/src/components/CCIP/ChainHero/ChainHero.tsx @@ -54,9 +54,13 @@ interface ChainHeroProps { symbol: string } environment: Environment + breadcrumbItems?: Array<{ + name: string + url: string + }> } -function ChainHero({ chains, tokens, network, token, environment, lanes }: ChainHeroProps) { +function ChainHero({ chains, tokens, network, token, environment, lanes, breadcrumbItems }: ChainHeroProps) { // Get chain-specific tooltip configuration const chainTooltipConfig = network?.chain ? getChainTooltip(network.chain) : null @@ -99,47 +103,51 @@ function ChainHero({ chains, tokens, network, token, environment, lanes }: Chain
-
- { - currentTarget.onerror = null // prevents looping - currentTarget.src = fallbackTokenIconUrl - }} - /> -

- {network?.name || token?.name} - {token?.id} + {(network || token) && ( +
+ { + currentTarget.onerror = null // prevents looping + currentTarget.src = fallbackTokenIconUrl + }} + /> +

+ {network?.name || token?.name} + {token?.id} - {chainTooltipConfig && ( - - )} -

-
+ {chainTooltipConfig && ( + + )} +

+
+ )} {network && (
diff --git a/src/components/CCIP/Tables/VerifiersTable.tsx b/src/components/CCIP/Tables/VerifiersTable.tsx new file mode 100644 index 00000000000..dc79bf8753e --- /dev/null +++ b/src/components/CCIP/Tables/VerifiersTable.tsx @@ -0,0 +1,124 @@ +import Address from "~/components/AddressReact.tsx" +import "./Table.css" +import { Environment, Verifier, getVerifierTypeDisplay } from "~/config/data/ccip/index.ts" +import TableSearchInput from "./TableSearchInput.tsx" +import { useState } from "react" +import { + getExplorerAddressUrl, + fallbackVerifierIconUrl, + getChainIcon, + getTitle, + directoryToSupportedChain, + getExplorer, + getChainTypeAndFamily, +} from "~/features/utils/index.ts" + +interface VerifiersTableProps { + verifiers: Verifier[] +} + +function VerifiersTable({ verifiers }: VerifiersTableProps) { + const [search, setSearch] = useState("") + + // Transform verifiers data to include network information + const verifiersWithNetworkInfo = verifiers.map((verifier) => { + const supportedChain = directoryToSupportedChain(verifier.network) + const networkName = getTitle(supportedChain) || verifier.network + const networkLogo = getChainIcon(supportedChain) || "" + const explorer = getExplorer(supportedChain) + const { chainType } = getChainTypeAndFamily(supportedChain) + + return { + ...verifier, + networkName, + networkLogo, + supportedChain, + explorer, + chainType, + } + }) + + const filteredVerifiers = verifiersWithNetworkInfo.filter( + (verifier) => + verifier.name.toLowerCase().includes(search.toLowerCase()) || + verifier.networkName.toLowerCase().includes(search.toLowerCase()) || + verifier.address.toLowerCase().includes(search.toLowerCase()) || + getVerifierTypeDisplay(verifier.type).toLowerCase().includes(search.toLowerCase()) + ) + + return ( + <> +
+
+ Verifiers ({verifiers.length}) +
+ +
+
+ + + + + + + + + + + {filteredVerifiers.map((verifier, index) => ( + + + + + + + ))} + +
VerifierNetworkVerifier addressVerifier type
+
+ + {`${verifier.name} { + currentTarget.onerror = null // prevents looping + currentTarget.src = fallbackVerifierIconUrl + }} + /> + + {verifier.name} +
+
+
+ + {`${verifier.networkName} { + currentTarget.onerror = null // prevents looping + currentTarget.src = fallbackVerifierIconUrl + }} + /> + + {verifier.networkName} +
+
+
+
{getVerifierTypeDisplay(verifier.type)}
+
{filteredVerifiers.length === 0 && <>No verifiers found}
+
+ + ) +} + +export default VerifiersTable diff --git a/src/components/CCIP/VerifierGrid/VerifierGrid.tsx b/src/components/CCIP/VerifierGrid/VerifierGrid.tsx index 5191cb5925d..133dbb95a97 100644 --- a/src/components/CCIP/VerifierGrid/VerifierGrid.tsx +++ b/src/components/CCIP/VerifierGrid/VerifierGrid.tsx @@ -20,7 +20,7 @@ function VerifierGrid({ verifiers, environment }: VerifierGridProps) { items={verifiers} initialDisplayCount={BEFORE_SEE_MORE} seeMoreLabel="View all verifiers" - seeMoreLink="/verifiers" + seeMoreLink={`/ccip/directory/${environment}/verifiers`} renderItem={(verifier) => { const subtitle = `${verifier.totalNetworks} ${verifier.totalNetworks === 1 ? "network" : "networks"}` const logoElement = ( @@ -34,8 +34,8 @@ function VerifierGrid({ verifiers, environment }: VerifierGridProps) { logo={logoElement} title={verifier.name} subtitle={subtitle} - link={`/ccip/directory/${environment}/verifier/${verifier.id}`} - ariaLabel={`View ${verifier.name} verifier details`} + link={`/ccip/directory/${environment}/verifiers`} + ariaLabel={`View verifiers page for ${verifier.name}`} /> ) }} diff --git a/src/components/CCIP/Verifiers/Verifiers.astro b/src/components/CCIP/Verifiers/Verifiers.astro new file mode 100644 index 00000000000..2672f983332 --- /dev/null +++ b/src/components/CCIP/Verifiers/Verifiers.astro @@ -0,0 +1,98 @@ +--- +import CcipDirectoryLayout from "~/layouts/CcipDirectoryLayout.astro" +import { getEntry, render } from "astro:content" +import { getAllNetworks, getAllVerifiers, getSearchLanes, Version, Environment } from "~/config/data/ccip" +import Table from "~/components/CCIP/Tables/VerifiersTable" +import { getAllUniqueVerifiers } from "~/config/data/ccip/data.ts" +import { DOCS_BASE_URL } from "~/utils/structuredData" +import ChainHero from "~/components/CCIP/ChainHero/ChainHero" +import "./Verifiers.css" + +interface Props { + environment: Environment +} + +const { environment } = Astro.props as Props + +const entry = await getEntry("ccip", "index") +if (!entry) { + throw new Error('Could not find "ccip/index" doc. Check src/content/ccip/index.mdx!') +} + +const { headings } = await render(entry) + +const networks = getAllNetworks({ filter: environment }) + +const allVerifiers = getAllVerifiers({ + environment, + version: Version.V1_2_0, +}) + +const uniqueVerifiers = getAllUniqueVerifiers({ + environment, + version: Version.V1_2_0, +}) + +const searchLanes = getSearchLanes({ environment }) + +// Generate dynamic metadata for verifiers page +const environmentText = environment === Environment.Mainnet ? "Mainnet" : "Testnet" +const verifiersMetadata = { + title: `CCIP Verifiers - ${environmentText} Networks`, + description: `View all CCIP verifiers across ${environmentText} networks. Explore ${allVerifiers.length} verifiers, their addresses, types, and supported networks for cross-chain verification.`, + image: "/assets/product-logos/ccip-logo.svg", + excerpt: `CCIP verifiers ${environmentText.toLowerCase()} networks addresses types committee api cross-chain verification blockchain interoperability`, +} + +// Generate structured data for verifiers page +const currentPath = new URL(Astro.request.url).pathname +const canonicalForJsonLd = `${DOCS_BASE_URL}${currentPath}` +--- + + + ({ + id: verifier.id, + totalNetworks: verifier.totalNetworks, + logo: verifier.logo, + }))} + lanes={searchLanes} + environment={environment} + breadcrumbItems={[ + { + name: "CCIP Directory", + url: `/ccip/directory/${environment}`, + }, + { + name: "Verifiers", + url: `/ccip/directory/${environment}/verifiers`, + }, + ]} + client:load + /> + +
+
+ + + + diff --git a/src/components/CCIP/Verifiers/Verifiers.css b/src/components/CCIP/Verifiers/Verifiers.css new file mode 100644 index 00000000000..eda464e886c --- /dev/null +++ b/src/components/CCIP/Verifiers/Verifiers.css @@ -0,0 +1,43 @@ +.layout { + margin: 0 auto; + padding: var(--space-6x); +} + +.layout h2 { + color: var(--gray-900); + font-size: 22px; + line-height: var(--space-10x); + padding-bottom: var(--space-4x); + border-bottom: 1px solid var(--gray-200); + margin-bottom: var(--space-6x); +} + +.layout h2 span { + color: var(--gray-400); + font-weight: 600; + letter-spacing: 0.5px; +} + +.networks__grid { + display: grid; + grid-template-columns: 1fr; + gap: var(--space-8x); +} + +.tokens__grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: var(--space-8x); +} + +@media (min-width: 50em) { + .layout { + max-width: 1500px; + } +} + +@media (min-width: 992px) { + .layout { + padding: var(--space-10x) var(--space-8x); + } +} diff --git a/src/pages/ccip/directory/mainnet/verifiers/index.astro b/src/pages/ccip/directory/mainnet/verifiers/index.astro new file mode 100644 index 00000000000..07aad33d482 --- /dev/null +++ b/src/pages/ccip/directory/mainnet/verifiers/index.astro @@ -0,0 +1,8 @@ +--- +import Verifiers from "~/components/CCIP/Verifiers/Verifiers.astro" +import { Environment } from "~/config/data/ccip" + +export const prerender = true +--- + + diff --git a/src/pages/ccip/directory/testnet/verifiers/index.astro b/src/pages/ccip/directory/testnet/verifiers/index.astro new file mode 100644 index 00000000000..a6101109fd7 --- /dev/null +++ b/src/pages/ccip/directory/testnet/verifiers/index.astro @@ -0,0 +1,8 @@ +--- +import Verifiers from "~/components/CCIP/Verifiers/Verifiers.astro" +import { Environment } from "~/config/data/ccip" + +export const prerender = true +--- + +