Skip to content

Commit

Permalink
Enable proxy contracts (#72)
Browse files Browse the repository at this point in the history
  • Loading branch information
portdeveloper authored Apr 2, 2024
1 parent 0f5dafc commit b8b8257
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 8 deletions.
15 changes: 12 additions & 3 deletions packages/nextjs/components/scaffold-eth/Contract/ContractUI.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,11 @@ const mainNetworks = getTargetNetworks();
**/
export const ContractUI = ({ className = "", initialContractData }: ContractUIProps) => {
const [refreshDisplayVariables, triggerRefreshDisplayVariables] = useReducer(value => !value, false);
const mainChainId = useAbiNinjaState(state => state.mainChainId);
const mainNetwork = mainNetworks.find(network => network.id === mainChainId);
const { implementationAddress, chainId } = useAbiNinjaState(state => ({
chainId: state.mainChainId,
implementationAddress: state.implementationAddress,
}));
const mainNetwork = mainNetworks.find(network => network.id === chainId);
const networkColor = useNetworkColor(mainNetwork);
const router = useRouter();
const { network } = router.query as { network?: string };
Expand Down Expand Up @@ -123,7 +126,7 @@ export const ContractUI = ({ className = "", initialContractData }: ContractUIPr
const { data: contractNameData, isLoading: isContractNameLoading } = useContractRead({
address: initialContractData.address,
abi: initialContractData.abi,
chainId: mainChainId,
chainId: chainId,
functionName: "name",
});

Expand Down Expand Up @@ -197,6 +200,12 @@ export const ContractUI = ({ className = "", initialContractData }: ContractUIPr
<span className="font-medium text-base mr-4"> {displayContractName} </span>
<Address address={initialContractData.address} />
</div>
{implementationAddress && (
<div className="flex items-center gap-1">
<span className="font-medium text-base mr-4 text-green-600">Implementation Address</span>
<Address address={implementationAddress} />
</div>
)}
<div className="flex items-center gap-1">
<span className="text-sm font-bold">Balance:</span>
<Balance address={initialContractData.address} className="h-1.5 min-h-[0.375rem] px-0" />
Expand Down
1 change: 1 addition & 0 deletions packages/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@uniswap/v2-sdk": "^3.0.1",
"blo": "^1.0.1",
"daisyui": "^4.4.19",
"evm-proxy-detection": "^1.2.0",
"next": "13.3.4",
"next-plausible": "^3.12.0",
"nextjs-progressbar": "^0.0.16",
Expand Down
16 changes: 14 additions & 2 deletions packages/nextjs/pages/[contractAddress]/[network].tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { useEffect, useState } from "react";
import Link from "next/link";
import { useRouter } from "next/router";
import { AlchemyProvider } from "@ethersproject/providers";
import detectProxyTarget from "evm-proxy-detection";
import { ParsedUrlQuery } from "querystring";
import { Abi, isAddress } from "viem";
import * as chains from "viem/chains";
import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
import { MetaHeader } from "~~/components/MetaHeader";
import { MiniHeader } from "~~/components/MiniHeader";
import { ContractUI } from "~~/components/scaffold-eth";
import scaffoldConfig from "~~/scaffold.config";
import { useAbiNinjaState } from "~~/services/store/store";
import { fetchContractABIFromAnyABI, fetchContractABIFromEtherscan } from "~~/utils/abi";

Expand All @@ -32,10 +35,12 @@ const ContractDetailPage = () => {
contractAbi: storedAbi,
setMainChainId,
chainId,
setImplementationAddress,
} = useAbiNinjaState(state => ({
contractAbi: state.contractAbi,
setMainChainId: state.setMainChainId,
chainId: state.mainChainId,
setImplementationAddress: state.setImplementationAddress,
}));

const getNetworkName = (chainId: number) => {
Expand Down Expand Up @@ -72,7 +77,14 @@ const ContractDetailPage = () => {
}

try {
const abi = await fetchContractABIFromAnyABI(contractAddress, parsedNetworkId);
const alchemyProvider = new AlchemyProvider(parseInt(network), scaffoldConfig.alchemyApiKey);
const requestFunc = ({ method, params }: { method: string; params: any }) =>
alchemyProvider.send(method, params);
const implementationAddress = await detectProxyTarget(contractAddress, requestFunc);
if (implementationAddress) {
setImplementationAddress(implementationAddress);
}
const abi = await fetchContractABIFromAnyABI(implementationAddress || contractAddress, parsedNetworkId);
if (!abi) throw new Error("Got empty or undefined ABI from AnyABI");
setContractData({ abi, address: contractAddress });
setError(null);
Expand Down Expand Up @@ -102,7 +114,7 @@ const ContractDetailPage = () => {
}
}
}
}, [contractAddress, network, storedAbi, setMainChainId]);
}, [contractAddress, network, storedAbi, setMainChainId, setImplementationAddress]);

