Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/components/CCIP/Chain/Chain.astro
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
Network,
getAllNetworkLanes,
getAllNetworks,
getAllUniqueVerifiers,
getSearchLanes,
getTokensOfChain,
Version,
Expand Down Expand Up @@ -49,6 +50,11 @@ const lanes = await getAllNetworkLanes({

const searchLanes = getSearchLanes({ environment })

const allVerifiers = getAllUniqueVerifiers({
environment,
version: Version.V1_2_0,
})

// Generate dynamic metadata for this specific chain
const environmentText = environment === Environment.Mainnet ? "Mainnet" : "Testnet"
const logoPath = network.logo || ""
Expand Down Expand Up @@ -107,6 +113,7 @@ const chainStructuredData = generateChainStructuredData(
network={network}
environment={environment}
lanes={searchLanes}
verifiers={allVerifiers}
client:load
/>
<section class="layout">
Expand Down
27 changes: 25 additions & 2 deletions src/components/CCIP/ChainHero/ChainHero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ interface ChainHeroProps {
}
lane: LaneConfig
}[]
verifiers?: {
id: string
name: string
type: string
logo: string
totalNetworks: number
}[]
network?: Network
token?: {
id: string
Expand All @@ -60,7 +67,16 @@ interface ChainHeroProps {
}>
}

function ChainHero({ chains, tokens, network, token, environment, lanes, breadcrumbItems }: ChainHeroProps) {
function ChainHero({
chains,
tokens,
network,
token,
environment,
lanes,
verifiers = [],
breadcrumbItems,
}: ChainHeroProps) {
// Get chain-specific tooltip configuration
const chainTooltipConfig = network?.chain ? getChainTooltip(network.chain) : null

Expand Down Expand Up @@ -119,7 +135,14 @@ function ChainHero({ chains, tokens, network, token, environment, lanes, breadcr
}
/>
<div className="ccip-chain-hero__chainSearch">
<Search chains={chains} tokens={tokens} small environment={environment} lanes={lanes} />
<Search
chains={chains}
tokens={tokens}
small
environment={environment}
lanes={lanes}
verifiers={verifiers}
/>
</div>
</div>

Expand Down
11 changes: 9 additions & 2 deletions src/components/CCIP/Hero/Hero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,24 @@ interface HeroProps {
}
lane: LaneConfig
}[]
verifiers?: {
id: string
name: string
type: string
logo: string
totalNetworks: number
}[]
environment: Environment
}

function Hero({ chains, tokens, environment, lanes }: HeroProps) {
function Hero({ chains, tokens, environment, lanes, verifiers = [] }: HeroProps) {
return (
<section className="ccip-hero">
<div className="ccip-hero__content">
<Typography variant="h1" className="ccip-hero__heading">
CCIP Directory
</Typography>
<Search chains={chains} tokens={tokens} environment={environment} lanes={lanes} />
<Search chains={chains} tokens={tokens} environment={environment} lanes={lanes} verifiers={verifiers} />
</div>
</section>
)
Expand Down
9 changes: 8 additions & 1 deletion src/components/CCIP/Landing/ccip-landing.astro
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,14 @@ const directoryStructuredData = generateDirectoryStructuredData(environment, net
}}
suppressDefaultStructuredData={true}
>
<Hero chains={networks} tokens={allTokens} environment={environment} client:visible lanes={searchLanes} />
<Hero
chains={networks}
tokens={allTokens}
environment={environment}
client:visible
lanes={searchLanes}
verifiers={allVerifiers}
/>
<section class="layout">
<div>
<div class="ccip-heading">
Expand Down
60 changes: 52 additions & 8 deletions src/components/CCIP/Search/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,26 @@ interface SearchProps {
}
lane: LaneConfig
}[]
verifiers?: {
id: string
name: string
type: string
logo: string
totalNetworks: number
}[]
small?: boolean
environment: Environment
}

function Search({ chains, tokens, small, environment, lanes }: SearchProps) {
function Search({ chains, tokens, small, environment, lanes, verifiers = [] }: SearchProps) {
const [search, setSearch] = useState("")
const [debouncedSearch, setDebouncedSearch] = useState("")
const [openSearchMenu, setOpenSearchMenu] = useState(false)
const [isActive, setIsActive] = useState(false)
const [networksResults, setNetworksResults] = useState<typeof chains>([])
const [tokensResults, setTokensResults] = useState<typeof tokens>([])
const [lanesResults, setLanesResults] = useState<typeof lanes>([])
const [verifiersResults, setVerifiersResults] = useState<typeof verifiers>([])
const searchRef = useRef<HTMLDivElement>(null)
const workerRef = useRef<Worker | null>(null)
const workerReadyRef = useRef(false)
Expand All @@ -59,10 +67,11 @@ function Search({ chains, tokens, small, environment, lanes }: SearchProps) {
if (workerReadyRef.current || typeof window === "undefined") return
workerRef.current = new Worker(new URL("~/workers/data-worker.ts", import.meta.url), { type: "module" })
workerRef.current.onmessage = (event: MessageEvent<WorkerResponse>) => {
const { networks, tokens: workerTokens, lanes: workerLanes } = event.data
const { networks, tokens: workerTokens, lanes: workerLanes, verifiers: workerVerifiers } = event.data
setNetworksResults(networks || [])
setTokensResults(workerTokens || [])
setLanesResults(workerLanes || [])
setVerifiersResults(workerVerifiers || [])
}
workerReadyRef.current = true
}
Expand Down Expand Up @@ -90,6 +99,7 @@ function Search({ chains, tokens, small, environment, lanes }: SearchProps) {
setNetworksResults([])
setTokensResults([])
setLanesResults([])
setVerifiersResults([])
return
}

Expand All @@ -102,11 +112,12 @@ function Search({ chains, tokens, small, environment, lanes }: SearchProps) {
chains,
tokens,
lanes,
verifiers,
},
}
workerRef.current.postMessage(message)
}
}, [debouncedSearch, chains, tokens, lanes])
}, [debouncedSearch, chains, tokens, lanes, verifiers])

