Skip to content

Commit

Permalink
App router migration (#535)
Browse files Browse the repository at this point in the history
Co-authored-by: Carlos Sánchez <oceanrdn@gmail.com>
Co-authored-by: Rinat <rinat@hey.com>
Co-authored-by: Shiv Bhonde <shivbhonde04@gmail.com>
Co-authored-by: tokodev <pablo@asdloop.com>
  • Loading branch information
5 people committed Jan 14, 2024
1 parent 2dc4a3b commit 27b6831
Show file tree
Hide file tree
Showing 68 changed files with 3,478 additions and 2,936 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest]
node: [16.x]
node: [lts/*]

steps:
- name: Checkout
Expand All @@ -25,13 +25,13 @@ jobs:
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
cache : yarn
cache: yarn

- name: Install dependencies
run: yarn install --immutable

- name: Run hardhat node, deploy contracts (& generate contracts typescript output)
run: yarn chain & yarn deploy
run: yarn chain & yarn deploy

- name: Run nextjs lint
run: yarn next:lint --max-warnings=0
Expand Down
2 changes: 1 addition & 1 deletion .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

yarn lint-staged --verbose
yarn lint-staged --verbose
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

Before you begin, you need to install the following tools:

- [Node (v18 LTS)](https://nodejs.org/en/download/)
- [Node (>= v18.17)](https://nodejs.org/en/download/)
- Yarn ([v1](https://classic.yarnpkg.com/en/docs/install/) or [v2+](https://yarnpkg.com/getting-started/install))
- [Git](https://git-scm.com/downloads)

Expand Down
2 changes: 1 addition & 1 deletion packages/hardhat/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"@nomicfoundation/hardhat-chai-matchers": "^2.0.3",
"@nomicfoundation/hardhat-ethers": "^3.0.5",
"@nomicfoundation/hardhat-network-helpers": "^1.0.6",
"@nomicfoundation/hardhat-verify": "^2.0.1",
"@nomicfoundation/hardhat-verify": "^2.0.3",
"@typechain/ethers-v5": "^10.1.0",
"@typechain/hardhat": "^9.1.0",
"@types/eslint": "^8",
Expand Down
35 changes: 35 additions & 0 deletions packages/nextjs/app/blockexplorer/_components/AddressComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
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 (
<div className="m-10 mb-20">
<div className="flex justify-start mb-5">
<BackButton />
</div>
<div className="col-span-5 grid grid-cols-1 lg:grid-cols-2 gap-8 lg:gap-10">
<div className="col-span-1 flex flex-col">
<div className="bg-base-100 border-base-300 border shadow-md shadow-secondary rounded-3xl px-6 lg:px-8 mb-6 space-y-1 py-4 overflow-x-auto">
<div className="flex">
<div className="flex flex-col gap-1">
<Address address={address} format="long" />
<div className="flex gap-1 items-center">
<span className="font-bold text-sm">Balance:</span>
<Balance address={address} className="text" />
</div>
</div>
</div>
</div>
</div>
</div>
<ContractTabs address={address} contractData={contractData} />
</div>
);
};
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"use client";

import { useEffect, useState } from "react";
import { Address, createPublicClient, http, toHex } from "viem";
import { hardhat } from "viem/chains";
Expand Down
12 changes: 12 additions & 0 deletions packages/nextjs/app/blockexplorer/_components/BackButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"use client";

import { useRouter } from "next/navigation";

export const BackButton = () => {
const router = useRouter();
return (
<button className="btn btn-sm btn-primary" onClick={() => router.back()}>
Back
</button>
);
};
92 changes: 92 additions & 0 deletions packages/nextjs/app/blockexplorer/_components/ContractTabs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
"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 && (
<div className="tabs tabs-lifted w-min">
<button
className={`tab ${activeTab === "transactions" ? "tab-active" : ""}`}
onClick={() => setActiveTab("transactions")}
>
Transactions
</button>
<button className={`tab ${activeTab === "code" ? "tab-active" : ""}`} onClick={() => setActiveTab("code")}>
Code
</button>
<button
className={`tab ${activeTab === "storage" ? "tab-active" : ""}`}
onClick={() => setActiveTab("storage")}
>
Storage
</button>
<button className={`tab ${activeTab === "logs" ? "tab-active" : ""}`} onClick={() => setActiveTab("logs")}>
Logs
</button>
</div>
)}
{activeTab === "transactions" && (
<div className="pt-4">
<TransactionsTable blocks={filteredBlocks} transactionReceipts={transactionReceipts} />
<PaginationButton
currentPage={currentPage}
totalItems={Number(totalBlocks)}
setCurrentPage={setCurrentPage}
/>
</div>
)}
{activeTab === "code" && contractData && (
<AddressCodeTab bytecode={contractData.bytecode} assembly={contractData.assembly} />
)}
{activeTab === "storage" && <AddressStorageTab address={address} />}
{activeTab === "logs" && <AddressLogsTab address={address} />}
</>
);
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"use client";

import { useState } from "react";
import { useRouter } from "next/router";
import { useRouter } from "next/navigation";
import { isAddress, isHex } from "viem";
import { hardhat } from "viem/chains";
import { usePublicClient } from "wagmi";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"use client";

import { useState } from "react";
import Link from "next/link";
import { CopyToClipboard } from "react-copy-to-clipboard";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { TransactionHash } from "./TransactionHash";
import { formatEther } from "viem";
import { TransactionHash } from "~~/components/blockexplorer/TransactionHash";
import { Address } from "~~/components/scaffold-eth";
import { useTargetNetwork } from "~~/hooks/scaffold-eth/useTargetNetwork";
import { TransactionWithFunction } from "~~/utils/scaffold-eth";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export * from "./AddressCodeTab";
export * from "./AddressLogsTab";
export * from "./AddressStorageTab";
export * from "./PaginationButton";
export * from "./SearchBar";
export * from "./BackButton";
export * from "./AddressCodeTab";
export * from "./TransactionHash";
export * from "./ContractTabs";
export * from "./PaginationButton";
export * from "./TransactionsTable";
85 changes: 85 additions & 0 deletions packages/nextjs/app/blockexplorer/address/[address]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
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) {
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 <AddressComponent address={address} contractData={contractData} />;
};

export default AddressPage;
12 changes: 12 additions & 0 deletions packages/nextjs/app/blockexplorer/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
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;
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"use client";

import { useEffect } from "react";
import { PaginationButton, SearchBar, TransactionsTable } from "./_components";
import type { NextPage } from "next";
import { hardhat } from "viem/chains";
import { PaginationButton } from "~~/components/blockexplorer/PaginationButton";
import { SearchBar } from "~~/components/blockexplorer/SearchBar";
import { TransactionsTable } from "~~/components/blockexplorer/TransactionsTable";
import { useFetchBlocks } from "~~/hooks/scaffold-eth";
import { useTargetNetwork } from "~~/hooks/scaffold-eth/useTargetNetwork";
import { notification } from "~~/utils/scaffold-eth";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"use client";

import { useEffect, useState } from "react";
import { useRouter } from "next/router";
import { useRouter } from "next/navigation";
import type { NextPage } from "next";
import { Hash, Transaction, TransactionReceipt, formatEther, formatUnits } from "viem";
import { hardhat } from "viem/chains";
Expand All @@ -9,11 +11,13 @@ import { useTargetNetwork } from "~~/hooks/scaffold-eth/useTargetNetwork";
import { decodeTransactionData, getFunctionDetails } from "~~/utils/scaffold-eth";
import { replacer } from "~~/utils/scaffold-eth/common";

const TransactionPage: NextPage = () => {
type PageProps = {
params: { txHash?: Hash };
};
const TransactionPage: NextPage<PageProps> = ({ params }: PageProps) => {
const client = usePublicClient({ chainId: hardhat.id });

const txHash = params?.txHash as Hash;
const router = useRouter();
const { txHash } = router.query as { txHash?: Hash };
const [transaction, setTransaction] = useState<Transaction>();
const [receipt, setReceipt] = useState<TransactionReceipt>();
const [functionCalled, setFunctionCalled] = useState<string>();
Expand Down
Loading

0 comments on commit 27b6831

Please sign in to comment.