簡単なコントラクトをデプロイする


# File-Timestamp-App

```
mkdir File-Timestamp-App
cd File-Timestamp-App
```

## npmの初期化

```nom
$ npm init -y                                               
{
  "name": "peachheart",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}
```

プロジェクトを初期化するコマンド
- カレントディレクトリにpackage.jsonを自動生成
- `npm init` で聞かれる質問をスキップしてデフォルトを設定

## Hardhatのインストール

`npm install --save-dev hardhat `
- `npm install`npmを使ってhardhatをインストール
- `—save-dev` インストールするパッケージを開発依存関係として指定する
  - hardhatはpackage.jsonのdevDependenciesセクションに追加される
  - 開発依存関係 : 開発時のみ必要で本番環境では必要ではないということ
  - hardhatはスマートコントラクトの開発やテスト，デプロイなどの開発プロセスのサポートのために使う
  - npm5以降では—saveオプションがデフォルトになっていて `npm install`では自動的に dependenciesセクションに追加されるが開発依存関係を明示的に指定するためには `—save-dev`オブションをつけないといけない

## Hardhatプロジェクトの初期化

以下のコマンドを実行するとCLIインターフェースが起動してプロジェクトのオプションを選択できる。

```
npx hardhat init
888    888                      888 888               888
888    888                      888 888               888
888    888                      888 888               888
8888888888  8888b.  888d888 .d88888 88888b.   8888b.  888888
888    888     "88b 888P"  d88" 888 888 "88b     "88b 888
888    888 .d888888 888    888  888 888  888 .d888888 888
888    888 888  888 888    Y88b 888 888  888 888  888 Y88b.
888    888 "Y888888 888     "Y88888 888  888 "Y888888  "Y888

👷 Welcome to Hardhat v2.22.17 👷‍

✔ What do you want to do? · Create a TypeScript project
✔ Hardhat project root: デフォでEnter
✔ Do you want to add a .gitignore? (Y/n) · y
✔ Do you want to install this sample project's dependencies with npm (@nomicfoundation/hardhat-toolbox)? (Y/n) · y

...

✨ Project created ✨

See the README.md file for some example tasks you can run

Give Hardhat a star on Github if you're enjoying it! ⭐️✨

     https://github.com/NomicFoundation/hardhat
```

選択したオプションに基づいてhardhatが必要なファイルとフォルダ構造を生成する

## テストの実行

デフォルトであるLock.solのテストを実行してみましょう

```
$ npx hardhat test
Compiled 1 Solidity file successfully (evm target: paris).


  Lock
    Deployment
      ✔ Should set the right unlockTime (860ms)
      ✔ Should set the right owner
      ✔ Should receive and store the funds to lock
      ✔ Should fail if the unlockTime is not in the future
    Withdrawals
      Validations
        ✔ Should revert with the right error if called too soon
        ✔ Should revert with the right error if called from another account
        ✔ Shouldn't fail if the unlockTime has arrived and the owner calls it
      Events
        ✔ Should emit an event on withdrawals
      Transfers
        ✔ Should transfer the funds to the owner


  9 passing (888ms)
```


## コントラクトの作成

```contract/FileTimestamp.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.19;

import "hardhat/console.sol";

contract FileTimestamp {

    mapping(uint256 => uint256) public hashToTimestamp;

    constructor() {
        console.log("Deploying a  contract");
    }

    function setTimestamp(uint256 hash, uint256 timestamp) public {
        require(hashToTimestamp[hash] == 0, "Timestamp already set");
        hashToTimestamp[hash] = timestamp;
    }

    function getTimestamp(uint256 hash) public view returns (uint256) {
        require(hashToTimestamp[hash] != 0, "Timestamp not set");
        return hashToTimestamp[hash];
    }
}
```

### 実行してみる

---

Scripts/run.tsを作成し実行するスクリプトを作成する

```
import { ethers } from "hardhat";

const main = async () => {
    const fileTimestampContract = await ethers.deployContract("FileTimestamp");
    await fileTimestampContract.waitForDeployment();
    console.log("FileTimestamp deployed to:", fileTimestampContract.target);
}

const runMain = async () => {
    try {
        await main();
        process.exit(0);
    } catch (error) {
        console.error(error);
        process.exit(1);
    }

};

runMain();
```

