import React, { useEffect, useContext, useCallback, useMemo } from "react"; import { useParams, useNavigate, Routes, Route, useSearchParams, } from "react-router-dom"; import { Tab } from "@headlessui/react"; import Blockies from "react-blockies"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faCircleNotch } from "@fortawesome/free-solid-svg-icons/faCircleNotch"; import { faQuestionCircle } from "@fortawesome/free-regular-svg-icons/faQuestionCircle"; import StandardFrame from "./StandardFrame"; import StandardSubtitle from "./StandardSubtitle"; import AddressOrENSNameNotFound from "./components/AddressOrENSNameNotFound"; import Copy from "./components/Copy"; import Faucet from "./components/Faucet"; import NavTab from "./components/NavTab"; import SourcifyLogo from "./sourcify/SourcifyLogo"; import AddressTransactionResults from "./address/AddressTransactionResults"; import Contracts from "./address/Contracts"; import { RuntimeContext } from "./useRuntime"; import { useAppConfigContext } from "./useAppConfig"; import { useAddressOrENS } from "./useResolvedAddresses"; import { useMultipleMetadata } from "./sourcify/useSourcify"; import { ChecksummedAddress } from "./types"; import { useAddressesWithCode } from "./useErigonHooks"; import { useChainInfo } from "./useChainInfo"; const AddressTransactionByNonce = React.lazy( () => import( /* webpackChunkName: "addresstxbynonce", webpackPrefetch: true */ "./AddressTransactionByNonce" ) ); const Address: React.FC = () => { const { provider } = useContext(RuntimeContext); const { addressOrName, direction } = useParams(); if (addressOrName === undefined) { throw new Error("addressOrName couldn't be undefined here"); } const navigate = useNavigate(); const [searchParams] = useSearchParams(); const urlFixer = useCallback( (address: ChecksummedAddress) => { navigate( `/address/${address}${ direction ? "/" + direction : "" }?${searchParams.toString()}`, { replace: true } ); }, [navigate, direction, searchParams] ); const [checksummedAddress, isENS, error] = useAddressOrENS( addressOrName, urlFixer ); useEffect(() => { if (isENS || checksummedAddress === undefined) { document.title = `Address ${addressOrName} | Otterscan`; } else { document.title = `Address ${checksummedAddress} | Otterscan`; } }, [addressOrName, checksummedAddress, isENS]); const { sourcifySource } = useAppConfigContext(); const checksummedAddressAsArray = useMemo( () => (checksummedAddress !== undefined ? [checksummedAddress] : []), [checksummedAddress] ); const contractAddresses = useAddressesWithCode( provider, checksummedAddressAsArray ); const metadatas = useMultipleMetadata( undefined, contractAddresses, provider?.network.chainId, sourcifySource ); const addressMetadata = checksummedAddress !== undefined ? metadatas[checksummedAddress] : undefined; const { network, faucets } = useChainInfo(); // Search address by nonce === transaction @ nonce const rawNonce = searchParams.get("nonce"); if (rawNonce !== null) { return ( <AddressTransactionByNonce checksummedAddress={checksummedAddress} rawNonce={rawNonce} /> ); } return ( <StandardFrame> {error ? ( <AddressOrENSNameNotFound addressOrENSName={addressOrName} supportsENS={provider?.network.ensAddress !== undefined} /> ) : ( checksummedAddress && ( <> <StandardSubtitle> <div className="flex space-x-2 items-baseline"> <Blockies className="self-center rounded" seed={checksummedAddress.toLowerCase()} scale={3} /> <span>Address</span> <span className="font-address text-base text-gray-500"> {checksummedAddress} </span> <Copy value={checksummedAddress} rounded /> {/* Only display faucets for testnets who actually have any */} {network === "testnet" && faucets && faucets.length > 0 && ( <Faucet address={checksummedAddress} rounded /> )} {isENS && ( <span className="rounded-lg px-2 py-1 bg-gray-200 text-gray-500 text-xs"> ENS: {addressOrName} </span> )} </div> </StandardSubtitle> <Tab.Group> <Tab.List className="flex space-x-2 border-l border-r border-t rounded-t-lg bg-white"> <NavTab href={`/address/${addressOrName}`}>Overview</NavTab> {(contractAddresses?.length ?? 0) > 0 && ( <NavTab href={`/address/${addressOrName}/contract`}> <span className={`flex items-baseline space-x-2 ${ addressMetadata === undefined ? "italic opacity-50" : "" }`} > <span>Contract</span> {addressMetadata === undefined ? ( <span className="self-center"> <FontAwesomeIcon className="animate-spin" icon={faCircleNotch} /> </span> ) : addressMetadata === null ? ( <span className="self-center text-red-500"> <FontAwesomeIcon icon={faQuestionCircle} /> </span> ) : ( <span className="self-center"> <SourcifyLogo /> </span> )} </span> </NavTab> )} </Tab.List> <Tab.Panels> <Routes> <Route index element={ <AddressTransactionResults address={checksummedAddress} /> } /> <Route path="txs/:direction" element={ <AddressTransactionResults address={checksummedAddress} /> } /> <Route path="contract" element={ <Contracts checksummedAddress={checksummedAddress} rawMetadata={ contractAddresses !== undefined && contractAddresses.length === 0 ? null : addressMetadata } /> } /> </Routes> </Tab.Panels> </Tab.Group> </> ) )} </StandardFrame> ); }; export default Address;