diff --git a/src/features/messages/pi-queries/usePiChainMessageQuery.ts b/src/features/messages/pi-queries/usePiChainMessageQuery.ts index aa04645..881fa49 100644 --- a/src/features/messages/pi-queries/usePiChainMessageQuery.ts +++ b/src/features/messages/pi-queries/usePiChainMessageQuery.ts @@ -46,7 +46,7 @@ export function usePiChainMessageSearchQuery({ ], queryFn: async () => { const hasInput = !!sanitizedInput; - const isValidInput = isValidSearchQuery(sanitizedInput, true); + const isValidInput = isValidSearchQuery(sanitizedInput); if (pause || !multiProvider || !hasInput || !isValidInput) return []; logger.debug('Starting PI Chain message search for:', sanitizedInput); // TODO handle time-based filters here diff --git a/src/features/messages/queries/build.ts b/src/features/messages/queries/build.ts index 46208e1..9b49369 100644 --- a/src/features/messages/queries/build.ts +++ b/src/features/messages/queries/build.ts @@ -71,14 +71,12 @@ export function buildMessageSearchQuery( limit: number, useStub = false, ) { - const hasInput = !!searchInput; - 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 = { - search: hasInput ? searchValueToPostgresBytea(searchInput) : undefined, + search: searchValueToPostgresBytea(searchInput), originChains, destinationChains, startTime, diff --git a/src/features/messages/queries/encoding.ts b/src/features/messages/queries/encoding.ts index 6fa9c11..9f621e6 100644 --- a/src/features/messages/queries/encoding.ts +++ b/src/features/messages/queries/encoding.ts @@ -1,10 +1,16 @@ import { ChainMetadata } from '@hyperlane-xyz/sdk'; import { addressToByteHexString, + base58ToBuffer, + bufferToBase58, bytesToProtocolAddress, ensure0x, isAddress, isAddressEvm, + isValidTransactionHashCosmos, + isValidTransactionHashEvm, + isValidTransactionHashSealevel, + ProtocolType, strip0x, } from '@hyperlane-xyz/utils'; @@ -34,7 +40,33 @@ export function postgresByteaToAddress( return bytesToProtocolAddress(addressBytes, chainMetadata.protocol, chainMetadata.bech32Prefix); } -export function searchValueToPostgresBytea(input: string): string { - if (isAddress(input)) return addressToPostgresBytea(input); - else return stringToPostgresBytea(input); +export function postgresByteaToTxHash( + byteString: string, + chainMetadata: ChainMetadata | null | undefined, +): string { + const hexString = postgresByteaToString(byteString); + // Return hex string for protocols other than Sealevel + if (chainMetadata?.protocol !== ProtocolType.Sealevel) return hexString; + const bytes = Buffer.from(strip0x(hexString), 'hex'); + return bufferToBase58(bytes); +} + +export function searchValueToPostgresBytea(input: string): string | undefined { + if (!input) return undefined; + try { + if (isAddress(input)) { + return addressToPostgresBytea(input); + } + if (isValidTransactionHashEvm(input) || isValidTransactionHashCosmos(input)) { + return stringToPostgresBytea(input); + } + if (isValidTransactionHashSealevel(input)) { + const bytes = base58ToBuffer(input); + return stringToPostgresBytea(bytes.toString('hex')); + } + return undefined; + } catch (error) { + // Search input couldn't be decoded and recoded properly + return undefined; + } } diff --git a/src/features/messages/queries/parse.ts b/src/features/messages/queries/parse.ts index 276efdb..4cc9e40 100644 --- a/src/features/messages/queries/parse.ts +++ b/src/features/messages/queries/parse.ts @@ -6,7 +6,7 @@ import { tryUtf8DecodeBytes } from '../../../utils/string'; import { DomainsEntry } from '../../chains/queries/fragments'; import { isPiChain } from '../../chains/utils'; -import { postgresByteaToAddress, postgresByteaToString } from './encoding'; +import { postgresByteaToAddress, postgresByteaToString, postgresByteaToTxHash } from './encoding'; import { MessageEntry, MessageStubEntry, @@ -79,13 +79,13 @@ function parseMessageStub( destinationDomainId: m.destination_domain_id, origin: { timestamp: parseTimestampString(m.send_occurred_at), - hash: postgresByteaToString(m.origin_tx_hash), + hash: postgresByteaToTxHash(m.origin_tx_hash, originMetadata), from: postgresByteaToAddress(m.origin_tx_sender, originMetadata), }, destination: m.is_delivered ? { timestamp: parseTimestampString(m.delivery_occurred_at!), - hash: postgresByteaToString(m.destination_tx_hash!), + hash: postgresByteaToTxHash(m.destination_tx_hash!, destinationMetadata), from: postgresByteaToAddress(m.destination_tx_sender!, destinationMetadata), } : undefined, diff --git a/src/features/messages/queries/useMessageQuery.ts b/src/features/messages/queries/useMessageQuery.ts index ec55e43..1e09470 100644 --- a/src/features/messages/queries/useMessageQuery.ts +++ b/src/features/messages/queries/useMessageQuery.ts @@ -1,18 +1,13 @@ import { useCallback, useMemo } from 'react'; import { useQuery } from 'urql'; -import { - isAddress, - isValidTransactionHashCosmos, - isValidTransactionHashEvm, -} from '@hyperlane-xyz/utils'; - import { useMultiProvider } from '../../../store'; import { MessageStatus } from '../../../types'; import { useInterval } from '../../../utils/useInterval'; import { useScrapedDomains } from '../../chains/queries/useScrapedChains'; import { MessageIdentifierType, buildMessageQuery, buildMessageSearchQuery } from './build'; +import { searchValueToPostgresBytea } from './encoding'; import { MessagesQueryResult, MessagesStubQueryResult } from './fragments'; import { parseMessageQueryResult, parseMessageStubResult } from './parse'; @@ -21,13 +16,9 @@ const MSG_AUTO_REFRESH_DELAY = 10_000; // 10s const LATEST_QUERY_LIMIT = 100; const SEARCH_QUERY_LIMIT = 50; -export function isValidSearchQuery(input: string, allowAddress?: boolean) { +export function isValidSearchQuery(input: string) { if (!input) return false; - return !!( - isValidTransactionHashEvm(input) || - isValidTransactionHashCosmos(input) || - (allowAddress && isAddress(input)) - ); + return !!searchValueToPostgresBytea(input); } export function useMessageSearchQuery( @@ -40,7 +31,7 @@ export function useMessageSearchQuery( const { scrapedDomains: scrapedChains } = useScrapedDomains(); const hasInput = !!sanitizedInput; - const isValidInput = hasInput ? isValidSearchQuery(sanitizedInput, true) : true; + const isValidInput = !hasInput || isValidSearchQuery(sanitizedInput); // Assemble GraphQL query const { query, variables } = buildMessageSearchQuery( diff --git a/src/utils/string.ts b/src/utils/string.ts index fb71df1..637451d 100644 --- a/src/utils/string.ts +++ b/src/utils/string.ts @@ -5,7 +5,7 @@ import { strip0x } from '@hyperlane-xyz/utils'; const alphanumericRgex = /[^a-zA-Z0-9]/gi; export function sanitizeString(str: string) { if (!str || typeof str !== 'string') return ''; - return str.replaceAll(alphanumericRgex, '').toLowerCase(); + return str.replaceAll(alphanumericRgex, ''); } export function tryUtf8DecodeBytes(value: string, fatal = true) {