Get scraped chains list from DB instead of registry (#106)

- Update Next and Hyperlane deps
- Query scraped chains from domains DB table
pull/109/head
J M Rossy 3 months ago committed by GitHub
parent d93cc6ca37
commit 6b116b1dc2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 10
      package.json
  2. 18
      src/components/search/SearchFilterBar.tsx
  3. 4
      src/consts/config.ts
  4. 6
      src/features/api/getMessages.ts
  5. 6
      src/features/api/getStatus.ts
  6. 6
      src/features/api/searchMessages.ts
  7. 11
      src/features/api/utils.ts
  8. 21
      src/features/chains/queries/fragments.ts
  9. 30
      src/features/chains/queries/useScrapedChains.ts
  10. 21
      src/features/chains/utils.ts
  11. 7
      src/features/messages/pi-queries/usePiChainMessageQuery.ts
  12. 24
      src/features/messages/queries/parse.ts
  13. 13
      src/features/messages/queries/useMessageQuery.ts
  14. 5
      src/store.ts
  15. 741
      yarn.lock

@ -6,9 +6,9 @@
"dependencies": { "dependencies": {
"@headlessui/react": "^1.7.17", "@headlessui/react": "^1.7.17",
"@hyperlane-xyz/registry": "2.5.0", "@hyperlane-xyz/registry": "2.5.0",
"@hyperlane-xyz/sdk": "3.13.0", "@hyperlane-xyz/sdk": "5.1.0",
"@hyperlane-xyz/utils": "3.13.0", "@hyperlane-xyz/utils": "5.1.0",
"@hyperlane-xyz/widgets": "4.1.0", "@hyperlane-xyz/widgets": "5.1.0",
"@metamask/jazzicon": "https://github.com/jmrossy/jazzicon#7a8df28974b4e81129bfbe3cab76308b889032a6", "@metamask/jazzicon": "https://github.com/jmrossy/jazzicon#7a8df28974b4e81129bfbe3cab76308b889032a6",
"@tanstack/react-query": "^5.35.5", "@tanstack/react-query": "^5.35.5",
"bignumber.js": "^9.1.2", "bignumber.js": "^9.1.2",
@ -16,7 +16,7 @@
"ethers": "^5.7.2", "ethers": "^5.7.2",
"formik": "^2.2.9", "formik": "^2.2.9",
"graphql": "^16.6.0", "graphql": "^16.6.0",
"next": "^13.4.19", "next": "^13.5.6",
"nextjs-cors": "^2.1.2", "nextjs-cors": "^2.1.2",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
@ -44,7 +44,7 @@
"prettier": "^2.8.4", "prettier": "^2.8.4",
"tailwindcss": "^3.3.3", "tailwindcss": "^3.3.3",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"typescript": "^5.1.6" "typescript": "^5.5.4"
}, },
"homepage": "https://www.hyperlane.xyz", "homepage": "https://www.hyperlane.xyz",
"license": "Apache-2.0", "license": "Apache-2.0",

@ -5,11 +5,12 @@ import { useMemo, useState } from 'react';
import { ChainMetadata } from '@hyperlane-xyz/sdk'; import { ChainMetadata } from '@hyperlane-xyz/sdk';
import { arrayToObject } from '@hyperlane-xyz/utils'; import { arrayToObject } from '@hyperlane-xyz/utils';
import { useScrapedChains } from '../../features/chains/queries/useScrapedChains';
import { import {
getChainDisplayName, getChainDisplayName,
isEvmChain, isEvmChain,
isPiChain, isPiChain,
isUnscrapedEvmChain, isUnscrapedDbChain,
} from '../../features/chains/utils'; } from '../../features/chains/utils';
import GearIcon from '../../images/icons/gear.svg'; import GearIcon from '../../images/icons/gear.svg';
import { useMultiProvider } from '../../store'; import { useMultiProvider } from '../../store';
@ -87,22 +88,23 @@ function ChainMultiSelector({
onChangeValue: (value: string | null) => void; onChangeValue: (value: string | null) => void;
position?: string; position?: string;
}) { }) {
const { scrapedChains } = useScrapedChains();
const multiProvider = useMultiProvider(); const multiProvider = useMultiProvider();
const { chains, mainnets, testnets } = useMemo(() => { const { chains, mainnets, testnets } = useMemo(() => {
const chains = Object.values(multiProvider.metadata); const chains = Object.values(multiProvider.metadata);
// Filtering to EVM is necessary to prevent errors until cosmos support is added // Filtering to EVM is necessary to prevent errors until cosmos support is added
// https://github.com/hyperlane-xyz/hyperlane-explorer/issues/61 // https://github.com/hyperlane-xyz/hyperlane-explorer/issues/61
const coreEvmChains = chains.filter( const scrapedEvmChains = chains.filter(
(c) => (c) =>
isEvmChain(multiProvider, c.chainId) && isEvmChain(multiProvider, c.chainId) &&
!isPiChain(multiProvider, c.chainId) && !isPiChain(multiProvider, scrapedChains, c.chainId) &&
!isUnscrapedEvmChain(multiProvider, c.chainId), !isUnscrapedDbChain(multiProvider, c.chainId),
); );
const mainnets = coreEvmChains.filter((c) => !c.isTestnet); const mainnets = scrapedEvmChains.filter((c) => !c.isTestnet);
const testnets = coreEvmChains.filter((c) => !!c.isTestnet); const testnets = scrapedEvmChains.filter((c) => !!c.isTestnet);
// Return only evmChains because of graphql only accept query non-evm chains (with bigint type not string) // Return only evmChains because of graphql only accept query non-evm chains (with bigint type not string)
return { chains: coreEvmChains, mainnets, testnets }; return { chains: scrapedEvmChains, mainnets, testnets };
}, [multiProvider]); }, [multiProvider, scrapedChains]);
// Need local state as buffer before user hits apply // Need local state as buffer before user hits apply
const [checkedChains, setCheckedChains] = useState( const [checkedChains, setCheckedChains] = useState(

@ -1,5 +1,3 @@
import { CoreChain } from '@hyperlane-xyz/registry';
const isDevMode = process?.env?.NODE_ENV === 'development'; const isDevMode = process?.env?.NODE_ENV === 'development';
const version = process?.env?.NEXT_PUBLIC_VERSION ?? null; const version = process?.env?.NEXT_PUBLIC_VERSION ?? null;
const explorerApiKeys = JSON.parse(process?.env?.EXPLORER_API_KEYS || '{}'); const explorerApiKeys = JSON.parse(process?.env?.EXPLORER_API_KEYS || '{}');
@ -20,4 +18,4 @@ export const config: Config = Object.freeze({
// Based on https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/main/typescript/infra/config/environments/mainnet3/agent.ts // Based on https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/main/typescript/infra/config/environments/mainnet3/agent.ts
// Based on https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/main/typescript/infra/config/environments/testnet4/agent.ts // Based on https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/main/typescript/infra/config/environments/testnet4/agent.ts
export const unscrapedEvmChains = [CoreChain.proteustestnet, CoreChain.sei, CoreChain.viction]; export const unscrapedChainsInDb = ['proteustestnet', 'sei', 'viction'];

@ -9,7 +9,7 @@ import { MessagesQueryResult } from '../messages/queries/fragments';
import { parseMessageQueryResult } from '../messages/queries/parse'; import { parseMessageQueryResult } from '../messages/queries/parse';
import { ApiHandlerResult, ApiMessage, toApiMessage } from './types'; import { ApiHandlerResult, ApiMessage, toApiMessage } from './types';
import { failureResult, getMultiProvider, successResult } from './utils'; import { failureResult, getMultiProvider, getScrapedChains, successResult } from './utils';
export async function handler( export async function handler(
req: NextApiRequest, req: NextApiRequest,
@ -27,7 +27,9 @@ export async function handler(
const result = await client.query<MessagesQueryResult>(query, variables).toPromise(); const result = await client.query<MessagesQueryResult>(query, variables).toPromise();
const multiProvider = await getMultiProvider(); const multiProvider = await getMultiProvider();
const messages = parseMessageQueryResult(multiProvider, result.data); const scrapedChains = await getScrapedChains(client);
const messages = parseMessageQueryResult(multiProvider, scrapedChains, result.data);
return successResult(messages.map(toApiMessage)); return successResult(messages.map(toApiMessage));
} }

@ -10,7 +10,7 @@ import { parseMessageStubResult } from '../messages/queries/parse';
import { parseQueryParams } from './getMessages'; import { parseQueryParams } from './getMessages';
import { ApiHandlerResult } from './types'; import { ApiHandlerResult } from './types';
import { failureResult, getMultiProvider, successResult } from './utils'; import { failureResult, getMultiProvider, getScrapedChains, successResult } from './utils';
interface MessageStatusResult { interface MessageStatusResult {
id: string; id: string;
@ -34,7 +34,9 @@ export async function handler(
const result = await client.query<MessagesStubQueryResult>(query, variables).toPromise(); const result = await client.query<MessagesStubQueryResult>(query, variables).toPromise();
const multiProvider = await getMultiProvider(); const multiProvider = await getMultiProvider();
const messages = parseMessageStubResult(multiProvider, result.data); const scrapedChains = await getScrapedChains(client);
const messages = parseMessageStubResult(multiProvider, scrapedChains, result.data);
return successResult(messages.map((m) => ({ id: m.msgId, status: m.status }))); return successResult(messages.map((m) => ({ id: m.msgId, status: m.status })));
} }

@ -9,7 +9,7 @@ import { MessagesQueryResult } from '../messages/queries/fragments';
import { parseMessageQueryResult } from '../messages/queries/parse'; import { parseMessageQueryResult } from '../messages/queries/parse';
import { ApiHandlerResult, ApiMessage, toApiMessage } from './types'; import { ApiHandlerResult, ApiMessage, toApiMessage } from './types';
import { failureResult, getMultiProvider, successResult } from './utils'; import { failureResult, getMultiProvider, getScrapedChains, successResult } from './utils';
const SEARCH_QUERY_PARAM_NAME = 'query'; const SEARCH_QUERY_PARAM_NAME = 'query';
@ -33,7 +33,9 @@ export async function handler(
const result = await client.query<MessagesQueryResult>(query, variables).toPromise(); const result = await client.query<MessagesQueryResult>(query, variables).toPromise();
const multiProvider = await getMultiProvider(); const multiProvider = await getMultiProvider();
const messages = parseMessageQueryResult(multiProvider, result.data); const scrapedChains = await getScrapedChains(client);
const messages = parseMessageQueryResult(multiProvider, scrapedChains, result.data);
return successResult(messages.map(toApiMessage)); return successResult(messages.map(toApiMessage));
} }

@ -1,6 +1,11 @@
import { Client } from '@urql/core';
import { GithubRegistry } from '@hyperlane-xyz/registry'; import { GithubRegistry } from '@hyperlane-xyz/registry';
import { MultiProvider } from '@hyperlane-xyz/sdk'; import { MultiProvider } from '@hyperlane-xyz/sdk';
import { logger } from '../../utils/logger';
import { DOMAINS_QUERY, DomainsEntry } from '../chains/queries/fragments';
export function successResult<R>(data: R): { success: true; data: R } { export function successResult<R>(data: R): { success: true; data: R } {
return { success: true, data }; return { success: true, data };
} }
@ -15,3 +20,9 @@ export async function getMultiProvider(): Promise<MultiProvider> {
const chainMetadata = await registry.getMetadata(); const chainMetadata = await registry.getMetadata();
return new MultiProvider(chainMetadata); return new MultiProvider(chainMetadata);
} }
export async function getScrapedChains(client: Client): Promise<Array<DomainsEntry>> {
logger.debug('Fetching list of scraped chains');
const result = await client.query<{ domain: Array<DomainsEntry> }>(DOMAINS_QUERY, {}).toPromise();
return result.data?.domain || [];
}

@ -0,0 +1,21 @@
export const DOMAINS_QUERY = `
query @cached {
domain {
id
native_token
name
is_test_net
is_deprecated
chain_id
}
}
`;
export interface DomainsEntry {
id: number; // domainId
native_token: string;
name: string;
is_test_net: boolean;
is_deprecated: boolean;
chain_id: string | number;
}

@ -0,0 +1,30 @@
import { useEffect } from 'react';
import { useQuery } from 'urql';
import { useStore } from '../../../store';
import { DOMAINS_QUERY, DomainsEntry } from './fragments';
export function useScrapedChains() {
const { scrapedChains, setScrapedChains } = useStore((s) => ({
scrapedChains: s.scrapedChains,
setScrapedChains: s.setScrapedChains,
}));
const [result] = useQuery<{ domain: Array<DomainsEntry> }>({
query: DOMAINS_QUERY,
pause: !!scrapedChains?.length,
});
const { data, fetching: isFetching, error } = result;
useEffect(() => {
if (!data) return;
setScrapedChains(data.domain);
}, [data, error, setScrapedChains]);
return {
scrapedChains,
isFetching,
isError: !!error,
};
}

@ -1,11 +1,12 @@
import { CoreChain, CoreChains, IRegistry } from '@hyperlane-xyz/registry'; import { IRegistry } from '@hyperlane-xyz/registry';
import { ChainMap, MultiProvider } from '@hyperlane-xyz/sdk'; import { ChainMap, MultiProvider } from '@hyperlane-xyz/sdk';
import { ProtocolType, toTitleCase } from '@hyperlane-xyz/utils'; import { ProtocolType, toTitleCase } from '@hyperlane-xyz/utils';
import { unscrapedEvmChains } from '../../consts/config'; import { unscrapedChainsInDb } from '../../consts/config';
import { Environment } from '../../consts/environments'; import { Environment } from '../../consts/environments';
import { ChainConfig } from './chainConfig'; import { ChainConfig } from './chainConfig';
import { DomainsEntry } from './queries/fragments';
export async function getMailboxAddress( export async function getMailboxAddress(
chainName: string, chainName: string,
@ -35,9 +36,15 @@ export function getChainEnvironment(multiProvider: MultiProvider, chainIdOrName:
return isTestnet ? Environment.Testnet : Environment.Mainnet; return isTestnet ? Environment.Testnet : Environment.Mainnet;
} }
export function isPiChain(multiProvider: MultiProvider, chainIdOrName: number | string) { // Is a 'Permisionless Interop' chain (i.e. one not deployed and scraped by Abacus Works)
export function isPiChain(
multiProvider: MultiProvider,
scrapedChains: DomainsEntry[],
chainIdOrName: number | string,
) {
const chainName = multiProvider.tryGetChainName(chainIdOrName); const chainName = multiProvider.tryGetChainName(chainIdOrName);
return !chainName || !CoreChains.includes(chainName as CoreChain); // Note: .trim() because one chain name in the DB has a trailing \n char for some reason
return !chainName || !scrapedChains.find((chain) => chain.name.trim() === chainName);
} }
export function isEvmChain(multiProvider: MultiProvider, chainIdOrName: number | string) { export function isEvmChain(multiProvider: MultiProvider, chainIdOrName: number | string) {
@ -45,8 +52,8 @@ export function isEvmChain(multiProvider: MultiProvider, chainIdOrName: number |
return protocol === ProtocolType.Ethereum; return protocol === ProtocolType.Ethereum;
} }
// TODO: Remove once we fetch CoreChains dynamically from the DB https://github.com/hyperlane-xyz/hyperlane-explorer/issues/74 // TODO: Remove once all chains in the DB are scraped
export function isUnscrapedEvmChain(multiProvider: MultiProvider, chainIdOrName: number | string) { export function isUnscrapedDbChain(multiProvider: MultiProvider, chainIdOrName: number | string) {
const chainName = multiProvider.tryGetChainName(chainIdOrName); const chainName = multiProvider.tryGetChainName(chainIdOrName);
return chainName && unscrapedEvmChains.includes(chainName as CoreChain); return chainName && unscrapedChainsInDb.includes(chainName);
} }

@ -8,6 +8,7 @@ import { useReadyMultiProvider, useRegistry } from '../../../store';
import { Message } from '../../../types'; import { Message } from '../../../types';
import { logger } from '../../../utils/logger'; import { logger } from '../../../utils/logger';
import { ChainConfig } from '../../chains/chainConfig'; import { ChainConfig } from '../../chains/chainConfig';
import { useScrapedChains } from '../../chains/queries/useScrapedChains';
import { isEvmChain, isPiChain } from '../../chains/utils'; import { isEvmChain, isPiChain } from '../../chains/utils';
import { isValidSearchQuery } from '../queries/useMessageQuery'; import { isValidSearchQuery } from '../queries/useMessageQuery';
@ -30,8 +31,10 @@ export function usePiChainMessageSearchQuery({
piQueryType?: PiQueryType; piQueryType?: PiQueryType;
pause: boolean; pause: boolean;
}) { }) {
const { scrapedChains } = useScrapedChains();
const multiProvider = useReadyMultiProvider(); const multiProvider = useReadyMultiProvider();
const registry = useRegistry(); const registry = useRegistry();
const { isLoading, isError, data } = useQuery({ const { isLoading, isError, data } = useQuery({
queryKey: [ queryKey: [
'usePiChainMessageSearchQuery', 'usePiChainMessageSearchQuery',
@ -51,7 +54,9 @@ export function usePiChainMessageSearchQuery({
const query = { input: ensure0x(sanitizedInput) }; const query = { input: ensure0x(sanitizedInput) };
const allChains = Object.values(multiProvider.metadata); const allChains = Object.values(multiProvider.metadata);
const piChains = allChains.filter( const piChains = allChains.filter(
(c) => isEvmChain(multiProvider, c.chainId) && isPiChain(multiProvider, c.chainId), (c) =>
isEvmChain(multiProvider, c.chainId) &&
isPiChain(multiProvider, scrapedChains, c.chainId),
); );
try { try {
const results = await Promise.allSettled( const results = await Promise.allSettled(

@ -3,6 +3,7 @@ import { MultiProvider } from '@hyperlane-xyz/sdk';
import { Message, MessageStatus, MessageStub } from '../../../types'; import { Message, MessageStatus, MessageStub } from '../../../types';
import { logger } from '../../../utils/logger'; import { logger } from '../../../utils/logger';
import { tryUtf8DecodeBytes } from '../../../utils/string'; import { tryUtf8DecodeBytes } from '../../../utils/string';
import { DomainsEntry } from '../../chains/queries/fragments';
import { isPiChain } from '../../chains/utils'; import { isPiChain } from '../../chains/utils';
import { postgresByteaToString } from './encoding'; import { postgresByteaToString } from './encoding';
@ -22,29 +23,35 @@ import {
export function parseMessageStubResult( export function parseMessageStubResult(
multiProvider: MultiProvider, multiProvider: MultiProvider,
scrapedChains: DomainsEntry[],
data: MessagesStubQueryResult | undefined, data: MessagesStubQueryResult | undefined,
): MessageStub[] { ): MessageStub[] {
if (!data || !Object.keys(data).length) return []; if (!data || !Object.keys(data).length) return [];
return Object.values(data) return Object.values(data)
.flat() .flat()
.map((m) => parseMessageStub(multiProvider, m)) .map((m) => parseMessageStub(multiProvider, scrapedChains, m))
.filter((m): m is MessageStub => !!m) .filter((m): m is MessageStub => !!m)
.sort((a, b) => b.origin.timestamp - a.origin.timestamp); .sort((a, b) => b.origin.timestamp - a.origin.timestamp);
} }
export function parseMessageQueryResult( export function parseMessageQueryResult(
multiProvider: MultiProvider, multiProvider: MultiProvider,
scrapedChains: DomainsEntry[],
data: MessagesQueryResult | undefined, data: MessagesQueryResult | undefined,
): Message[] { ): Message[] {
if (!data || !Object.keys(data).length) return []; if (!data || !Object.keys(data).length) return [];
return Object.values(data) return Object.values(data)
.flat() .flat()
.map((m) => parseMessage(multiProvider, m)) .map((m) => parseMessage(multiProvider, scrapedChains, m))
.filter((m): m is Message => !!m) .filter((m): m is Message => !!m)
.sort((a, b) => b.origin.timestamp - a.origin.timestamp); .sort((a, b) => b.origin.timestamp - a.origin.timestamp);
} }
function parseMessageStub(multiProvider: MultiProvider, m: MessageStubEntry): MessageStub | null { function parseMessageStub(
multiProvider: MultiProvider,
scrapedChains: DomainsEntry[],
m: MessageStubEntry,
): MessageStub | null {
try { try {
const destinationDomainId = m.destination_domain_id; const destinationDomainId = m.destination_domain_id;
let destinationChainId = let destinationChainId =
@ -54,7 +61,8 @@ function parseMessageStub(multiProvider: MultiProvider, m: MessageStubEntry): Me
destinationChainId = destinationDomainId; destinationChainId = destinationDomainId;
} }
const isPiMsg = const isPiMsg =
isPiChain(multiProvider, m.origin_chain_id) || isPiChain(multiProvider, destinationChainId); isPiChain(multiProvider, scrapedChains, m.origin_chain_id) ||
isPiChain(multiProvider, scrapedChains, destinationChainId);
return { return {
status: getMessageStatus(m), status: getMessageStatus(m),
@ -87,9 +95,13 @@ function parseMessageStub(multiProvider: MultiProvider, m: MessageStubEntry): Me
} }
} }
function parseMessage(multiProvider: MultiProvider, m: MessageEntry): Message | null { function parseMessage(
multiProvider: MultiProvider,
scrapedChains: DomainsEntry[],
m: MessageEntry,
): Message | null {
try { try {
const stub = parseMessageStub(multiProvider, m); const stub = parseMessageStub(multiProvider, scrapedChains, m);
if (!stub) throw new Error('Message stub required'); if (!stub) throw new Error('Message stub required');
const body = postgresByteaToString(m.message_body ?? ''); const body = postgresByteaToString(m.message_body ?? '');

@ -6,6 +6,7 @@ import { isAddressEvm, isValidTransactionHashEvm } from '@hyperlane-xyz/utils';
import { useMultiProvider } from '../../../store'; import { useMultiProvider } from '../../../store';
import { MessageStatus } from '../../../types'; import { MessageStatus } from '../../../types';
import { useInterval } from '../../../utils/useInterval'; import { useInterval } from '../../../utils/useInterval';
import { useScrapedChains } from '../../chains/queries/useScrapedChains';
import { MessageIdentifierType, buildMessageQuery, buildMessageSearchQuery } from './build'; import { MessageIdentifierType, buildMessageQuery, buildMessageSearchQuery } from './build';
import { MessagesQueryResult, MessagesStubQueryResult } from './fragments'; import { MessagesQueryResult, MessagesStubQueryResult } from './fragments';
@ -29,6 +30,8 @@ export function useMessageSearchQuery(
startTimeFilter: number | null, startTimeFilter: number | null,
endTimeFilter: number | null, endTimeFilter: number | null,
) { ) {
const { scrapedChains } = useScrapedChains();
const hasInput = !!sanitizedInput; const hasInput = !!sanitizedInput;
const isValidInput = hasInput ? isValidSearchQuery(sanitizedInput, true) : true; const isValidInput = hasInput ? isValidSearchQuery(sanitizedInput, true) : true;
@ -54,8 +57,8 @@ export function useMessageSearchQuery(
// Parse results // Parse results
const multiProvider = useMultiProvider(); const multiProvider = useMultiProvider();
const messageList = useMemo( const messageList = useMemo(
() => parseMessageStubResult(multiProvider, data), () => parseMessageStubResult(multiProvider, scrapedChains, data),
[multiProvider, data], [multiProvider, scrapedChains, data],
); );
const isMessagesFound = messageList.length > 0; const isMessagesFound = messageList.length > 0;
@ -77,6 +80,8 @@ export function useMessageSearchQuery(
} }
export function useMessageQuery({ messageId, pause }: { messageId: string; pause: boolean }) { export function useMessageQuery({ messageId, pause }: { messageId: string; pause: boolean }) {
const { scrapedChains } = useScrapedChains();
// Assemble GraphQL Query // Assemble GraphQL Query
const { query, variables } = buildMessageQuery(MessageIdentifierType.Id, messageId, 1); const { query, variables } = buildMessageQuery(MessageIdentifierType.Id, messageId, 1);
@ -90,8 +95,8 @@ export function useMessageQuery({ messageId, pause }: { messageId: string; pause
// Parse results // Parse results
const multiProvider = useMultiProvider(); const multiProvider = useMultiProvider();
const messageList = useMemo( const messageList = useMemo(
() => parseMessageQueryResult(multiProvider, data), () => parseMessageQueryResult(multiProvider, scrapedChains, data),
[multiProvider, data], [multiProvider, scrapedChains, data],
); );
const isMessageFound = messageList.length > 0; const isMessageFound = messageList.length > 0;
const message = isMessageFound ? messageList[0] : null; const message = isMessageFound ? messageList[0] : null;

@ -5,6 +5,7 @@ import { GithubRegistry, IRegistry } from '@hyperlane-xyz/registry';
import { ChainMap, MultiProvider } from '@hyperlane-xyz/sdk'; import { ChainMap, MultiProvider } from '@hyperlane-xyz/sdk';
import { ChainConfig } from './features/chains/chainConfig'; import { ChainConfig } from './features/chains/chainConfig';
import { DomainsEntry } from './features/chains/queries/fragments';
import { logger } from './utils/logger'; import { logger } from './utils/logger';
// Increment this when persist state has breaking changes // Increment this when persist state has breaking changes
@ -13,6 +14,8 @@ const PERSIST_STATE_VERSION = 1;
// Keeping everything here for now as state is simple // Keeping everything here for now as state is simple
// Will refactor into slices as necessary // Will refactor into slices as necessary
interface AppState { interface AppState {
scrapedChains: Array<DomainsEntry>;
setScrapedChains: (chains: Array<DomainsEntry>) => void;
chainConfigs: ChainMap<ChainConfig>; chainConfigs: ChainMap<ChainConfig>;
setChainConfigs: (configs: ChainMap<ChainConfig>) => void; setChainConfigs: (configs: ChainMap<ChainConfig>) => void;
multiProvider: MultiProvider; multiProvider: MultiProvider;
@ -26,6 +29,8 @@ interface AppState {
export const useStore = create<AppState>()( export const useStore = create<AppState>()(
persist( persist(
(set, get) => ({ (set, get) => ({
scrapedChains: [],
setScrapedChains: (chains: Array<DomainsEntry>) => set({ scrapedChains: chains }),
chainConfigs: {}, chainConfigs: {},
setChainConfigs: async (configs: ChainMap<ChainConfig>) => { setChainConfigs: async (configs: ChainMap<ChainConfig>) => {
const multiProvider = await buildMultiProvider(get().registry, configs); const multiProvider = await buildMultiProvider(get().registry, configs);

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save