From 50ac6f7a3e612081533b1b9caebeb81daec34d2d Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Tue, 9 Aug 2022 01:50:38 -0300 Subject: [PATCH 01/15] Extract swr fetcher --- src/use4Bytes.ts | 59 +++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/src/use4Bytes.ts b/src/use4Bytes.ts index 85f4e98..ddf8e2b 100644 --- a/src/use4Bytes.ts +++ b/src/use4Bytes.ts @@ -5,6 +5,7 @@ import { TransactionDescription, } from "@ethersproject/abi"; import { BigNumberish } from "@ethersproject/bignumber"; +import { Fetcher } from "swr"; import useSWRImmutable from "swr/immutable"; import { RuntimeContext } from "./useRuntime"; import { fourBytesURL } from "./url"; @@ -29,16 +30,32 @@ export const extract4Bytes = (rawInput: string): string | null => { return rawInput.slice(0, 10); }; -const fetch4Bytes = async ( - assetsURLPrefix: string, - fourBytes: string -): Promise => { - const signatureURL = fourBytesURL(assetsURLPrefix, fourBytes); +type FourBytesKey = [id: "4bytes", fourBytes: string]; +type FourBytesFetcher = Fetcher< + FourBytesEntry | null | undefined, + FourBytesKey +>; + +const fourBytesFetcher = + (assetsURLPrefix: string): FourBytesFetcher => + async (_, key) => { + if (key === null || key === "0x") { + return undefined; + } + + // Handle simple transfers with invalid selector like tx: + // 0x8bcbdcc1589b5c34c1e55909c8269a411f0267a4fed59a73dd4348cc71addbb9, + // which contains 0x00 as data + if (key.length !== 10) { + return undefined; + } + + const fourBytes = key.slice(2); + const signatureURL = fourBytesURL(assetsURLPrefix, fourBytes); - try { const res = await fetch(signatureURL); if (!res.ok) { - console.error(`Signature does not exist in 4bytes DB: ${fourBytes}`); + console.warn(`Signature does not exist in 4bytes DB: ${fourBytes}`); return null; } @@ -53,11 +70,7 @@ const fetch4Bytes = async ( signature: sig, }; return entry; - } catch (err) { - console.error(`Couldn't fetch signature URL ${signatureURL}`, err); - return null; - } -}; + }; /** * Extract 4bytes DB info @@ -75,26 +88,10 @@ export const use4Bytes = ( const { config } = useContext(RuntimeContext); const assetsURLPrefix = config?.assetsURLPrefix; + const fourBytesKey = assetsURLPrefix !== undefined ? rawFourBytes : null; - const fourBytesFetcher = (key: string | null) => { - if (key === null || key === "0x") { - return undefined; - } - - // Handle simple transfers with invalid selector like tx: - // 0x8bcbdcc1589b5c34c1e55909c8269a411f0267a4fed59a73dd4348cc71addbb9, - // which contains 0x00 as data - if (key.length !== 10) { - return undefined; - } - - return fetch4Bytes(assetsURLPrefix!, key.slice(2)); - }; - - const { data, error } = useSWRImmutable( - assetsURLPrefix !== undefined ? rawFourBytes : null, - fourBytesFetcher - ); + const fetcher = fourBytesFetcher(assetsURLPrefix!); + const { data, error } = useSWRImmutable(["4bytes", fourBytesKey], fetcher); return error ? undefined : data; }; From 0b210fd446111a0e2afbb238db2cc7e0aa358db1 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Tue, 9 Aug 2022 19:23:38 -0300 Subject: [PATCH 02/15] Use SWR for Sourcify metadata fetching --- src/TransactionPageContent.tsx | 7 ++++-- src/sourcify/useSourcify.ts | 41 +++++++++++----------------------- src/url.ts | 4 ++++ 3 files changed, 22 insertions(+), 30 deletions(-) diff --git a/src/TransactionPageContent.tsx b/src/TransactionPageContent.tsx index 7320a2e..9211b97 100644 --- a/src/TransactionPageContent.tsx +++ b/src/TransactionPageContent.tsx @@ -11,7 +11,10 @@ import { SelectionContext, useSelection } from "./useSelection"; import { SelectedTransactionContext } from "./useSelectedTransaction"; import { BlockNumberContext } from "./useBlockTagContext"; import { useAppConfigContext } from "./useAppConfig"; -import { useSourcify, useTransactionDescription } from "./sourcify/useSourcify"; +import { + useSourcifyMetadata, + useTransactionDescription, +} from "./sourcify/useSourcify"; const Details = React.lazy(() => import("./transaction/Details")); const Logs = React.lazy(() => import("./transaction/Logs")); @@ -44,7 +47,7 @@ const TransactionPageContent: React.FC = ({ const selectionCtx = useSelection(); const { sourcifySource } = useAppConfigContext(); - const metadata = useSourcify( + const metadata = useSourcifyMetadata( txData?.to, provider?.network.chainId, sourcifySource diff --git a/src/sourcify/useSourcify.ts b/src/sourcify/useSourcify.ts index 4f8f31f..89aecae 100644 --- a/src/sourcify/useSourcify.ts +++ b/src/sourcify/useSourcify.ts @@ -1,6 +1,7 @@ import { useState, useEffect, useMemo } from "react"; import { Interface } from "@ethersproject/abi"; import { ErrorDescription } from "@ethersproject/abi/lib/interface"; +import useSWRImmutable from "swr/immutable"; import { ChecksummedAddress, TransactionData } from "../types"; import { sourcifyMetadata, SourcifySource, sourcifySourceFile } from "../url"; @@ -102,38 +103,22 @@ const fetchSourcifyMetadata = async ( } }; -// TODO: replace every occurrence with the multiple version one -export const useSourcify = ( +export const useSourcifyMetadata = ( address: ChecksummedAddress | undefined, chainId: number | undefined, source: SourcifySource ): Metadata | null | undefined => { - const [rawMetadata, setRawMetadata] = useState(); - - useEffect(() => { - if (!address || chainId === undefined) { - return; - } - setRawMetadata(undefined); - - const abortController = new AbortController(); - const fetchMetadata = async () => { - const _metadata = await fetchSourcifyMetadata( - address, - chainId, - source, - abortController - ); - setRawMetadata(_metadata); - }; - fetchMetadata(); - - return () => { - abortController.abort(); - }; - }, [address, chainId, source]); - - return rawMetadata; + const metadataURL = () => + address === undefined || chainId === undefined + ? null + : sourcifyMetadata(address, chainId, source); + const { data, error } = useSWRImmutable(metadataURL, (url) => + fetch(url).then((res) => res.json()) + ); + if (error) { + return null; + } + return data; }; export const useMultipleMetadata = ( diff --git a/src/url.ts b/src/url.ts index 6a7a411..69d8397 100644 --- a/src/url.ts +++ b/src/url.ts @@ -53,6 +53,10 @@ const resolveSourcifySource = (source: SourcifySource) => { throw new Error(`Unknown Sourcify integration source code: ${source}`); }; +/** + * Builds a complete Sourcify metadata.json URL given the contract address + * and chain. + */ export const sourcifyMetadata = ( address: ChecksummedAddress, chainId: number, From 39c11a1ed4825de10b8cce24ee59fec4adba3b27 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Tue, 9 Aug 2022 20:04:15 -0300 Subject: [PATCH 03/15] Replace Sourcify metadata reading by SWR; 2nd batch of refactorings --- src/TokenTransferItem.tsx | 14 ++------- src/address/AddressTransactionResults.tsx | 18 ------------ src/block/BlockTransactionResults.tsx | 22 +------------- src/components/DecoratedAddressLink.tsx | 13 +++++++-- src/components/TransactionAddress.tsx | 4 --- src/search/TransactionItem.tsx | 7 +---- src/transaction/Details.tsx | 35 ++--------------------- src/transaction/LogEntry.tsx | 8 ++---- src/transaction/Logs.tsx | 7 +---- 9 files changed, 20 insertions(+), 108 deletions(-) diff --git a/src/TokenTransferItem.tsx b/src/TokenTransferItem.tsx index 5838428..0284a58 100644 --- a/src/TokenTransferItem.tsx +++ b/src/TokenTransferItem.tsx @@ -6,28 +6,20 @@ import TransactionAddress from "./components/TransactionAddress"; import ValueHighlighter from "./components/ValueHighlighter"; import FormattedBalance from "./components/FormattedBalance"; import USDAmount from "./components/USDAmount"; -import { - AddressContext, - ChecksummedAddress, - TokenMeta, - TokenTransfer, -} from "./types"; +import { AddressContext, TokenMeta, TokenTransfer } from "./types"; import { RuntimeContext } from "./useRuntime"; import { useBlockNumberContext } from "./useBlockTagContext"; -import { Metadata } from "./sourcify/useSourcify"; import { useTokenUSDOracle } from "./usePriceOracle"; type TokenTransferItemProps = { t: TokenTransfer; tokenMeta?: TokenMeta | null | undefined; - metadatas: Record; }; // TODO: handle partial const TokenTransferItem: React.FC = ({ t, tokenMeta, - metadatas, }) => { const { provider } = useContext(RuntimeContext); const blockNumber = useBlockNumberContext(); @@ -40,7 +32,6 @@ const TokenTransferItem: React.FC = ({ @@ -51,7 +42,6 @@ const TokenTransferItem: React.FC = ({ @@ -67,7 +57,7 @@ const TokenTransferItem: React.FC = ({ /> - + {tokenMeta && quote !== undefined && decimals !== undefined && ( = ({ }, [page]); const priceMap = useMultipleETHUSDOracle(provider, blockTags); - // Calculate Sourcify metadata for all addresses that appear on this page results - const addresses = useMemo(() => { - const _addresses = [address]; - if (page) { - for (const t of page) { - if (t.to) { - _addresses.push(t.to); - } - if (t.createdContractAddress) { - _addresses.push(t.createdContractAddress); - } - } - } - return _addresses; - }, [address, page]); - const metadatas = useContractsMetadata(addresses, provider); const balance = useAddressBalance(provider, address); const creator = useContractCreator(provider, address); @@ -181,7 +164,6 @@ const AddressTransactionResults: React.FC = ({ selectedAddress={address} feeDisplay={feeDisplay} priceMap={priceMap} - metadatas={metadatas} /> ))} diff --git a/src/block/BlockTransactionResults.tsx b/src/block/BlockTransactionResults.tsx index 2d768cc..5c1824f 100644 --- a/src/block/BlockTransactionResults.tsx +++ b/src/block/BlockTransactionResults.tsx @@ -8,10 +8,9 @@ import TransactionItem from "../search/TransactionItem"; import { useFeeToggler } from "../search/useFeeToggler"; import { RuntimeContext } from "../useRuntime"; import { SelectionContext, useSelection } from "../useSelection"; -import { ChecksummedAddress, ProcessedTransaction } from "../types"; +import { ProcessedTransaction } from "../types"; import { PAGE_SIZE } from "../params"; import { useMultipleETHUSDOracle } from "../usePriceOracle"; -import { useContractsMetadata } from "../hooks"; type BlockTransactionResultsProps = { blockTag: BlockTag; @@ -32,24 +31,6 @@ const BlockTransactionResults: React.FC = ({ const blockTags = useMemo(() => [blockTag], [blockTag]); const priceMap = useMultipleETHUSDOracle(provider, blockTags); - const addresses = useMemo((): ChecksummedAddress[] => { - if (!page) { - return []; - } - - const _addresses: ChecksummedAddress[] = []; - for (const t of page) { - if (t.to) { - _addresses.push(t.to); - } - if (t.createdContractAddress) { - _addresses.push(t.createdContractAddress); - } - } - return _addresses; - }, [page]); - const metadatas = useContractsMetadata(addresses, provider); - return (
@@ -78,7 +59,6 @@ const BlockTransactionResults: React.FC = ({ tx={tx} feeDisplay={feeDisplay} priceMap={priceMap} - metadatas={metadatas} /> ))}
diff --git a/src/components/DecoratedAddressLink.tsx b/src/components/DecoratedAddressLink.tsx index e85753e..62ccd3a 100644 --- a/src/components/DecoratedAddressLink.tsx +++ b/src/components/DecoratedAddressLink.tsx @@ -8,8 +8,9 @@ import { faBurn } from "@fortawesome/free-solid-svg-icons/faBurn"; import { faCoins } from "@fortawesome/free-solid-svg-icons/faCoins"; import SourcifyLogo from "../sourcify/SourcifyLogo"; import PlainAddress from "./PlainAddress"; -import { Metadata } from "../sourcify/useSourcify"; import { RuntimeContext } from "../useRuntime"; +import { useAppConfigContext } from "../useAppConfig"; +import { useSourcifyMetadata } from "../sourcify/useSourcify"; import { useResolvedAddress } from "../useResolvedAddresses"; import { AddressContext, ChecksummedAddress, ZERO_ADDRESS } from "../types"; import { resolverRendererRegistry } from "../api/address-resolver"; @@ -23,7 +24,6 @@ type DecoratedAddressLinkProps = { selfDestruct?: boolean | undefined; txFrom?: boolean | undefined; txTo?: boolean | undefined; - metadata?: Metadata | null | undefined; eoa?: boolean | undefined; }; @@ -36,9 +36,16 @@ const DecoratedAddressLink: React.FC = ({ selfDestruct, txFrom, txTo, - metadata, eoa, }) => { + const { provider } = useContext(RuntimeContext); + const { sourcifySource } = useAppConfigContext(); + const metadata = useSourcifyMetadata( + address, + provider?.network.chainId, + sourcifySource + ); + const mint = addressCtx === AddressContext.FROM && address === ZERO_ADDRESS; const burn = addressCtx === AddressContext.TO && address === ZERO_ADDRESS; diff --git a/src/components/TransactionAddress.tsx b/src/components/TransactionAddress.tsx index 46f427f..823bd50 100644 --- a/src/components/TransactionAddress.tsx +++ b/src/components/TransactionAddress.tsx @@ -5,20 +5,17 @@ import { useSelectedTransaction } from "../useSelectedTransaction"; import { useBlockNumberContext } from "../useBlockTagContext"; import { RuntimeContext } from "../useRuntime"; import { useHasCode } from "../useErigonHooks"; -import { Metadata } from "../sourcify/useSourcify"; import { AddressContext, ChecksummedAddress } from "../types"; type TransactionAddressProps = { address: ChecksummedAddress; addressCtx?: AddressContext | undefined; - metadata?: Metadata | null | undefined; showCodeIndicator?: boolean; }; const TransactionAddress: React.FC = ({ address, addressCtx, - metadata, showCodeIndicator = false, }) => { const txData = useSelectedTransaction(); @@ -46,7 +43,6 @@ const TransactionAddress: React.FC = ({ txFrom={address === txData?.from} txTo={address === txData?.to || creation} creation={creation} - metadata={metadata} eoa={ showCodeIndicator && blockNumber !== undefined ? !toHasCode diff --git a/src/search/TransactionItem.tsx b/src/search/TransactionItem.tsx index 21e1b84..3fee32c 100644 --- a/src/search/TransactionItem.tsx +++ b/src/search/TransactionItem.tsx @@ -14,20 +14,18 @@ import TransactionDirection, { Flags, } from "../components/TransactionDirection"; import TransactionValue from "../components/TransactionValue"; -import { ChecksummedAddress, ProcessedTransaction } from "../types"; +import { ProcessedTransaction } from "../types"; import { FeeDisplay } from "./useFeeToggler"; import { RuntimeContext } from "../useRuntime"; import { useHasCode } from "../useErigonHooks"; import { formatValue } from "../components/formatter"; import ETH2USDValue from "../components/ETH2USDValue"; -import { Metadata } from "../sourcify/useSourcify"; type TransactionItemProps = { tx: ProcessedTransaction; selectedAddress?: string; feeDisplay: FeeDisplay; priceMap: Record; - metadatas: Record; }; const TransactionItem: React.FC = ({ @@ -35,7 +33,6 @@ const TransactionItem: React.FC = ({ selectedAddress, feeDisplay, priceMap, - metadatas, }) => { const { provider } = useContext(RuntimeContext); const toHasCode = useHasCode( @@ -113,7 +110,6 @@ const TransactionItem: React.FC = ({ address={tx.to} selectedAddress={selectedAddress} miner={tx.miner === tx.to} - metadata={metadatas[tx.to]} eoa={toHasCode === undefined ? undefined : !toHasCode} /> @@ -123,7 +119,6 @@ const TransactionItem: React.FC = ({ address={tx.createdContractAddress!} selectedAddress={selectedAddress} creation - metadata={metadatas[tx.createdContractAddress!]} eoa={false} /> diff --git a/src/transaction/Details.tsx b/src/transaction/Details.tsx index 9b9449f..3c0340a 100644 --- a/src/transaction/Details.tsx +++ b/src/transaction/Details.tsx @@ -1,4 +1,4 @@ -import React, { useContext, useMemo, useState } from "react"; +import React, { useContext, useState } from "react"; import { Tab } from "@headlessui/react"; import { TransactionDescription } from "@ethersproject/abi"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; @@ -25,11 +25,7 @@ import USDValue from "../components/USDValue"; import FormattedBalance from "../components/FormattedBalance"; import ETH2USDValue from "../components/ETH2USDValue"; import TokenTransferItem from "../TokenTransferItem"; -import { - TransactionData, - InternalOperation, - ChecksummedAddress, -} from "../types"; +import { TransactionData, InternalOperation } from "../types"; import PercentageBar from "../components/PercentageBar"; import ExternalLink from "../components/ExternalLink"; import RelativePosition from "../components/RelativePosition"; @@ -43,7 +39,6 @@ import { } from "../use4Bytes"; import { DevDoc, Metadata, useError, UserDoc } from "../sourcify/useSourcify"; import { RuntimeContext } from "../useRuntime"; -import { useContractsMetadata } from "../hooks"; import { useTransactionError } from "../useErigonHooks"; import { useChainInfo } from "../useChainInfo"; import { useETHUSDOracle } from "../usePriceOracle"; @@ -94,22 +89,6 @@ const Details: React.FC = ({ txData?.confirmedData?.blockNumber ); - const addresses = useMemo(() => { - const _addresses: ChecksummedAddress[] = []; - if (txData.to) { - _addresses.push(txData.to); - } - if (txData.confirmedData?.createdContractAddress) { - _addresses.push(txData.confirmedData.createdContractAddress); - } - for (const t of txData.tokenTransfers) { - _addresses.push(t.from); - _addresses.push(t.to); - _addresses.push(t.token); - } - return _addresses; - }, [txData]); - const metadatas = useContractsMetadata(addresses, provider); const [errorMsg, outputData, isCustomError] = useTransactionError( provider, txData.transactionHash @@ -269,11 +248,7 @@ const Details: React.FC = ({ {txData.to ? (
- +
) : txData.confirmedData === undefined ? ( @@ -284,9 +259,6 @@ const Details: React.FC = ({
@@ -316,7 +288,6 @@ const Details: React.FC = ({ key={i} t={t} tokenMeta={txData.tokenMetas[t.token]} - metadatas={metadatas} /> ))}
diff --git a/src/transaction/LogEntry.tsx b/src/transaction/LogEntry.tsx index a6597eb..d1be10d 100644 --- a/src/transaction/LogEntry.tsx +++ b/src/transaction/LogEntry.tsx @@ -14,10 +14,9 @@ import { Metadata } from "../sourcify/useSourcify"; type LogEntryProps = { log: Log; logDesc: LogDescription | null | undefined; - metadatas: Record; }; -const LogEntry: React.FC = ({ log, logDesc, metadatas }) => { +const LogEntry: React.FC = ({ log, logDesc }) => { const rawTopic0 = log.topics[0]; const topic0 = useTopic0(rawTopic0); @@ -56,10 +55,7 @@ const LogEntry: React.FC = ({ log, logDesc, metadatas }) => {
Address
- +
diff --git a/src/transaction/Logs.tsx b/src/transaction/Logs.tsx index 9b903b7..b02b4a6 100644 --- a/src/transaction/Logs.tsx +++ b/src/transaction/Logs.tsx @@ -62,12 +62,7 @@ const Logs: React.FC = ({ txData, metadata }) => { {txData.confirmedData.logs.length > 0 ? ( <> {txData.confirmedData.logs.map((l, i) => ( - + ))} ) : ( From 17d44433ca2d0f172b6e92b3598c316b0cdf8435 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Tue, 9 Aug 2022 20:27:51 -0300 Subject: [PATCH 04/15] Remove unnecessary export --- src/hooks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks.ts b/src/hooks.ts index e924314..9581ba6 100644 --- a/src/hooks.ts +++ b/src/hooks.ts @@ -5,7 +5,7 @@ import { Metadata, useMultipleMetadata } from "./sourcify/useSourcify"; import { useAppConfigContext } from "./useAppConfig"; import { useAddressesWithCode } from "./useErigonHooks"; -export const useDedupedAddresses = ( +const useDedupedAddresses = ( addresses: ChecksummedAddress[] ): ChecksummedAddress[] => { return useMemo(() => { From a2c2403d05a7228d7434087334961bdbe143cc4f Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Tue, 9 Aug 2022 21:39:31 -0300 Subject: [PATCH 05/15] Replace useMultipleMetadata by SWR --- src/Address.tsx | 14 ++---- src/TransactionPageContent.tsx | 5 +- src/hooks.ts | 33 ------------- src/sourcify/useSourcify.ts | 49 -------------------- src/transaction/LogEntry.tsx | 38 ++++++++++++--- src/transaction/Logs.tsx | 85 +++++++--------------------------- 6 files changed, 56 insertions(+), 168 deletions(-) delete mode 100644 src/hooks.ts diff --git a/src/Address.tsx b/src/Address.tsx index 891a315..b8767c8 100644 --- a/src/Address.tsx +++ b/src/Address.tsx @@ -23,7 +23,7 @@ import Contracts from "./address/Contracts"; import { RuntimeContext } from "./useRuntime"; import { useAppConfigContext } from "./useAppConfig"; import { useAddressOrENS } from "./useResolvedAddresses"; -import { useMultipleMetadata } from "./sourcify/useSourcify"; +import { useSourcifyMetadata } from "./sourcify/useSourcify"; import { ChecksummedAddress } from "./types"; import { useAddressesWithCode } from "./useErigonHooks"; import { useChainInfo } from "./useChainInfo"; @@ -65,7 +65,6 @@ const Address: React.FC = () => { } }, [addressOrName, checksummedAddress, isENS]); - const { sourcifySource } = useAppConfigContext(); const checksummedAddressAsArray = useMemo( () => (checksummedAddress !== undefined ? [checksummedAddress] : []), [checksummedAddress] @@ -74,16 +73,13 @@ const Address: React.FC = () => { provider, checksummedAddressAsArray ); - const metadatas = useMultipleMetadata( - undefined, - contractAddresses, + // TODO: restore filter to only check Sourcify metadata if the address is a contract (hasCode) + const { sourcifySource } = useAppConfigContext(); + const addressMetadata = useSourcifyMetadata( + checksummedAddress, provider?.network.chainId, sourcifySource ); - const addressMetadata = - checksummedAddress !== undefined - ? metadatas[checksummedAddress] - : undefined; const { network, faucets } = useChainInfo(); diff --git a/src/TransactionPageContent.tsx b/src/TransactionPageContent.tsx index 9211b97..ef9ba70 100644 --- a/src/TransactionPageContent.tsx +++ b/src/TransactionPageContent.tsx @@ -98,10 +98,7 @@ const TransactionPageContent: React.FC = ({ /> } /> - } - /> + } /> } /> diff --git a/src/hooks.ts b/src/hooks.ts deleted file mode 100644 index 9581ba6..0000000 --- a/src/hooks.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { useMemo } from "react"; -import { JsonRpcProvider } from "@ethersproject/providers"; -import { ChecksummedAddress } from "./types"; -import { Metadata, useMultipleMetadata } from "./sourcify/useSourcify"; -import { useAppConfigContext } from "./useAppConfig"; -import { useAddressesWithCode } from "./useErigonHooks"; - -const useDedupedAddresses = ( - addresses: ChecksummedAddress[] -): ChecksummedAddress[] => { - return useMemo(() => { - const deduped = new Set(addresses); - return [...deduped]; - }, [addresses]); -}; - -export const useContractsMetadata = ( - addresses: ChecksummedAddress[], - provider: JsonRpcProvider | undefined, - baseMetadatas?: Record -) => { - const deduped = useDedupedAddresses(addresses); - const contracts = useAddressesWithCode(provider, deduped); - const { sourcifySource } = useAppConfigContext(); - const metadatas = useMultipleMetadata( - baseMetadatas, - contracts, - provider?.network.chainId, - sourcifySource - ); - - return metadatas; -}; diff --git a/src/sourcify/useSourcify.ts b/src/sourcify/useSourcify.ts index 89aecae..e2a4498 100644 --- a/src/sourcify/useSourcify.ts +++ b/src/sourcify/useSourcify.ts @@ -121,55 +121,6 @@ export const useSourcifyMetadata = ( return data; }; -export const useMultipleMetadata = ( - baseMetadatas: Record | undefined, - addresses: ChecksummedAddress[] | undefined, - chainId: number | undefined, - source: SourcifySource -): Record => { - const [rawMetadata, setRawMetadata] = useState< - Record - >({}); - useEffect(() => { - if (addresses === undefined || chainId === undefined) { - return; - } - setRawMetadata({}); - - const abortController = new AbortController(); - const fetchMetadata = async (_addresses: string[]) => { - const fetchers: Promise[] = []; - for (const address of _addresses) { - fetchers.push( - fetchSourcifyMetadata(address, chainId, source, abortController) - ); - } - - const results = await Promise.all(fetchers); - if (abortController.signal.aborted) { - return; - } - let metadatas: Record = {}; - if (baseMetadatas) { - metadatas = { ...baseMetadatas }; - } - for (let i = 0; i < results.length; i++) { - metadatas[_addresses[i]] = results[i]; - } - setRawMetadata(metadatas); - }; - - const filtered = addresses.filter((a) => baseMetadatas?.[a] === undefined); - fetchMetadata(filtered); - - return () => { - abortController.abort(); - }; - }, [baseMetadatas, addresses, chainId, source]); - - return rawMetadata; -}; - export const useContract = ( checksummedAddress: string, networkId: number, diff --git a/src/transaction/LogEntry.tsx b/src/transaction/LogEntry.tsx index d1be10d..d67d30e 100644 --- a/src/transaction/LogEntry.tsx +++ b/src/transaction/LogEntry.tsx @@ -1,6 +1,6 @@ -import React, { useMemo } from "react"; +import React, { useContext, useMemo } from "react"; import { Log } from "@ethersproject/abstract-provider"; -import { Fragment, Interface, LogDescription } from "@ethersproject/abi"; +import { Fragment, Interface } from "@ethersproject/abi"; import { Tab } from "@headlessui/react"; import TransactionAddress from "../components/TransactionAddress"; import Copy from "../components/Copy"; @@ -8,15 +8,41 @@ import ModeTab from "../components/ModeTab"; import DecodedParamsTable from "./decoder/DecodedParamsTable"; import DecodedLogSignature from "./decoder/DecodedLogSignature"; import { useTopic0 } from "../useTopic0"; -import { ChecksummedAddress } from "../types"; -import { Metadata } from "../sourcify/useSourcify"; +import { RuntimeContext } from "../useRuntime"; +import { useAppConfigContext } from "../useAppConfig"; +import { useSourcifyMetadata } from "../sourcify/useSourcify"; type LogEntryProps = { log: Log; - logDesc: LogDescription | null | undefined; }; -const LogEntry: React.FC = ({ log, logDesc }) => { +const LogEntry: React.FC = ({ log }) => { + const { provider } = useContext(RuntimeContext); + const { sourcifySource } = useAppConfigContext(); + const metadata = useSourcifyMetadata( + log.address, + provider?.network.chainId, + sourcifySource + ); + + const logDesc = useMemo(() => { + if (!metadata) { + return metadata; + } + + const abi = metadata.output.abi; + const intf = new Interface(abi as any); + try { + return intf.parseLog({ + topics: log.topics, + data: log.data, + }); + } catch (err) { + console.warn("Couldn't find function signature", err); + return null; + } + }, [log, metadata]); + const rawTopic0 = log.topics[0]; const topic0 = useTopic0(rawTopic0); diff --git a/src/transaction/Logs.tsx b/src/transaction/Logs.tsx index b02b4a6..01bb1d8 100644 --- a/src/transaction/Logs.tsx +++ b/src/transaction/Logs.tsx @@ -1,77 +1,28 @@ -import React, { useContext, useMemo } from "react"; -import { Interface } from "@ethersproject/abi"; +import React from "react"; import ContentFrame from "../ContentFrame"; import LogEntry from "./LogEntry"; import { TransactionData } from "../types"; -import { Metadata } from "../sourcify/useSourcify"; -import { RuntimeContext } from "../useRuntime"; -import { useContractsMetadata } from "../hooks"; type LogsProps = { txData: TransactionData; - metadata: Metadata | null | undefined; }; -const Logs: React.FC = ({ txData, metadata }) => { - const baseMetadatas = useMemo((): Record => { - if (!txData.to || metadata === undefined) { - return {}; - } - - const md: Record = {}; - md[txData.to] = metadata; - return md; - }, [txData.to, metadata]); - - const logAddresses = useMemo( - () => txData.confirmedData?.logs.map((l) => l.address) ?? [], - [txData] - ); - const { provider } = useContext(RuntimeContext); - const metadatas = useContractsMetadata(logAddresses, provider, baseMetadatas); - - const logDescs = useMemo(() => { - if (!txData) { - return undefined; - } - - return txData.confirmedData?.logs.map((l) => { - const mt = metadatas[l.address]; - if (!mt) { - return mt; - } - - const abi = mt.output.abi; - const intf = new Interface(abi as any); - try { - return intf.parseLog({ - topics: l.topics, - data: l.data, - }); - } catch (err) { - console.warn("Couldn't find function signature", err); - return null; - } - }); - }, [metadatas, txData]); - - return ( - - {txData.confirmedData && ( - <> - {txData.confirmedData.logs.length > 0 ? ( - <> - {txData.confirmedData.logs.map((l, i) => ( - - ))} - - ) : ( -
Transaction didn't emit any logs
- )} - - )} -
- ); -}; +const Logs: React.FC = ({ txData }) => ( + + {txData.confirmedData && ( + <> + {txData.confirmedData.logs.length > 0 ? ( + <> + {txData.confirmedData.logs.map((l, i) => ( + + ))} + + ) : ( +
Transaction didn't emit any logs
+ )} + + )} +
+); export default React.memo(Logs); From 731a8190ed871960145e554e0fa2d79b467d5283 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Wed, 10 Aug 2022 02:04:36 -0300 Subject: [PATCH 06/15] Dont try to download files that dont exist on sourcify --- src/sourcify/useSourcify.ts | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/src/sourcify/useSourcify.ts b/src/sourcify/useSourcify.ts index e2a4498..f4f0a8a 100644 --- a/src/sourcify/useSourcify.ts +++ b/src/sourcify/useSourcify.ts @@ -81,24 +81,17 @@ export type Metadata = { }; }; -const fetchSourcifyMetadata = async ( - address: ChecksummedAddress, - chainId: number, - source: SourcifySource, - abortController: AbortController -): Promise => { +const sourcifyFetcher = async (url: string) => { try { - const metadataURL = sourcifyMetadata(address, chainId, source); - const result = await fetch(metadataURL, { - signal: abortController.signal, - }); - if (result.ok) { - return await result.json(); + const res = await fetch(url); + if (res.ok) { + return res.json(); } - return null; } catch (err) { - console.error(err); + console.warn( + `error while getting Sourcify metadata: url=${url} err=${err}` + ); return null; } }; @@ -112,8 +105,9 @@ export const useSourcifyMetadata = ( address === undefined || chainId === undefined ? null : sourcifyMetadata(address, chainId, source); - const { data, error } = useSWRImmutable(metadataURL, (url) => - fetch(url).then((res) => res.json()) + const { data, error } = useSWRImmutable( + metadataURL, + sourcifyFetcher ); if (error) { return null; From 49abfbda2f5fe963e7e29a2eae86773481aeb8e7 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Wed, 10 Aug 2022 03:01:07 -0300 Subject: [PATCH 07/15] Apply SWR on topic0 fetching --- src/useTopic0.ts | 79 ++++++++++++++++++------------------------------ 1 file changed, 30 insertions(+), 49 deletions(-) diff --git a/src/useTopic0.ts b/src/useTopic0.ts index adcef1e..2ebadf0 100644 --- a/src/useTopic0.ts +++ b/src/useTopic0.ts @@ -1,4 +1,5 @@ -import { useState, useEffect, useContext } from "react"; +import { useContext } from "react"; +import useSWRImmutable from "swr/immutable"; import { RuntimeContext } from "./useRuntime"; import { topic0URL } from "./url"; @@ -6,7 +7,28 @@ export type Topic0Entry = { signatures: string[] | undefined; }; -const fullCache = new Map(); +const topic0Fetcher = async ( + signatureURL: string +): Promise => { + try { + const res = await fetch(signatureURL); + if (!res.ok) { + console.error(`Signature does not exist in topic0 DB: ${signatureURL}`); + return null; + } + + // Get only the first occurrence, for now ignore alternative param names + const sig = await res.text(); + const sigs = sig.split(";"); + const entry: Topic0Entry = { + signatures: sigs, + }; + return entry; + } catch (err) { + console.error(`Couldn't fetch signature URL ${signatureURL}`, err); + return null; + } +}; /** * Extract topic0 DB info @@ -26,52 +48,11 @@ export const useTopic0 = ( const assetsURLPrefix = runtime.config?.assetsURLPrefix; const topic0 = rawTopic0.slice(2); - const [entry, setEntry] = useState( - fullCache.get(topic0) - ); - useEffect(() => { - if (assetsURLPrefix === undefined) { - return; - } - - const signatureURL = topic0URL(assetsURLPrefix, topic0); - fetch(signatureURL) - .then(async (res) => { - if (!res.ok) { - console.error(`Signature does not exist in topic0 DB: ${topic0}`); - fullCache.set(topic0, null); - setEntry(null); - return; - } - - // Get only the first occurrence, for now ignore alternative param names - const sig = await res.text(); - const sigs = sig.split(";"); - const entry: Topic0Entry = { - signatures: sigs, - }; - setEntry(entry); - fullCache.set(topic0, entry); - }) - .catch((err) => { - console.error(`Couldn't fetch signature URL ${signatureURL}`, err); - setEntry(null); - fullCache.set(topic0, null); - }); - }, [topic0, assetsURLPrefix]); - - if (assetsURLPrefix === undefined) { - return undefined; + const signatureURL = () => + assetsURLPrefix === undefined ? null : topic0URL(assetsURLPrefix, topic0); + const { data, error } = useSWRImmutable(signatureURL, topic0Fetcher); + if (error) { + return null; } - - // Try to resolve topic0 name - if (entry === null || entry === undefined) { - return entry; - } - - // Simulates LRU - // TODO: implement LRU purging - fullCache.delete(topic0); - fullCache.set(topic0, entry); - return entry; + return data; }; From 5ab336041e40e596c9eb5f5727a95032d278b0bf Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Wed, 10 Aug 2022 04:47:26 -0300 Subject: [PATCH 08/15] Apply SWR to Sourcify contract source code fetching --- src/address/Contract.tsx | 43 +++++++-------------------- src/address/ContractFromRepo.tsx | 37 +++++++++++++++++++++++ src/address/Contracts.tsx | 20 ++++++++----- src/sourcify/useSourcify.ts | 50 +++++++++++++------------------- 4 files changed, 81 insertions(+), 69 deletions(-) create mode 100644 src/address/ContractFromRepo.tsx diff --git a/src/address/Contract.tsx b/src/address/Contract.tsx index 360e8bc..2f366a8 100644 --- a/src/address/Contract.tsx +++ b/src/address/Contract.tsx @@ -1,40 +1,19 @@ import React from "react"; import { SyntaxHighlighter, docco } from "../highlight-init"; -import { useContract } from "../sourcify/useSourcify"; -import { useAppConfigContext } from "../useAppConfig"; type ContractProps = { - checksummedAddress: string; - networkId: number; - filename: string; - source: any; + content: any; }; -const Contract: React.FC = ({ - checksummedAddress, - networkId, - filename, - source, -}) => { - const { sourcifySource } = useAppConfigContext(); - const content = useContract( - checksummedAddress, - networkId, - filename, - source, - sourcifySource - ); - - return ( - - {content ?? ""} - - ); -}; +const Contract: React.FC = ({ content }) => ( + + {content ?? ""} + +); export default React.memo(Contract); diff --git a/src/address/ContractFromRepo.tsx b/src/address/ContractFromRepo.tsx new file mode 100644 index 0000000..568babf --- /dev/null +++ b/src/address/ContractFromRepo.tsx @@ -0,0 +1,37 @@ +import React from "react"; +import { SyntaxHighlighter, docco } from "../highlight-init"; +import { useContract } from "../sourcify/useSourcify"; +import { useAppConfigContext } from "../useAppConfig"; + +type ContractFromRepoProps = { + checksummedAddress: string; + networkId: number; + filename: string; +}; + +const ContractFromRepo: React.FC = ({ + checksummedAddress, + networkId, + filename, +}) => { + const { sourcifySource } = useAppConfigContext(); + const content = useContract( + checksummedAddress, + networkId, + filename, + sourcifySource + ); + + return ( + + {content ?? ""} + + ); +}; + +export default React.memo(ContractFromRepo); diff --git a/src/address/Contracts.tsx b/src/address/Contracts.tsx index 19ce669..ffd92b0 100644 --- a/src/address/Contracts.tsx +++ b/src/address/Contracts.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useContext, Fragment } from "react"; +import React, { useState, useEffect, useContext } from "react"; import { commify } from "@ethersproject/units"; import { Menu } from "@headlessui/react"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; @@ -6,6 +6,7 @@ import { faChevronDown } from "@fortawesome/free-solid-svg-icons/faChevronDown"; import ContentFrame from "../ContentFrame"; import InfoRow from "../components/InfoRow"; import Contract from "./Contract"; +import ContractFromRepo from "./ContractFromRepo"; import { RuntimeContext } from "../useRuntime"; import { Metadata } from "../sourcify/useSourcify"; import ExternalLink from "../components/ExternalLink"; @@ -113,12 +114,17 @@ const Contracts: React.FC = ({
{selected && ( - + <> + {rawMetadata.sources[selected].content ? ( + + ) : ( + + )} + )}
diff --git a/src/sourcify/useSourcify.ts b/src/sourcify/useSourcify.ts index f4f0a8a..a925943 100644 --- a/src/sourcify/useSourcify.ts +++ b/src/sourcify/useSourcify.ts @@ -115,43 +115,33 @@ export const useSourcifyMetadata = ( return data; }; +const contractFetcher = async (url: string): Promise => { + const res = await fetch(url); + if (res.ok) { + return await res.text(); + } + return null; +}; + export const useContract = ( checksummedAddress: string, networkId: number, filename: string, - source: any, sourcifySource: SourcifySource ) => { - const [content, setContent] = useState(source.content); + const normalizedFilename = filename.replaceAll(/[@:]/g, "_"); + const url = sourcifySourceFile( + checksummedAddress, + networkId, + normalizedFilename, + sourcifySource + ); - useEffect(() => { - if (source.content) { - return; - } - - const abortController = new AbortController(); - const readContent = async () => { - const normalizedFilename = filename.replaceAll(/[@:]/g, "_"); - const url = sourcifySourceFile( - checksummedAddress, - networkId, - normalizedFilename, - sourcifySource - ); - const res = await fetch(url, { signal: abortController.signal }); - if (res.ok) { - const _content = await res.text(); - setContent(_content); - } - }; - readContent(); - - return () => { - abortController.abort(); - }; - }, [checksummedAddress, networkId, filename, source.content, sourcifySource]); - - return content; + const { data, error } = useSWRImmutable(url, contractFetcher); + if (error) { + return undefined; + } + return data; }; export const useTransactionDescription = ( From 478d4b5def100bab5966d3306c335a8ef6af8962 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Wed, 10 Aug 2022 04:58:06 -0300 Subject: [PATCH 09/15] Fix warning --- src/address/Contracts.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/address/Contracts.tsx b/src/address/Contracts.tsx index ffd92b0..d3e4179 100644 --- a/src/address/Contracts.tsx +++ b/src/address/Contracts.tsx @@ -102,7 +102,7 @@ const Contracts: React.FC = ({ className={`flex text-sm px-2 py-1 ${ selected === k ? "font-bold bg-gray-200 text-gray-500" - : "hover:border-orange-200 hover:text-gray-500 text-gray-400 transition-transform transition-colors duration-75" + : "hover:text-gray-500 text-gray-400 transition-colors duration-75" }`} onClick={() => setSelected(k)} > From a187040c8049218e38578e45929019fe2ea8b4c2 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Wed, 10 Aug 2022 04:58:16 -0300 Subject: [PATCH 10/15] Remove unused imports --- src/sourcify/useSourcify.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sourcify/useSourcify.ts b/src/sourcify/useSourcify.ts index a925943..0ae3958 100644 --- a/src/sourcify/useSourcify.ts +++ b/src/sourcify/useSourcify.ts @@ -1,4 +1,4 @@ -import { useState, useEffect, useMemo } from "react"; +import { useMemo } from "react"; import { Interface } from "@ethersproject/abi"; import { ErrorDescription } from "@ethersproject/abi/lib/interface"; import useSWRImmutable from "swr/immutable"; From e86133d6d7d2a3cb80199d5ec872c4a423bbdc52 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Wed, 10 Aug 2022 05:02:02 -0300 Subject: [PATCH 11/15] Remove unused comments --- src/useErigonHooks.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/useErigonHooks.ts b/src/useErigonHooks.ts index c949481..79745f4 100644 --- a/src/useErigonHooks.ts +++ b/src/useErigonHooks.ts @@ -582,7 +582,6 @@ export const prefetchTransactionBySenderAndNonce = ( } return getTransactionBySenderAndNonceFetcher(provider)(key); }); - // } }; export const useTransactionBySenderAndNonce = ( @@ -705,7 +704,6 @@ export const providerFetcher = const method = key[0]; const args = key.slice(1); const result = await provider.send(method, args); - // console.log(`providerFetcher: ${method} ${args} === ${result}`); return result; }; From d5ba6ac7811d223e92728c46517e9a7ac4edf7b9 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Wed, 10 Aug 2022 05:15:58 -0300 Subject: [PATCH 12/15] Push down Sourcify metadata reading --- src/TransactionPageContent.tsx | 17 ----------------- src/transaction/Details.tsx | 30 ++++++++++++++++++------------ 2 files changed, 18 insertions(+), 29 deletions(-) diff --git a/src/TransactionPageContent.tsx b/src/TransactionPageContent.tsx index ef9ba70..ca1f770 100644 --- a/src/TransactionPageContent.tsx +++ b/src/TransactionPageContent.tsx @@ -10,11 +10,6 @@ import { useInternalOperations, useTxData } from "./useErigonHooks"; import { SelectionContext, useSelection } from "./useSelection"; import { SelectedTransactionContext } from "./useSelectedTransaction"; import { BlockNumberContext } from "./useBlockTagContext"; -import { useAppConfigContext } from "./useAppConfig"; -import { - useSourcifyMetadata, - useTransactionDescription, -} from "./sourcify/useSourcify"; const Details = React.lazy(() => import("./transaction/Details")); const Logs = React.lazy(() => import("./transaction/Logs")); @@ -46,14 +41,6 @@ const TransactionPageContent: React.FC = ({ const selectionCtx = useSelection(); - const { sourcifySource } = useAppConfigContext(); - const metadata = useSourcifyMetadata( - txData?.to, - provider?.network.chainId, - sourcifySource - ); - const txDesc = useTransactionDescription(metadata, txData); - return ( @@ -89,10 +76,6 @@ const TransactionPageContent: React.FC = ({ element={
diff --git a/src/transaction/Details.tsx b/src/transaction/Details.tsx index 3c0340a..4bb9c2e 100644 --- a/src/transaction/Details.tsx +++ b/src/transaction/Details.tsx @@ -1,6 +1,5 @@ import React, { useContext, useState } from "react"; import { Tab } from "@headlessui/react"; -import { TransactionDescription } from "@ethersproject/abi"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faCheckCircle } from "@fortawesome/free-solid-svg-icons/faCheckCircle"; import { faCube } from "@fortawesome/free-solid-svg-icons/faCube"; @@ -37,7 +36,12 @@ import { use4Bytes, useTransactionDescription, } from "../use4Bytes"; -import { DevDoc, Metadata, useError, UserDoc } from "../sourcify/useSourcify"; +import { useAppConfigContext } from "../useAppConfig"; +import { + useError, + useSourcifyMetadata, + useTransactionDescription as useSourcifyTransactionDescription, +} from "../sourcify/useSourcify"; import { RuntimeContext } from "../useRuntime"; import { useTransactionError } from "../useErigonHooks"; import { useChainInfo } from "../useChainInfo"; @@ -45,20 +49,12 @@ import { useETHUSDOracle } from "../usePriceOracle"; type DetailsProps = { txData: TransactionData; - txDesc: TransactionDescription | null | undefined; - toMetadata: Metadata | null | undefined; - userDoc?: UserDoc | undefined; - devDoc?: DevDoc | undefined; internalOps?: InternalOperation[]; sendsEthToMiner: boolean; }; const Details: React.FC = ({ txData, - txDesc, - toMetadata, - userDoc, - devDoc, internalOps, sendsEthToMiner, }) => { @@ -75,11 +71,21 @@ const Details: React.FC = ({ txData.value ); + const { provider } = useContext(RuntimeContext); + const { sourcifySource } = useAppConfigContext(); + const metadata = useSourcifyMetadata( + txData?.to, + provider?.network.chainId, + sourcifySource + ); + + const txDesc = useSourcifyTransactionDescription(metadata, txData); + const userDoc = metadata?.output.userdoc; + const devDoc = metadata?.output.devdoc; const resolvedTxDesc = txDesc ?? fourBytesTxDesc; const userMethod = txDesc ? userDoc?.methods[txDesc.signature] : undefined; const devMethod = txDesc ? devDoc?.methods[txDesc.signature] : undefined; - const { provider } = useContext(RuntimeContext); const { nativeCurrency: { name, symbol }, } = useChainInfo(); @@ -94,7 +100,7 @@ const Details: React.FC = ({ txData.transactionHash ); const errorDescription = useError( - toMetadata, + metadata, isCustomError ? outputData : undefined ); const userError = errorDescription From edd3b87245f40f2230101bfc02a484eb0249bead Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Wed, 10 Aug 2022 05:21:08 -0300 Subject: [PATCH 13/15] Push down more hooks executions --- src/TransactionPageContent.tsx | 28 +++------------------------- src/transaction/Details.tsx | 31 ++++++++++++++++++++----------- 2 files changed, 23 insertions(+), 36 deletions(-) diff --git a/src/TransactionPageContent.tsx b/src/TransactionPageContent.tsx index ca1f770..239dcb0 100644 --- a/src/TransactionPageContent.tsx +++ b/src/TransactionPageContent.tsx @@ -1,4 +1,4 @@ -import React, { useContext, useMemo } from "react"; +import React, { useContext } from "react"; import { Route, Routes } from "react-router-dom"; import { Tab } from "@headlessui/react"; import StandardFrame from "./StandardFrame"; @@ -6,7 +6,7 @@ import StandardSubtitle from "./StandardSubtitle"; import ContentFrame from "./ContentFrame"; import NavTab from "./components/NavTab"; import { RuntimeContext } from "./useRuntime"; -import { useInternalOperations, useTxData } from "./useErigonHooks"; +import { useTxData } from "./useErigonHooks"; import { SelectionContext, useSelection } from "./useSelection"; import { SelectedTransactionContext } from "./useSelectedTransaction"; import { BlockNumberContext } from "./useBlockTagContext"; @@ -25,19 +25,6 @@ const TransactionPageContent: React.FC = ({ const { provider } = useContext(RuntimeContext); const txData = useTxData(provider, txHash); - const internalOps = useInternalOperations(provider, txData); - const sendsEthToMiner = useMemo(() => { - if (!txData || !internalOps) { - return false; - } - - for (const t of internalOps) { - if (t.to === txData.confirmedData?.miner) { - return true; - } - } - return false; - }, [txData, internalOps]); const selectionCtx = useSelection(); @@ -71,16 +58,7 @@ const TransactionPageContent: React.FC = ({ - - } - /> + } /> } /> } /> diff --git a/src/transaction/Details.tsx b/src/transaction/Details.tsx index 4bb9c2e..a34a212 100644 --- a/src/transaction/Details.tsx +++ b/src/transaction/Details.tsx @@ -1,4 +1,4 @@ -import React, { useContext, useState } from "react"; +import React, { useContext, useMemo, useState } from "react"; import { Tab } from "@headlessui/react"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faCheckCircle } from "@fortawesome/free-solid-svg-icons/faCheckCircle"; @@ -24,7 +24,7 @@ import USDValue from "../components/USDValue"; import FormattedBalance from "../components/FormattedBalance"; import ETH2USDValue from "../components/ETH2USDValue"; import TokenTransferItem from "../TokenTransferItem"; -import { TransactionData, InternalOperation } from "../types"; +import { TransactionData } from "../types"; import PercentageBar from "../components/PercentageBar"; import ExternalLink from "../components/ExternalLink"; import RelativePosition from "../components/RelativePosition"; @@ -43,21 +43,17 @@ import { useTransactionDescription as useSourcifyTransactionDescription, } from "../sourcify/useSourcify"; import { RuntimeContext } from "../useRuntime"; -import { useTransactionError } from "../useErigonHooks"; +import { useInternalOperations, useTransactionError } from "../useErigonHooks"; import { useChainInfo } from "../useChainInfo"; import { useETHUSDOracle } from "../usePriceOracle"; type DetailsProps = { txData: TransactionData; - internalOps?: InternalOperation[]; - sendsEthToMiner: boolean; }; -const Details: React.FC = ({ - txData, - internalOps, - sendsEthToMiner, -}) => { +const Details: React.FC = ({ txData }) => { + const { provider } = useContext(RuntimeContext); + const hasEIP1559 = txData.confirmedData?.blockBaseFeePerGas !== undefined && txData.confirmedData?.blockBaseFeePerGas !== null; @@ -71,7 +67,20 @@ const Details: React.FC = ({ txData.value ); - const { provider } = useContext(RuntimeContext); + const internalOps = useInternalOperations(provider, txData); + const sendsEthToMiner = useMemo(() => { + if (!txData || !internalOps) { + return false; + } + + for (const t of internalOps) { + if (t.to === txData.confirmedData?.miner) { + return true; + } + } + return false; + }, [txData, internalOps]); + const { sourcifySource } = useAppConfigContext(); const metadata = useSourcifyMetadata( txData?.to, From 003d0ae5b8f91948b04d86bff834191323778ed6 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Wed, 10 Aug 2022 05:26:07 -0300 Subject: [PATCH 14/15] Collapse components into one --- src/Transaction.tsx | 70 +++++++++++++++++++++++++++++--- src/TransactionPageContent.tsx | 74 ---------------------------------- 2 files changed, 64 insertions(+), 80 deletions(-) delete mode 100644 src/TransactionPageContent.tsx diff --git a/src/Transaction.tsx b/src/Transaction.tsx index 94b2e43..905b5d8 100644 --- a/src/Transaction.tsx +++ b/src/Transaction.tsx @@ -1,13 +1,71 @@ -import React from "react"; -import { useParams } from "react-router-dom"; -import TransactionPageContent from "./TransactionPageContent"; +import React, { useContext } from "react"; +import { useParams, Route, Routes } from "react-router-dom"; +import { Tab } from "@headlessui/react"; +import StandardFrame from "./StandardFrame"; +import StandardSubtitle from "./StandardSubtitle"; +import ContentFrame from "./ContentFrame"; +import NavTab from "./components/NavTab"; +import { RuntimeContext } from "./useRuntime"; +import { useTxData } from "./useErigonHooks"; +import { SelectionContext, useSelection } from "./useSelection"; +import { SelectedTransactionContext } from "./useSelectedTransaction"; +import { BlockNumberContext } from "./useBlockTagContext"; + +const Details = React.lazy(() => import("./transaction/Details")); +const Logs = React.lazy(() => import("./transaction/Logs")); +const Trace = React.lazy(() => import("./transaction/Trace")); const Transaction: React.FC = () => { - const { txhash } = useParams(); - if (txhash === undefined) { + const { txhash: txHash } = useParams(); + if (txHash === undefined) { throw new Error("txhash couldn't be undefined here"); } - return ; + + const { provider } = useContext(RuntimeContext); + const txData = useTxData(provider, txHash); + const selectionCtx = useSelection(); + + return ( + + + + Transaction Details + {txData === null && ( + +
+ Transaction {txHash} not + found. +
+
+ )} + {txData && ( + + + + Overview + {txData.confirmedData?.blockNumber !== undefined && ( + + Logs + {txData && + ` (${txData.confirmedData?.logs?.length ?? 0})`} + + )} + Trace + + + + + } /> + } /> + } /> + + + + )} +
+
+
+ ); }; export default Transaction; diff --git a/src/TransactionPageContent.tsx b/src/TransactionPageContent.tsx deleted file mode 100644 index 239dcb0..0000000 --- a/src/TransactionPageContent.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import React, { useContext } from "react"; -import { Route, Routes } from "react-router-dom"; -import { Tab } from "@headlessui/react"; -import StandardFrame from "./StandardFrame"; -import StandardSubtitle from "./StandardSubtitle"; -import ContentFrame from "./ContentFrame"; -import NavTab from "./components/NavTab"; -import { RuntimeContext } from "./useRuntime"; -import { useTxData } from "./useErigonHooks"; -import { SelectionContext, useSelection } from "./useSelection"; -import { SelectedTransactionContext } from "./useSelectedTransaction"; -import { BlockNumberContext } from "./useBlockTagContext"; - -const Details = React.lazy(() => import("./transaction/Details")); -const Logs = React.lazy(() => import("./transaction/Logs")); -const Trace = React.lazy(() => import("./transaction/Trace")); - -type TransactionPageContentProps = { - txHash: string; -}; - -const TransactionPageContent: React.FC = ({ - txHash, -}) => { - const { provider } = useContext(RuntimeContext); - - const txData = useTxData(provider, txHash); - - const selectionCtx = useSelection(); - - return ( - - - - Transaction Details - {txData === null && ( - -
- Transaction {txHash} not - found. -
-
- )} - {txData && ( - - - - Overview - {txData.confirmedData?.blockNumber !== undefined && ( - - Logs - {txData && - ` (${txData.confirmedData?.logs?.length ?? 0})`} - - )} - Trace - - - - - } /> - } /> - } /> - - - - )} -
-
-
- ); -}; - -export default TransactionPageContent; From d316f30f501b8e5c833bea00907645796b5a6019 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Mon, 15 Aug 2022 20:44:48 -0300 Subject: [PATCH 15/15] Use useHasCode hook instead --- src/Address.tsx | 25 ++++++------------------- src/useErigonHooks.ts | 38 -------------------------------------- 2 files changed, 6 insertions(+), 57 deletions(-) diff --git a/src/Address.tsx b/src/Address.tsx index b8767c8..583966f 100644 --- a/src/Address.tsx +++ b/src/Address.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useContext, useCallback, useMemo } from "react"; +import React, { useEffect, useContext, useCallback } from "react"; import { useParams, useNavigate, @@ -25,7 +25,7 @@ import { useAppConfigContext } from "./useAppConfig"; import { useAddressOrENS } from "./useResolvedAddresses"; import { useSourcifyMetadata } from "./sourcify/useSourcify"; import { ChecksummedAddress } from "./types"; -import { useAddressesWithCode } from "./useErigonHooks"; +import { useHasCode } from "./useErigonHooks"; import { useChainInfo } from "./useChainInfo"; const AddressTransactionByNonce = React.lazy( @@ -65,18 +65,10 @@ const Address: React.FC = () => { } }, [addressOrName, checksummedAddress, isENS]); - const checksummedAddressAsArray = useMemo( - () => (checksummedAddress !== undefined ? [checksummedAddress] : []), - [checksummedAddress] - ); - const contractAddresses = useAddressesWithCode( - provider, - checksummedAddressAsArray - ); - // TODO: restore filter to only check Sourcify metadata if the address is a contract (hasCode) + const hasCode = useHasCode(provider, checksummedAddress, "latest"); const { sourcifySource } = useAppConfigContext(); const addressMetadata = useSourcifyMetadata( - checksummedAddress, + hasCode ? checksummedAddress : undefined, provider?.network.chainId, sourcifySource ); @@ -130,7 +122,7 @@ const Address: React.FC = () => { Overview - {(contractAddresses?.length ?? 0) > 0 && ( + {hasCode && ( { element={ } /> diff --git a/src/useErigonHooks.ts b/src/useErigonHooks.ts index 79745f4..6382877 100644 --- a/src/useErigonHooks.ts +++ b/src/useErigonHooks.ts @@ -427,44 +427,6 @@ export const useTraceTransaction = ( return traceGroups; }; -const hasCode = async ( - provider: JsonRpcProvider, - address: ChecksummedAddress -): Promise => { - const result = await provider.send("ots_hasCode", [address, "latest"]); - return result as boolean; -}; - -export const useAddressesWithCode = ( - provider: JsonRpcProvider | undefined, - addresses: ChecksummedAddress[] -): ChecksummedAddress[] | undefined => { - const [results, setResults] = useState(); - - useEffect(() => { - // Reset - setResults(undefined); - - if (provider === undefined) { - return; - } - - const readCodes = async () => { - const checkers: Promise[] = []; - for (const a of addresses) { - checkers.push(hasCode(provider, a)); - } - - const result = await Promise.all(checkers); - const filtered = addresses.filter((_, i) => result[i]); - setResults(filtered); - }; - readCodes(); - }, [provider, addresses]); - - return results; -}; - // Error(string) const ERROR_MESSAGE_SELECTOR = "0x08c379a0";