Skip to content

Commit

Permalink
update home page
Browse files Browse the repository at this point in the history
  • Loading branch information
technophile-04 committed Aug 21, 2024
1 parent 37f10ef commit 0038c99
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 69 deletions.
25 changes: 19 additions & 6 deletions packages/nextjs/hooks/useFetchContractAbi.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
import { useState } from "react";
import { useQuery } from "@tanstack/react-query";
import { fetchContractABIFromAnyABI, fetchContractABIFromEtherscan } from "~~/utils/abi";
import { isAddress } from "viem";
import { UsePublicClientReturnType } from "wagmi";
import { fetchContractABIFromAnyABI, fetchContractABIFromEtherscan, parseAndCorrectJSON } from "~~/utils/abi";
import { detectProxyTarget } from "~~/utils/abi-ninja/proxyContracts";

const ANYABI_TIMEOUT = 3000;

const useFetchContractAbi = (contractAddress: string, parsedNetworkId: number, publicClient: any) => {
type FetchContractAbiParams = {
contractAddress: string;
chainId: number;
publicClient: UsePublicClientReturnType;
};

const useFetchContractAbi = ({ contractAddress, chainId, publicClient }: FetchContractAbiParams) => {
const [implementationAddress, setImplementationAddress] = useState<string | null>(null);

const fetchAbi = async () => {
if (!isAddress(contractAddress)) {
throw new Error("Invalid contract address");
}

try {
const implAddress = await detectProxyTarget(contractAddress, publicClient);
if (implAddress) {
Expand All @@ -23,7 +35,7 @@ const useFetchContractAbi = (contractAddress: string, parsedNetworkId: number, p
});

// Race between the AnyABI fetch and the timeout
const abi = await Promise.race([fetchContractABIFromAnyABI(addressToUse, parsedNetworkId), timeoutPromise]);
const abi = await Promise.race([fetchContractABIFromAnyABI(addressToUse, chainId), timeoutPromise]);

if (!abi) throw new Error("Got empty or undefined ABI from AnyABI");

Expand All @@ -32,15 +44,16 @@ const useFetchContractAbi = (contractAddress: string, parsedNetworkId: number, p
console.error("Error or timeout fetching ABI from AnyABI: ", error);
console.log("Falling back to Etherscan...");

const abiString = await fetchContractABIFromEtherscan(contractAddress, parsedNetworkId);
const parsedAbi = JSON.parse(abiString);
const abiString = await fetchContractABIFromEtherscan(contractAddress, chainId);
const parsedAbi = parseAndCorrectJSON(abiString);
return { abi: parsedAbi, address: contractAddress };
}
};

const { data, error, isLoading } = useQuery({
queryKey: ["contractAbi", { contractAddress, chainId: parsedNetworkId }],
queryKey: ["contractAbi", { contractAddress, chainId: chainId }],
queryFn: fetchAbi,
enabled: isAddress(contractAddress) && chainId !== 31337,
});

return {
Expand Down
2 changes: 1 addition & 1 deletion packages/nextjs/pages/[contractAddress]/[network].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ const ContractDetailPage = ({ addressFromUrl, chainIdFromUrl }: ServerSideProps)
error: fetchError,
isLoading,
implementationAddress,
} = useFetchContractAbi(contractAddress, parseInt(network), publicClient);
} = useFetchContractAbi({ contractAddress, chainId: parseInt(network), publicClient });

const effectiveContractData = isUseLocalAbi && contractData ? contractData : fetchedContractData;

Expand Down
115 changes: 53 additions & 62 deletions packages/nextjs/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useState } from "react";
import { useCallback, useEffect, useState } from "react";
import Image from "next/image";
import Link from "next/link";
import { useRouter } from "next/router";
Expand All @@ -12,9 +12,9 @@ import { MiniFooter } from "~~/components/MiniFooter";
import { NetworksDropdown } from "~~/components/NetworksDropdown/NetworksDropdown";
import { SwitchTheme } from "~~/components/SwitchTheme";
import { AddressInput } from "~~/components/scaffold-eth";
import useFetchContractAbi from "~~/hooks/useFetchContractAbi";
import { useAbiNinjaState } from "~~/services/store/store";
import { fetchContractABIFromAnyABI, fetchContractABIFromEtherscan, parseAndCorrectJSON } from "~~/utils/abi";
import { detectProxyTarget } from "~~/utils/abi-ninja/proxyContracts";
import { parseAndCorrectJSON } from "~~/utils/abi";
import { notification } from "~~/utils/scaffold-eth";

enum TabName {
Expand All @@ -30,7 +30,6 @@ const Home: NextPage = () => {
const [verifiedContractAddress, setVerifiedContractAddress] = useState("");
const [localAbiContractAddress, setLocalAbiContractAddress] = useState("");
const [localContractAbi, setLocalContractAbi] = useState("");
const [isFetchingAbi, setIsFetchingAbi] = useState(false);

const publicClient = usePublicClient({
chainId: parseInt(network),
Expand All @@ -42,66 +41,64 @@ const Home: NextPage = () => {
setImplementationAddress: state.setImplementationAddress,
}));

const [isAbiAvailable, setIsAbiAvailable] = useState(false);

const router = useRouter();

useEffect(() => {
const fetchContractAbi = async () => {
setIsFetchingAbi(true);
try {
const implementationAddress = await detectProxyTarget(verifiedContractAddress as Address, publicClient);

if (implementationAddress) {
setImplementationAddress(implementationAddress);
}
const abi = await fetchContractABIFromAnyABI(
implementationAddress || verifiedContractAddress,
parseInt(network),
);
if (!abi) throw new Error("Got empty or undefined ABI from AnyABI");
setContractAbi(abi);
setIsAbiAvailable(true);
} catch (error) {
console.error("Error fetching ABI from AnyABI: ", error);
console.log("Trying to fetch ABI from Etherscan...");
try {
const abiString = await fetchContractABIFromEtherscan(verifiedContractAddress, parseInt(network));
const abi = JSON.parse(abiString);
setContractAbi(abi);
setIsAbiAvailable(true);
} catch (etherscanError: any) {
setIsAbiAvailable(false);
console.error("Error fetching ABI from Etherscan: ", etherscanError);
const {
contractData,
error,
isLoading: isFetchingAbi,
implementationAddress,
} = useFetchContractAbi({ contractAddress: verifiedContractAddress, chainId: parseInt(network), publicClient });

const bytecode = await publicClient?.getBytecode({
address: verifiedContractAddress as Address,
});
const isContract = Boolean(bytecode) && bytecode !== "0x";
const isAbiAvailable = contractData?.abi && contractData.abi.length > 0;

if (isContract) {
setLocalAbiContractAddress(verifiedContractAddress);
setActiveTab(TabName.addressAbi);
} else {
notification.error("Address is not a contract, are you sure you are on the correct chain?");
}
}
} finally {
setIsFetchingAbi(false);
}
};
const handleFetchError = useCallback(async () => {
try {
const bytecode = await publicClient?.getBytecode({
address: verifiedContractAddress as Address,
});
const isContract = Boolean(bytecode) && bytecode !== "0x";

if (isAddress(verifiedContractAddress)) {
if (network === "31337") {
setActiveTab(TabName.addressAbi);
if (isContract) {
setLocalAbiContractAddress(verifiedContractAddress);
return;
setActiveTab(TabName.addressAbi);
} else {
notification.error("Address is not a contract, are you sure you are on the correct chain?");
}
fetchContractAbi();
} else {
setIsAbiAvailable(false);
} catch (error) {
console.error("Error checking if address is a contract:", error);
notification.error("Error checking if address is a contract. Please try again.");
}
}, [publicClient, verifiedContractAddress, setLocalAbiContractAddress, setActiveTab]);

useEffect(() => {
if (implementationAddress) {
setImplementationAddress(implementationAddress);
}

if (contractData?.abi) {
setContractAbi(contractData.abi);
}

if (network === "31337") {
setActiveTab(TabName.addressAbi);
setLocalAbiContractAddress(verifiedContractAddress);
return;
}

if (error && isAddress(verifiedContractAddress)) {
handleFetchError();
}
}, [verifiedContractAddress, network, setContractAbi, publicClient, setImplementationAddress]);
}, [
contractData,
error,
implementationAddress,
network,
verifiedContractAddress,
handleFetchError,
setContractAbi,
setImplementationAddress,
]);

useEffect(() => {
if (router.pathname === "/") {
Expand Down Expand Up @@ -132,7 +129,6 @@ const Home: NextPage = () => {
};

const fetchAbiFromHeimdall = async (contractAddress: Address) => {
setIsFetchingAbi(true);
try {
const rpcUrlWithoutHttps = publicClient?.chain.rpcUrls.default.http[0].substring(8);
const response = await fetch(
Expand All @@ -141,19 +137,14 @@ const Home: NextPage = () => {
const abi = await response.json();
if (abi.length === 0) {
notification.error("Failed to fetch ABI from Heimdall. Please try again or enter ABI manually.");
setIsFetchingAbi(false);
return;
}
setContractAbi(abi);
setIsAbiAvailable(true);
setAbiContractAddress(contractAddress);
router.push(`/${contractAddress}/${network}`);
} catch (error) {
console.error("Error fetching ABI from Heimdall: ", error);
notification.error("Failed to fetch ABI from Heimdall. Please try again or enter ABI manually.");
setIsAbiAvailable(false);
} finally {
setIsFetchingAbi(false);
}
};

Expand Down

0 comments on commit 0038c99

Please sign in to comment.