以太坊開發工具完整指南

從開發環境搭建到部署上線,系統介紹 Hardhat、Foundry、調試工具、測試框架等實務內容。

以太坊開發工具完整指南:從環境搭建到部署上線

概述

以太坊智慧合約開發是一個涉及多個工具和流程的複雜工程。從本地開發環境的搭建、智慧合約的編寫和調試、自動化測試的執行、到最終的部署上線,每個環節都有專門的工具支援。本文系統性地介紹以太坊開發的工具生態系,涵蓋開發環境、調試工具、測試框架、部署流程等實務內容,幫助開發者建立完整的開發工作流。

本文適合已經掌握程式設計基礎、對區塊鏈有基本了解的開發者。無論你是從傳統軟體開發轉型而來,還是 Web2 開發者想要擴展到 Web3,這篇文章都會幫助你快速上手以太坊開發。

開發環境搭建

開發環境的選擇

以太坊智慧合約主要使用 Solidity 語言編寫,這是一種類似 JavaScript 的高階語言,專為智慧合約設計。開發 Solidity 合約需要以下基礎設施:

Node.js 環境:以太坊開發工具大多基於 Node.js構建。安裝 Node.js 的推薦方式是使用 nvm(Node Version Manager),因為不同專案可能需要不同版本的 Node.js。

# 安裝 nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash

# 安裝 Node.js LTS 版本
nvm install --lts
nvm use --lts

# 驗證安裝
node --version  # 應該顯示 v18.x.x 或更高
npm --version

Git:版本控制是現代軟體開發的必備工具。

# Ubuntu/Debian
sudo apt install git

# macOS
brew install git

# 驗證
git --version

程式碼編輯器配置

Visual Studio Code(VS Code)是以太坊開發最受歡迎的編輯器。建議安裝以下擴展:

Solidity 擴展

輔助擴展

VS Code 配置示例(settings.json):

{
  "solidity.compileUsingRemoteVersion": "v0.8.20",
  "solidity.defaultCompiler": "remote",
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "prettier.requireConfig": true
}

Hardhat 開發框架

Hardhat 是目前最主流的以太坊開發框架,提供本地測試網、編譯、調試、部署等功能。

安裝 Hardhat

# 建立專案目錄
mkdir my-eth-project
cd my-eth-project

# 初始化 npm 專案
npm init -y

# 安裝 Hardhat
npm install --save-dev hardhat

# 初始化 Hardhat 配置
npx hardhat init

選擇「Create a JavaScript project」或「Create a TypeScript project」,Hardhat 會自動生成專案結構:

my-eth-project/
├── contracts/           # 智慧合約原始碼
│   └── Lock.sol
├── scripts/             # 部署腳本
│   └── deploy.js
├── test/                # 測試檔案
│   └── Lock.js
├── hardhat.config.js    # Hardhat 配置
└── package.json

Hardhat 配置詳解(hardhat.config.js):

require("@nomicfoundation/hardhat-toolbox");
require("@nomiclabs/hardhat-ethers");

/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
  solidity: {
    version: "0.8.20",
    settings: {
      optimizer: {
        enabled: true,
        runs: 200
      }
    }
  },
  networks: {
    hardhat: {
      chainId: 31337
    },
    localhost: {
      url: "http://127.0.0.1:8545"
    },
    sepolia: {
      url: process.env.SEPOLIA_RPC_URL,
      accounts: [process.env.PRIVATE_KEY]
    }
  },
  etherscan: {
    apiKey: process.env.ETHERSCAN_API_KEY
  }
};

本地測試網絡

Hardhat 內建了一個本地 Ethereum 網絡稱為 Hardhat Network。這是一個內存中的測試網絡,啟動速度快,每次重啟都是全新的乾淨狀態。

啟動本地網絡

npx hardhat node

這會啟動一個本地節點,預設監聽 http://localhost:8545,並生成 20 個測試帳戶,每個帳戶有 10000 ETH 的測試資金。

連接到本地網絡

在 JavaScript/TypeScript 中使用 ethers.js 連接:

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

async function main() {
  // 連接到本地節點
  const provider = new ethers.JsonRpcProvider("http://localhost:8545");

  // 獲取帳戶列表(Hardhat node 啟動時會顯示)
  const [signer] = await ethers.getSigners();

  console.log("Connected account:", signer.address);

  // 餘額查詢
  const balance = await provider.getBalance(signer.address);
  console.log("Balance:", ethers.formatEther(balance), "ETH");
}

main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });

其他開發框架

Foundry:Foundry 是由 Paradigm 推出的高性能開發框架,使用 Rust 編寫,特點是測試執行速度極快。

# 安裝 Foundry
curl -L https://foundry.paradigm.xyz | bash
foundryup

# 初始化專案
forge init my-project

Foundry 的優勢:

