# Hardhat 教學

類似 Truffle 為一個智能合約開發 SDK。

{% embed url="<https://hardhat.org/>" %}

### 安裝與初始化

```
mkdir hardhat-test && cd hardhat-test
npx hardhat init
```

### 專案結構

![](https://3415061728-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M0u4DA5jITnh37VYXmj%2F-MjXUfOPk-TalwvKSdKO%2F-MjXUnue3fMxqBD7HP_B%2F%E6%88%AA%E5%9C%96%202021-09-14%20%E4%B8%8B%E5%8D%8812.09.39.png?alt=media\&token=50c78181-e12d-4435-9a71-898705d9951e)

必須照著官方規定取名資料夾，分別為放合約以及部署script，與測試檔案，以及網路等 config。

<https://hardhat.org/guides/project-setup.html#sample-hardhat-project>

### 在本地測試與部屬

1. 啟動本地節點

> \[可選] 如果沒有啟動節點與指定節點，預設都會使用 in-memory instance of Hardhat Network

```
npx hardhat node
```

2.部署合約

```
npx hardhat run --network localhost scripts/deploy.js
```

3.使用 console 測試功能

```
npx hardhat console --network localhost
```

之後輸入

```
const Greeter = await ethers.getContractFactory("Greeter");
```

```
const greeter = await Greeter.attach(<剛才部署本地的合約地址>);
```

```
await greeter.greet()
```

### 寫測試檔案

test/test.js

```javascript
const { expect } = require("chai");
const { ethers } = require("hardhat");

describe("Greeter12", function () {
  it("Should return the new greeting once it's changed", async function () {
    
    const Greeter = await ethers.getContractFactory("Greeter");
    // const greeter = await Greeter.deploy("Hello, world!");
    // await greeter.deployed();

    //expect(await greeter.greet()).to.equal("Hello, world!");
    const greeter = await Greeter.attach("0x5FbDB2315678afecb367f032d93F642f64180aa3");

    const setGreetingTx = await greeter.setGreeting("Hola, mundo!");

    // wait until the transaction is mined
    await setGreetingTx.wait();
    const a = await greeter.greet()
    //console.log(a)

    expect(await greeter.greet()).to.equal("Hola, mundo!");
  });
});

```

之後輸入

```
npx hardhat test --network localhost
```

## Artifacts

部署合約或 compile 後會產生此資料夾，裡面包含一些 json 檔案，為合約的 ABI

{% embed url="<https://hardhat.org/guides/compile-contracts.html#compiling-your-contracts>" %}

## 使用 Mainnet fork 測試

> 使用 mainnet 特定 block data 來測試合約
>
> <https://hardhat.org/hardhat-network/docs/guides/forking-other-networks>

設置 hardhat.config.js 即可

```javascript
require("@nomicfoundation/hardhat-toolbox");

/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
  solidity: "0.8.28",
  networks: {
      hardhat: {
          forking: {
              url: "https://eth-mainnet.g.alchemy.com/v2/<API KEY>",
          },
      },
  },
};
```

之後可獲取當前主網地址餘額

貼上以下檔案到 /test 後，輸入 \`npx hardhat test\`

```javascript
const { expect } = require("chai");
const { ethers } = require("hardhat"); // Import ethers from Hardhat

describe("Query Account Balance on Forked Mainnet", function () {
    it("should fetch the balance of the specified account", async function () {
        // Replace this with the address you want to query
        const targetAddress = "...";

        // Use ethers to get the balance
        const balance = await ethers.provider.getBalance(targetAddress);

        // Log the balance for debugging
        console.log(`Balance of ${targetAddress}: ${balance} ETH`);

        // Verify that the balance is returned as a BigNumber
        expect(balance).to.be.a("BigInt");
    });
});
```

或是以下獲取主網特定地址的 USDC 餘額

```javascript
const { ethers } = require("hardhat");
const { expect } = require("chai");

describe("USDC Balance Check", function () {
  it("Should return the USDC balance of the address", async function () {
    const usdcAddress = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; // USDC Contract Address
    const addressToCheck = "..."; // Address whose balance you want to check

    // USDC Contract ABI (Simplified; use the full ABI from Etherscan)
    const usdcAbi = ["function balanceOf(address owner) view returns (uint256)"];
    
    // Connect to the USDC contract
    const usdcContract = new ethers.Contract(usdcAddress, usdcAbi, ethers.provider);

    // Fetch the balance
    const balance = await usdcContract.balanceOf(addressToCheck);

    // Log the balance (optional)
    console.log("USDC Balance:", balance.toString());


    // 也可手動改變地址 ETH 餘額
    const mainnetAccount = addressToCheck; // Replace with a mainnet account address
    await hre.network.provider.request({
      method: "hardhat_impersonateAccount",
      params: [mainnetAccount],
    });
    await hre.network.provider.send("hardhat_setBalance", [
      mainnetAccount,
      "0x8AC7230489E80000000", // 40960 ETH
    ]);

    const ethBalance = await ethers.provider.getBalance(addressToCheck);

    // Log the balance (optional)
    console.log("ETH Balance:", ethers.formatEther(ethBalance), "ETH");

    // Assert (for example, checking if the balance is greater than a certain amount)
    expect(balance).to.be.gt(ethers.parseUnits("100", 6)); // Check if balance is greater than 100 USDC
  });
});
```

## 測試檔案內設置區塊時間

```typescript
const { ethers } = require("hardhat");

// Fast forward time by 7 days for testing
await ethers.provider.send("evm_increaseTime", [7 * 24 * 60 * 60]);
await ethers.provider.send("evm_mine");
```

## 部署合約

要先去 hardhat.config.ts 檔案設置網路設置

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

const config: HardhatUserConfig = {
  solidity: "0.8.24",
  networks: {
    someTestnet: {
      url: "<rpc url>",
      accounts: [`${process.env.PRIVATE_KEY}`],
      gasPrice: 160000000000, // 160 GWei (部署的 Gas Price)
    }
  }
};

export default config;
```

部署腳本 scripts/deploy.ts

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

async function main() {
  const EthStaker = await ethers.deployContract("EthStaker");

  await EthStaker.waitForDeployment();

  console.log(
    `EthStaker deployed to ${EthStaker.target}`
  );
}

// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});
```

之後輸入

```
npx hardhat run scripts/deploy.ts --network someTestnet
```

<https://github.com/wighawag/hardhat-deploy#hardhat-deploy-in-a-nutshell>

## Etherscan 驗證合約

設置hardhat config

> 不同網路有不同的 api key

```typescript
etherscan: {
  //apiKey: process.env.ETHERSCAN_API_KEY,
  apiKey: process.env.POLYGONSCAN_API_KEY,
},
```

之後輸入以下

```
npx hardhat verify --network <網路名稱> <要驗證的合約地址> <建構子參數>
```

{% embed url="<https://hardhat.org/plugins/nomiclabs-hardhat-etherscan.html>" %}

## 測試單一檔案

可使用：<https://hardhat.org/plugins/hardhat-watcher.html>

會監聽檔案改變，自動跑測試，並且可在 config 寫要執行哪個檔案

## 相關連結

範例專案：<https://github.com/symfoni/hardhat-react-boilerplate>

## 常見問題

1.Error: call revert exception

```
通常是直接使用 hardhat vm 部署與測試合約所產生，可改用 hardhat localhost node 試試
```

2.Error HH12: Trying to use a non-local installation of Hardhat

{% embed url="<https://stackoverflow.com/a/69973620/4622645>" %}

3.TypeError: unsupported addressable value (argument="target", value=null, code=INVALID\_ARGUMENT, version=6.11.1)

v6 之後要用 .target 取代 .address

```typescript
SimplifiedNFT1155 = await ethers.getContractFactory("SimplifiedNFT1155");
simplifiedNFT1155 = await SimplifiedNFT1155.deploy("baseURI/");

NFTStaker = await ethers.getContractFactory("NFTStaker");
nftStaker = await NFTStaker.deploy(simplifiedNFT1155.target);
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://easonwang.gitbook.io/blockchain/smart/hardhat-jiao-xue.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