`package.json`を書き換える

```
...  
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "run:script" : "npx hardhat run script/run.ts"
  },
...
```

`npm run run:script`

```
> file-timestamp-app@1.0.0 run:script
> npx hardhat run scripts/run.ts

Deploying a  contract
FileTimestamp deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3
```

成功しました

`run.ts`のmainを以下のように書き換えてみます

```
const main = async () => {
    const fileTimestampContract = await ethers.deployContract("FileTimestamp");
    await fileTimestampContract.waitForDeployment();
    console.log("FileTimestamp deployed to:", fileTimestampContract.target);


    const now_timestamp = Date.now();

    await fileTimestampContract.setTimestamp(0, now_timestamp);


    const timestamp = await fileTimestampContract.getTimestamp(0);

    if (timestamp != BigInt(now_timestamp)) {
        throw new Error("Timestamps do not match");
    } else {
        console.log("Timestamps match");
    }


}
```

```
$ npm run run:script
> file-timestamp-app@1.0.0 run:script
> npx hardhat run scripts/run.ts

Deploying a  contract
FileTimestamp deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3
Timestamps match
```
これでハッシュ値をキーにしてタイムスタンプを格納できることが確認できた

## ローカル環境にデプロイ

  package.jsonに以下を追加する
```

...
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start":"npx hardhat node",
    "run:script" : "npx hardhat run scripts/run.ts"
  },
  ...

```

そして以下のコマンドを実行する

```
npm start
```
このように出力されました

```
> file-timestamp-app@1.0.0 start
> npx hardhat node

Started HTTP and WebSocket JSON-RPC server at http://127.0.0.1:8545/

Accounts
========

WARNING: These accounts, and their private keys, are publicly known.
Any funds sent to them on Mainnet or any other live network WILL BE LOST.

Account #0: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (10000 ETH)
Private Key: 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80

Account #1: 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 (10000 ETH)
Private Key: 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d

Account #2: 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC (10000 ETH)
Private Key: 0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a

Account #3: 0x90F79bf6EB2c4f870365E785982E1f101E93b906 (10000 ETH)
Private Key: 0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6

Account #4: 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 (10000 ETH)
Private Key: 0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a

Account #5: 0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc (10000 ETH)
Private Key: 0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba

Account #6: 0x976EA74026E726554dB657fA54763abd0C3a0aa9 (10000 ETH)
Private Key: 0x92db14e403b83dfe3df233f83dfa3a0d7096f21ca9b0d6d6b8d88b2b4ec1564e

Account #7: 0x14dC79964da2C08b23698B3D3cc7Ca32193d9955 (10000 ETH)
Private Key: 0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356

Account #8: 0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f (10000 ETH)
Private Key: 0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97

Account #9: 0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 (10000 ETH)
Private Key: 0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6

Account #10: 0xBcd4042DE499D14e55001CcbB24a551F3b954096 (10000 ETH)
Private Key: 0xf214f2b2cd398c806f84e317254e0f0b801d0643303237d97a22a48e01628897

Account #11: 0x71bE63f3384f5fb98995898A86B02Fb2426c5788 (10000 ETH)
Private Key: 0x701b615bbdfb9de65240bc28bd21bbc0d996645a3dd57e7b12bc2bdf6f192c82

Account #12: 0xFABB0ac9d68B0B445fB7357272Ff202C5651694a (10000 ETH)
Private Key: 0xa267530f49f8280200edf313ee7af6b827f2a8bce2897751d06a843f644967b1

Account #13: 0x1CBd3b2770909D4e10f157cABC84C7264073C9Ec (10000 ETH)
Private Key: 0x47c99abed3324a2707c28affff1267e45918ec8c3f20b8aa892e8b065d2942dd

Account #14: 0xdF3e18d64BC6A983f673Ab319CCaE4f1a57C7097 (10000 ETH)
Private Key: 0xc526ee95bf44d8fc405a158bb884d9d1238d99f0612e9f33d006bb0789009aaa

Account #15: 0xcd3B766CCDd6AE721141F452C550Ca635964ce71 (10000 ETH)
Private Key: 0x8166f546bab6da521a8369cab06c5d2b9e46670292d85c875ee9ec20e84ffb61

Account #16: 0x2546BcD3c84621e976D8185a91A922aE77ECEc30 (10000 ETH)
Private Key: 0xea6c44ac03bff858b476bba40716402b03e41b8e97e276d1baec7c37d42484a0

Account #17: 0xbDA5747bFD65F08deb54cb465eB87D40e51B197E (10000 ETH)
Private Key: 0x689af8efa8c651a91ad287602527f3af2fe9f6501a7ac4b061667b5a93e037fd

Account #18: 0xdD2FD4581271e230360230F9337D5c0430Bf44C0 (10000 ETH)
Private Key: 0xde9be858da4a475276426320d5e9262ecfc3ba460bfac56360bfa6c4c28b4ee0

Account #19: 0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199 (10000 ETH)
Private Key: 0xdf57089febbacf7ba0bc227dafbffa9fc08a93fdc68e1e42411a14efcf23656e

WARNING: These accounts, and their private keys, are publicly known.
Any funds sent to them on Mainnet or any other live network WILL BE LOST.

eth_blockNumber (2)
eth_call
  WARNING: Calling an account which is not a contract
  From: 0x491ecb84868b5233168a826d6755c2193f0332f3
  To:   0xf02d6e666e309e75108f4676d3b99af52678d766

```