Truffle:較早期的開發框架,現在維護頻率較低,但仍有不少 legacy 專案使用。

npm install -g truffle
truffle init

智慧合約編寫

Solidity 語言基礎

Solidity 是以太坊智慧合約的主要編程語言。理解其核心概念對開發安全合約至關重要。

基本合約結構

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract SimpleStorage {
    // 狀態變量
    uint256 private storedData;

    // 事件
    event DataStored(uint256 value, address indexed owner);

    // 修飾器
    modifier onlyOwner() {
        require(msg.sender == owner, "Not owner");
        _;
    }

    address public owner;

    // 構造函數
    constructor() {
        owner = msg.sender;
    }

    // 函數
    function set(uint256 x) public {
        storedData = x;
        emit DataStored(x, msg.sender);
    }

    function get() public view returns (uint256) {
        return storedData;
    }
}

資料類型

函數可見性

常用程式庫

OpenZeppelin:智慧合約開發的必備庫,提供經過審計的安全實現。

npm install @openzeppelin/contracts

常用合約示例:

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract MyToken is ERC20, Ownable {
    constructor() ERC20("MyToken", "MTK") Ownable(msg.sender) {
        // 鑄造初始供應量
        _mint(msg.sender, 1000000 * 10 ** decimals());
    }

    function mint(address to, uint256 amount) public onlyOwner {
        _mint(to, amount);
    }

    function burn(uint256 amount) public {
        _burn(msg.sender, amount);
    }
}

OpenZeppelin 提供的模組包括:

編譯與錯誤處理

編譯合約

npx hardhat compile

編譯成功後,artifacts 目錄會生成合約的 ABI 和位元組碼。

常見編譯錯誤

  1. Type Error:類型不匹配
   // 錯誤:嘗試將 address 賦值給 uint256
   uint256 x = msg.sender;

   // 正確:使用 address 類型
   address x = msg.sender;
  1. Visibility Error:可見性錯誤
   // 錯誤:狀態變量預設是 internal,不能從外部訪問
   uint256 private data;

   // 需要添加 getter 或改為 public
   uint256 public data;
  1. Stack Too Deep:棧溢出

當函數內部使用過多局部變量時會出現。可以通過將相關變量打包成 struct 來解決。

調試工具

Hardhat 調試功能

Hardhat 提供了強大的調試功能,幫助開發者定位問題。

Stack Traces

當交易失敗時,Hardhat 會顯示詳細的堆棧追蹤,定位到具體的源代碼行:

npx hardhat test

失敗輸出示例:

Error: Transaction reverted without a reason
      at SimpleStore.set (contracts/SimpleStore.sol:16)
      at processTicksAndRejections (node:internal/process/task_queues:95)

Console.log

Solidity 0.8.20+ 支持在合約中使用 console.log:

import "hardhat/console.sol";

function set(uint256 x) public {
    console.log("Setting value to:", x);
    storedData = x;
    console.log("Value set successfully");
}

Ethers.js 與合約交互

ethers.js 是與以太坊網絡交互的主要庫。

讀取合約

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

async function readContract() {
  // 合約地址(在部署後獲得)
  const contractAddress = "0x...";

  // ABI(編譯後生成)
  const abi = [
    "function get() view returns (uint256)",
    "function storedData() view returns (uint256)"
  ];

  // 創建合約實例
  const contract = new ethers.Contract(contractAddress, abi, provider);

  // 調用 view 函數(不需要簽名)
  const value = await contract.get();
  console.log("Stored value:", value);
}

寫入合約

async function writeContract() {
  const [signer] = await ethers.getSigners();

  const contract = new ethers.Contract(contractAddress, abi, signer);

  // 發送交易
  const tx = await contract.set(42);
  console.log("Transaction sent:", tx.hash);

  // 等待交易確認
  const receipt = await tx.wait();
  console.log("Transaction confirmed in block:", receipt.blockNumber);
}

Tenderly 進階調試

Tenderly 是一個專業的智慧合約監控和調試平台。

功能特點

  1. 交易模擬:在實際部署前模擬交易執行
  2. 調試追蹤:視覺化交易執行過程
  3. Gas 分析:優化 Gas 消耗
  4. 監控告警:即時通知合約事件

使用示例

const { tenderly } = require("@tenderly/hardhat-tenderly");

// 配置 Tenderly
tenderly.init({
  username: "your-username",
  project: "your-project",
  accessKey: "your-access-key"
});

// 模擬交易
const simulation = await tenderly.simulate(
  contractAddress,
  "set",
  [42],
  { from: userAddress }
);

常見錯誤與解決方案

