diff --git a/apps/explorer/src/app/components/search/detect-search.spec.ts b/apps/explorer/src/app/components/search/detect-search.spec.ts index d4a08f2c4..1a321013b 100644 --- a/apps/explorer/src/app/components/search/detect-search.spec.ts +++ b/apps/explorer/src/app/components/search/detect-search.spec.ts @@ -89,8 +89,8 @@ describe('Detect Search', () => { expect(actual).toStrictEqual(expected); }); - it("detectTypeByFetching should call fetch with hex query it's a transaction", async () => { - const query = 'abc'; + it("detectTypeByFetching should call fetch with non-hex query it's a transaction", async () => { + const query = '0xabc'; const type = SearchTypes.Transaction; // @ts-ignore issue related to polyfill fetch.mockImplementation( @@ -99,45 +99,21 @@ describe('Detect Search', () => { ok: true, json: () => Promise.resolve({ - result: { - tx: query, + transaction: { + hash: query, }, }), }) ) ); - const result = await detectTypeByFetching(query, type); + const result = await detectTypeByFetching(query); expect(fetch).toHaveBeenCalledWith( - `${DATA_SOURCES.tendermintUrl}/tx?hash=0x${query}` + `${DATA_SOURCES.blockExplorerUrl}/transactions/${toNonHex(query)}` ); expect(result).toBe(type); }); it("detectTypeByFetching should call fetch with non-hex query it's a party", async () => { - const query = 'abc'; - const type = SearchTypes.Party; - // @ts-ignore issue related to polyfill - fetch.mockImplementation( - jest.fn(() => - Promise.resolve({ - ok: true, - json: () => - Promise.resolve({ - result: { - txs: [query], - }, - }), - }) - ) - ); - const result = await detectTypeByFetching(query, type); - expect(fetch).toHaveBeenCalledWith( - `${DATA_SOURCES.tendermintUrl}/tx_search?query="tx.submitter='${query}'"` - ); - expect(result).toBe(type); - }); - - it('detectTypeByFetching should return undefined if no matches', async () => { const query = 'abc'; const type = SearchTypes.Party; // @ts-ignore issue related to polyfill @@ -148,11 +124,8 @@ describe('Detect Search', () => { }) ) ); - const result = await detectTypeByFetching(query, type); - expect(fetch).toHaveBeenCalledWith( - `${DATA_SOURCES.tendermintUrl}/tx_search?query="tx.submitter='${query}'"` - ); - expect(result).toBe(undefined); + const result = await detectTypeByFetching(query); + expect(result).toBe(type); }); it('getSearchType should return party from fetch response', async () => { @@ -163,13 +136,7 @@ describe('Detect Search', () => { fetch.mockImplementation( jest.fn(() => Promise.resolve({ - ok: true, - json: () => - Promise.resolve({ - result: { - txs: [query], - }, - }), + ok: false, }) ) ); @@ -177,7 +144,7 @@ describe('Detect Search', () => { expect(result).toBe(expected); }); - it('getSearchType should return party from transaction response', async () => { + it('getSearchType should return transaction from fetch response', async () => { const query = '4624293CFE3D8B67A0AB448BAFF8FBCF1A1B770D9D5F263761D3D6CBEA94D97F'; const expected = SearchTypes.Transaction; @@ -188,8 +155,8 @@ describe('Detect Search', () => { ok: true, json: () => Promise.resolve({ - result: { - tx: query, + transaction: { + hash: query, }, }), }) @@ -200,17 +167,8 @@ describe('Detect Search', () => { }); it('getSearchType should return undefined from transaction response', async () => { - const query = - '0x4624293CFE3D8B67A0AB448BAFF8FBCF1A1B770D9D5F263761D3D6CBEA94D97F'; + const query = 'u'; const expected = undefined; - // @ts-ignore issue related to polyfill - fetch.mockImplementation( - jest.fn(() => - Promise.resolve({ - ok: false, - }) - ) - ); const result = await getSearchType(query); expect(result).toBe(expected); }); diff --git a/apps/explorer/src/app/components/search/detect-search.ts b/apps/explorer/src/app/components/search/detect-search.ts index 757bae779..19297f1bf 100644 --- a/apps/explorer/src/app/components/search/detect-search.ts +++ b/apps/explorer/src/app/components/search/detect-search.ts @@ -1,4 +1,5 @@ import { DATA_SOURCES } from '../../config'; +import type { BlockExplorerTransaction } from '../../routes/types/block-explorer-response'; export enum SearchTypes { Transaction = 'transaction', @@ -42,46 +43,88 @@ export const detectTypeFromQuery = ( }; export const detectTypeByFetching = async ( - query: string, - type: SearchTypes + query: string ): Promise => { - const TYPES = [SearchTypes.Party, SearchTypes.Transaction]; + const hash = toNonHex(query); + const request = await fetch( + `${DATA_SOURCES.blockExplorerUrl}/transactions/${hash}` + ); - if (!TYPES.includes(type)) { - throw new Error('Search type provided not recognised'); - } + if (request?.ok) { + const body: BlockExplorerTransaction = await request.json(); - if (type === SearchTypes.Transaction) { - const hash = toHex(query); - const request = await fetch( - `${DATA_SOURCES.tendermintUrl}/tx?hash=${hash}` - ); - - if (request?.ok) { - const body = await request.json(); - - if (body?.result?.tx) { - return SearchTypes.Transaction; - } - } - } else if (type === SearchTypes.Party) { - const party = toNonHex(query); - const request = await fetch( - `${DATA_SOURCES.tendermintUrl}/tx_search?query="tx.submitter='${party}'"` - ); - - if (request.ok) { - const body = await request.json(); - - if (body?.result?.txs?.length) { - return SearchTypes.Party; - } + if (body?.transaction) { + return SearchTypes.Transaction; } } - return undefined; + return SearchTypes.Party; }; +// Code commented out because the current solution to detect a hex is temporary (by process of elimination) +// export const detectTypeByFetching = async ( +// query: string, +// type: SearchTypes +// ): Promise => { +// const TYPES = [SearchTypes.Party, SearchTypes.Transaction]; +// +// if (!TYPES.includes(type)) { +// throw new Error('Search type provided not recognised'); +// } +// +// if (type === SearchTypes.Transaction) { +// const hash = toNonHex(query); +// const request = await fetch( +// `${DATA_SOURCES.blockExplorerUrl}/transactions/${hash}` +// ); +// +// if (request?.ok) { +// const body: BlockExplorerTransaction = await request.json(); +// +// if (body?.transaction) { +// return SearchTypes.Transaction; +// } +// } +// } else if (type === SearchTypes.Party) { +// const party = toNonHex(query); +// +// const request = await fetch( +// `${DATA_SOURCES.blockExplorerUrl}/transactions?limit=1&filters[tx.submitter]=${party}` +// ); +// +// if (request.ok) { +// const body: BlockExplorerTransactions = await request.json(); +// +// if (body?.transactions?.length) { +// return SearchTypes.Party; +// } +// } +// } +// +// return undefined; +// }; + +// export const getSearchType = async ( +// query: string +// ): Promise => { +// const searchTypes = detectTypeFromQuery(query); +// const hasResults = searchTypes?.length; +// +// if (hasResults) { +// if (hasResults > 1) { +// const promises = searchTypes.map((type) => +// detectTypeByFetching(query, type) +// ); +// const results = await Promise.all(promises); +// return results.find((result) => result !== undefined); +// } +// +// return searchTypes[0]; +// } +// +// return undefined; +// }; + export const getSearchType = async ( query: string ): Promise => { @@ -90,11 +133,7 @@ export const getSearchType = async ( if (hasResults) { if (hasResults > 1) { - const promises = searchTypes.map((type) => - detectTypeByFetching(query, type) - ); - const results = await Promise.all(promises); - return results.find((type) => type !== undefined); + return await detectTypeByFetching(query); } return searchTypes[0]; diff --git a/apps/explorer/src/app/components/search/search.tsx b/apps/explorer/src/app/components/search/search.tsx index 827b144f8..cb1043bc4 100644 --- a/apps/explorer/src/app/components/search/search.tsx +++ b/apps/explorer/src/app/components/search/search.tsx @@ -22,32 +22,29 @@ export const Search = () => { const query = fields.search; if (!query) { - setError(new Error(t('Search query required'))); - } else { - const result = await getSearchType(query); - const urlAsHex = toHex(query); - const unrecognisedError = new Error( - t('Transaction type is not recognised') - ); - - if (result) { - switch (result) { - case SearchTypes.Party: - navigate(`${Routes.PARTIES}/${urlAsHex}`); - break; - case SearchTypes.Transaction: - navigate(`${Routes.TX}/${urlAsHex}`); - break; - case SearchTypes.Block: - navigate(`${Routes.BLOCKS}/${Number(query)}`); - break; - default: - setError(unrecognisedError); - } - } - - setError(unrecognisedError); + return setError(new Error(t('Search query required'))); } + + const result = await getSearchType(query); + const urlAsHex = toHex(query); + const unrecognisedError = new Error( + t('Transaction type is not recognised') + ); + + if (result) { + switch (result) { + case SearchTypes.Party: + return navigate(`${Routes.PARTIES}/${urlAsHex}`); + case SearchTypes.Transaction: + return navigate(`${Routes.TX}/${urlAsHex}`); + case SearchTypes.Block: + return navigate(`${Routes.BLOCKS}/${Number(query)}`); + default: + return setError(unrecognisedError); + } + } + + return setError(unrecognisedError); }, [navigate] ); @@ -61,22 +58,24 @@ export const Search = () => { {t('Search by block number or transaction hash')}
- - {error?.message && ( -
- - {error.message} - -
- )} +
+ + {error?.message && ( +
+ + {error.message} + +
+ )} +
diff --git a/apps/explorer/src/app/components/txs/txs-infinite-list-item.tsx b/apps/explorer/src/app/components/txs/txs-infinite-list-item.tsx index 8dedfddec..c8540994a 100644 --- a/apps/explorer/src/app/components/txs/txs-infinite-list-item.tsx +++ b/apps/explorer/src/app/components/txs/txs-infinite-list-item.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { TruncatedLink } from '../truncate/truncated-link'; import { Routes } from '../../routes/route-names'; import { TxOrderType } from './tx-order-type'; -import type { BlockExplorerTransaction } from '../../routes/types/block-explorer-response'; +import type { BlockExplorerTransactionResult } from '../../routes/types/block-explorer-response'; import { toHex } from '../search/detect-search'; const TRUNCATE_LENGTH = 14; @@ -13,7 +13,7 @@ export const TxsInfiniteListItem = ({ type, block, index, -}: Partial) => { +}: Partial) => { if ( !hash || !submitter || diff --git a/apps/explorer/src/app/components/txs/txs-infinite-list.spec.tsx b/apps/explorer/src/app/components/txs/txs-infinite-list.spec.tsx index 9031496e8..7ca1bd3f2 100644 --- a/apps/explorer/src/app/components/txs/txs-infinite-list.spec.tsx +++ b/apps/explorer/src/app/components/txs/txs-infinite-list.spec.tsx @@ -1,9 +1,9 @@ import { TxsInfiniteList } from './txs-infinite-list'; import { render, screen, fireEvent, act } from '@testing-library/react'; import { MemoryRouter } from 'react-router-dom'; -import type { BlockExplorerTransaction } from '../../routes/types/block-explorer-response'; +import type { BlockExplorerTransactionResult } from '../../routes/types/block-explorer-response'; -const generateTxs = (number: number): BlockExplorerTransaction[] => { +const generateTxs = (number: number): BlockExplorerTransactionResult[] => { return Array.from(Array(number)).map((_) => ({ block: '87901', index: 2, diff --git a/apps/explorer/src/app/components/txs/txs-infinite-list.tsx b/apps/explorer/src/app/components/txs/txs-infinite-list.tsx index 055412cb3..7670198cf 100644 --- a/apps/explorer/src/app/components/txs/txs-infinite-list.tsx +++ b/apps/explorer/src/app/components/txs/txs-infinite-list.tsx @@ -3,19 +3,19 @@ import { FixedSizeList as List } from 'react-window'; import InfiniteLoader from 'react-window-infinite-loader'; import { t } from '@vegaprotocol/react-helpers'; import { TxsInfiniteListItem } from './txs-infinite-list-item'; -import type { BlockExplorerTransaction } from '../../routes/types/block-explorer-response'; +import type { BlockExplorerTransactionResult } from '../../routes/types/block-explorer-response'; interface TxsInfiniteListProps { hasMoreTxs: boolean; areTxsLoading: boolean | undefined; - txs: BlockExplorerTransaction[] | undefined; + txs: BlockExplorerTransactionResult[] | undefined; loadMoreTxs: () => void; error: Error | undefined; className?: string; } interface ItemProps { - index: BlockExplorerTransaction; + index: BlockExplorerTransactionResult; style: React.CSSProperties; isLoading: boolean; error: Error | undefined; diff --git a/apps/explorer/src/app/routes/parties/id/index.tsx b/apps/explorer/src/app/routes/parties/id/index.tsx index 41fe39408..6dea40d15 100644 --- a/apps/explorer/src/app/routes/parties/id/index.tsx +++ b/apps/explorer/src/app/routes/parties/id/index.tsx @@ -13,12 +13,13 @@ import { SubHeading } from '../../../components/sub-heading'; import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit'; import { Panel } from '../../../components/panel'; import { InfoPanel } from '../../../components/info-panel'; +import { toNonHex } from '../../../components/search/detect-search'; import { DATA_SOURCES } from '../../../config'; -import type { TendermintSearchTransactionResponse } from '../tendermint-transaction-response'; import type { PartyAssetsQuery, PartyAssetsQueryVariables, } from './__generated__/PartyAssetsQuery'; +import type { BlockExplorerTransactions } from '../../../routes/types/block-explorer-response'; const PARTY_ASSETS_QUERY = gql` query PartyAssetsQuery($partyId: ID!) { @@ -57,13 +58,12 @@ const PARTY_ASSETS_QUERY = gql` const Party = () => { const { party } = useParams<{ party: string }>(); + const partyId = party ? toNonHex(party) : ''; const { state: { data: partyData }, - } = useFetch( - `${ - DATA_SOURCES.tendermintUrl - }/tx_search?query="tx.submitter='${party?.replace('0x', '')}'"` + } = useFetch( + `${DATA_SOURCES.blockExplorerUrl}/transactions?limit=1&filters[tx.submitter]=${partyId}` ); const { data } = useQuery( @@ -71,7 +71,7 @@ const Party = () => { { // Don't cache data for this query, party information can move quite quickly fetchPolicy: 'network-only', - variables: { partyId: party?.replace('0x', '') || '' }, + variables: { partyId }, skip: !party, } ); diff --git a/apps/explorer/src/app/routes/txs/home/index.tsx b/apps/explorer/src/app/routes/txs/home/index.tsx index d2ae19b1c..6ef1c1500 100644 --- a/apps/explorer/src/app/routes/txs/home/index.tsx +++ b/apps/explorer/src/app/routes/txs/home/index.tsx @@ -5,12 +5,12 @@ import { RouteTitle } from '../../../components/route-title'; import { BlocksRefetch } from '../../../components/blocks'; import { TxsInfiniteList } from '../../../components/txs'; import type { - BlockExplorerTransaction, + BlockExplorerTransactionResult, BlockExplorerTransactions, } from '../../../routes/types/block-explorer-response'; interface TxsStateProps { - txsData: BlockExplorerTransaction[]; + txsData: BlockExplorerTransactionResult[]; hasMoreTxs: boolean; lastCursor: string; } diff --git a/apps/explorer/src/app/routes/txs/id/index.tsx b/apps/explorer/src/app/routes/txs/id/index.tsx index 74345bc80..befc2f7cf 100644 --- a/apps/explorer/src/app/routes/txs/id/index.tsx +++ b/apps/explorer/src/app/routes/txs/id/index.tsx @@ -1,49 +1,43 @@ import React from 'react'; import { useParams } from 'react-router-dom'; import { useFetch } from '@vegaprotocol/react-helpers'; -import type { TendermintTransactionResponse } from '../tendermint-transaction-response.d'; -import type { ChainExplorerTxResponse } from '../../types/chain-explorer-response'; import { DATA_SOURCES } from '../../../config'; import { RouteTitle } from '../../../components/route-title'; import { RenderFetched } from '../../../components/render-fetched'; import { TxContent } from './tx-content'; import { TxDetails } from './tx-details'; import { t } from '@vegaprotocol/react-helpers'; +import type { BlockExplorerTransaction } from '../../../routes/types/block-explorer-response'; +import { toNonHex } from '../../../components/search/detect-search'; const Tx = () => { const { txHash } = useParams<{ txHash: string }>(); + const hash = txHash ? toNonHex(txHash) : ''; const { - state: { data: tTxData, loading: tTxLoading, error: tTxError }, - } = useFetch( - `${DATA_SOURCES.tendermintUrl}/tx?hash=${txHash}` + state: { data, loading: tTxLoading, error: tTxError }, + } = useFetch( + `${DATA_SOURCES.blockExplorerUrl}/transactions/${toNonHex(hash)}` ); - const { - state: { data: ceTxData, loading: ceTxLoading, error: ceTxError }, - } = useFetch(DATA_SOURCES.chainExplorerUrl, { - method: 'POST', - body: JSON.stringify({ - tx_hash: txHash, - node_url: `${DATA_SOURCES.tendermintUrl}/`, - }), - }); - return (
{t('Transaction details')} - - + <> + -

{t('Transaction content')}

- - +

+ {t('Transaction content')} +

+ + +
); diff --git a/apps/explorer/src/app/routes/txs/id/tx-content.tsx b/apps/explorer/src/app/routes/txs/id/tx-content.tsx index 80b98736d..9d3246b94 100644 --- a/apps/explorer/src/app/routes/txs/id/tx-content.tsx +++ b/apps/explorer/src/app/routes/txs/id/tx-content.tsx @@ -8,14 +8,14 @@ import { TableRow, } from '../../../components/table'; import { TxOrderType } from '../../../components/txs'; -import type { ChainExplorerTxResponse } from '../../types/chain-explorer-response'; +import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response'; interface TxContentProps { - data: ChainExplorerTxResponse | undefined; + data: BlockExplorerTransactionResult | undefined; } export const TxContent = ({ data }: TxContentProps) => { - if (!data?.Command) { + if (!data?.command) { return ( {t('Could not retrieve transaction content')} @@ -31,13 +31,13 @@ export const TxContent = ({ data }: TxContentProps) => { {t('Type')} - +

{t('Decoded transaction content')}

- + ); }; diff --git a/apps/explorer/src/app/routes/txs/id/tx-details.spec.tsx b/apps/explorer/src/app/routes/txs/id/tx-details.spec.tsx index 5a5328b14..ac10a479e 100644 --- a/apps/explorer/src/app/routes/txs/id/tx-details.spec.tsx +++ b/apps/explorer/src/app/routes/txs/id/tx-details.spec.tsx @@ -1,33 +1,24 @@ import { BrowserRouter as Router } from 'react-router-dom'; import { render, screen } from '@testing-library/react'; -import { truncateByChars } from '@vegaprotocol/react-helpers'; -import { TxDetails, txDetailsTruncateLength } from './tx-details'; -import type { Result } from '../tendermint-transaction-response.d'; +import { TxDetails } from './tx-details'; +import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response'; const pubKey = 'test'; const hash = '7416753A30622A9E24A06F0172D6C33A95186B36806D96345C6DC5A23FA3F283'; const height = '52987'; -const tx = - 'ClMIpeWjmKnn77FNEPedA5J9QhJAOGI3YjQzMWNlMmNhNzc4MWMzMTQ1M2IyYjc0MWYwMTJlNzQ1MzBhZDhjMDgzODVkMWQ1YjRiY2VkMTJiNDc1MhKTAQqAATM5NDFlNmExMzQ3MGVhNTlhNGExNmQzMjRiYzlkZjI5YWZkMzYxMDRiZjQ5MzEwZWMxM2ZiOTMxNTM2NGY3ZjU2ZTQyOTJmYTAyZDlhNTBlZDc0OWE0ZjExMzJiNjM2ZTZmMzQ3YzQ2NjdkYmM5OThmYzcyZjYzYzQxMzU4ZTAzEgx2ZWdhL2VkMjU1MTkYAYB9AtI+QDA5MzFhOGZkOGNjOTM1NDU4ZjQ3MGU0MzVhMDU0MTQzODdjZWE2ZjMyOWQ2NDhiZTg5NGZjZDQ0YmQ1MTdhMmI='; -const txData = { +const txData: BlockExplorerTransactionResult = { hash, - height, - tx, + block: height, index: 0, - tx_result: { - code: 0, - data: null, - log: '', - info: '', - events: [], - gas_wanted: '0', - gas_used: '0', - codespace: '', - }, + submitter: pubKey, + code: 0, + cursor: `${height}.0`, + type: 'type', + command: {}, }; -const renderComponent = (txData: Result) => ( +const renderComponent = (txData: BlockExplorerTransactionResult) => ( @@ -49,15 +40,6 @@ describe('Transaction details', () => { expect(screen.getByText(height)).toBeInTheDocument(); }); - it('Renders the truncated tx text', () => { - render(renderComponent(txData)); - expect( - screen.getByText( - truncateByChars(tx, txDetailsTruncateLength, txDetailsTruncateLength) - ) - ).toBeInTheDocument(); - }); - it('Renders a copy button', () => { render(renderComponent(txData)); expect(screen.getByTestId('copy-tx-to-clipboard')).toBeInTheDocument(); diff --git a/apps/explorer/src/app/routes/txs/id/tx-details.tsx b/apps/explorer/src/app/routes/txs/id/tx-details.tsx index 8c5c44cc8..1ad97af0a 100644 --- a/apps/explorer/src/app/routes/txs/id/tx-details.tsx +++ b/apps/explorer/src/app/routes/txs/id/tx-details.tsx @@ -1,18 +1,18 @@ import { Routes } from '../../route-names'; -import { ButtonLink, CopyWithTooltip } from '@vegaprotocol/ui-toolkit'; +import { CopyWithTooltip, Icon } from '@vegaprotocol/ui-toolkit'; import { TableWithTbody, TableCell, TableHeader, TableRow, } from '../../../components/table'; -import { TruncateInline } from '../../../components/truncate/truncate'; import { t } from '@vegaprotocol/react-helpers'; import { HighlightedLink } from '../../../components/highlighted-link'; -import type { Result } from '../tendermint-transaction-response.d'; +import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response'; +import React from 'react'; interface TxDetailsProps { - txData: Result | undefined; + txData: BlockExplorerTransactionResult | undefined; pubKey: string | undefined; className?: string; } @@ -21,7 +21,7 @@ export const txDetailsTruncateLength = 30; export const TxDetails = ({ txData, pubKey, className }: TxDetailsProps) => { if (!txData) { - return <>{t('Awaiting Tendermint transaction details')}; + return <>{t('Awaiting Block Explorer transaction details')}; } return ( @@ -30,6 +30,15 @@ export const TxDetails = ({ txData, pubKey, className }: TxDetailsProps) => { {t('Hash')} {txData.hash} + + + @@ -44,31 +53,11 @@ export const TxDetails = ({ txData, pubKey, className }: TxDetailsProps) => { {t('Block')} - - {t('Encoded txn')} - - - - - - - ); }; diff --git a/apps/explorer/src/app/routes/types/block-explorer-response.d.ts b/apps/explorer/src/app/routes/types/block-explorer-response.d.ts index d0c381fdc..7682a644c 100644 --- a/apps/explorer/src/app/routes/types/block-explorer-response.d.ts +++ b/apps/explorer/src/app/routes/types/block-explorer-response.d.ts @@ -1,4 +1,4 @@ -export interface BlockExplorerTransaction { +export interface BlockExplorerTransactionResult { block: string; index: number; hash: string; @@ -10,5 +10,9 @@ export interface BlockExplorerTransaction { } export interface BlockExplorerTransactions { - transactions: BlockExplorerTransaction[]; + transactions: BlockExplorerTransactionResult[]; +} + +export interface BlockExplorerTransaction { + transaction: BlockExplorerTransactionResult; }