このコマンドを実行することによって，模擬的なEthereumネットワークをローカルに起動することができます．

このネットワークにスマートコントラクトをデプロイしていきます．擬似的な環境なのでガス代がかかる心配はありせん

ネットワークを起動したターミナルは消さないでいてください．

### `deploy.ts`の作成

scriptsにdeploy.tsを作成してデプロイを行うためのプログラムを書いていきます．

```scripts/deploy.ts  
import { ethers } from "hardhat";

const main = async () => {

    const [deployer] = await ethers.getSigners();
    const accountBalance = await deployer.provider.getBalance(deployer.address);
    console.log("Deploying contracts with the account:", deployer.address);
    console.log("Deployer account balance:", accountBalance.toString());



    const fileTimestampContract = await ethers.deployContract("FileTimestamp");
    await fileTimestampContract.waitForDeployment();
    console.log("FileTimestamp deployed to:", fileTimestampContract.target);
    console.log("Deployer account balance:", accountBalance.toString());

    const now_timestamp = Date.now();

    const setTx = await  fileTimestampContract.setTimestamp(0, now_timestamp);
    await setTx.wait();

    const timestamp = await fileTimestampContract.getTimestamp(0);


    if (timestamp != BigInt(now_timestamp)) {
        throw new Error("Timestamps do not match");
    } else {
        console.log("Timestamps match");
    }


}

const runMain = async () => {
    try {
        await main();
        process.exit(0);
    } catch (error) {
        console.error(error);
        process.exit(1);
    }

};

runMain();

```

内容は `run.ts`とほぼ一緒です

### デプロイする

`package.json`にデプロイ用のコマンドを書き加えます

```
...package.json
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start":"npx hardhat node",
    "run:script" : "npx hardhat run scripts/run.ts",
    "deploy:localhost": "npx hardhat run scripts/deploy.ts --network localhost",
  },
...

```
続いてこのコマンドを打ちます

```
npm run deploy:localhost
```

```

> file-timestamp-app@1.0.0 deploy:localhost
> npx hardhat run scripts/deploy.ts --network localhost

Deploying contracts with the account: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
Deployer account balance: 10000000000000000000000
FileTimestamp deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3
Deployer account balance: 10000000000000000000000
Timestamps match
```

