@@ -8,7 +8,12 @@ import {
88 getIsMetaMaskInfuraEndpointUrl ,
99 getIsQuicknodeEndpointUrl ,
1010 isPublicEndpointUrl ,
11+ initializeChainlistDomains ,
12+ isChainlistDomain ,
13+ isChainlistEndpointUrl ,
14+ resetChainlistDomainsCache ,
1115} from './network-utils' ;
16+ import * as storageHelpers from './storage-helpers' ;
1217
1318jest . mock ( '../constants/network' , ( ) => ( {
1419 FEATURED_RPCS : [
@@ -193,12 +198,231 @@ describe('isPublicEndpointUrl', () => {
193198 ) . toBe ( true ) ;
194199 } ) ;
195200
196- it ( 'returns false for unknown URLs' , ( ) => {
201+ it ( 'returns false for unknown URLs when chainlist is not initialized' , ( ) => {
202+ resetChainlistDomainsCache ( ) ;
197203 expect (
198204 isPublicEndpointUrl (
199205 'https://unknown.example.com' ,
200206 MOCK_METAMASK_INFURA_PROJECT_ID ,
201207 ) ,
202208 ) . toBe ( false ) ;
203209 } ) ;
210+
211+ it ( 'returns true for chainlist domains after initialization' , async ( ) => {
212+ resetChainlistDomainsCache ( ) ;
213+ jest . spyOn ( storageHelpers , 'getStorageItem' ) . mockResolvedValue ( {
214+ cachedResponse : [
215+ {
216+ name : 'Test Chain' ,
217+ chainId : 1 ,
218+ rpc : [ 'https://chainlist-rpc.example.com/rpc' ] ,
219+ } ,
220+ ] ,
221+ } ) ;
222+
223+ await initializeChainlistDomains ( ) ;
224+
225+ expect (
226+ isPublicEndpointUrl (
227+ 'https://chainlist-rpc.example.com/v1/abc123' ,
228+ MOCK_METAMASK_INFURA_PROJECT_ID ,
229+ ) ,
230+ ) . toBe ( true ) ;
231+ } ) ;
232+ } ) ;
233+
234+ describe ( 'initializeChainlistDomains' , ( ) => {
235+ beforeEach ( ( ) => {
236+ resetChainlistDomainsCache ( ) ;
237+ jest . clearAllMocks ( ) ;
238+ } ) ;
239+
240+ it ( 'initializes domains from cached chains list' , async ( ) => {
241+ jest . spyOn ( storageHelpers , 'getStorageItem' ) . mockResolvedValue ( {
242+ cachedResponse : [
243+ {
244+ name : 'Ethereum Mainnet' ,
245+ chainId : 1 ,
246+ rpc : [
247+ 'https://mainnet.infura.io/v3/key' ,
248+ 'https://eth-mainnet.alchemyapi.io/v2/key' ,
249+ ] ,
250+ } ,
251+ {
252+ name : 'Polygon' ,
253+ chainId : 137 ,
254+ rpc : [ 'https://polygon-rpc.com' ] ,
255+ } ,
256+ ] ,
257+ } ) ;
258+
259+ await initializeChainlistDomains ( ) ;
260+
261+ expect ( isChainlistDomain ( 'mainnet.infura.io' ) ) . toBe ( true ) ;
262+ expect ( isChainlistDomain ( 'eth-mainnet.alchemyapi.io' ) ) . toBe ( true ) ;
263+ expect ( isChainlistDomain ( 'polygon-rpc.com' ) ) . toBe ( true ) ;
264+ expect ( isChainlistDomain ( 'unknown.com' ) ) . toBe ( false ) ;
265+ } ) ;
266+
267+ it ( 'handles empty chains list' , async ( ) => {
268+ jest . spyOn ( storageHelpers , 'getStorageItem' ) . mockResolvedValue ( {
269+ cachedResponse : [ ] ,
270+ } ) ;
271+
272+ await initializeChainlistDomains ( ) ;
273+
274+ expect ( isChainlistDomain ( 'any-domain.com' ) ) . toBe ( false ) ;
275+ } ) ;
276+
277+ it ( 'handles missing cache' , async ( ) => {
278+ jest . spyOn ( storageHelpers , 'getStorageItem' ) . mockResolvedValue ( undefined ) ;
279+
280+ await initializeChainlistDomains ( ) ;
281+
282+ expect ( isChainlistDomain ( 'any-domain.com' ) ) . toBe ( false ) ;
283+ } ) ;
284+
285+ it ( 'handles chains without rpc field' , async ( ) => {
286+ jest . spyOn ( storageHelpers , 'getStorageItem' ) . mockResolvedValue ( {
287+ cachedResponse : [
288+ {
289+ name : 'Chain Without RPC' ,
290+ chainId : 999 ,
291+ } ,
292+ ] ,
293+ } ) ;
294+
295+ await initializeChainlistDomains ( ) ;
296+
297+ expect ( isChainlistDomain ( 'any-domain.com' ) ) . toBe ( false ) ;
298+ } ) ;
299+
300+ it ( 'skips invalid URLs in rpc list' , async ( ) => {
301+ jest . spyOn ( storageHelpers , 'getStorageItem' ) . mockResolvedValue ( {
302+ cachedResponse : [
303+ {
304+ name : 'Chain With Invalid RPC' ,
305+ chainId : 999 ,
306+ rpc : [ 'not-a-valid-url' , 'https://valid-rpc.com' ] ,
307+ } ,
308+ ] ,
309+ } ) ;
310+
311+ await initializeChainlistDomains ( ) ;
312+
313+ expect ( isChainlistDomain ( 'valid-rpc.com' ) ) . toBe ( true ) ;
314+ } ) ;
315+
316+ it ( 'only fetches from storage once on subsequent calls' , async ( ) => {
317+ const getStorageItemSpy = jest
318+ . spyOn ( storageHelpers , 'getStorageItem' )
319+ . mockResolvedValue ( {
320+ cachedResponse : [ ] ,
321+ } ) ;
322+
323+ await initializeChainlistDomains ( ) ;
324+ await initializeChainlistDomains ( ) ;
325+ await initializeChainlistDomains ( ) ;
326+
327+ // Storage should only be accessed once despite multiple calls
328+ expect ( getStorageItemSpy ) . toHaveBeenCalledTimes ( 1 ) ;
329+ } ) ;
330+ } ) ;
331+
332+ describe ( 'isChainlistDomain' , ( ) => {
333+ beforeEach ( ( ) => {
334+ resetChainlistDomainsCache ( ) ;
335+ } ) ;
336+
337+ it ( 'returns false when not initialized' , ( ) => {
338+ expect ( isChainlistDomain ( 'any-domain.com' ) ) . toBe ( false ) ;
339+ } ) ;
340+
341+ it ( 'returns false for empty domain' , async ( ) => {
342+ jest . spyOn ( storageHelpers , 'getStorageItem' ) . mockResolvedValue ( {
343+ cachedResponse : [ { name : 'Test' , chainId : 1 , rpc : [ 'https://test.com' ] } ] ,
344+ } ) ;
345+ await initializeChainlistDomains ( ) ;
346+
347+ expect ( isChainlistDomain ( '' ) ) . toBe ( false ) ;
348+ } ) ;
349+
350+ it ( 'is case insensitive' , async ( ) => {
351+ jest . spyOn ( storageHelpers , 'getStorageItem' ) . mockResolvedValue ( {
352+ cachedResponse : [
353+ { name : 'Test' , chainId : 1 , rpc : [ 'https://Test-RPC.COM/path' ] } ,
354+ ] ,
355+ } ) ;
356+ await initializeChainlistDomains ( ) ;
357+
358+ expect ( isChainlistDomain ( 'test-rpc.com' ) ) . toBe ( true ) ;
359+ expect ( isChainlistDomain ( 'TEST-RPC.COM' ) ) . toBe ( true ) ;
360+ expect ( isChainlistDomain ( 'Test-RPC.com' ) ) . toBe ( true ) ;
361+ } ) ;
362+ } ) ;
363+
364+ describe ( 'isChainlistEndpointUrl' , ( ) => {
365+ beforeEach ( ( ) => {
366+ resetChainlistDomainsCache ( ) ;
367+ } ) ;
368+
369+ it ( 'returns false when not initialized' , ( ) => {
370+ expect ( isChainlistEndpointUrl ( 'https://any-domain.com/rpc' ) ) . toBe ( false ) ;
371+ } ) ;
372+
373+ it ( 'returns true for URLs with chainlist domains' , async ( ) => {
374+ jest . spyOn ( storageHelpers , 'getStorageItem' ) . mockResolvedValue ( {
375+ cachedResponse : [
376+ { name : 'Test' , chainId : 1 , rpc : [ 'https://chainlist-domain.com' ] } ,
377+ ] ,
378+ } ) ;
379+ await initializeChainlistDomains ( ) ;
380+
381+ expect (
382+ isChainlistEndpointUrl ( 'https://chainlist-domain.com/v1/abc123' ) ,
383+ ) . toBe ( true ) ;
384+ expect ( isChainlistEndpointUrl ( 'https://chainlist-domain.com' ) ) . toBe ( true ) ;
385+ } ) ;
386+
387+ it ( 'returns false for URLs with unknown domains' , async ( ) => {
388+ jest . spyOn ( storageHelpers , 'getStorageItem' ) . mockResolvedValue ( {
389+ cachedResponse : [
390+ { name : 'Test' , chainId : 1 , rpc : [ 'https://known-domain.com' ] } ,
391+ ] ,
392+ } ) ;
393+ await initializeChainlistDomains ( ) ;
394+
395+ expect ( isChainlistEndpointUrl ( 'https://unknown-domain.com/rpc' ) ) . toBe (
396+ false ,
397+ ) ;
398+ } ) ;
399+
400+ it ( 'returns false for invalid URLs' , ( ) => {
401+ expect ( isChainlistEndpointUrl ( 'not-a-url' ) ) . toBe ( false ) ;
402+ expect ( isChainlistEndpointUrl ( '' ) ) . toBe ( false ) ;
403+ } ) ;
404+ } ) ;
405+
406+ describe ( 'resetChainlistDomainsCache' , ( ) => {
407+ it ( 'clears the cache and allows reinitialization' , async ( ) => {
408+ jest . spyOn ( storageHelpers , 'getStorageItem' ) . mockResolvedValue ( {
409+ cachedResponse : [
410+ { name : 'First' , chainId : 1 , rpc : [ 'https://first-domain.com' ] } ,
411+ ] ,
412+ } ) ;
413+ await initializeChainlistDomains ( ) ;
414+ expect ( isChainlistDomain ( 'first-domain.com' ) ) . toBe ( true ) ;
415+
416+ resetChainlistDomainsCache ( ) ;
417+ expect ( isChainlistDomain ( 'first-domain.com' ) ) . toBe ( false ) ;
418+
419+ jest . spyOn ( storageHelpers , 'getStorageItem' ) . mockResolvedValue ( {
420+ cachedResponse : [
421+ { name : 'Second' , chainId : 2 , rpc : [ 'https://second-domain.com' ] } ,
422+ ] ,
423+ } ) ;
424+ await initializeChainlistDomains ( ) ;
425+ expect ( isChainlistDomain ( 'second-domain.com' ) ) . toBe ( true ) ;
426+ expect ( isChainlistDomain ( 'first-domain.com' ) ) . toBe ( false ) ;
427+ } ) ;
204428} ) ;
0 commit comments