Migrate explorer from SDK consts to registry

Update to latest hyperlane libs
pull/72/head
J M Rossy 7 months ago
parent 2ed49779b7
commit 84a9a3420a
  1. 2
      next.config.js
  2. 7
      package.json
  3. 25
      src/components/icons/ChainLogo.tsx
  4. 39
      src/components/icons/ChainToChain.tsx
  5. 12
      src/components/nav/Header.tsx
  6. 38
      src/components/search/SearchFilterBar.tsx
  7. 3
      src/consts/environments.ts
  8. 1
      src/consts/links.ts
  9. 7
      src/features/api/getMessages.ts
  10. 6
      src/features/api/getStatus.ts
  11. 6
      src/features/api/searchMessages.ts
  12. 9
      src/features/api/utils.ts
  13. 46
      src/features/chains/ConfigureChains.tsx
  14. 36
      src/features/chains/utils.ts
  15. 7
      src/features/debugger/debugMessage.ts
  16. 6
      src/features/deliveryStatus/fetchDeliveryStatus.ts
  17. 10
      src/features/deliveryStatus/useMessageDeliveryStatus.tsx
  18. 3
      src/features/messages/MessageDetails.tsx
  19. 2
      src/features/messages/MessageTable.tsx
  20. 2
      src/features/messages/cards/ContentDetailsCard.tsx
  21. 2
      src/features/messages/cards/GasDetailsCard.tsx
  22. 2
      src/features/messages/cards/TransactionCard.tsx
  23. 2
      src/features/messages/ica.ts
  24. 7
      src/features/messages/pi-queries/fetchPiChainMessages.test.ts
  25. 2
      src/features/messages/pi-queries/usePiChainMessageQuery.ts
  26. 3
      src/features/messages/queries/parse.ts
  27. 2
      src/features/messages/queries/useMessageQuery.ts
  28. 5
      src/features/providers/multiProvider.ts
  29. 5
      src/pages/api/latest-nonce.ts
  30. 40
      src/store.ts
  31. 2806
      yarn.lock

@ -25,7 +25,7 @@ const securityHeaders = [
key: 'Content-Security-Policy',
value: `default-src 'self'; script-src 'self'${
isDev ? " 'unsafe-eval'" : ''
}; connect-src *; img-src 'self' data:; style-src 'self' 'unsafe-inline'; font-src 'self' data:; base-uri 'self'; form-action 'self'`,
}; connect-src *; img-src 'self' data: https://raw.githubusercontent.com; style-src 'self' 'unsafe-inline'; font-src 'self' data:; base-uri 'self'; form-action 'self'`,
},
]