// Handle menu visibility
useEffect(() => {
Expand Down Expand Up @@ -146,15 +157,15 @@ function Search({ chains, tokens, small, environment, lanes }: SearchProps) {
<img src="/assets/icons/search.svg" alt="Search icon" />
<input
type="search"
placeholder="Network/Token/Lane"
placeholder="Network/Token/Lane/Verifier"
value={search}
onChange={(e) => setSearch(e.target.value)}
onFocus={() => {
setIsActive(true)
ensureWorker()
}}
onBlur={() => setIsActive(false)}
aria-label="Search networks, tokens, and lanes"
aria-label="Search networks, tokens, lanes, and verifiers"
aria-describedby={openSearchMenu ? "search-results" : undefined}
/>
{openSearchMenu && (
Expand All @@ -167,9 +178,12 @@ function Search({ chains, tokens, small, environment, lanes }: SearchProps) {
aria-live="polite"
aria-label="Search results"
>
{networksResults.length === 0 && tokensResults.length === 0 && (
<span className="ccip-hero__search-results__no-result">No results found</span>
)}
{networksResults.length === 0 &&
tokensResults.length === 0 &&
lanesResults.length === 0 &&
verifiersResults.length === 0 && (
<span className="ccip-hero__search-results__no-result">No results found</span>
)}
{networksResults.length > 0 && (
<>
<span className="ccip-hero__search-results__title">Networks</span>
Expand Down Expand Up @@ -284,6 +298,36 @@ function Search({ chains, tokens, small, environment, lanes }: SearchProps) {
</ul>
</>
)}

{verifiersResults.length > 0 && (
<>
<span className="ccip-hero__search-results__title">Verifiers</span>
<ul aria-label="Verifiers">
{verifiersResults.map((verifier) => (
<li key={verifier.id}>
<a href={`/ccip/directory/${environment}/verifiers#${verifier.id}`}>
<img
src={verifier.logo}
alt={`${verifier.name} verifier logo`}
loading="lazy"
onError={({ currentTarget }) => {
currentTarget.onerror = null // prevents looping
currentTarget.src = fallbackTokenIconUrl
}}
/>
{verifier.name}
{!small && (
<span>
{verifier.totalNetworks} {verifier.totalNetworks > 1 ? "networks" : "network"} |{" "}
{verifier.type}
</span>
)}
</a>
</li>
))}
</ul>
</>
)}
</div>
)}
</div>
Expand Down
7 changes: 7 additions & 0 deletions src/components/CCIP/Token/Token.astro
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
getAllNetworks,
getAllSupportedTokens,
getAllTokenLanes,
getAllUniqueVerifiers,
getChainsOfToken,
getSearchLanes,
getTokenData,
Expand Down Expand Up @@ -76,6 +77,11 @@ const tokenLanes = getAllTokenLanes({

const searchLanes = getSearchLanes({ environment })

const allVerifiers = getAllUniqueVerifiers({
environment,
version: Version.V1_2_0,
})

// Generate dynamic metadata for this specific token
const environmentText = environment === Environment.Mainnet ? "Mainnet" : "Testnet"
const tokenMetadata = {
Expand Down Expand Up @@ -117,6 +123,7 @@ const tokenStructuredData = generateTokenStructuredData(token, environment, chai
tokens={allTokens}
client:load
lanes={searchLanes}
verifiers={allVerifiers}
token={{
id: token,
name: data[firstSupportedChain]?.name || "",
Expand Down
13 changes: 8 additions & 5 deletions src/components/CCIP/Verifiers/Verifiers.astro
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ const uniqueVerifiers = getAllUniqueVerifiers({

const searchLanes = getSearchLanes({ environment })

const allTokens = uniqueVerifiers.map((verifier) => ({
id: verifier.id,
totalNetworks: verifier.totalNetworks,
logo: verifier.logo,
}))

// Generate dynamic metadata for verifiers page
const environmentText = environment === Environment.Mainnet ? "Mainnet" : "Testnet"
const verifiersMetadata = {
Expand Down Expand Up @@ -70,12 +76,9 @@ const canonicalForJsonLd = `${DOCS_BASE_URL}${currentPath}`
>
<ChainHero
chains={networks}
tokens={uniqueVerifiers.map((verifier) => ({
id: verifier.id,
totalNetworks: verifier.totalNetworks,
logo: verifier.logo,
}))}
tokens={allTokens}
lanes={searchLanes}
verifiers={uniqueVerifiers}
environment={environment}
breadcrumbItems={[
{
Expand Down
20 changes: 18 additions & 2 deletions src/workers/data-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ interface SearchData {
}
lane: LaneConfig
}>
verifiers: Array<{
id: string
name: string
type: string
logo: string
totalNetworks: number
}>
}

interface WorkerMessage {
Expand All @@ -44,14 +51,15 @@ interface WorkerResponse {
networks: SearchData["chains"]
tokens: SearchData["tokens"]
lanes: SearchData["lanes"]
verifiers: SearchData["verifiers"]
}

self.onmessage = (event: MessageEvent<WorkerMessage>) => {
// Basic validation - Web Workers run in isolated contexts, lower security risk
const { search, data } = event.data

if (!search || !data) {
self.postMessage({ networks: [], tokens: [], lanes: [] } as WorkerResponse)
self.postMessage({ networks: [], tokens: [], lanes: [], verifiers: [] } as WorkerResponse)
return
}

Expand All @@ -74,7 +82,15 @@ self.onmessage = (event: MessageEvent<WorkerMessage>) => {
return matchesNetwork && hasTokens
})

self.postMessage({ networks, tokens, lanes } as WorkerResponse)
// Filter verifiers
const verifiers = data.verifiers.filter(
(verifier) =>
verifier.name.toLowerCase().includes(searchLower) ||
verifier.id.toLowerCase().includes(searchLower) ||
verifier.type.toLowerCase().includes(searchLower)
)

self.postMessage({ networks, tokens, lanes, verifiers } as WorkerResponse)
}

// Export types for use in main thread
Expand Down
Loading