交易失敗常見原因

  1. Gas 不足
   // 指定 Gas 限制
   const tx = await contract.set(42, { gasLimit: 100000 });
  1. Nonce 錯誤
   // 獲取當前 Nonce
   const nonce = await signer.getNonce();
   const tx = await contract.set(42, { nonce: nonce });
  1. Revert 錯誤
   try {
     await contract.set(42);
   } catch (error) {
     // 解析 revert 原因
     if (error.data) {
       const reason = ethers.toUtf8String(error.data.slice(10));
       console.log("Revert reason:", reason);
     }
   }

測試框架

測試的重要性

智慧合約一旦部署就很難修改,任何漏洞都可能導致巨大的資金損失。充分的測試是確保合約安全的第一道防線。

測試覆蓋率目標

Hardhat 測試框架

Hardhat 使用 Mocha 測試框架,結合 Chai 斷言庫。

基本測試結構

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

describe("SimpleStorage", function() {
  let contract;
  let owner;
  let otherAccount;

  // 部署合約
  beforeEach(async function() {
    const SimpleStorage = await ethers.getContractFactory("SimpleStorage");
    contract = await SimpleStorage.deploy();

    [owner, otherAccount] = await ethers.getSigners();
  });

  describe("Deployment", function() {
    it("should set the right owner", async function() {
      expect(await contract.owner()).to.equal(owner.address);
    });

    it("should have initial value of 0", async function() {
      expect(await contract.get()).to.equal(0);
    });
  });

  describe("set()", function() {
    it("should store the value", async function() {
      const tx = await contract.set(42);
      await tx.wait();

      expect(await contract.get()).to.equal(42);
    });

    it("should emit event", async function() {
      const tx = await contract.set(42);
      const receipt = await tx.wait();

      const event = receipt.logs[0];
      expect(event.args.value).to.equal(42);
      expect(event.args.owner).to.equal(owner.address);
    });
  });
});

運行測試

# 運行所有測試
npx hardhat test

# 運行特定測試檔案
npx hardhat test test/SimpleStorage.js

# 運行特定 describe 區塊
npx hardhat test --grep "should store the value"

Foundry 測試

Foundry 的 Forge 測試框架以其極快的執行速度著稱。

Solidity 測試

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "forge-std/Test.sol";

contract SimpleStorageTest is Test {
    SimpleStorage public simpleStorage;

    function setUp() public {
        simpleStorage = new SimpleStorage();
    }

    function testSetValue() public {
        simpleStorage.set(42);
        assertEq(simpleStorage.get(), 42);
    }

    function testOnlyOwner() public {
        vm.prank(address(1));
        vm.expectRevert();
        simpleStorage.set(100);
    }
}

運行測試

# 運行測試
forge test

# 顯示詳細輸出
forge test -vvvv

# 生成覆蓋率報告
forge coverage

進階測試技術

不變量測試(Invariant Testing)

不變量測試透過隨機化輸入來驗證合約狀態機的正確性。

contract Handler is Test {
    SimpleStorage public target;

    function callSet(uint256 val) public {
        target.set(val);
    }

    // 定義不變量
    function invariantValueNotNegative() public view {
        assert(target.get() >= 0);
    }
}

contract InvariantTest is Test {
    function testInvariants() public {
        Handler handler = new Handler();
        target.honeypot(handler);

        handler.run(1000);
    }
}

模糊測試(Fuzz Testing)

function testFuzzSetValue(uint256 val) public {
    simpleStorage.set(val);
    assertEq(simpleStorage.get(), val);
}

Foundry 會自動生成大量隨機輸入來測試這個函數,發現邊界情況。

測試最佳實踐

  1. AAA 模式:Arrange(準備)、Act(執行)、Assert(斷言)
  2. 獨立性:每個測試應該獨立運行,不依賴其他測試的狀態
  3. 命名規範:清晰的測試名稱描述預期行為
  4. 邊界測試:測試零值、最大值、邊界條件
  5. 負面測試:測試錯誤輸入、權限檢查、Revert 情況

部署流程

部署到測試網絡

在部署到主網之前,應該先在測試網絡上驗證。

常見測試網絡

獲取測試 ETH

  1. 水龍頭網站(如 Sepolia Faucet)
  2. 質押節點獎勵
  3. 中心化交易所測試網充值

部署腳本(scripts/deploy.js):

const hre = require("hardhat");

async function main() {
  console.log("Deploying SimpleStorage...");

  const SimpleStorage = await hre.ethers.getContractFactory("SimpleStorage");
  const contract = await SimpleStorage.deploy();

  await contract.waitForDeployment();
  const address = await contract.getAddress();

  console.log("SimpleStorage deployed to:", address);

  // 驗證合約(需要 Etherscan API Key)
  if (process.env.ETHERSCAN_API_KEY) {
    try {
      await hre.run("verify:verify", {
        address: address,
        constructorArguments: []
      });
      console.log("Contract verified on Etherscan");
    } catch (error) {
      console.log("Verification failed:", error.message);
    }
  }
}