```
eth_blockNumber (2)
eth_call
  WARNING: Calling an account which is not a contract
  From: 0x491ecb84868b5233168a826d6755c2193f0332f3
  To:   0xf02d6e666e309e75108f4676d3b99af52678d766

eth_accounts
hardhat_metadata (20)
eth_getBalance
eth_accounts
hardhat_metadata (20)
eth_blockNumber
eth_getBlockByNumber
eth_feeHistory
eth_maxPriorityFeePerGas
eth_sendTransaction
  Contract deployment: FileTimestamp
  Contract address:    0x5fbdb2315678afecb367f032d93f642f64180aa3
  Transaction:         0xf2746fe6736d6b90b627c5bfbe46589acd3c368d33a64443b4042966fc061a41
  From:                0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
  Value:               0 ETH
  Gas used:            285411 of 30000000
  Block #1:            0xe908341ae5451cdaee8c8fca04aecf63831505216f7047072d410d4df4ffb38b

  console.log:
    Deploying a  contract

eth_getTransactionByHash
eth_getTransactionReceipt
eth_blockNumber (2)
eth_feeHistory
eth_sendTransaction
  Contract call:       FileTimestamp#setTimestamp
  Transaction:         0x903fb61eefa30ce9cb2c76a5077505b574c5b14d059caa180f5c42681960d07a
  From:                0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
  To:                  0x5fbdb2315678afecb367f032d93f642f64180aa3
  Value:               0 ETH
  Gas used:            44286 of 30000000
  Block #2:            0x5530d3c1211cb6928e33b17c40961de14728b4512a7179624c6ad897110fdf2d

eth_getTransactionByHash
eth_call
  Contract call:       FileTimestamp#getTimestamp
  From:                0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
  To:                  0x5fbdb2315678afecb367f032d93f642f64180aa3
```



## テストネットにデプロイ

実際のプロックチェーンにコントラクトをデプロイする準備をします.

### MetaMaskに登録する

MetaMaskをブラウザの拡張機能に追加します．
https://metamask.io/download/

MetaMaskはイーサリアムなどのブロックチェーンに対応した仮想通貨ウォレットです．拡張機能版の他にモバイル版もあります．

イーサリアムやトークンを管理する機能があり，今回はコントラクトをデプロイしたりコントラクトを利用するために使用します．

### Alchemyに登録，ネットワークを作成

Alchemyはブロックチェーン開発者向けのプラットフォームで，DAppの開発を支援するインフラを提供しています．
アカウントを作成してください．
https://www.alchemy.com/

Alchemyでプロジェクトを作成します．


1. Alchemyのアカウントを作成，サインイン
2. 「Apps」ページから「Create new app」
3. 「1. Create new app」では名前と目的を適当に入力  
4. 「2. Choose chains」は Ethereumを選択してNext
5. 「3. Activate default services」はデフォルトでNext
6. NetworkをSepoliaにします．
7. Network URLをあとで使います

今回はSepoliaというEthereumのテストネットにデプロイします．
テストネットは定期的にリセットされるので仮のデプロイ先だと思ってください．

### Sepoliaのfaucetを入手する

Sepoliaテストネットで使う擬似的なETHを入手します．「Sepolia faucet」などで検索すれば少額のETHを配布しているサービスがたくさんあります．
今回はhttps://cloud.google.com/application/web3/faucet/ethereum/sepoliaを使います

MetaMaskに表示されているアドレスを入力してfaucetを入手します．

## API・秘密鍵の設定

### `.env`の作成

`.gitignore`ファイルに`.env`が設定されていることを確認して`.env`を作成します．

```
touch .env
```

`.env`ファイルを扱うdotenvパッケージを追加します．

```
npm install dotenv
```

`.env`ファイルに設定を記述していきます

```
ALCHEMY_API_URL = ここにAlchemyのAPI URLを貼り付ける
PRIVATE_SEPOLIA_ACCOUNT_KEY = ここにMetaMaskから取得した秘密鍵を貼り付ける
```

### ` hardhat.config.ts`ファイルを編集

```
import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";
require("dotenv").config();

const config: HardhatUserConfig = {
  solidity: "0.8.28",
  networks: {
    sepolia: {
      url: process.env.ALCHEMY_API_URL ?? "",
      accounts: [process.env.PRIVATE_SEPOLIA_ACCOUNT_KEY ?? ""],
    }
  }
};

export default config;

```
### package.jsonを編集

```
  "scripts": {
    "run:script": "npx hardhat run scripts/run.ts",
    "test": "npx hardhat test",
    "deploy": "npx hardhat run scripts/deploy.ts --network sepolia",
    "deploy:localhost": "npx hardhat run scripts/deploy.ts --network localhost",
    "start": "npx hardhat node"
  },

```

デプロイします

```
npm run deploy
```