return (
<>
Expand Down
22 changes: 19 additions & 3 deletions packages/nextjs/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ import { useEffect, useState } from "react";
import Image from "next/image";
import Link from "next/link";
import { useRouter } from "next/router";
import { AlchemyProvider } from "@ethersproject/providers";
import detectProxyTarget from "evm-proxy-detection";
import type { NextPage } from "next";
import { Address, isAddress } from "viem";
import { usePublicClient } from "wagmi";
import { MetaHeader } from "~~/components/MetaHeader";
import { MiniFooter } from "~~/components/MiniFooter";
import { NetworksDropdown } from "~~/components/NetworksDropdown";
import { AddressInput, InputBase } from "~~/components/scaffold-eth";
import scaffoldConfig from "~~/scaffold.config";
import { useAbiNinjaState } from "~~/services/store/store";
import { fetchContractABIFromAnyABI, fetchContractABIFromEtherscan, parseAndCorrectJSON } from "~~/utils/abi";
import { getTargetNetworks, notification } from "~~/utils/scaffold-eth";
Expand All @@ -32,13 +35,17 @@ const Home: NextPage = () => {
const [isCheckingContractAddress, setIsCheckingContractAddress] = useState(false);
const [isContract, setIsContract] = useState(false);

const alchemyProvider = new AlchemyProvider(parseInt(network), scaffoldConfig.alchemyApiKey);
const requestFunc = ({ method, params }: { method: string; params: any }) => alchemyProvider.send(method, params);

const publicClient = usePublicClient({
chainId: parseInt(network),
});

const { setContractAbi, setAbiContractAddress } = useAbiNinjaState(state => ({
const { setContractAbi, setAbiContractAddress, setImplementationAddress } = useAbiNinjaState(state => ({
setContractAbi: state.setContractAbi,
setAbiContractAddress: state.setAbiContractAddress,
setImplementationAddress: state.setImplementationAddress,
}));

const [isAbiAvailable, setIsAbiAvailable] = useState(false);
Expand All @@ -49,7 +56,14 @@ const Home: NextPage = () => {
const fetchContractAbi = async () => {
setIsFetchingAbi(true);
try {
const abi = await fetchContractABIFromAnyABI(verifiedContractAddress, parseInt(network));
const implementationAddress = await detectProxyTarget(verifiedContractAddress, requestFunc);
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);
Expand Down Expand Up @@ -80,6 +94,7 @@ const Home: NextPage = () => {
} else {
setIsAbiAvailable(false);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [verifiedContractAddress, network, setContractAbi]);

useEffect(() => {
Expand Down Expand Up @@ -114,8 +129,9 @@ const Home: NextPage = () => {
useEffect(() => {
if (router.pathname === "/") {
setContractAbi([]);
setImplementationAddress("");
}
}, [router.pathname, setContractAbi]);
}, [router.pathname, setContractAbi, setImplementationAddress]);

const handleLoadContract = () => {
if (activeTab === TabName.verifiedContract) {
Expand Down
4 changes: 4 additions & 0 deletions packages/nextjs/services/store/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ type AbiNinjaState = {
setContractAbi: (newAbi: Abi) => void;
abiContractAddress: Address;
setAbiContractAddress: (newAbiContractAddress: Address) => void;
implementationAddress: Address;
setImplementationAddress: (newImplementationAddress: Address) => void;
};

export const useGlobalState = create<GlobalState>(set => ({
Expand All @@ -33,4 +35,6 @@ export const useAbiNinjaState = create<AbiNinjaState>(set => ({
setContractAbi: (newAbi: Abi): void => set({ contractAbi: newAbi }),
abiContractAddress: "",
setAbiContractAddress: (newAddress: Address): void => set({ abiContractAddress: newAddress }),
implementationAddress: "",
setImplementationAddress: (newAddress: Address): void => set({ implementationAddress: newAddress }),
}));
8 changes: 8 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1370,6 +1370,7 @@ __metadata:
eslint-config-next: ^13.1.6
eslint-config-prettier: ^8.5.0
eslint-plugin-prettier: ^4.2.1
evm-proxy-detection: ^1.2.0
next: 13.3.4
next-plausible: ^3.12.0
nextjs-progressbar: ^0.0.16
Expand Down Expand Up @@ -4876,6 +4877,13 @@ __metadata:
languageName: node
linkType: hard

"evm-proxy-detection@npm:^1.2.0":
version: 1.2.0
resolution: "evm-proxy-detection@npm:1.2.0"
checksum: d9996cbcd22eadd0b1209116d1f5c90ebf063edfe08c71c2f92372040dc004248ab280887a2f6d968d3e4963f40909f7f4cece584992e8e1f6ea136d6e18f2ee
languageName: node
linkType: hard

"execa@npm:^6.1.0":
version: 6.1.0
resolution: "execa@npm:6.1.0"
Expand Down

0 comments on commit b8b8257

Please sign in to comment.