Skip to content

Commit 950bfe0

Browse files
authored
feat: add verifiers to the search (#184)
* feat: add verifiers to the search * refactor: simplify verifiers mapping in Chain component * refactor: simplify verifiers mapping in CCIP components * refactor: optimize token mapping for verifiers in CCIP components
1 parent d16d244 commit 950bfe0

File tree

8 files changed

+134
-20
lines changed

8 files changed

+134
-20
lines changed

src/components/CCIP/Chain/Chain.astro

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
Network,
77
getAllNetworkLanes,
88
getAllNetworks,
9+
getAllUniqueVerifiers,
910
getSearchLanes,
1011
getTokensOfChain,
1112
Version,
@@ -49,6 +50,11 @@ const lanes = await getAllNetworkLanes({
4950
5051
const searchLanes = getSearchLanes({ environment })
5152
53+
const allVerifiers = getAllUniqueVerifiers({
54+
environment,
55+
version: Version.V1_2_0,
56+
})
57+
5258
// Generate dynamic metadata for this specific chain
5359
const environmentText = environment === Environment.Mainnet ? "Mainnet" : "Testnet"
5460
const logoPath = network.logo || ""
@@ -107,6 +113,7 @@ const chainStructuredData = generateChainStructuredData(
107113
network={network}
108114
environment={environment}
109115
lanes={searchLanes}
116+
verifiers={allVerifiers}
110117
client:load
111118
/>
112119
<section class="layout">

src/components/CCIP/ChainHero/ChainHero.tsx

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@ interface ChainHeroProps {
4646
}
4747
lane: LaneConfig
4848
}[]
49+
verifiers?: {
50+
id: string
51+
name: string
52+
type: string
53+
logo: string
54+
totalNetworks: number
55+
}[]
4956
network?: Network
5057
token?: {
5158
id: string
@@ -60,7 +67,16 @@ interface ChainHeroProps {
6067
}>
6168
}
6269

63-
function ChainHero({ chains, tokens, network, token, environment, lanes, breadcrumbItems }: ChainHeroProps) {
70+
function ChainHero({
71+
chains,
72+
tokens,
73+
network,
74+
token,
75+
environment,
76+
lanes,
77+
verifiers = [],
78+
breadcrumbItems,
79+
}: ChainHeroProps) {
6480
// Get chain-specific tooltip configuration
6581
const chainTooltipConfig = network?.chain ? getChainTooltip(network.chain) : null
6682

@@ -119,7 +135,14 @@ function ChainHero({ chains, tokens, network, token, environment, lanes, breadcr
119135
}
120136
/>
121137
<div className="ccip-chain-hero__chainSearch">
122-
<Search chains={chains} tokens={tokens} small environment={environment} lanes={lanes} />
138+
<Search
139+
chains={chains}
140+
tokens={tokens}
141+
small
142+
environment={environment}
143+
lanes={lanes}
144+
verifiers={verifiers}
145+
/>
123146
</div>
124147
</div>
125148

src/components/CCIP/Hero/Hero.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,24 @@ interface HeroProps {
3333
}
3434
lane: LaneConfig
3535
}[]
36+
verifiers?: {
37+
id: string
38+
name: string
39+
type: string
40+
logo: string
41+
totalNetworks: number
42+
}[]
3643
environment: Environment
3744
}
3845

39-
function Hero({ chains, tokens, environment, lanes }: HeroProps) {
46+
function Hero({ chains, tokens, environment, lanes, verifiers = [] }: HeroProps) {
4047
return (
4148
<section className="ccip-hero">
4249
<div className="ccip-hero__content">
4350
<Typography variant="h1" className="ccip-hero__heading">
4451
CCIP Directory
4552
</Typography>
46-
<Search chains={chains} tokens={tokens} environment={environment} lanes={lanes} />
53+
<Search chains={chains} tokens={tokens} environment={environment} lanes={lanes} verifiers={verifiers} />
4754
</div>
4855
</section>
4956
)

src/components/CCIP/Landing/ccip-landing.astro

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,14 @@ const directoryStructuredData = generateDirectoryStructuredData(environment, net
7272
}}
7373
suppressDefaultStructuredData={true}
7474
>
75-
<Hero chains={networks} tokens={allTokens} environment={environment} client:visible lanes={searchLanes} />
75+
<Hero
76+
chains={networks}
77+
tokens={allTokens}
78+
environment={environment}
79+
client:visible
80+
lanes={searchLanes}
81+
verifiers={allVerifiers}
82+
/>
7683
<section class="layout">
7784
<div>
7885
<div class="ccip-heading">

src/components/CCIP/Search/Search.tsx

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,18 +38,26 @@ interface SearchProps {
3838
}
3939
lane: LaneConfig
4040
}[]
41+
verifiers?: {
42+
id: string
43+
name: string
44+
type: string
45+
logo: string
46+
totalNetworks: number
47+
}[]
4148
small?: boolean
4249
environment: Environment
4350
}
4451

45-
function Search({ chains, tokens, small, environment, lanes }: SearchProps) {
52+
function Search({ chains, tokens, small, environment, lanes, verifiers = [] }: SearchProps) {
4653
const [search, setSearch] = useState("")
4754
const [debouncedSearch, setDebouncedSearch] = useState("")
4855
const [openSearchMenu, setOpenSearchMenu] = useState(false)
4956
const [isActive, setIsActive] = useState(false)
5057
const [networksResults, setNetworksResults] = useState<typeof chains>([])
5158
const [tokensResults, setTokensResults] = useState<typeof tokens>([])
5259
const [lanesResults, setLanesResults] = useState<typeof lanes>([])
60+
const [verifiersResults, setVerifiersResults] = useState<typeof verifiers>([])
5361
const searchRef = useRef<HTMLDivElement>(null)
5462
const workerRef = useRef<Worker | null>(null)
5563
const workerReadyRef = useRef(false)
@@ -59,10 +67,11 @@ function Search({ chains, tokens, small, environment, lanes }: SearchProps) {
5967
if (workerReadyRef.current || typeof window === "undefined") return
6068
workerRef.current = new Worker(new URL("~/workers/data-worker.ts", import.meta.url), { type: "module" })
6169
workerRef.current.onmessage = (event: MessageEvent<WorkerResponse>) => {
62-
const { networks, tokens: workerTokens, lanes: workerLanes } = event.data
70+
const { networks, tokens: workerTokens, lanes: workerLanes, verifiers: workerVerifiers } = event.data
6371
setNetworksResults(networks || [])
6472
setTokensResults(workerTokens || [])
6573
setLanesResults(workerLanes || [])
74+
setVerifiersResults(workerVerifiers || [])
6675
}
6776
workerReadyRef.current = true
6877
}
@@ -90,6 +99,7 @@ function Search({ chains, tokens, small, environment, lanes }: SearchProps) {
9099
setNetworksResults([])
91100
setTokensResults([])
92101
setLanesResults([])
102+
setVerifiersResults([])
93103
return
94104
}
95105

@@ -102,11 +112,12 @@ function Search({ chains, tokens, small, environment, lanes }: SearchProps) {
102112
chains,
103113
tokens,
104114
lanes,
115+
verifiers,
105116
},
106117
}
107118
workerRef.current.postMessage(message)
108119
}
109-
}, [debouncedSearch, chains, tokens, lanes])
120+
}, [debouncedSearch, chains, tokens, lanes, verifiers])
110121