出力されたコントラクトアドレスはあとで使うのでメモしておいてください
https://sepolia.etherscan.io/
でアドレスを検索しトランザクションを確認できたら成功です

## フロントエンドの作成

### Next.jsの環境構築

Next.jsのプロジェクトを作成します．ターミナルで以下のコマンドを実行します

```
npx create-next-app@latest
```
```
Need to install the following packages:
create-next-app@15.1.4
Ok to proceed? (y) y

✔ What is your project named? … front
✔ Would you like to use TypeScript? … Yes
✔ Would you like to use ESLint? …  Yes
✔ Would you like to use Tailwind CSS? … Yes
✔ Would you like your code inside a `src/` directory? … Yes
✔ Would you like to use App Router? (recommended) … Yes
✔ Would you like to use Turbopack for `next dev`? … Yes
✔ Would you like to customize the import alias (`@/*` by default)? … No
Creating a new Next.js app in /Users/kubotadaichi/Dev/Solidity/File-Timestamp-App/front.

Using npm.

Initializing project with template: app-tw 

...
```



実行が完了するとfrotディレクトリが作成されているので移動してnext.jsプロジェクトを実行していきます．

```
cd front
npm run dev
```


```
> front@0.1.0 dev
> next dev --turbopack

   ▲ Next.js 15.1.4 (Turbopack)
   - Local:        http://localhost:3000
   - Network:      http://131.206.227.151:3000

 ✓ Starting...
```

http://localhost:3000にアクセスします

![alt text](image.png)

control + c で一旦終了して次の作業に移ります．

### ethers.jsのインストール

ethers.jsはethereumブロックチェーンと対話するためのJavaScriptのライブラリです．
ethers.jsはweb3.jsの代替として開発されより効率的で使いやすく設計されています．

```
npm install ethers
```

### コンポーネントの作成

1. Headerコンポーネント:

    アプリケーションのタイトルやナビゲーションリンクを表示します。

2. ConnectWalletコンポーネント:

    ユーザーがウォレットを接続するためのボタンを提供します。

3. SetTimestampコンポーネント:

    ユーザーがハッシュとタイムスタンプを入力し、setTimestamp関数を呼び出すためのフォームを提供します。

4. GetTimestampコンポーネント:

    ユーザーがハッシュを入力し、getTimestamp関数を呼び出してタイムスタンプを取得するためのフォームを提供します。

5. DisplayTimestampコンポーネント:

    取得したタイムスタンプを表示します。

6. Footerコンポーネント:

    フッター情報を表示します。

```
mkdir src/components                            
touch src/components/Header.tx                  
touch src/components/ConnectWallet.tx           
touch src/components/SetTimestamp.tx            
touch src/components/GetTimestamp.tx            
touch src/components/DisplayTimestamp.tx        
touch src/components/Footer.tx  
```

一旦ヘッダーとフッターとpage.tsxを次のように編集します．

```Header.tsx
import React from 'react';

const Header = () => {
    return (
        <header>
            <h1>File Timestamp DApp</h1>
        </header>
    );
};

export default Header;
```

```Footer.tsx
import React from 'react';

const Footer = () => {
    return (
        <footer>
            <p>&copy; 2025 File Timestamp DApp</p>
        </footer>
    );
};

export default Footer;


```
```page.tsx
"use client";

import React, { useState } from 'react';
import { ethers } from 'ethers';
import Header from '../components/Header';
import ConnectWallet from '../components/ConnectWallet';
import SetTimestamp from '../components/SetTimestamp';
import GetTimestamp from '../components/GetTimestamp';
import DisplayTimestamp from '../components/DisplayTimestamp';
import Footer from '../components/Footer';


const App = () => {

    return (
        <div>
            <Header />
            <Footer />
        </div>
    );
};

export default App;
```


`/front/src/app/globals.css`の中身を消し

```
npm run dev
```

![alt text](image-1.png)

## Webアプリからスマートコントラクトを呼び出す

まずはウォレットの接続部分を作っていきます

`page.tsx`
```page.tsx
...

const App = () => {

    const [currentAccount, setCurrentAccount] = useState<ethers.Signer | null>(null);


    return (
        <div>
            <Header />
            <ConnectWallet setCurrentAccount={setCurrentAccount} />
            {currentAccount && (
                <div>
                    <p> Current Account: {currentAccount?.toString()}</p>
                </div>
            )}
            <Footer />
        </div>
    );
};

export default App;
```
`ConnectWallet.tsx`
```ConnectWallet.tsx
import React from 'react';
import { ethers } from 'ethers';

const ConnectWallet = ({ setCurrentAccount }) => {



    const connectWallet = async () => {
        try {
            const { ethereum } = window;
            if (!ethereum) {
                alert('Please install MetaMask!');
                return;
            } 

            const accounts = await ethereum.request({
                method: 'eth_requestAccounts' 
            }) as string[];
            console.log("Connected: "+ accounts[0]);
            setCurrentAccount(accounts[0]);
        } catch (error) {
            console.log(error);
        }
    };

    return (
        <button onClick={connectWallet}>Connect Wallet</button>
    );
};

export default ConnectWallet;
```
ボタンを押して接続するとアドレスが表示されます

![alt text](image-2.png)

### SetTimestanp

続いてコントラクトにアクセスしてタイムスタンプを登録する部分を作成していきます

コントラクトを取得します
`page.tsx`
```
import React, { useState, useEffect } from 'react';

const App = () => {

    const [currentAccount, setCurrentAccount] = useState<ethers.Signer | null>(null);
    const [contract, setContract] = useState<ethers.Contract | null>(null);

    const contractAddress = "";
    const contractABI = [];

    const getContract = async () => {
        if (!currentAccount) return;

        const provider = new ethers.providers.Web3Provider(currentAccount.provider);
        const signer = provider.getSigner();
        const contract = new ethers.Contract(
            contractAddress, 
            contractABI, 
            signer);
        setContract(contract);
    };

    useEffect(() => {
        getContract();
    }, [currentAccount]);

...
```
contractAddress = "0xb79016ed80cdf217cba5ffbdd0a5ad7f4f83505bfb0b88f355867115d00c93ae"

### ABIファイルを取得する

`artifacts/contracts/FileTimestamp.sol/FileTimestamp.json`
を
`File-Timestamp-App/front/src/app/utils`にコピーします
```

```
 cp artifacts/contracts/FileTimestamp.sol/FileTimestamp.json  front/src/app/utils 
 ```

```

import abi from "./utils/FileTimestamp.json";

const App = () => {

    const [currentAccount, setCurrentAccount] = useState<ethers.Signer | null>(null);
    const [contract, setContract] = useState<ethers.Contract | null>(null);

    const contractAddress = "0xb79016ed80cdf217cba5ffbdd0a5ad7f4f83505bfb0b88f355867115d00c93ae";
    const contractABI = abi.abi;

```

```

`SetTimestamp.tsx`
```
import React, { useState } from 'react';
import { ethers } from 'ethers';


const GetTimestamp = ({ contract }) => {
    const [file, setFile] = useState<File | null>(null);
    const [timestamp, setTimestamp] = useState<string | null>(null);

    const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
        if (e.target.files && e.target.files.length > 0) {
            setFile(e.target.files[0]);
        }
    };

    const handleSubmit = async (e: React.FormEvent) => {
        e.preventDefault();
        if (!file) return;

        const fileBuffer = await file.arrayBuffer();
        const hash = ethers.keccak256(new Uint8Array(fileBuffer));

        const ts = Date.now();

        try {
            const timestamp = await contract.setTimestamp(hash, ts);
            console.log("Mining transaction...", timestamp);
            await timestamp.wait();
            console.log("Transaction mined!");
            setTimestamp(timestamp.toString());
        } catch (error) {
            console.log("Error fetching timestamp:", error);
            setTimestamp(null);
        }
    };

    return (
        <div>
            <form onSubmit={handleSubmit}>
                <input type="file" onChange={handleFileChange} required />
                <button type="submit">Get Timestamp</button>
            </form>
            {timestamp && <p>Timestamp: {timestamp}</p>}
        </div>
    );
};

export default GetTimestamp;
```

## Vercelにアプリをホストする

誠意執筆中


# 参考

https://zenn.dev/lycp152/books/bb2010b7cb37f9/viewer/8daa73