diff --git a/src/address/AddressTransactionResults.tsx b/src/address/AddressTransactionResults.tsx index b13bbd2..07f8e4f 100644 --- a/src/address/AddressTransactionResults.tsx +++ b/src/address/AddressTransactionResults.tsx @@ -8,13 +8,12 @@ import TransactionItem from "../search/TransactionItem"; import UndefinedPageControl from "../search/UndefinedPageControl"; import { useFeeToggler } from "../search/useFeeToggler"; import { SelectionContext, useSelection } from "../useSelection"; -import { useMultipleMetadata } from "../sourcify/useSourcify"; import { useMultipleETHUSDOracle } from "../usePriceOracle"; import { RuntimeContext } from "../useRuntime"; import { pageCollector, useResolvedAddresses } from "../useResolvedAddresses"; -import { useAppConfigContext } from "../useAppConfig"; import { useParams, useSearchParams } from "react-router-dom"; import { ChecksummedAddress } from "../types"; +import { useContractsMetadata } from "../hooks"; type AddressTransactionResultsProps = { address: ChecksummedAddress; @@ -119,13 +118,7 @@ const AddressTransactionResults: React.FC = ({ } return _addresses; }, [address, page]); - const { sourcifySource } = useAppConfigContext(); - const metadatas = useMultipleMetadata( - undefined, - addresses, - provider?.network.chainId, - sourcifySource - ); + const metadatas = useContractsMetadata(addresses, provider); return ( diff --git a/src/block/BlockTransactionResults.tsx b/src/block/BlockTransactionResults.tsx index f2d8d08..fc5bf9f 100644 --- a/src/block/BlockTransactionResults.tsx +++ b/src/block/BlockTransactionResults.tsx @@ -12,8 +12,7 @@ import { pageCollector, useResolvedAddresses } from "../useResolvedAddresses"; import { ChecksummedAddress, ProcessedTransaction } from "../types"; import { PAGE_SIZE } from "../params"; import { useMultipleETHUSDOracle } from "../usePriceOracle"; -import { useAppConfigContext } from "../useAppConfig"; -import { useMultipleMetadata } from "../sourcify/useSourcify"; +import { useContractsMetadata } from "../hooks"; type BlockTransactionResultsProps = { blockTag: BlockTag; @@ -43,13 +42,7 @@ const BlockTransactionResults: React.FC = ({ return page.map((t) => t.to).filter((to): to is string => to !== undefined); }, [page]); - const { sourcifySource } = useAppConfigContext(); - const metadatas = useMultipleMetadata( - undefined, - addresses, - provider?.network.chainId, - sourcifySource - ); + const metadatas = useContractsMetadata(addresses, provider); return ( diff --git a/src/hooks.ts b/src/hooks.ts new file mode 100644 index 0000000..a81f10b --- /dev/null +++ b/src/hooks.ts @@ -0,0 +1,27 @@ +import { JsonRpcProvider } from "@ethersproject/providers"; +import { ChecksummedAddress } from "./types"; +import { + Metadata, + useDedupedAddresses, + useMultipleMetadata, +} from "./sourcify/useSourcify"; +import { useAppConfigContext } from "./useAppConfig"; +import { useAddressesWithCode } from "./useErigonHooks"; + +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/params.ts b/src/params.ts index 5c95cfa..7ac916a 100644 --- a/src/params.ts +++ b/src/params.ts @@ -1,3 +1,3 @@ -export const MIN_API_LEVEL = 3; +export const MIN_API_LEVEL = 4; export const PAGE_SIZE = 25; diff --git a/src/sourcify/useSourcify.ts b/src/sourcify/useSourcify.ts index e4d1ab1..85baaaa 100644 --- a/src/sourcify/useSourcify.ts +++ b/src/sourcify/useSourcify.ts @@ -131,51 +131,55 @@ export const useSingleMetadata = ( return address !== undefined ? metadatas[address] : undefined; }; +export const useDedupedAddresses = ( + addresses: ChecksummedAddress[] +): ChecksummedAddress[] => { + return useMemo(() => { + const deduped = new Set(addresses); + return [...deduped]; + }, [addresses]); +}; + export const useMultipleMetadata = ( baseMetadatas: Record | undefined, - addresses: (ChecksummedAddress | undefined)[], + addresses: ChecksummedAddress[] | undefined, chainId: number | undefined, source: SourcifySource ): Record => { const [rawMetadata, setRawMetadata] = useState< Record >({}); - useEffect(() => { - if (!addresses || chainId === undefined) { + if (addresses === undefined || chainId === undefined) { return; } setRawMetadata({}); const abortController = new AbortController(); - const fetchMetadata = async (dedupedAddresses: string[]) => { - const promises: Promise[] = []; - for (const address of dedupedAddresses) { - promises.push( + 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(promises); + const results = await Promise.all(fetchers); if (abortController.signal.aborted) { return; } - const metadatas: Record = baseMetadatas - ? { ...baseMetadatas } - : {}; + let metadatas: Record = {}; + if (baseMetadatas) { + metadatas = { ...baseMetadatas }; + } for (let i = 0; i < results.length; i++) { - metadatas[dedupedAddresses[i]] = results[i]; + metadatas[_addresses[i]] = results[i]; } setRawMetadata(metadatas); }; - const deduped = new Set( - addresses.filter( - (a): a is ChecksummedAddress => - a !== undefined && baseMetadatas?.[a] === undefined - ) - ); - fetchMetadata(Array.from(deduped)); + const filtered = addresses.filter((a) => baseMetadatas?.[a] === undefined); + fetchMetadata(filtered); return () => { abortController.abort(); diff --git a/src/transaction/Details.tsx b/src/transaction/Details.tsx index 994ab51..e4daf2f 100644 --- a/src/transaction/Details.tsx +++ b/src/transaction/Details.tsx @@ -37,10 +37,10 @@ import { use4Bytes, useTransactionDescription, } from "../use4Bytes"; -import { DevDoc, useMultipleMetadata, UserDoc } from "../sourcify/useSourcify"; +import { DevDoc, UserDoc } from "../sourcify/useSourcify"; import { ResolvedAddresses } from "../api/address-resolver"; import { RuntimeContext } from "../useRuntime"; -import { useAppConfigContext } from "../useAppConfig"; +import { useContractsMetadata } from "../hooks"; type DetailsProps = { txData: TransactionData; @@ -95,13 +95,7 @@ const Details: React.FC = ({ } return _addresses; }, [txData]); - const { sourcifySource } = useAppConfigContext(); - const metadatas = useMultipleMetadata( - undefined, - addresses, - provider?.network.chainId, - sourcifySource - ); + const metadatas = useContractsMetadata(addresses, provider); return ( diff --git a/src/transaction/Logs.tsx b/src/transaction/Logs.tsx index 4a9cc93..eba82e1 100644 --- a/src/transaction/Logs.tsx +++ b/src/transaction/Logs.tsx @@ -3,10 +3,10 @@ import { Interface } from "@ethersproject/abi"; import ContentFrame from "../ContentFrame"; import LogEntry from "./LogEntry"; import { TransactionData } from "../types"; -import { useAppConfigContext } from "../useAppConfig"; -import { Metadata, useMultipleMetadata } from "../sourcify/useSourcify"; +import { Metadata } from "../sourcify/useSourcify"; import { ResolvedAddresses } from "../api/address-resolver"; import { RuntimeContext } from "../useRuntime"; +import { useContractsMetadata } from "../hooks"; type LogsProps = { txData: TransactionData; @@ -30,13 +30,8 @@ const Logs: React.FC = ({ txData, metadata, resolvedAddresses }) => { [txData] ); const { provider } = useContext(RuntimeContext); - const { sourcifySource } = useAppConfigContext(); - const metadatas = useMultipleMetadata( - baseMetadatas, - logAddresses, - provider?.network.chainId, - sourcifySource - ); + const metadatas = useContractsMetadata(logAddresses, provider, baseMetadatas); + const logDescs = useMemo(() => { if (!txData) { return undefined; diff --git a/src/useErigonHooks.ts b/src/useErigonHooks.ts index 17d629f..f722f03 100644 --- a/src/useErigonHooks.ts +++ b/src/useErigonHooks.ts @@ -14,6 +14,7 @@ import { InternalOperation, ProcessedTransaction, OperationType, + ChecksummedAddress, } from "./types"; import erc20 from "./erc20.json"; @@ -440,3 +441,39 @@ export const useUniqueSignatures = (traces: TraceGroup[] | undefined) => { return uniqueSignatures; }; + +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(() => { + if (provider === undefined) { + setResults(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; +};