2021-11-25 18:41:40 +00:00
|
|
|
import React, { useState, useEffect, useContext } from "react";
|
2021-07-23 18:10:08 +00:00
|
|
|
import {
|
|
|
|
useParams,
|
2021-11-25 09:28:45 +00:00
|
|
|
useNavigate,
|
|
|
|
Routes,
|
2021-07-23 18:10:08 +00:00
|
|
|
Route,
|
2021-11-25 09:28:45 +00:00
|
|
|
useSearchParams,
|
2021-07-23 18:10:08 +00:00
|
|
|
} from "react-router-dom";
|
2021-08-08 22:49:45 +00:00
|
|
|
import { getAddress, isAddress } from "@ethersproject/address";
|
2021-09-05 20:39:11 +00:00
|
|
|
import { Tab } from "@headlessui/react";
|
2021-07-01 18:21:40 +00:00
|
|
|
import Blockies from "react-blockies";
|
2021-09-10 07:31:24 +00:00
|
|
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
|
|
import { faCircleNotch } from "@fortawesome/free-solid-svg-icons/faCircleNotch";
|
|
|
|
import { faQuestionCircle } from "@fortawesome/free-regular-svg-icons/faQuestionCircle";
|
2021-07-01 18:21:40 +00:00
|
|
|
import StandardFrame from "./StandardFrame";
|
|
|
|
import StandardSubtitle from "./StandardSubtitle";
|
|
|
|
import Copy from "./components/Copy";
|
2021-09-06 06:34:13 +00:00
|
|
|
import NavTab from "./components/NavTab";
|
2021-11-25 10:03:29 +00:00
|
|
|
import AddressTransactionResults from "./address/AddressTransactionResults";
|
2021-07-24 23:33:45 +00:00
|
|
|
import Contracts from "./address/Contracts";
|
2021-07-09 05:07:20 +00:00
|
|
|
import { RuntimeContext } from "./useRuntime";
|
2021-09-23 21:25:52 +00:00
|
|
|
import { useAppConfigContext } from "./useAppConfig";
|
2021-11-25 18:41:40 +00:00
|
|
|
import { useSingleMetadata } from "./useSourcify";
|
2021-10-21 21:25:57 +00:00
|
|
|
import SourcifyLogo from "./sourcify.svg";
|
2021-07-01 18:21:40 +00:00
|
|
|
|
|
|
|
const AddressTransactions: React.FC = () => {
|
2021-07-09 05:07:20 +00:00
|
|
|
const { provider } = useContext(RuntimeContext);
|
2021-11-25 09:28:45 +00:00
|
|
|
const { addressOrName, direction } = useParams();
|
|
|
|
if (addressOrName === undefined) {
|
|
|
|
throw new Error("addressOrName couldn't be undefined here");
|
|
|
|
}
|
|
|
|
|
|
|
|
const navigate = useNavigate();
|
|
|
|
const [searchParams] = useSearchParams();
|
2021-07-01 18:21:40 +00:00
|
|
|
|
2021-11-25 09:28:45 +00:00
|
|
|
const [checksummedAddress, setChecksummedAddress] = useState<
|
|
|
|
string | undefined
|
|
|
|
>();
|
2021-07-02 19:05:18 +00:00
|
|
|
const [isENS, setENS] = useState<boolean>();
|
2021-07-02 19:20:50 +00:00
|
|
|
const [error, setError] = useState<boolean>();
|
2021-07-02 19:05:18 +00:00
|
|
|
|
|
|
|
// If it looks like it is an ENS name, try to resolve it
|
|
|
|
useEffect(() => {
|
2021-11-25 09:28:45 +00:00
|
|
|
// TODO: handle and offer fallback to bad checksummed addresses
|
|
|
|
if (isAddress(addressOrName)) {
|
2021-07-03 04:46:15 +00:00
|
|
|
setENS(false);
|
|
|
|
setError(false);
|
|
|
|
|
2021-07-02 19:05:18 +00:00
|
|
|
// Normalize to checksummed address
|
2021-11-25 09:28:45 +00:00
|
|
|
const _checksummedAddress = getAddress(addressOrName);
|
|
|
|
if (_checksummedAddress !== addressOrName) {
|
2021-07-03 04:46:15 +00:00
|
|
|
// Request came with a non-checksummed address; fix the URL
|
2021-11-25 09:28:45 +00:00
|
|
|
navigate(
|
2021-07-03 04:46:15 +00:00
|
|
|
`/address/${_checksummedAddress}${
|
2021-11-25 09:28:45 +00:00
|
|
|
direction ? "/" + direction : ""
|
|
|
|
}?${searchParams.toString()}`,
|
|
|
|
{ replace: true }
|
2021-07-03 04:46:15 +00:00
|
|
|
);
|
2021-11-25 09:28:45 +00:00
|
|
|
return;
|
2021-07-03 04:46:15 +00:00
|
|
|
}
|
2021-11-25 09:28:45 +00:00
|
|
|
|
2021-07-03 04:46:15 +00:00
|
|
|
setChecksummedAddress(_checksummedAddress);
|
2021-11-25 09:28:45 +00:00
|
|
|
document.title = `Address ${_checksummedAddress} | Otterscan`;
|
2021-07-02 19:05:18 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-07-08 19:02:42 +00:00
|
|
|
if (!provider) {
|
|
|
|
return;
|
|
|
|
}
|
2021-07-02 19:05:18 +00:00
|
|
|
const resolveName = async () => {
|
2021-11-25 09:28:45 +00:00
|
|
|
const resolvedAddress = await provider.resolveName(addressOrName);
|
2021-07-02 19:05:18 +00:00
|
|
|
if (resolvedAddress !== null) {
|
|
|
|
setENS(true);
|
2021-07-02 19:20:50 +00:00
|
|
|
setError(false);
|
2021-07-03 04:46:15 +00:00
|
|
|
setChecksummedAddress(resolvedAddress);
|
2021-11-25 09:28:45 +00:00
|
|
|
document.title = `Address ${addressOrName} | Otterscan`;
|
2021-07-02 19:20:50 +00:00
|
|
|
} else {
|
2021-07-03 04:46:15 +00:00
|
|
|
setENS(false);
|
2021-07-02 19:20:50 +00:00
|
|
|
setError(true);
|
2021-07-03 04:46:15 +00:00
|
|
|
setChecksummedAddress(undefined);
|
2021-07-02 19:05:18 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
resolveName();
|
2021-11-25 09:28:45 +00:00
|
|
|
}, [provider, addressOrName, navigate, direction, searchParams]);
|
2021-07-01 18:21:40 +00:00
|
|
|
|
2021-09-23 21:25:52 +00:00
|
|
|
const { sourcifySource } = useAppConfigContext();
|
2021-11-25 18:41:40 +00:00
|
|
|
const addressMetadata = useSingleMetadata(
|
|
|
|
checksummedAddress,
|
2021-09-06 21:32:11 +00:00
|
|
|
provider?.network.chainId,
|
2021-09-10 21:27:42 +00:00
|
|
|
sourcifySource
|
2021-09-06 00:13:01 +00:00
|
|
|
);
|
2021-07-14 06:52:31 +00:00
|
|
|
|
2021-07-01 18:21:40 +00:00
|
|
|
return (
|
|
|
|
<StandardFrame>
|
2021-07-02 19:20:50 +00:00
|
|
|
{error ? (
|
|
|
|
<span className="text-base">
|
2021-11-25 09:28:45 +00:00
|
|
|
"{addressOrName}" is not an ETH address or ENS name.
|
2021-07-02 19:20:50 +00:00
|
|
|
</span>
|
|
|
|
) : (
|
|
|
|
checksummedAddress && (
|
2021-07-01 18:21:40 +00:00
|
|
|
<>
|
2021-07-02 19:20:50 +00:00
|
|
|
<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 />
|
|
|
|
{isENS && (
|
|
|
|
<span className="rounded-lg px-2 py-1 bg-gray-200 text-gray-500 text-xs">
|
2021-11-25 09:28:45 +00:00
|
|
|
ENS: {addressOrName}
|
2021-07-02 19:20:50 +00:00
|
|
|
</span>
|
2021-07-01 18:21:40 +00:00
|
|
|
)}
|
|
|
|
</div>
|
2021-07-02 19:20:50 +00:00
|
|
|
</StandardSubtitle>
|
2021-09-05 20:39:11 +00:00
|
|
|
<Tab.Group>
|
|
|
|
<Tab.List className="flex space-x-2 border-l border-r border-t rounded-t-lg bg-white">
|
2021-09-06 06:43:20 +00:00
|
|
|
<NavTab href={`/address/${checksummedAddress}`}>
|
|
|
|
Overview
|
|
|
|
</NavTab>
|
|
|
|
<NavTab href={`/address/${checksummedAddress}/contract`}>
|
2021-09-10 07:31:24 +00:00
|
|
|
<span
|
2021-09-10 08:16:06 +00:00
|
|
|
className={`flex items-baseline space-x-2 ${
|
2021-11-10 08:00:27 +00:00
|
|
|
addressMetadata === undefined ? "italic opacity-50" : ""
|
2021-09-10 07:31:24 +00:00
|
|
|
}`}
|
|
|
|
>
|
2021-09-10 08:16:06 +00:00
|
|
|
<span>Contract</span>
|
2021-11-10 08:00:27 +00:00
|
|
|
{addressMetadata === undefined ? (
|
2021-09-10 08:16:06 +00:00
|
|
|
<span className="self-center">
|
|
|
|
<FontAwesomeIcon
|
|
|
|
className="animate-spin"
|
|
|
|
icon={faCircleNotch}
|
|
|
|
/>
|
2021-09-10 07:31:24 +00:00
|
|
|
</span>
|
2021-11-10 08:00:27 +00:00
|
|
|
) : addressMetadata === null ? (
|
2021-09-10 08:16:06 +00:00
|
|
|
<span className="self-center text-red-500">
|
2021-09-10 07:31:24 +00:00
|
|
|
<FontAwesomeIcon icon={faQuestionCircle} />
|
|
|
|
</span>
|
|
|
|
) : (
|
2021-11-25 18:41:40 +00:00
|
|
|
<span className="self-center">
|
2021-10-18 06:15:20 +00:00
|
|
|
<img
|
2021-10-21 21:25:57 +00:00
|
|
|
src={SourcifyLogo}
|
2021-10-18 06:15:20 +00:00
|
|
|
alt="Sourcify logo"
|
|
|
|
title="Verified by Sourcify"
|
|
|
|
width={16}
|
|
|
|
height={16}
|
|
|
|
/>
|
2021-09-10 07:31:24 +00:00
|
|
|
</span>
|
|
|
|
)}
|
|
|
|
</span>
|
2021-09-06 06:43:20 +00:00
|
|
|
</NavTab>
|
2021-09-05 20:39:11 +00:00
|
|
|
</Tab.List>
|
|
|
|
<Tab.Panels>
|
2021-11-25 09:28:45 +00:00
|
|
|
<Routes>
|
|
|
|
<Route
|
|
|
|
path="*"
|
|
|
|
element={
|
2021-11-25 18:41:40 +00:00
|
|
|
<AddressTransactionResults address={checksummedAddress} />
|
2021-11-25 09:28:45 +00:00
|
|
|
}
|
|
|
|
/>
|
|
|
|
<Route
|
|
|
|
path="contract"
|
|
|
|
element={
|
|
|
|
<Contracts
|
|
|
|
checksummedAddress={checksummedAddress}
|
|
|
|
rawMetadata={addressMetadata}
|
|
|
|
/>
|
|
|
|
}
|
|
|
|
/>
|
|
|
|
</Routes>
|
2021-09-05 20:39:11 +00:00
|
|
|
</Tab.Panels>
|
|
|
|
</Tab.Group>
|
2021-07-01 18:21:40 +00:00
|
|
|
</>
|
2021-07-02 19:20:50 +00:00
|
|
|
)
|
|
|
|
)}
|
2021-07-01 18:21:40 +00:00
|
|
|
</StandardFrame>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2021-11-25 09:28:45 +00:00
|
|
|
export default AddressTransactions;
|