diff --git a/README.md b/README.md index f794da2..2b43b28 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Hyperlane Explorer App -A multi-chain explorer for the Hyperlane protocol and network. +An interchain explorer for the Hyperlane protocol and network. ## Setup diff --git a/package.json b/package.json index 6c2c190..3f17006 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@hyperlane-xyz/explorer", - "description": "A multi-chain explorer for the Hyperlane protocol and network.", + "description": "An interchain explorer for the Hyperlane protocol and network.", "version": "0.1.0", "author": "J M Rossy", "dependencies": { diff --git a/src/components/layout/Card.tsx b/src/components/layout/Card.tsx index 52bd583..b5f55ec 100644 --- a/src/components/layout/Card.tsx +++ b/src/components/layout/Card.tsx @@ -1,17 +1,13 @@ import { PropsWithChildren } from 'react'; interface Props { - width?: string; classes?: string; } -export function Card(props: PropsWithChildren) { - const { width, classes } = props; +export function Card({ classes, children }: PropsWithChildren) { return ( -
- {props.children} +
+ {children}
); } diff --git a/src/components/nav/Footer.tsx b/src/components/nav/Footer.tsx index 6ccc0ba..d29a5cf 100644 --- a/src/components/nav/Footer.tsx +++ b/src/components/nav/Footer.tsx @@ -14,7 +14,7 @@ export function Footer() {
- +

diff --git a/src/components/nav/Header.tsx b/src/components/nav/Header.tsx index bc07c31..6a09209 100644 --- a/src/components/nav/Header.tsx +++ b/src/components/nav/Header.tsx @@ -23,7 +23,7 @@ export function Header({ pathName }: { pathName: string }) { const showSearch = !PAGES_EXCLUDING_SEARCH.includes(pathName); return ( -

+
diff --git a/src/consts/config.ts b/src/consts/config.ts index 908fd6a..bb251d8 100644 --- a/src/consts/config.ts +++ b/src/consts/config.ts @@ -14,6 +14,6 @@ export const config: Config = Object.freeze({ debug: isDevMode, version, url: 'https://explorer.hyperlane.xyz', - apiUrl: 'https://api.hyperlane.xyz/v1/graphql', + apiUrl: 'https://hyperlane-explorer-2.hasura.app/v1/graphql', explorerApiKeys, }); diff --git a/src/consts/domains.ts b/src/consts/domains.ts deleted file mode 100644 index 25c74e5..0000000 --- a/src/consts/domains.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { invertKeysAndValues } from '../utils/objects'; - -// TODO move these to SDK -// Hard-coding here for better perf and reduced queries -// Should match Domain table in db -export const domainToChain = { - 1000: 44787, // alfajores - 6386274: 42161, // arbitrum - 1634872690: 421611, // arbitrumrinkeby - 421613: 421613, // arbitrumgoerli - 1635069300: 1313161555, // auroratestnet - 1635148152: 43114, // avalanche - 6452067: 56, // bsc - 1651715444: 97, // bsctestnet - 1667591279: 42220, // celo - 6648936: 1, // ethereum - 43113: 43113, // fuji - 5: 5, // goerli - 3000: 42, // kovan - 1836002657: 1287, // moonbasealpha - 1836002669: 1284, // moonbeam - 80001: 80001, // mumbai - 28528: 10, // optimism - 420: 420, // optimismgoerli - 1869622635: 69, // optimismkovan - 1886350457: 137, // polygon - 280: 280, // zksync2testnet -}; - -export const chainToDomain = invertKeysAndValues(domainToChain); diff --git a/src/features/deliveryStatus/fetchDeliveryStatus.ts b/src/features/deliveryStatus/fetchDeliveryStatus.ts index 9d69c91..f0fdf05 100644 --- a/src/features/deliveryStatus/fetchDeliveryStatus.ts +++ b/src/features/deliveryStatus/fetchDeliveryStatus.ts @@ -4,7 +4,6 @@ import { hyperlaneCoreAddresses } from '@hyperlane-xyz/sdk'; import { utils } from '@hyperlane-xyz/utils'; import { chainIdToName } from '../../consts/chains'; -import { chainToDomain, domainToChain } from '../../consts/domains'; import { Message, MessageStatus } from '../../types'; import { validateAddress } from '../../utils/addresses'; import { getChainEnvironment } from '../../utils/chains'; @@ -147,14 +146,10 @@ function validateMessage(message: Message) { sender, } = message; - if (!originDomainId || !domainToChain[originDomainId]) - throw new Error(`Invalid origin domain ${originDomainId}`); - if (!destinationDomainId || !domainToChain[destinationDomainId]) - throw new Error(`Invalid dest domain ${destinationDomainId}`); - if (!originChainId || !chainToDomain[originChainId]) - throw new Error(`Invalid origin chain ${originChainId}`); - if (!destinationChainId || !chainToDomain[destinationChainId]) - throw new Error(`Invalid dest chain ${destinationChainId}`); + if (!originDomainId) throw new Error(`Invalid origin domain ${originDomainId}`); + if (!destinationDomainId) throw new Error(`Invalid dest domain ${destinationDomainId}`); + if (!originChainId) throw new Error(`Invalid origin chain ${originChainId}`); + if (!destinationChainId) throw new Error(`Invalid dest chain ${destinationChainId}`); if (!chainIdToName[originChainId]) throw new Error(`No name found for chain ${originChainId}`); if (!chainIdToName[destinationChainId]) throw new Error(`No name found for chain ${destinationChainId}`); diff --git a/src/features/messages/MessageDetails.tsx b/src/features/messages/MessageDetails.tsx index 864e6a2..fcb9f74 100644 --- a/src/features/messages/MessageDetails.tsx +++ b/src/features/messages/MessageDetails.tsx @@ -183,15 +183,19 @@ function StatusHeader({ } const messageDetailsQuery = ` -query MessageDetails ($messageId: bigint!){ - message(where: {id: {_eq: $messageId}}, limit: 1) { +query MessageDetails ($messageId: String!){ + message(where: {msg_id: {_eq: $messageId}}, limit: 1) { destination id - leaf_index - hash + msg_id + nonce msg_body origin origin_tx_id + origin_mailbox + recipient + sender + timestamp transaction { id block_id @@ -206,14 +210,10 @@ query MessageDetails ($messageId: bigint!){ timestamp } } - outbox_address - recipient - sender - timestamp delivered_message { id tx_id - inbox_address + destination_mailbox transaction { block_id gas_used diff --git a/src/features/messages/MessageSearch.tsx b/src/features/messages/MessageSearch.tsx index a8e6a62..59427c9 100644 --- a/src/features/messages/MessageSearch.tsx +++ b/src/features/messages/MessageSearch.tsx @@ -10,7 +10,6 @@ import { SearchUnknownError, } from '../../components/search/SearchError'; import { SearchFilterBar } from '../../components/search/SearchFilterBar'; -import { chainToDomain } from '../../consts/domains'; import { trimLeading0x } from '../../utils/addresses'; import useDebounce from '../../utils/debounce'; import { logger } from '../../utils/logger'; @@ -128,12 +127,8 @@ function assembleQuery( ) { const hasInput = !!searchInput; - const originChains = originFilter - ? originFilter.split(',').map((c) => chainToDomain[c]) - : undefined; - const destinationChains = destFilter - ? destFilter.split(',').map((c) => chainToDomain[c]) - : undefined; + const originChains = originFilter ? originFilter.split(',') : undefined; + const destinationChains = destFilter ? destFilter.split(',') : undefined; const startTime = startTimeFilter ? adjustToUtcTime(startTimeFilter) : undefined; const endTime = endTimeFilter ? adjustToUtcTime(endTimeFilter) : undefined; const variables = { @@ -181,6 +176,7 @@ const searchWhereClause = ` const messageStubProps = ` id + msg_id destination origin recipient @@ -189,7 +185,7 @@ const messageStubProps = ` delivered_message { id tx_id - inbox_address + destination_mailbox transaction { block { timestamp diff --git a/src/features/messages/MessageTable.tsx b/src/features/messages/MessageTable.tsx index e599ef4..fd76fd0 100644 --- a/src/features/messages/MessageTable.tsx +++ b/src/features/messages/MessageTable.tsx @@ -45,7 +45,7 @@ export function MessageTable({ export function MessageSummaryRow({ message }: { message: MessageStub }) { const { - id, + msgId, status, sender, recipient, @@ -67,25 +67,25 @@ export function MessageSummaryRow({ message }: { message: MessageStub }) { return ( <> - +
{getChainDisplayName(originChainId, true)}
- +
{getChainDisplayName(destinationChainId, true)}
- + {shortenAddress(sender) || 'Invalid Address'} - + {shortenAddress(recipient) || 'Invalid Address'} - + {getHumanReadableTimeString(originTimestamp)} @@ -93,7 +93,7 @@ export function MessageSummaryRow({ message }: { message: MessageStub }) { ? getHumanReadableDuration(destinationTimestamp - originTimestamp, 3) : '-'} - +
{statusText}
@@ -107,7 +107,7 @@ function LinkCell({ tdClasses, aClasses, children, -}: PropsWithChildren<{ id: number; tdClasses?: string; aClasses?: string }>) { +}: PropsWithChildren<{ id: string; tdClasses?: string; aClasses?: string }>) { return ( diff --git a/src/features/messages/cards/ContentDetailsCard.tsx b/src/features/messages/cards/ContentDetailsCard.tsx index 3b7bd44..fa2f47c 100644 --- a/src/features/messages/cards/ContentDetailsCard.tsx +++ b/src/features/messages/cards/ContentDetailsCard.tsx @@ -19,14 +19,15 @@ interface Props { export function ContentDetailsCard({ message: { + msgId, + nonce, + leafIndex, originDomainId, originChainId, destinationDomainId, destinationChainId, sender, recipient, - leafIndex, - hash, body, decodedBody, }, @@ -54,7 +55,7 @@ export function ContentDetailsCard({ ); return ( - +
@@ -68,28 +69,43 @@ export function ContentDetailsCard({
+
+ + +
@@ -103,7 +119,6 @@ export function ContentDetailsCard({
- ); } diff --git a/src/features/messages/cards/IcaDetailsCard.tsx b/src/features/messages/cards/IcaDetailsCard.tsx index 8c63357..ac5b076 100644 --- a/src/features/messages/cards/IcaDetailsCard.tsx +++ b/src/features/messages/cards/IcaDetailsCard.tsx @@ -23,7 +23,7 @@ export function IcaDetailsCard({ message: { originDomainId, body }, shouldBlur } } = useIcaAddress(originDomainId, decodeResult?.sender); return ( - +
diff --git a/src/features/messages/cards/KeyValueRow.tsx b/src/features/messages/cards/KeyValueRow.tsx index f7ab4ba..91787dd 100644 --- a/src/features/messages/cards/KeyValueRow.tsx +++ b/src/features/messages/cards/KeyValueRow.tsx @@ -4,7 +4,7 @@ interface Props { label: string; labelWidth: string; display: string; - displayWidth: string; + displayWidth?: string; subDisplay?: string; showCopy?: boolean; blurValue?: boolean; @@ -22,7 +22,7 @@ export function KeyValueRow({ return (
-
+
{display} {subDisplay && {subDisplay}}
diff --git a/src/features/messages/cards/TimelineCard.tsx b/src/features/messages/cards/TimelineCard.tsx index ae82b16..fa1d422 100644 --- a/src/features/messages/cards/TimelineCard.tsx +++ b/src/features/messages/cards/TimelineCard.tsx @@ -36,19 +36,19 @@ interface Props { export function TimelineCard({ message, resolvedStatus: status, resolvedDestinationTx }: Props) { const { + leafIndex, originChainId, destinationChainId, originTimestamp, destinationTimestamp, - leafIndex, originTransaction, } = message; const { stage, timings } = useMessageStage( status, + leafIndex, originChainId, destinationChainId, - leafIndex, originTransaction.blockNumber, originTimestamp, destinationTimestamp || resolvedDestinationTx?.timestamp, @@ -57,7 +57,7 @@ export function TimelineCard({ message, resolvedStatus: status, resolvedDestinat const timeSent = new Date(originTimestamp); return ( - + {/*

Delivery Timeline

@@ -203,9 +203,9 @@ function getStageClass(targetStage: Stage, currentStage: Stage, messageStatus: M function useMessageStage( status: MessageStatus, + leafIndex: number, originChainId: number, destChainId: number, - leafIndex: number, originBlockNumber: number, originTimestamp: number, destinationTimestamp?: number, @@ -214,11 +214,11 @@ function useMessageStage( [ 'messageStage', status, + leafIndex, originChainId, destChainId, originTimestamp, destinationTimestamp, - leafIndex, originBlockNumber, ], async () => { diff --git a/src/features/messages/cards/TransactionCard.tsx b/src/features/messages/cards/TransactionCard.tsx index 1a09fb1..5ce2feb 100644 --- a/src/features/messages/cards/TransactionCard.tsx +++ b/src/features/messages/cards/TransactionCard.tsx @@ -2,7 +2,6 @@ import { Spinner } from '../../../components/animation/Spinner'; import { ChainIcon } from '../../../components/icons/ChainIcon'; import { HelpIcon } from '../../../components/icons/HelpIcon'; import { Card } from '../../../components/layout/Card'; -import { chainToDomain } from '../../../consts/domains'; import { MessageStatus, PartialTransactionReceipt } from '../../../types'; import { getChainDisplayName } from '../../../utils/chains'; import { getTxExplorerUrl } from '../../../utils/explorers'; @@ -55,7 +54,7 @@ export function TransactionCard({ diff --git a/src/features/messages/parseMessage.ts b/src/features/messages/parseMessage.ts index 12b6245..b7b757b 100644 --- a/src/features/messages/parseMessage.ts +++ b/src/features/messages/parseMessage.ts @@ -1,5 +1,4 @@ import { TEST_RECIPIENT_ADDRESS } from '../../consts/addresses'; -import { domainToChain } from '../../consts/domains'; import { Message, MessageStatus, MessageStub, PartialTransactionReceipt } from '../../types'; import { areAddressesEqual, ensureLeading0x, trimLeading0x } from '../../utils/addresses'; import { logger } from '../../utils/logger'; @@ -30,13 +29,14 @@ function parseMessageStub(m: MessageStubEntry): MessageStub | null { : undefined; return { id: m.id, + msgId: m.msg_id, status, sender: parsePaddedAddress(m.sender), recipient: parsePaddedAddress(m.recipient), originDomainId: m.origin, destinationDomainId: m.destination, - originChainId: domainToChain[m.origin], - destinationChainId: domainToChain[m.destination], + originChainId: m.origin, + destinationChainId: m.destination, originTimestamp: parseTimestampString(m.timestamp), destinationTimestamp, }; @@ -58,13 +58,14 @@ function parseMessage(m: MessageEntry): Message | null { const body = decodePostgresBinaryHex(m.msg_body ?? ''); const isTestRecipient = areAddressesEqual(stub.recipient, TEST_RECIPIENT_ADDRESS); const decodedBody = isTestRecipient ? tryUtf8DecodeBytes(body) : undefined; + const leafIndex = 1; //TODO return { ...stub, + nonce: m.nonce, + leafIndex, body, decodedBody, - leafIndex: m.leaf_index, - hash: ensureLeading0x(m.hash), originTransaction: parseTransaction(m.transaction), destinationTransaction, }; diff --git a/src/features/messages/placeholderMessages.ts b/src/features/messages/placeholderMessages.ts index ed684b5..1b11f92 100644 --- a/src/features/messages/placeholderMessages.ts +++ b/src/features/messages/placeholderMessages.ts @@ -17,12 +17,13 @@ const BODY_ZERO = export const PLACEHOLDER_MESSAGE: Message = { id: 1, + msgId: TX_HASH_ZERO.substring(2), leafIndex: 1, + nonce: 1, status: MessageStatus.Pending, sender: constants.AddressZero, recipient: constants.AddressZero, body: BODY_ZERO, - hash: TX_HASH_ZERO, originDomainId: 0, destinationDomainId: 0, originChainId: 0, diff --git a/src/features/messages/types.ts b/src/features/messages/types.ts index adcbe6a..6141048 100644 --- a/src/features/messages/types.ts +++ b/src/features/messages/types.ts @@ -24,8 +24,8 @@ export interface TransactionEntry { export interface DeliveredMessageStubEntry { id: number; - inbox_address: string; // binary e.g. \\x123 tx_id: number; + destination_mailbox: string; transaction: { block: { timestamp: string; // e.g. "2022-08-28T17:30:15" @@ -48,22 +48,22 @@ export interface MessageStateEntry { export interface MessageStubEntry { id: number; + msg_id: string; destination: number; origin: number; recipient: string; // binary e.g. \\x123 sender: string; // binary e.g. \\x123 timestamp: string; // e.g. "2022-08-28T17:30:15" - transaction: TransactionEntry; // origin transaction delivered_message: DeliveredMessageStubEntry | null | undefined; message_states: MessageStateEntry[]; } export interface MessageEntry extends MessageStubEntry { - outbox_address: string; // binary e.g. \\x123 + nonce: number; msg_body: string | null | undefined; // binary e.g. \\x123 - hash: string; // message hash, not related to tx - leaf_index: number; + origin_mailbox: string; origin_tx_id: number; + transaction: TransactionEntry; // origin transaction delivered_message: DeliveredMessageEntry | null | undefined; } diff --git a/src/types.ts b/src/types.ts index 7a5ef2a..1e9764b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -15,7 +15,8 @@ export enum MessageStatus { } export interface MessageStub { - id: number; + id: number; // Database id + msgId: string; // Message hash status: MessageStatus; sender: Address; recipient: Address; @@ -28,10 +29,10 @@ export interface MessageStub { } export interface Message extends MessageStub { + nonce: number; + leafIndex: number; body: string; decodedBody?: string; - leafIndex: number; - hash: string; // message hash, not related to txs originTransaction: PartialTransactionReceipt; destinationTransaction?: PartialTransactionReceipt; }