@ -1,12 +1,13 @@
{
"name": "@hyperlane-xyz/explorer",
"description": "An interchain explorer for the Hyperlane protocol and network.",
"version": "3.8.0",
"version": "3.11.0",
"author": "J M Rossy",
"dependencies": {
"@headlessui/react": "^1.7.17",
"@hyperlane-xyz/sdk": "3.8.0",
"@hyperlane-xyz/utils": "3.8.0",
"@hyperlane-xyz/registry": "^1.1.0",
"@hyperlane-xyz/sdk": "3.11.1",
"@hyperlane-xyz/utils": "3.11.1",
"@hyperlane-xyz/widgets": "3.8.0",
"@metamask/jazzicon": "https://github.com/jmrossy/jazzicon#7a8df28974b4e81129bfbe3cab76308b889032a6",
"@rainbow-me/rainbowkit": "0.12.16",

@ -1,13 +1,22 @@
import { ComponentProps } from 'react';
import { ChainLogo as ChainLogoInner } from '@hyperlane-xyz/widgets';
import { getChainName } from '../../features/chains/utils';
import { useMultiProvider } from '../../features/providers/multiProvider';
import { useMultiProvider, useRegistry } from '../../store';
export function ChainLogo(props: ComponentProps<typeof ChainLogoInner>) {
const { chainName, chainId, ...rest } = props;
export function ChainLogo({
chainId,
chainName,
background,
size,
}: {
chainId: ChainId;
chainName?: string;
background?: boolean;
size?: number;
}) {
const multiProvider = useMultiProvider();
const name = chainName || getChainName(multiProvider, props.chainId);
return <ChainLogoInner {...rest} chainName={name} chainId={chainId} />;
const registry = useRegistry();
const name = chainName || multiProvider.tryGetChainName(chainId) || '';
return (
<ChainLogoInner chainName={name} registry={registry} size={size} background={background} />
);
}

@ -1,39 +0,0 @@
import Image from 'next/image';
import { memo } from 'react';
import ArrowRightIcon from '../../images/icons/arrow-right-short.svg';
import { useIsMobile } from '../../styles/mediaQueries';
import { ChainLogo } from './ChainLogo';
function ChainToChain_({
originChainId,
destinationChainId,
size = 32,
arrowSize = 32,
isNarrow = false,
}: {
originChainId: ChainId;
destinationChainId: ChainId;
size?: number;
arrowSize?: number;
isNarrow?: boolean;
}) {
const isMobile = useIsMobile();
if (isMobile) {
size = Math.floor(size * 0.8);
arrowSize = Math.floor(arrowSize * 0.8);
}
return (
<div
className={`flex items-center justify-center sm:space-x-1 ${isNarrow ? '' : 'md:space-x-2'}`}
>
<ChainLogo chainId={originChainId} size={size} />
<Image src={ArrowRightIcon} width={arrowSize} height={arrowSize} alt="" />
<ChainLogo chainId={destinationChainId} size={size} />
</div>
);
}
export const ChainToChain = memo(ChainToChain_);

@ -59,15 +59,12 @@ export function Header({ pathName }: { pathName: string }) {
<Link href="/" className={navLinkClass('/')}>
Home
</Link>
<Link href="/settings" className={navLinkClass('/settings')}>
Settings
</Link>
<Link href="/api-docs" className={navLinkClass('/api-docs')}>
API
</Link>
<a className={navLinkClass()} target="_blank" href={links.home} rel="noopener noreferrer">
About
</a>
<Link href="/api-docs" className={navLinkClass('/api-docs')}>
API
</Link>
<a
className={navLinkClass()}
target="_blank"
@ -76,6 +73,9 @@ export function Header({ pathName }: { pathName: string }) {
>
Docs
</a>
<Link href="/settings" className={navLinkClass('/settings')}>
Settings
</Link>
{showSearch && <MiniSearchBar />}
</nav>
{/* Dropdown menu, used on mobile */}

@ -1,13 +1,13 @@
import Image from 'next/image';
import Link from 'next/link';
import { useState } from 'react';
import { useMemo, useState } from 'react';
import { ChainMetadata, mainnetChainsMetadata, testnetChainsMetadata } from '@hyperlane-xyz/sdk';
import { ChainMetadata } from '@hyperlane-xyz/sdk';
import { arrayToObject } from '@hyperlane-xyz/utils';
import { getChainDisplayName } from '../../features/chains/utils';
import { useMultiProvider } from '../../features/providers/multiProvider';
import GearIcon from '../../images/icons/gear.svg';
import { useMultiProvider } from '../../store';
import { Color } from '../../styles/Color';
import { SolidButton } from '../buttons/SolidButton';
import { TextButton } from '../buttons/TextButton';
@ -17,8 +17,6 @@ import { CheckBox } from '../input/Checkbox';
import { DatetimeField } from '../input/DatetimeField';
import { DropdownModal } from '../layout/Dropdown';
const mainnetAndTestChains = [...mainnetChainsMetadata, ...testnetChainsMetadata];
interface Props {
originChain: string | null;
onChangeOrigin: (value: string | null) => void;
@ -85,12 +83,18 @@ function ChainMultiSelector({
position?: string;
}) {
const multiProvider = useMultiProvider();
const { chains, mainnets, testnets } = useMemo(() => {
const chains = Object.values(multiProvider.metadata);
const mainnets = chains.filter((c) => !c.isTestnet);
const testnets = chains.filter((c) => !!c.isTestnet);
return { chains, mainnets, testnets };
}, [multiProvider]);
// Need local state as buffer before user hits apply
const [checkedChains, setCheckedChains] = useState(
value
? arrayToObject(value.split(','))
: arrayToObject(mainnetAndTestChains.map((c) => c.chainId.toString())),
: arrayToObject(chains.map((c) => c.chainId.toString())),
);
const hasAnyUncheckedChain = (chains: ChainMetadata[]) => {
@ -102,7 +106,7 @@ function ChainMultiSelector({
const onToggle = (chainId: string | number) => {
return (checked: boolean) => {
if (!hasAnyUncheckedChain(mainnetAndTestChains)) {
if (!hasAnyUncheckedChain(chains)) {
// If none are unchecked, uncheck all except this one
setCheckedChains({ [chainId]: true });
} else {
@ -125,7 +129,7 @@ function ChainMultiSelector({
};
const onToggleAll = () => {
setCheckedChains(arrayToObject(mainnetAndTestChains.map((c) => c.chainId.toString())));
setCheckedChains(arrayToObject(chains.map((c) => c.chainId.toString())));
};
const onToggleNone = () => {
@ -134,7 +138,7 @@ function ChainMultiSelector({
const onClickApply = (closeDropdown?: () => void) => {
const checkedList = Object.keys(checkedChains).filter((c) => !!checkedChains[c]);
if (checkedList.length === 0 || checkedList.length === mainnetAndTestChains.length) {
if (checkedList.length === 0 || checkedList.length === chains.length) {
// Use null value, indicating to filter needed
onChangeValue(null);
} else {
@ -175,14 +179,14 @@ function ChainMultiSelector({
<div className="flex flex-col">
<div className="pb-1.5">
<CheckBox
checked={!hasAnyUncheckedChain(mainnetChainsMetadata)}
onToggle={onToggleSection(mainnetChainsMetadata)}
checked={!hasAnyUncheckedChain(mainnets)}
onToggle={onToggleSection(mainnets)}
name="mainnet-chains"
>
<h4 className="ml-2 text-gray-800">Mainnet Chains</h4>
</CheckBox>
</div>
{mainnetChainsMetadata.map((c) => (
{mainnets.map((c) => (
<CheckBox
key={c.name}
checked={!!checkedChains[c.chainId]}
@ -193,7 +197,7 @@ function ChainMultiSelector({
<span className="mr-2 font-light">
{getChainDisplayName(multiProvider, c.chainId, true)}
</span>
<ChainLogo chainId={c.chainId} size={12} color={false} background={false} />
<ChainLogo chainId={c.chainId} size={12} background={false} />
</div>
</CheckBox>
))}
@ -202,14 +206,14 @@ function ChainMultiSelector({
<div className="flex flex-col">
<div className="pb-1.5">
<CheckBox
checked={!hasAnyUncheckedChain(testnetChainsMetadata)}
onToggle={onToggleSection(testnetChainsMetadata)}
checked={!hasAnyUncheckedChain(testnets)}
onToggle={onToggleSection(testnets)}
name="testnet-chains"
>
<h4 className="ml-2 text-gray-800">Testnet Chains</h4>
</CheckBox>
</div>
{testnetChainsMetadata.map((c) => (
{testnets.map((c) => (
<CheckBox
key={c.name}
checked={!!checkedChains[c.chainId]}
@ -220,7 +224,7 @@ function ChainMultiSelector({
<span className="mr-2 font-light">
{getChainDisplayName(multiProvider, c.chainId, true)}
</span>
<ChainLogo chainId={c.chainId} size={12} color={false} background={false} />
<ChainLogo chainId={c.chainId} size={12} background={false} />
</div>
</CheckBox>
))}

@ -8,6 +8,3 @@ export const ENVIRONMENT_BUCKET_SEGMENT: Record<Environment, string> = {
[Environment.Mainnet]: 'mainnet3',
[Environment.Testnet]: 'testnet4',
};
// TODO replace with SDK version
export const MAILBOX_VERSION = 3;

@ -18,4 +18,5 @@ export const docLinks = {
pi: 'https://v3.hyperlane.xyz/docs/deploy-hyperlane',
ism: 'https://v3.hyperlane.xyz/docs/reference/ISM/specify-your-ISM',
gas: 'https://v3.hyperlane.xyz/docs/protocol/interchain-gas-payment',
registry: 'https://docs.hyperlane.xyz/docs/reference/registries',
};

@ -1,8 +1,6 @@
import { Client } from '@urql/core';
import type { NextApiRequest } from 'next';
import { MultiProvider } from '@hyperlane-xyz/sdk';
import { API_GRAPHQL_QUERY_LIMIT } from '../../consts/api';
import { logger } from '../../utils/logger';
import { sanitizeString } from '../../utils/string';
@ -11,7 +9,7 @@ import { MessagesQueryResult } from '../messages/queries/fragments';
import { parseMessageQueryResult } from '../messages/queries/parse';
import { ApiHandlerResult, ApiMessage, toApiMessage } from './types';
import { failureResult, successResult } from './utils';
import { failureResult, getMultiProvider, successResult } from './utils';
export async function handler(
req: NextApiRequest,
@ -27,7 +25,8 @@ export async function handler(
API_GRAPHQL_QUERY_LIMIT,
);
const result = await client.query<MessagesQueryResult>(query, variables).toPromise();
const multiProvider = new MultiProvider();
const multiProvider = await getMultiProvider();
const messages = parseMessageQueryResult(multiProvider, result.data);
return successResult(messages.map(toApiMessage));
}

@ -1,8 +1,6 @@
import { Client } from '@urql/core';
import type { NextApiRequest } from 'next';
import { MultiProvider } from '@hyperlane-xyz/sdk';
import { API_GRAPHQL_QUERY_LIMIT } from '../../consts/api';
import { MessageStatus } from '../../types';
import { logger } from '../../utils/logger';
@ -12,7 +10,7 @@ import { parseMessageStubResult } from '../messages/queries/parse';
import { parseQueryParams } from './getMessages';
import { ApiHandlerResult } from './types';
import { failureResult, successResult } from './utils';
import { failureResult, getMultiProvider, successResult } from './utils';
interface MessageStatusResult {
id: string;
@ -35,7 +33,7 @@ export async function handler(
);
const result = await client.query<MessagesStubQueryResult>(query, variables).toPromise();
const multiProvider = new MultiProvider();
const multiProvider = await getMultiProvider();
const messages = parseMessageStubResult(multiProvider, result.data);
return successResult(messages.map((m) => ({ id: m.msgId, status: m.status })));

@ -1,8 +1,6 @@
import { Client } from '@urql/core';
import type { NextApiRequest } from 'next';
import { MultiProvider } from '@hyperlane-xyz/sdk';
import { API_GRAPHQL_QUERY_LIMIT } from '../../consts/api';
import { logger } from '../../utils/logger';
import { sanitizeString } from '../../utils/string';
@ -11,7 +9,7 @@ import { MessagesQueryResult } from '../messages/queries/fragments';
import { parseMessageQueryResult } from '../messages/queries/parse';
import { ApiHandlerResult, ApiMessage, toApiMessage } from './types';
import { failureResult, successResult } from './utils';
import { failureResult, getMultiProvider, successResult } from './utils';
const SEARCH_QUERY_PARAM_NAME = 'query';
@ -34,7 +32,7 @@ export async function handler(
);
const result = await client.query<MessagesQueryResult>(query, variables).toPromise();
const multiProvider = new MultiProvider();
const multiProvider = await getMultiProvider();
const messages = parseMessageQueryResult(multiProvider, result.data);
return successResult(messages.map(toApiMessage));

@ -1,3 +1,6 @@
import { GithubRegistry } from '@hyperlane-xyz/registry';
import { MultiProvider } from '@hyperlane-xyz/sdk';
export function successResult<R>(data: R): { success: true; data: R } {
return { success: true, data };
}
@ -5,3 +8,9 @@ export function successResult<R>(data: R): { success: true; data: R } {
export function failureResult(error: string): { success: false; error: string } {
return { success: false, error };
}
export async function getMultiProvider(): Promise<MultiProvider> {
const registry = new GithubRegistry();
const chainMetadata = await registry.getMetadata();
return new MultiProvider(chainMetadata);
}

@ -1,6 +1,6 @@
import { ChangeEventHandler, useState } from 'react';
import { ChainName, mainnetChainsMetadata, testnetChainsMetadata } from '@hyperlane-xyz/sdk';
import { ChainName } from '@hyperlane-xyz/sdk';
import { CopyButton } from '../../components/buttons/CopyButton';
import { SolidButton } from '../../components/buttons/SolidButton';
@ -9,11 +9,10 @@ import { ChainLogo } from '../../components/icons/ChainLogo';
import { Card } from '../../components/layout/Card';
import { Modal } from '../../components/layout/Modal';
import { docLinks } from '../../consts/links';
import { useMultiProvider } from '../providers/multiProvider';
import { useMultiProvider } from '../../store';
import { tryParseChainConfig } from './chainConfig';
import { useChainConfigsRW } from './useChainConfigs';
import { getChainDisplayName } from './utils';
export function ConfigureChains() {
const { chainConfigs, setChainConfigs } = useChainConfigsRW();
@ -70,33 +69,18 @@ export function ConfigureChains() {
</a>
. This explorer can be configured to search for messages on any PI chain.
</p>
<h3 className="mt-6 text-lg text-blue-500 font-medium">Default Chains</h3>
<div className="mt-4 flex">
<h4 className="text-gray-600 font-medium text-sm">Mainnets:</h4>
<div className="ml-3 flex gap-3.5 flex-wrap">
{mainnetChainsMetadata.map((c) => (
<div className="shrink-0 text-sm flex items-center" key={c.name}>
<ChainLogo chainId={c.chainId} size={15} color={true} background={false} />
<span className="ml-1.5 font-light">
{getChainDisplayName(multiProvider, c.chainId, true)}
</span>
</div>
))}
</div>
</div>
<div className="mt-5 flex">
<h4 className="text-gray-600 font-medium text-sm">Testnets:</h4>
<div className="ml-3 flex gap-3.5 flex-wrap">
{testnetChainsMetadata.map((c) => (
<div className="shrink-0 text-sm flex items-center" key={c.name}>
<ChainLogo chainId={c.chainId} size={15} color={true} background={false} />
<div className="ml-1.5 font-light">
{getChainDisplayName(multiProvider, c.chainId, true)}
</div>
</div>
))}
</div>
</div>
<p className="mt-3 font-light">
To make you chain available to all users, add its metadata to the
<a
href={docLinks.registry}
target="_blank"
rel="noopener noreferrer"
className="underline underline-offset-2 text-blue-500 hover:text-blue-400"
>
canonical Hyperlane Registry
</a>
. Or use the section below to add for just your own use.
</p>
<h3 className="mt-6 text-lg text-blue-500 font-medium">Custom Chains</h3>
<table className="mt-2 w-full">
<thead>
@ -114,7 +98,7 @@ export function ConfigureChains() {
{Object.values(chainConfigs).map((chain) => (
<tr key={`chain-${chain.chainId}`}>
<td>
<ChainLogo chainId={chain.chainId} size={32} color={true} background={true} />
<ChainLogo chainId={chain.chainId} size={32} background={true} />
</td>
<td className={styles.value}>{chain.chainId}</td>
<td className={styles.value}>{chain.domainId || chain.chainId}</td>

@ -1,40 +1,40 @@
import {
ChainMap,
type MultiProvider,
chainIdToMetadata,
hyperlaneContractAddresses,
} from '@hyperlane-xyz/sdk';
import { CoreChain, CoreChains, IRegistry } from '@hyperlane-xyz/registry';
import { ChainMap, MultiProvider } from '@hyperlane-xyz/sdk';
import { toTitleCase } from '@hyperlane-xyz/utils';
import { Environment } from '../../consts/environments';
import { ChainConfig } from './chainConfig';
export function getChainName(mp: MultiProvider, chainId?: number | string) {
return mp.tryGetChainName(chainId || 0) || undefined;
}
export function getMailboxAddress(customChainConfigs: ChainMap<ChainConfig>, chainName: string) {
return customChainConfigs[chainName]?.mailbox ?? hyperlaneContractAddresses[chainName]?.mailbox;
export async function getMailboxAddress(
chainName: string,
customChainConfigs: ChainMap<ChainConfig>,
registry: IRegistry,
) {
if (customChainConfigs[chainName]?.mailbox) return customChainConfigs[chainName].mailbox;
const addresses = await registry.getChainAddresses(chainName);
if (addresses?.mailbox) return addresses.mailbox;
else return undefined;
}
export function getChainDisplayName(
mp: MultiProvider,
multiProvider: MultiProvider,
chainOrDomainId?: ChainId | DomainId,
shortName = false,
fallbackToId = true,
) {
const metadata = mp.tryGetChainMetadata(chainOrDomainId || 0);
const metadata = multiProvider.tryGetChainMetadata(chainOrDomainId || 0);
if (!metadata) return fallbackToId && chainOrDomainId ? chainOrDomainId : 'Unknown';
const displayName = shortName ? metadata.displayNameShort : metadata.displayName;
return toTitleCase(displayName || metadata.displayName || metadata.name);
}
export function getChainEnvironment(mp: MultiProvider, chainIdOrName: number | string) {
const isTestnet = mp.tryGetChainMetadata(chainIdOrName)?.isTestnet;
export function getChainEnvironment(multiProvider: MultiProvider, chainIdOrName: number | string) {
const isTestnet = multiProvider.tryGetChainMetadata(chainIdOrName)?.isTestnet;
return isTestnet ? Environment.Testnet : Environment.Mainnet;
}
export function isPiChain(chainId: number | string) {
return !chainIdToMetadata[chainId];
export function isPiChain(multiProvider: MultiProvider, chainIdOrName: number | string) {
const chainName = multiProvider.tryGetChainName(chainIdOrName);
return !chainName || !CoreChains.includes(chainName as CoreChain);
}

@ -9,7 +9,8 @@ import {
IMultisigIsm__factory,
InterchainGasPaymaster__factory,
} from '@hyperlane-xyz/core';
import type { ChainMap, MultiProvider } from '@hyperlane-xyz/sdk';
import { IRegistry } from '@hyperlane-xyz/registry';
import { ChainMap, MAILBOX_VERSION, MultiProvider } from '@hyperlane-xyz/sdk';
import {
addressToBytes32,
errorToString,
@ -19,7 +20,6 @@ import {
trimToLength,
} from '@hyperlane-xyz/utils';
import { MAILBOX_VERSION } from '../../consts/environments';
import { Message } from '../../types';
import { logger } from '../../utils/logger';
import type { ChainConfig } from '../chains/chainConfig';
@ -34,6 +34,7 @@ const HANDLE_FUNCTION_SIG = 'handle(uint32,bytes32,bytes)';
export async function debugMessage(
multiProvider: MultiProvider,
registry: IRegistry,
customChainConfigs: ChainMap<ChainConfig>,
{
msgId,
@ -69,7 +70,7 @@ export async function debugMessage(
const recipInvalid = await isInvalidRecipient(destProvider, recipient);
if (recipInvalid) return recipInvalid;
const destMailbox = getMailboxAddress(customChainConfigs, destName);
const destMailbox = await getMailboxAddress(destName, customChainConfigs, registry);
if (!destMailbox)
throw new Error(`Cannot debug message, no mailbox address provided for chain ${destName}`);

@ -1,6 +1,7 @@
import { constants } from 'ethers';
import { IMailbox__factory } from '@hyperlane-xyz/core';
import { IRegistry } from '@hyperlane-xyz/registry';
import { ChainMap, MultiProvider } from '@hyperlane-xyz/sdk';
import { Message, MessageStatus } from '../../types';
@ -20,11 +21,12 @@ import {
export async function fetchDeliveryStatus(
multiProvider: MultiProvider,
registry: IRegistry,
customChainConfigs: ChainMap<ChainConfig>,
message: Message,
): Promise<MessageDeliveryStatusResponse> {
const destName = multiProvider.getChainName(message.destinationChainId);
const destMailboxAddr = getMailboxAddress(customChainConfigs, destName);
const destMailboxAddr = await getMailboxAddress(destName, customChainConfigs, registry);
if (!destMailboxAddr)
throw new Error(
`Cannot check delivery status, no mailbox address provided for chain ${destName}`,
@ -65,7 +67,7 @@ export async function fetchDeliveryStatus(
};
return result;
} else {
const debugResult = await debugMessage(multiProvider, customChainConfigs, message);
const debugResult = await debugMessage(multiProvider, registry, customChainConfigs, message);
const messageStatus =
debugResult.status === MessageDebugStatus.NoErrorsFound
? MessageStatus.Pending

@ -4,17 +4,18 @@ import { toast } from 'react-toastify';
import { errorToString } from '@hyperlane-xyz/utils';
import { useMultiProvider, useRegistry } from '../../store';
import { Message, MessageStatus } from '../../types';
import { logger } from '../../utils/logger';
import { MissingChainConfigToast } from '../chains/MissingChainConfigToast';
import { useChainConfigs } from '../chains/useChainConfigs';
import { useMultiProvider } from '../providers/multiProvider';
import { fetchDeliveryStatus } from './fetchDeliveryStatus';
export function useMessageDeliveryStatus({ message, pause }: { message: Message; pause: boolean }) {
const chainConfigs = useChainConfigs();
const multiProvider = useMultiProvider();
const registry = useRegistry();
const serializedMessage = JSON.stringify(message);
const { data, error, isFetching } = useQuery(
@ -41,7 +42,12 @@ export function useMessageDeliveryStatus({ message, pause }: { message: Message;
}
logger.debug('Fetching message delivery status for:', message.id);
const deliverStatus = await fetchDeliveryStatus(multiProvider, chainConfigs, message);
const deliverStatus = await fetchDeliveryStatus(
multiProvider,
registry,
chainConfigs,
message,
);
return deliverStatus;
},
{ retry: false },

@ -7,12 +7,11 @@ import { toTitleCase, trimToLength } from '@hyperlane-xyz/utils';
import { Spinner } from '../../components/animations/Spinner';
import { Card } from '../../components/layout/Card';
import CheckmarkIcon from '../../images/icons/checkmark-circle.svg';
import { useStore } from '../../store';
import { useMultiProvider, useStore } from '../../store';
import { Message, MessageStatus } from '../../types';
import { logger } from '../../utils/logger';
import { getChainDisplayName } from '../chains/utils';
import { useMessageDeliveryStatus } from '../deliveryStatus/useMessageDeliveryStatus';
import { useMultiProvider } from '../providers/multiProvider';
import { ContentDetailsCard } from './cards/ContentDetailsCard';
import { GasDetailsCard } from './cards/GasDetailsCard';

@ -5,10 +5,10 @@ import { MultiProvider } from '@hyperlane-xyz/sdk';
import { shortenAddress } from '@hyperlane-xyz/utils';
import { ChainLogo } from '../../components/icons/ChainLogo';
import { useMultiProvider } from '../../store';
import { MessageStatus, MessageStub } from '../../types';
import { getHumanReadableDuration, getHumanReadableTimeString } from '../../utils/time';
import { getChainDisplayName } from '../chains/utils';
import { useMultiProvider } from '../providers/multiProvider';
import { serializeMessage } from './utils';

@ -1,12 +1,12 @@
import Image from 'next/image';
import { useEffect, useMemo, useState } from 'react';
import { MAILBOX_VERSION } from '@hyperlane-xyz/sdk';
import { formatMessage } from '@hyperlane-xyz/utils';
import { HelpIcon } from '../../../components/icons/HelpIcon';
import { SelectField } from '../../../components/input/SelectField';
import { Card } from '../../../components/layout/Card';
import { MAILBOX_VERSION } from '../../../consts/environments';
import EnvelopeInfo from '../../../images/icons/envelope-info.svg';
import { Message } from '../../../types';
import { logger } from '../../../utils/logger';

@ -10,11 +10,11 @@ import { HelpIcon } from '../../../components/icons/HelpIcon';
import { Card } from '../../../components/layout/Card';
import { docLinks } from '../../../consts/links';
import FuelPump from '../../../images/icons/fuel-pump.svg';
import { useMultiProvider } from '../../../store';
import { Message } from '../../../types';
import { BigNumberMax } from '../../../utils/big-number';
import { logger } from '../../../utils/logger';
import { GasPayment } from '../../debugger/types';
import { useMultiProvider } from '../../providers/multiProvider';
import { KeyValueRow } from './KeyValueRow';

@ -9,12 +9,12 @@ import { HelpIcon } from '../../../components/icons/HelpIcon';
import { Card } from '../../../components/layout/Card';
import { Modal } from '../../../components/layout/Modal';
import { links } from '../../../consts/links';
import { useMultiProvider } from '../../../store';
import { MessageStatus, MessageTx } from '../../../types';
import { getDateTimeString, getHumanReadableTimeString } from '../../../utils/time';
import { getChainDisplayName } from '../../chains/utils';
import { debugStatusToDesc } from '../../debugger/strings';
import { MessageDebugResult } from '../../debugger/types';
import { useMultiProvider } from '../../providers/multiProvider';
import { LabelAndCodeBlock } from './CodeBlock';
import { KeyValueRow } from './KeyValueRow';

@ -5,8 +5,8 @@ import { useMemo } from 'react';
import { InterchainAccountRouter__factory } from '@hyperlane-xyz/core';
import { eqAddress, isValidAddress } from '@hyperlane-xyz/utils';
import { useMultiProvider } from '../../store';
import { logger } from '../../utils/logger';
import { useMultiProvider } from '../providers/multiProvider';
// This assumes all chains have the same ICA address
// const ICA_ADDRESS = hyperlaneEnvironments.mainnet.ethereum.interchainAccountRouter;

@ -1,4 +1,5 @@
import { MultiProvider, chainMetadata, hyperlaneEnvironments } from '@hyperlane-xyz/sdk';
import { chainAddresses, chainMetadata } from '@hyperlane-xyz/registry';
import { MultiProvider } from '@hyperlane-xyz/sdk';
import { Message, MessageStatus } from '../../../types';
import { ChainConfig } from '../../chains/chainConfig';
@ -11,8 +12,8 @@ import { fetchMessagesFromPiChain } from './fetchPiChainMessages';
jest.setTimeout(30000);
const sepoliaMailbox = hyperlaneEnvironments.testnet.sepolia.mailbox;
const sepoliaIgp = hyperlaneEnvironments.testnet.sepolia.interchainGasPaymaster;
const sepoliaMailbox = chainAddresses.sepolia.mailbox;
const sepoliaIgp = chainAddresses.sepolia.interchainGasPaymaster;
const sepoliaConfigWithExplorer: ChainConfig = {
...chainMetadata.sepolia,
mailbox: sepoliaMailbox,

@ -3,11 +3,11 @@ import { useQuery } from '@tanstack/react-query';
import { MultiProvider } from '@hyperlane-xyz/sdk';
import { ensure0x } from '@hyperlane-xyz/utils';
import { useMultiProvider } from '../../../store';
import { Message } from '../../../types';
import { logger } from '../../../utils/logger';
import { ChainConfig } from '../../chains/chainConfig';
import { useChainConfigs } from '../../chains/useChainConfigs';
import { useMultiProvider } from '../../providers/multiProvider';
import { isValidSearchQuery } from '../queries/useMessageQuery';
import { PiMessageQuery, PiQueryType, fetchMessagesFromPiChain } from './fetchPiChainMessages';

@ -53,7 +53,8 @@ function parseMessageStub(multiProvider: MultiProvider, m: MessageStubEntry): Me
logger.warn(`No chainId known for domain ${destinationDomainId}. Using domain as chainId`);
destinationChainId = destinationDomainId;
}
const isPiMsg = isPiChain(m.origin_chain_id) || isPiChain(destinationChainId);
const isPiMsg =
isPiChain(multiProvider, m.origin_chain_id) || isPiChain(multiProvider, destinationChainId);
return {
status: getMessageStatus(m),

@ -3,9 +3,9 @@ import { useQuery } from 'urql';
import { isAddressEvm, isValidTransactionHashEvm } from '@hyperlane-xyz/utils';
import { useMultiProvider } from '../../../store';
import { MessageStatus } from '../../../types';
import { useInterval } from '../../../utils/useInterval';
import { useMultiProvider } from '../../providers/multiProvider';
import {
MessageIdentifierType,
buildMessageQuery,

@ -1,5 +0,0 @@
import { useStore } from '../../store';
export function useMultiProvider() {
return useStore((s) => s.multiProvider);
}

@ -5,6 +5,7 @@ import NextCors from 'nextjs-cors';
import { MultiProvider } from '@hyperlane-xyz/sdk';
import { ENVIRONMENT_BUCKET_SEGMENT } from '../../consts/environments';
import { getMultiProvider } from '../../features/api/utils';
import { getChainEnvironment, isPiChain } from '../../features/chains/utils';
import { logger } from '../../utils/logger';
import { fetchWithTimeout } from '../../utils/timeout';
@ -21,9 +22,9 @@ export default async function handler(
try {
const body = req.body as { chainId: ChainId };
if (!body.chainId) throw new Error('No chainId in body');
const multiProvider = await getMultiProvider();
// TODO PI support here
if (isPiChain(body.chainId)) throw new Error('ChainId is unsupported');
const multiProvider = new MultiProvider();
if (isPiChain(multiProvider, body.chainId)) throw new Error('Only core chains are unsupported');
const nonce = await fetchLatestNonce(multiProvider, body.chainId);
res.status(200).json({ nonce });
} catch (error) {

@ -1,7 +1,8 @@
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import { ChainMap, MultiProvider, chainMetadata } from '@hyperlane-xyz/sdk';
import { GithubRegistry, IRegistry } from '@hyperlane-xyz/registry';
import { ChainMap, MultiProvider } from '@hyperlane-xyz/sdk';
import { ChainConfig } from './features/chains/chainConfig';
import { logger } from './utils/logger';
@ -16,20 +17,27 @@ interface AppState {
setChainConfigs: (configs: ChainMap<ChainConfig>) => void;
multiProvider: MultiProvider;
setMultiProvider: (mp: MultiProvider) => void;
registry: IRegistry;
setRegistry: (registry: IRegistry) => void;
bannerClassName: string;
setBanner: (className: string) => void;
}
export const useStore = create<AppState>()(
persist(
(set) => ({
(set, get) => ({
chainConfigs: {},
setChainConfigs: (configs: ChainMap<ChainConfig>) => {
set({ chainConfigs: configs, multiProvider: buildMultiProvider(configs) });
setChainConfigs: async (configs: ChainMap<ChainConfig>) => {
const multiProvider = await buildMultiProvider(get().registry, configs);
set({ chainConfigs: configs, multiProvider });
},
multiProvider: buildMultiProvider({}),
setMultiProvider: (mp: MultiProvider) => {
set({ multiProvider: mp });
multiProvider: new MultiProvider({}),
setMultiProvider: (multiProvider: MultiProvider) => {
set({ multiProvider });
},
registry: new GithubRegistry(),
setRegistry: (registry: IRegistry) => {
set({ registry });
},
bannerClassName: '',
setBanner: (className: string) => set({ bannerClassName: className }),
@ -45,14 +53,24 @@ export const useStore = create<AppState>()(
logger.error('Error during hydration', error);
return;
}
state.setMultiProvider(buildMultiProvider(state.chainConfigs));
logger.debug('Hydration finished');
buildMultiProvider(state.registry, state.chainConfigs)
.then((mp) => state.setMultiProvider(mp))
.catch((e) => logger.error('Error building MultiProvider', e));
};
},
},
),
);
function buildMultiProvider(customChainConfigs: ChainMap<ChainConfig>) {
return new MultiProvider({ ...chainMetadata, ...customChainConfigs });
export function useMultiProvider() {
return useStore((s) => s.multiProvider);
}
export function useRegistry() {
return useStore((s) => s.registry);
}
async function buildMultiProvider(registry: IRegistry, customChainConfigs: ChainMap<ChainConfig>) {
const registryChainMetadata = await registry.getMetadata();
return new MultiProvider({ ...registryChainMetadata, ...customChainConfigs });
}

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