111122
// Handle menu visibility
112123
useEffect(() => {
@@ -146,15 +157,15 @@ function Search({ chains, tokens, small, environment, lanes }: SearchProps) {
146157
<img src="/assets/icons/search.svg" alt="Search icon" />
147158
<input
148159
type="search"
149-
placeholder="Network/Token/Lane"
160+
placeholder="Network/Token/Lane/Verifier"
150161
value={search}
151162
onChange={(e) => setSearch(e.target.value)}
152163
onFocus={() => {
153164
setIsActive(true)
154165
ensureWorker()
155166
}}
156167
onBlur={() => setIsActive(false)}
157-
aria-label="Search networks, tokens, and lanes"
168+
aria-label="Search networks, tokens, lanes, and verifiers"
158169
aria-describedby={openSearchMenu ? "search-results" : undefined}
159170
/>
160171
{openSearchMenu && (
@@ -167,9 +178,12 @@ function Search({ chains, tokens, small, environment, lanes }: SearchProps) {
167178
aria-live="polite"
168179
aria-label="Search results"
169180
>
170-
{networksResults.length === 0 && tokensResults.length === 0 && (
171-
<span className="ccip-hero__search-results__no-result">No results found</span>
172-
)}
181+
{networksResults.length === 0 &&
182+
tokensResults.length === 0 &&
183+
lanesResults.length === 0 &&
184+
verifiersResults.length === 0 && (
185+
<span className="ccip-hero__search-results__no-result">No results found</span>
186+
)}
173187
{networksResults.length > 0 && (
174188
<>
175189
<span className="ccip-hero__search-results__title">Networks</span>
@@ -284,6 +298,36 @@ function Search({ chains, tokens, small, environment, lanes }: SearchProps) {
284298
</ul>
285299
</>
286300
)}
301+
302+
{verifiersResults.length > 0 && (
303+
<>
304+
<span className="ccip-hero__search-results__title">Verifiers</span>
305+
<ul aria-label="Verifiers">
306+
{verifiersResults.map((verifier) => (
307+
<li key={verifier.id}>
308+
<a href={`/ccip/directory/${environment}/verifiers#${verifier.id}`}>
309+
<img
310+
src={verifier.logo}
311+
alt={`${verifier.name} verifier logo`}
312+
loading="lazy"
313+
onError={({ currentTarget }) => {
314+
currentTarget.onerror = null // prevents looping
315+
currentTarget.src = fallbackTokenIconUrl
316+
}}
317+
/>
318+
{verifier.name}
319+
{!small && (
320+
<span>
321+
{verifier.totalNetworks} {verifier.totalNetworks > 1 ? "networks" : "network"} |{" "}
322+
{verifier.type}
323+
</span>
324+
)}
325+
</a>
326+
</li>
327+
))}
328+
</ul>
329+
</>
330+
)}
287331
</div>
288332
)}
289333
</div>

src/components/CCIP/Token/Token.astro

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
getAllNetworks,
66
getAllSupportedTokens,
77
getAllTokenLanes,
8+
getAllUniqueVerifiers,
89
getChainsOfToken,
910
getSearchLanes,
1011
getTokenData,
@@ -76,6 +77,11 @@ const tokenLanes = getAllTokenLanes({
7677
7778
const searchLanes = getSearchLanes({ environment })
7879
80+
const allVerifiers = getAllUniqueVerifiers({
81+
environment,
82+
version: Version.V1_2_0,
83+
})
84+
7985
// Generate dynamic metadata for this specific token
8086
const environmentText = environment === Environment.Mainnet ? "Mainnet" : "Testnet"
8187
const tokenMetadata = {
@@ -117,6 +123,7 @@ const tokenStructuredData = generateTokenStructuredData(token, environment, chai
117123
tokens={allTokens}
118124
client:load
119125
lanes={searchLanes}
126+
verifiers={allVerifiers}
120127
token={{
121128
id: token,
122129
name: data[firstSupportedChain]?.name || "",

src/components/CCIP/Verifiers/Verifiers.astro

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ const uniqueVerifiers = getAllUniqueVerifiers({
3535
3636
const searchLanes = getSearchLanes({ environment })
3737
38+
const allTokens = uniqueVerifiers.map((verifier) => ({
39+
id: verifier.id,
40+
totalNetworks: verifier.totalNetworks,
41+
logo: verifier.logo,
42+
}))
43+
3844
// Generate dynamic metadata for verifiers page
3945
const environmentText = environment === Environment.Mainnet ? "Mainnet" : "Testnet"
4046
const verifiersMetadata = {
@@ -70,12 +76,9 @@ const canonicalForJsonLd = `${DOCS_BASE_URL}${currentPath}`
7076
>
7177
<ChainHero
7278
chains={networks}
73-
tokens={uniqueVerifiers.map((verifier) => ({
74-
id: verifier.id,
75-
totalNetworks: verifier.totalNetworks,
76-
logo: verifier.logo,
77-
}))}
79+
tokens={allTokens}
7880
lanes={searchLanes}
81+
verifiers={uniqueVerifiers}
7982
environment={environment}
8083
breadcrumbItems={[
8184
{

src/workers/data-worker.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@ interface SearchData {
3333
}
3434
lane: LaneConfig
3535
}>
36+
verifiers: Array<{
37+
id: string
38+
name: string
39+
type: string
40+
logo: string
41+
totalNetworks: number
42+
}>
3643
}
3744

3845
interface WorkerMessage {
@@ -44,14 +51,15 @@ interface WorkerResponse {
4451
networks: SearchData["chains"]
4552
tokens: SearchData["tokens"]
4653
lanes: SearchData["lanes"]
54+
verifiers: SearchData["verifiers"]
4755
}
4856

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

5361
if (!search || !data) {
54-
self.postMessage({ networks: [], tokens: [], lanes: [] } as WorkerResponse)
62+
self.postMessage({ networks: [], tokens: [], lanes: [], verifiers: [] } as WorkerResponse)
5563
return
5664
}
5765

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

77-
self.postMessage({ networks, tokens, lanes } as WorkerResponse)
85+
// Filter verifiers
86+
const verifiers = data.verifiers.filter(
87+
(verifier) =>
88+
verifier.name.toLowerCase().includes(searchLower) ||
89+
verifier.id.toLowerCase().includes(searchLower) ||
90+
verifier.type.toLowerCase().includes(searchLower)
91+
)
92+
93+
self.postMessage({ networks, tokens, lanes, verifiers } as WorkerResponse)
7894
}
7995

8096
// Export types for use in main thread

0 commit comments

Comments
 (0)