main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });

部署命令

# 部署到本地網絡
npx hardhat run scripts/deploy.js --network localhost

# 部署到 Sepolia
npx hardhat run scripts/deploy.js --network sepolia

部署到主網

部署到以太坊主網需要實際的 ETH 作為 Gas 費用。

環境變量配置

# .env 文件
SEPOLIA_RPC_URL=https://sepolia.infura.io/v3/YOUR_PROJECT_ID
PRIVATE_KEY=0xyour_private_key
ETHERSCAN_API_KEY=your_etherscan_api_key

# 載入環境變量
npm install dotenv

在 hardhat.config.js 中引入:

require("dotenv").config();

部署檢查清單

  1. 測試網絡驗證完成
  2. 合約審計通過
  3. Gas 費用估算合理
  4. 多重簽名或時間鎖配置(如果是治理合約)
  5. 緊急暫停機制測試
  6. 合約地址記錄和備案

合約驗證

部署後應該在 Etherscan 上驗證合約原始碼。

手動驗證

  1. 打開 Etherscan 合約頁面
  2. 點擊「Verify and Publish」
  3. 填入編譯器版本和優化設置
  4. 粘貼合約原始碼
  5. 點擊「Verify」

自動驗證(Hardhat):

npx hardhat verify --network mainnet DEPLOYED_CONTRACT_ADDRESS

主網部署後的注意事項

  1. 記錄合約地址:在多個安全的地方記錄
  2. 初始化参数:如果需要初始化,盡快執行
  3. 權限檢查:確認所有者權限正確設置
  4. 初始餘額:確保有足夠的 ETH 支付未來 Gas
  5. 監控設置:配置 Tenderly 等監控工具

自動化與 CI/CD

GitHub Actions 集成

建立自動化部署流程可以減少人為錯誤。

# .github/workflows/test.yml
name: Test

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: '18'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run tests
        run: npx hardhat test

      - name: Run coverage
        run: npx hardhat coverage

自動化部署

# .github/workflows/deploy.yml
name: Deploy

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Install dependencies
        run: npm ci

      - name: Deploy to Sepolia
        run: npx hardhat run scripts/deploy.js --network sepolia
        env:
          SEPOLIA_RPC_URL: ${{ secrets.SEPOLIA_RPC_URL }}
          PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }}

開發工具生態系總覽

工具選擇指南

需求推薦工具
快速原型Hardhat
大規模測試Foundry
企業級支持Truffle + Ganache
形式化驗證Certora
調試追蹤Tenderly
Gas 優化Tenderly, OpenZeppelin Defender

學習路徑建議

  1. 第一階段:環境搭建 + 編寫第一個合約
  2. 第二階段:Hardhat 測試框架 + 單元測試
  3. 第三階段:部署到測試網絡 + Etherscan 驗證
  4. 第四階段:Foundry 進階測試 + 不變量測試
  5. 第五階段:安全審計 + 主網部署

結論

以太坊開發工具生態系已經相當成熟,從基礎的開發框架到專業的安全工具,各種需求都有對應的解決方案。掌握這些工具的使用方法,是成為合格智慧合約開發者的必經之路。

關鍵建議:

  1. 選擇一個框架深入:Hardhat 或 Foundry,兩者都能滿足大多數需求
  2. 重視測試:測試是安全的第一道防線,不要跳過
  3. 學會調試:善用 Hardhat Console 和 Tenderly
  4. 保持更新:以太坊工具更新頻繁,關注版本變化
  5. 安全優先:始終假設合約會被攻擊,設計相應的安全機制

常見問題

Hardhat 和 Foundry 應該選擇哪個?

兩者各有優勢。Hardhat 生態更成熟,文檔更完善,適合團隊協作。Foundry 測試速度極快,適合需要大量測試案例的項目。建議兩者都學習,根據專案需求選擇。

如何估算部署成本?

使用 Gas Reporter 插件:

npm install --save-dev hardhat-gas-reporter

配置後,每次部署和函數調用都會顯示預估成本。

合約審計是否必要?

對於涉及資金的合約,強烈建議進行專業審計。常見審計公司包括 Trail of Bits、OpenZeppelin、Certora 等。審計費用從數千到數十萬美元不等,取決於合約複雜度。

如何處理合約升級?

使用代理模式(Proxy Pattern):

// 部署邏輯合約
implementation = new LogicContract();

// 部署代理合約,指向邏輯合約
proxy = new UpgradeableProxy(implementation);

升級時只更新代理指向的新邏輯合約地址,用戶資金存儲在代理合約中不受影響。

延伸閱讀與來源

這篇文章對您有幫助嗎?

評論

發表評論

注意:由於這是靜態網站,您的評論將儲存在本地瀏覽器中,不會公開顯示。

目前尚無評論,成為第一個發表評論的人吧!