11import { useParams } from "next/navigation" ;
2- import { Hex } from "viem" ;
2+ import { Address , getAddress } from "viem" ;
3+ import { MUDChain } from "@latticexyz/common/chains" ;
34import { UseQueryResult , useQuery } from "@tanstack/react-query" ;
45import { supportedChains , validateChainName } from "../../../common" ;
6+ import { VerifiedWorld } from "../api/verified-worlds/route" ;
57import { useIndexerForChainId } from "../hooks/useIndexerForChainId" ;
68
7- type WorldsQueryResult = {
8- worlds : Hex [ ] ;
9+ export type WorldSelectItem = {
10+ address : Address ;
11+ name : string ;
12+ verified : boolean ;
13+ } ;
14+
15+ export type WorldsQueryResult = {
16+ worlds : WorldSelectItem [ ] ;
917} ;
1018
1119type ApiResponse = {
12- items : Array < { address : { hash : Hex } } > ;
20+ items : Array < { address : { hash : Address } } > ;
1321} ;
1422
1523type SqliteResponse = {
1624 result : [ string [ ] , ...string [ ] [ ] ] ;
1725} ;
1826
27+ async function getVerifiedWorlds ( chain : MUDChain ) : Promise < VerifiedWorld [ ] > {
28+ const response = await fetch ( `/api/verified-worlds?chainId=${ chain . id } ` ) ;
29+ const data = await response . json ( ) ;
30+ return data ;
31+ }
32+
33+ async function getLocalWorlds ( indexer : ReturnType < typeof useIndexerForChainId > ) {
34+ const response = await fetch ( indexer . url , {
35+ method : "POST" ,
36+ headers : {
37+ "Content-Type" : "application/json" ,
38+ } ,
39+ body : JSON . stringify ( [
40+ {
41+ query : "SELECT DISTINCT address FROM __mudStoreTables" ,
42+ } ,
43+ ] ) ,
44+ } ) ;
45+
46+ if ( ! response . ok ) {
47+ throw new Error ( `HTTP error! Status: ${ response . status } ` ) ;
48+ }
49+
50+ const data : SqliteResponse = await response . json ( ) ;
51+ const result = data . result [ 0 ] ;
52+
53+ if ( ! result || ! Array . isArray ( result ) || result . length < 2 ) {
54+ return [ ] ;
55+ }
56+
57+ const rows = result . slice ( 1 ) ;
58+ return rows . map ( ( row ) => getAddress ( row [ 0 ] as Address ) ) ;
59+ }
60+
61+ async function getExternalWorlds ( chain : MUDChain ) {
62+ if ( ! ( "blockExplorers" in chain ) || ! chain . blockExplorers ?. default . url ) {
63+ return [ ] ;
64+ }
65+
66+ const worldsApiUrl = `${ chain . blockExplorers . default . url } /api/v2/mud/worlds` ;
67+ const response = await fetch ( worldsApiUrl ) ;
68+
69+ if ( ! response . ok ) {
70+ throw new Error ( `HTTP error! Status: ${ response . status } ` ) ;
71+ }
72+
73+ const data : ApiResponse = await response . json ( ) ;
74+ return data . items . map ( ( world ) => getAddress ( world . address . hash ) ) ;
75+ }
76+
1977export function useWorldsQuery ( ) : UseQueryResult < WorldsQueryResult > {
2078 const { chainName } = useParams ( ) ;
2179 validateChainName ( chainName ) ;
@@ -25,49 +83,46 @@ export function useWorldsQuery(): UseQueryResult<WorldsQueryResult> {
2583 return useQuery ( {
2684 queryKey : [ "worlds" , chainName ] ,
2785 queryFn : async ( ) => {
28- if ( indexer . type === "sqlite" ) {
29- const response = await fetch ( indexer . url , {
30- method : "POST" ,
31- headers : {
32- "Content-Type" : "application/json" ,
33- } ,
34- body : JSON . stringify ( [
35- {
36- query : "SELECT DISTINCT address FROM __mudStoreTables" ,
37- } ,
38- ] ) ,
39- } ) ;
40-
41- if ( ! response . ok ) {
42- throw new Error ( `HTTP error! Status: ${ response . status } ` ) ;
43- }
86+ const worlds : WorldSelectItem [ ] = [ ] ;
87+ const worldsAddresses = new Set < Address > ( ) ;
4488
45- const data : SqliteResponse = await response . json ( ) ;
46- const result = data . result [ 0 ] ;
89+ let indexerWorlds : Address [ ] = [ ] ;
90+ if ( indexer . type === "sqlite" ) {
91+ indexerWorlds = await getLocalWorlds ( indexer ) ;
92+ } else {
93+ indexerWorlds = await getExternalWorlds ( chain ) ;
94+ }
4795
48- if ( ! result || ! Array . isArray ( result ) || result . length < 2 ) {
49- return { worlds : [ ] } ;
50- }
96+ for ( const world of indexerWorlds ) {
97+ worldsAddresses . add ( world ) ;
98+ worlds . push ( { address : world , name : "" , verified : false } ) ;
99+ }
51100
52- const rows = result . slice ( 1 ) ;
53- return {
54- worlds : rows . map ( ( row ) => row [ 0 ] as Hex ) ,
55- } ;
56- } else if ( "blockExplorers" in chain && chain . blockExplorers ?. default . url ) {
57- const worldsApiUrl = `${ chain . blockExplorers . default . url } /api/v2/mud/worlds` ;
58- const response = await fetch ( worldsApiUrl ) ;
101+ let verifiedWorldsMap = new Map < Address , string > ( ) ;
102+ try {
103+ const verifiedWorlds = await getVerifiedWorlds ( chain ) ;
104+ verifiedWorldsMap = new Map ( verifiedWorlds . map ( ( world ) => [ world . address , world . name ] ) ) ;
59105
60- if ( ! response . ok ) {
61- throw new Error ( `HTTP error! Status: ${ response . status } ` ) ;
106+ for ( const [ address , name ] of verifiedWorldsMap ) {
107+ if ( ! worldsAddresses . has ( address ) ) {
108+ worlds . push ( { address, name, verified : true } ) ;
109+ }
62110 }
111+ } catch ( error ) {
112+ console . error ( "Failed to fetch verified worlds:" , error ) ;
113+ }
63114
64- const data : ApiResponse = await response . json ( ) ;
65- return {
66- worlds : data . items . map ( ( world ) => world . address . hash ) ,
67- } ;
115+ for ( const world of worlds ) {
116+ const name = verifiedWorldsMap . get ( world . address ) ;
117+ if ( name ) {
118+ world . name = name ;
119+ world . verified = true ;
120+ }
68121 }
69122
70- return { worlds : [ ] } ;
123+ return {
124+ worlds : worlds . sort ( ( a ) => ( a . verified ? - 1 : 1 ) ) ,
125+ } ;
71126 } ,
72127 refetchInterval : 5000 ,
73128 } ) ;
0 commit comments