diff --git a/packages/nextjs/.env.example b/packages/nextjs/.env.example deleted file mode 100644 index c8d03d7..0000000 --- a/packages/nextjs/.env.example +++ /dev/null @@ -1,13 +0,0 @@ -# Template for NextJS environment variables. - -# For local development, copy this file, rename it to .env.local, and fill in the values. -# When deploying live, you'll need to store the vars in Vercel/System config. - -# If not set, we provide default values (check `scaffold.config.ts`) so developers can start prototyping out of the box, -# but we recommend getting your own API Keys for Production Apps. - -# To access the values stored in this env file you can use: process.env.VARIABLENAME -# You'll need to prefix the variables names with NEXT_PUBLIC_ if you want to access them on the client side. -# More info: https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables -NEXT_PUBLIC_ALCHEMY_API_KEY= -NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID= diff --git a/packages/nextjs/app/blockexplorer/_components/AddressCodeTab.tsx b/packages/nextjs/app/blockexplorer/_components/AddressCodeTab.tsx deleted file mode 100644 index ff57c43..0000000 --- a/packages/nextjs/app/blockexplorer/_components/AddressCodeTab.tsx +++ /dev/null @@ -1,25 +0,0 @@ -type AddressCodeTabProps = { - bytecode: string; - assembly: string; -}; - -export const AddressCodeTab = ({ bytecode, assembly }: AddressCodeTabProps) => { - const formattedAssembly = assembly.split(" ").join("\n"); - - return ( -
- Bytecode -
-
-          {bytecode}
-        
-
- Opcodes -
-
-          {formattedAssembly}
-        
-
-
- ); -}; diff --git a/packages/nextjs/app/blockexplorer/_components/AddressComponent.tsx b/packages/nextjs/app/blockexplorer/_components/AddressComponent.tsx deleted file mode 100644 index c0c14d6..0000000 --- a/packages/nextjs/app/blockexplorer/_components/AddressComponent.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { BackButton } from "./BackButton"; -import { ContractTabs } from "./ContractTabs"; -import { Address, Balance } from "~~/components/scaffold-eth"; - -export const AddressComponent = ({ - address, - contractData, -}: { - address: string; - contractData: { bytecode: string; assembly: string } | null; -}) => { - return ( -
-
- -
-
-
-
-
-
-
-
- Balance: - -
-
-
-
-
-
- -
- ); -}; diff --git a/packages/nextjs/app/blockexplorer/_components/AddressLogsTab.tsx b/packages/nextjs/app/blockexplorer/_components/AddressLogsTab.tsx deleted file mode 100644 index 9d2ab0e..0000000 --- a/packages/nextjs/app/blockexplorer/_components/AddressLogsTab.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { Address } from "viem"; -import { useContractLogs } from "~~/hooks/scaffold-eth"; -import { replacer } from "~~/utils/scaffold-eth/common"; - -export const AddressLogsTab = ({ address }: { address: Address }) => { - const contractLogs = useContractLogs(address); - - return ( -
-
-
-          {contractLogs.map((log, i) => (
-            
- Log: {JSON.stringify(log, replacer, 2)} -
- ))} -
-
-
- ); -}; diff --git a/packages/nextjs/app/blockexplorer/_components/AddressStorageTab.tsx b/packages/nextjs/app/blockexplorer/_components/AddressStorageTab.tsx deleted file mode 100644 index 86c4f21..0000000 --- a/packages/nextjs/app/blockexplorer/_components/AddressStorageTab.tsx +++ /dev/null @@ -1,61 +0,0 @@ -"use client"; - -import { useEffect, useState } from "react"; -import { Address, createPublicClient, http, toHex } from "viem"; -import { hardhat } from "viem/chains"; - -const publicClient = createPublicClient({ - chain: hardhat, - transport: http(), -}); - -export const AddressStorageTab = ({ address }: { address: Address }) => { - const [storage, setStorage] = useState([]); - - useEffect(() => { - const fetchStorage = async () => { - try { - const storageData = []; - let idx = 0; - - while (true) { - const storageAtPosition = await publicClient.getStorageAt({ - address: address, - slot: toHex(idx), - }); - - if (storageAtPosition === "0x" + "0".repeat(64)) break; - - if (storageAtPosition) { - storageData.push(storageAtPosition); - } - - idx++; - } - setStorage(storageData); - } catch (error) { - console.error("Failed to fetch storage:", error); - } - }; - - fetchStorage(); - }, [address]); - - return ( -
- {storage.length > 0 ? ( -
-
-            {storage.map((data, i) => (
-              
- Storage Slot {i}: {data} -
- ))} -
-
- ) : ( -
This contract does not have any variables.
- )} -
- ); -}; diff --git a/packages/nextjs/app/blockexplorer/_components/BackButton.tsx b/packages/nextjs/app/blockexplorer/_components/BackButton.tsx deleted file mode 100644 index bdfde8b..0000000 --- a/packages/nextjs/app/blockexplorer/_components/BackButton.tsx +++ /dev/null @@ -1,12 +0,0 @@ -"use client"; - -import { useRouter } from "next/navigation"; - -export const BackButton = () => { - const router = useRouter(); - return ( - - ); -}; diff --git a/packages/nextjs/app/blockexplorer/_components/ContractTabs.tsx b/packages/nextjs/app/blockexplorer/_components/ContractTabs.tsx deleted file mode 100644 index bb020ef..0000000 --- a/packages/nextjs/app/blockexplorer/_components/ContractTabs.tsx +++ /dev/null @@ -1,92 +0,0 @@ -"use client"; - -import { useEffect, useState } from "react"; -import { AddressCodeTab } from "./AddressCodeTab"; -import { AddressLogsTab } from "./AddressLogsTab"; -import { AddressStorageTab } from "./AddressStorageTab"; -import { PaginationButton } from "./PaginationButton"; -import { TransactionsTable } from "./TransactionsTable"; -import { createPublicClient, http } from "viem"; -import { hardhat } from "viem/chains"; -import { useFetchBlocks } from "~~/hooks/scaffold-eth"; - -type AddressCodeTabProps = { - bytecode: string; - assembly: string; -}; - -type PageProps = { - address: string; - contractData: AddressCodeTabProps | null; -}; - -const publicClient = createPublicClient({ - chain: hardhat, - transport: http(), -}); - -export const ContractTabs = ({ address, contractData }: PageProps) => { - const { blocks, transactionReceipts, currentPage, totalBlocks, setCurrentPage } = useFetchBlocks(); - const [activeTab, setActiveTab] = useState("transactions"); - const [isContract, setIsContract] = useState(false); - - useEffect(() => { - const checkIsContract = async () => { - const contractCode = await publicClient.getBytecode({ address: address }); - setIsContract(contractCode !== undefined && contractCode !== "0x"); - }; - - checkIsContract(); - }, [address]); - - const filteredBlocks = blocks.filter(block => - block.transactions.some(tx => { - if (typeof tx === "string") { - return false; - } - return tx.from.toLowerCase() === address.toLowerCase() || tx.to?.toLowerCase() === address.toLowerCase(); - }), - ); - - return ( - <> - {isContract && ( -
- - - - -
- )} - {activeTab === "transactions" && ( -
- - -
- )} - {activeTab === "code" && contractData && ( - - )} - {activeTab === "storage" && } - {activeTab === "logs" && } - - ); -}; diff --git a/packages/nextjs/app/blockexplorer/_components/PaginationButton.tsx b/packages/nextjs/app/blockexplorer/_components/PaginationButton.tsx deleted file mode 100644 index 77aefbc..0000000 --- a/packages/nextjs/app/blockexplorer/_components/PaginationButton.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { ArrowLeftIcon, ArrowRightIcon } from "@heroicons/react/24/outline"; - -type PaginationButtonProps = { - currentPage: number; - totalItems: number; - setCurrentPage: (page: number) => void; -}; - -const ITEMS_PER_PAGE = 20; - -export const PaginationButton = ({ currentPage, totalItems, setCurrentPage }: PaginationButtonProps) => { - const isPrevButtonDisabled = currentPage === 0; - const isNextButtonDisabled = currentPage + 1 >= Math.ceil(totalItems / ITEMS_PER_PAGE); - - const prevButtonClass = isPrevButtonDisabled ? "bg-gray-200 cursor-default" : "btn btn-primary"; - const nextButtonClass = isNextButtonDisabled ? "bg-gray-200 cursor-default" : "btn btn-primary"; - - if (isNextButtonDisabled && isPrevButtonDisabled) return null; - - return ( -
- - Page {currentPage + 1} - -
- ); -}; diff --git a/packages/nextjs/app/blockexplorer/_components/SearchBar.tsx b/packages/nextjs/app/blockexplorer/_components/SearchBar.tsx deleted file mode 100644 index 82b8839..0000000 --- a/packages/nextjs/app/blockexplorer/_components/SearchBar.tsx +++ /dev/null @@ -1,49 +0,0 @@ -"use client"; - -import { useState } from "react"; -import { useRouter } from "next/navigation"; -import { isAddress, isHex } from "viem"; -import { hardhat } from "viem/chains"; -import { usePublicClient } from "wagmi"; - -export const SearchBar = () => { - const [searchInput, setSearchInput] = useState(""); - const router = useRouter(); - - const client = usePublicClient({ chainId: hardhat.id }); - - const handleSearch = async (event: React.FormEvent) => { - event.preventDefault(); - if (isHex(searchInput)) { - try { - const tx = await client.getTransaction({ hash: searchInput }); - if (tx) { - router.push(`/blockexplorer/transaction/${searchInput}`); - return; - } - } catch (error) { - console.error("Failed to fetch transaction:", error); - } - } - - if (isAddress(searchInput)) { - router.push(`/blockexplorer/address/${searchInput}`); - return; - } - }; - - return ( -
- setSearchInput(e.target.value)} - /> - -
- ); -}; diff --git a/packages/nextjs/app/blockexplorer/_components/TransactionHash.tsx b/packages/nextjs/app/blockexplorer/_components/TransactionHash.tsx deleted file mode 100644 index d4fd929..0000000 --- a/packages/nextjs/app/blockexplorer/_components/TransactionHash.tsx +++ /dev/null @@ -1,39 +0,0 @@ -"use client"; - -import { useState } from "react"; -import Link from "next/link"; -import { CopyToClipboard } from "react-copy-to-clipboard"; -import { CheckCircleIcon, DocumentDuplicateIcon } from "@heroicons/react/24/outline"; - -export const TransactionHash = ({ hash }: { hash: string }) => { - const [addressCopied, setAddressCopied] = useState(false); - - return ( -
- - {hash?.substring(0, 6)}...{hash?.substring(hash.length - 4)} - - {addressCopied ? ( -
- ); -}; diff --git a/packages/nextjs/app/blockexplorer/_components/TransactionsTable.tsx b/packages/nextjs/app/blockexplorer/_components/TransactionsTable.tsx deleted file mode 100644 index b91892c..0000000 --- a/packages/nextjs/app/blockexplorer/_components/TransactionsTable.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import { TransactionHash } from "./TransactionHash"; -import { formatEther } from "viem"; -import { Address } from "~~/components/scaffold-eth"; -import { useTargetNetwork } from "~~/hooks/scaffold-eth/useTargetNetwork"; -import { TransactionWithFunction } from "~~/utils/scaffold-eth"; -import { TransactionsTableProps } from "~~/utils/scaffold-eth/"; - -export const TransactionsTable = ({ blocks, transactionReceipts }: TransactionsTableProps) => { - const { targetNetwork } = useTargetNetwork(); - - return ( -
-
- - - - - - - - - - - - - - {blocks.map(block => - (block.transactions as TransactionWithFunction[]).map(tx => { - const receipt = transactionReceipts[tx.hash]; - const timeMined = new Date(Number(block.timestamp) * 1000).toLocaleString(); - const functionCalled = tx.input.substring(0, 10); - - return ( - - - - - - - - - - ); - }), - )} - -
Transaction HashFunction CalledBlock NumberTime MinedFromToValue ({targetNetwork.nativeCurrency.symbol})
- - - {tx.functionName === "0x" ? "" : {tx.functionName}} - {functionCalled !== "0x" && ( - {functionCalled} - )} - {block.number?.toString()}{timeMined} -
-
- {!receipt?.contractAddress ? ( - tx.to &&
- ) : ( -
-
- (Contract Creation) -
- )} -
- {formatEther(tx.value)} {targetNetwork.nativeCurrency.symbol} -
-
-
- ); -}; diff --git a/packages/nextjs/app/blockexplorer/_components/index.tsx b/packages/nextjs/app/blockexplorer/_components/index.tsx deleted file mode 100644 index 20d8eb2..0000000 --- a/packages/nextjs/app/blockexplorer/_components/index.tsx +++ /dev/null @@ -1,7 +0,0 @@ -export * from "./SearchBar"; -export * from "./BackButton"; -export * from "./AddressCodeTab"; -export * from "./TransactionHash"; -export * from "./ContractTabs"; -export * from "./PaginationButton"; -export * from "./TransactionsTable"; diff --git a/packages/nextjs/app/blockexplorer/address/[address]/page.tsx b/packages/nextjs/app/blockexplorer/address/[address]/page.tsx deleted file mode 100644 index dda48ba..0000000 --- a/packages/nextjs/app/blockexplorer/address/[address]/page.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import fs from "fs"; -import path from "path"; -import { hardhat } from "viem/chains"; -import { AddressComponent } from "~~/app/blockexplorer/_components/AddressComponent"; -import deployedContracts from "~~/contracts/deployedContracts"; -import { GenericContractsDeclaration } from "~~/utils/scaffold-eth/contract"; - -type PageProps = { - params: { address: string }; -}; - -async function fetchByteCodeAndAssembly(buildInfoDirectory: string, contractPath: string) { - const buildInfoFiles = fs.readdirSync(buildInfoDirectory); - let bytecode = ""; - let assembly = ""; - - for (let i = 0; i < buildInfoFiles.length; i++) { - const filePath = path.join(buildInfoDirectory, buildInfoFiles[i]); - - const buildInfo = JSON.parse(fs.readFileSync(filePath, "utf8")); - - if (buildInfo.output.contracts[contractPath]) { - for (const contract in buildInfo.output.contracts[contractPath]) { - bytecode = buildInfo.output.contracts[contractPath][contract].evm.bytecode.object; - assembly = buildInfo.output.contracts[contractPath][contract].evm.bytecode.opcodes; - break; - } - } - - if (bytecode && assembly) { - break; - } - } - - return { bytecode, assembly }; -} - -const getContractData = async (address: string) => { - const contracts = deployedContracts as GenericContractsDeclaration | null; - const chainId = hardhat.id; - let contractPath = ""; - - const buildInfoDirectory = path.join( - __dirname, - "..", - "..", - "..", - "..", - "..", - "..", - "..", - "hardhat", - "artifacts", - "build-info", - ); - - if (!fs.existsSync(buildInfoDirectory)) { - throw new Error(`Directory ${buildInfoDirectory} not found.`); - } - - const deployedContractsOnChain = contracts ? contracts[chainId] : {}; - for (const [contractName, contractInfo] of Object.entries(deployedContractsOnChain)) { - if (contractInfo.address.toLowerCase() === address.toLowerCase()) { - contractPath = `contracts/${contractName}.sol`; - break; - } - } - - if (!contractPath) { - // No contract found at this address - return null; - } - - const { bytecode, assembly } = await fetchByteCodeAndAssembly(buildInfoDirectory, contractPath); - - return { bytecode, assembly }; -}; - -const AddressPage = async ({ params }: PageProps) => { - const address = params?.address as string; - const contractData: { bytecode: string; assembly: string } | null = await getContractData(address); - return ; -}; - -export default AddressPage; diff --git a/packages/nextjs/app/blockexplorer/layout.tsx b/packages/nextjs/app/blockexplorer/layout.tsx deleted file mode 100644 index 1abc7ec..0000000 --- a/packages/nextjs/app/blockexplorer/layout.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { getMetadata } from "~~/utils/scaffold-eth/getMetadata"; - -export const metadata = getMetadata({ - title: "Block Explorer", - description: "Block Explorer created with 🏗 Scaffold-ETH 2", -}); - -const BlockExplorerLayout = ({ children }: { children: React.ReactNode }) => { - return <>{children}; -}; - -export default BlockExplorerLayout; diff --git a/packages/nextjs/app/blockexplorer/page.tsx b/packages/nextjs/app/blockexplorer/page.tsx deleted file mode 100644 index 80f481c..0000000 --- a/packages/nextjs/app/blockexplorer/page.tsx +++ /dev/null @@ -1,62 +0,0 @@ -"use client"; - -import { useEffect } from "react"; -import { PaginationButton, SearchBar, TransactionsTable } from "./_components"; -import type { NextPage } from "next"; -import { hardhat } from "viem/chains"; -import { useFetchBlocks } from "~~/hooks/scaffold-eth"; -import { useTargetNetwork } from "~~/hooks/scaffold-eth/useTargetNetwork"; -import { notification } from "~~/utils/scaffold-eth"; - -const BlockExplorer: NextPage = () => { - const { blocks, transactionReceipts, currentPage, totalBlocks, setCurrentPage, error } = useFetchBlocks(); - const { targetNetwork } = useTargetNetwork(); - - useEffect(() => { - if (targetNetwork.id === hardhat.id && error) { - notification.error( - <> -

Cannot connect to local provider

-

- - Did you forget to run yarn chain ? -

-

- - Or you can change targetNetwork in{" "} - scaffold.config.ts -

- , - ); - } - - if (targetNetwork.id !== hardhat.id) { - notification.error( - <> -

- targeNetwork is not localhost -

-

- - You are on {targetNetwork.name} .This - block explorer is only for localhost. -

-

- - You can use{" "} - - {targetNetwork.blockExplorers?.default.name} - {" "} - instead -

- , - ); - } - }, [error, targetNetwork]); - - return ( -
- - - -
- ); -}; - -export default BlockExplorer; diff --git a/packages/nextjs/app/blockexplorer/transaction/[txHash]/page.tsx b/packages/nextjs/app/blockexplorer/transaction/[txHash]/page.tsx deleted file mode 100644 index d9c45f5..0000000 --- a/packages/nextjs/app/blockexplorer/transaction/[txHash]/page.tsx +++ /dev/null @@ -1,153 +0,0 @@ -"use client"; - -import { useEffect, useState } from "react"; -import { useRouter } from "next/navigation"; -import type { NextPage } from "next"; -import { Hash, Transaction, TransactionReceipt, formatEther, formatUnits } from "viem"; -import { hardhat } from "viem/chains"; -import { usePublicClient } from "wagmi"; -import { Address } from "~~/components/scaffold-eth"; -import { useTargetNetwork } from "~~/hooks/scaffold-eth/useTargetNetwork"; -import { decodeTransactionData, getFunctionDetails } from "~~/utils/scaffold-eth"; -import { replacer } from "~~/utils/scaffold-eth/common"; - -type PageProps = { - params: { txHash?: Hash }; -}; -const TransactionPage: NextPage = ({ params }: PageProps) => { - const client = usePublicClient({ chainId: hardhat.id }); - const txHash = params?.txHash as Hash; - const router = useRouter(); - const [transaction, setTransaction] = useState(); - const [receipt, setReceipt] = useState(); - const [functionCalled, setFunctionCalled] = useState(); - - const { targetNetwork } = useTargetNetwork(); - - useEffect(() => { - if (txHash) { - const fetchTransaction = async () => { - const tx = await client.getTransaction({ hash: txHash }); - const receipt = await client.getTransactionReceipt({ hash: txHash }); - - const transactionWithDecodedData = decodeTransactionData(tx); - setTransaction(transactionWithDecodedData); - setReceipt(receipt); - - const functionCalled = transactionWithDecodedData.input.substring(0, 10); - setFunctionCalled(functionCalled); - }; - - fetchTransaction(); - } - }, [client, txHash]); - - return ( -
- - {transaction ? ( -
-

Transaction Details

{" "} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Transaction Hash: - {transaction.hash}
- Block Number: - {Number(transaction.blockNumber)}
- From: - -
-
- To: - - {!receipt?.contractAddress ? ( - transaction.to &&
- ) : ( - - Contract Creation: -
- - )} -
- Value: - - {formatEther(transaction.value)} {targetNetwork.nativeCurrency.symbol} -
- Function called: - -
- {functionCalled === "0x" ? ( - "This transaction did not call any function." - ) : ( - <> - {getFunctionDetails(transaction)} - {functionCalled} - - )} -
-
- Gas Price: - {formatUnits(transaction.gasPrice || 0n, 9)} Gwei
- Data: - -