以太坊開發者本地開發環境完整指南:從零建置到部署調試

本文提供以太坊智慧合約開發者所需的完整本地開發環境建置指南,涵蓋開發語言選擇、本地區塊鏈網路、本地節點配置、除錯工具、測試框架選擇,以及完整的工作流程實踐。我們深入介紹 Hardhat、Foundry、Anvil 等主流開發工具的配置與使用,並提供測試框架比較、除錯技巧、持續整合配置等實務內容。

以太坊開發者本地開發環境完整指南:從零建置到部署調試

摘要

本文提供以太坊智慧合約開發者所需的完整本地開發環境建置指南。我們涵蓋開發語言選擇、本地區塊鏈網路、本地節點配置、除錯工具、測試框架選擇,以及完整的工作流程實踐。截至 2026 年第一季度,以太坊生態系統的工具鏈已經非常成熟,本地開發環境的建置比以往任何時候都更加簡便和高效。

第一章:開發環境總覽與規劃

1.1 必要的軟體元件

以太坊智慧合約開發需要以下核心元件:

節點軟體:用於連接以太坊網路的客戶端,提供區塊鏈數據讀寫能力。主流選擇包括 Geth、Reth、Nethermind、Besu。

開發框架:智慧合約編譯、部署、測試的自動化工具。主流選擇包括 Hardhat、Foundry、Remix IDE、Truffle。

程式碼編輯器:撰寫 Solidity 或 Vyper 合約的環境。強烈推薦 VS Code 配合 Solidity 擴展套件。

錢包與簽名工具:用於簽署交易和管理測試帳戶。MetaMask 用於瀏覽器交互,Hardhat/Foundry 內建測試帳戶。

1.2 作業系統與硬體要求

作業系統支援

硬體建議

1.3 開發語言選擇

Solidity 是以太坊智慧合約的主要程式語言,語法類似 JavaScript,學習曲線較為平緩。Solidity 擁有最大的生態系統支援、豐富的教程資源和活躍的開發者社區。

Vyper 是另一個選項,採用 Python 語法,強調可讀性和安全性。Vyper 故意省略了一些 Solidity 的特性(如繼承、修飾符)來減少安全風險。

Fe 是新興的合約語言,由以太坊基金會支持,採用 Rust 語法風格,強調記憶體安全和形式化驗證。

本指南將以 Solidity 為主要語言,因為它擁有最完整的工具生態。

第二章:基礎軟體安裝

2.1 Node.js 與 npm 安裝

Hardhat 和許多開發工具基於 Node.js 執行。建議使用 nvm(Node Version Manager)管理 Node.js 版本。

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

# 重啟終端後,安裝 Node.js LTS 版本
nvm install 20
nvm use 20
nvm alias default 20

# 驗證安裝
node --version  # 應該顯示 v20.x.x
npm --version   # 應該顯示 10.x.x

2.2 Rust 與 Foundry 安裝

Foundry 是高效能的智慧合約開發框架,需要 Rust 環境。

# 安裝 Rust(Linux/macOS)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# 初始化 Rust 環境
source $HOME/.cargo/env

# 驗證 Rust 安裝
rustc --version   # 應該顯示 1.75.x 或更高
cargo --version

# 使用 cargo 安裝 Foundry
cargo install --git https://github.com/foundry-rs/foundry foundryup anvil --locked

# 驗證 Foundry 安裝
forge --version
anvil --version
cast --version
chisel --version

2.3 Go 與 Geth 安裝

Geth 是最廣泛使用的以太坊客戶端,需要 Go 語言環境。

# 安裝 Go(以 Ubuntu 為例)
wget https://go.dev/dl/go1.21.5.linux-amd64.tar.gz
sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go1.21.5.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/bin
go version  # 應該顯示 go1.21.5

# 下載並編譯 Geth
git clone https://github.com/ethereum/go-ethereum.git
cd go-ethereum
make geth

# 將 Geth 加入 PATH
sudo ln -s /home/user/go-ethereum/build/bin/geth /usr/local/bin/geth
geth version

2.4 Docker 與開發容器

Docker 可以簡化依賴管理,提供一致的開發環境。

# 安裝 Docker
sudo apt-get update
sudo apt-get install docker.io docker-compose

# 啟動 Docker 服務
sudo systemctl start docker
sudo systemctl enable docker

# 將使用者加入 docker 群組
sudo usermod -aG docker $USER
# 重新登入後生效

第三章:本地區塊鏈網路建置

3.1 Hardhat Network 使用指南

Hardhat 內建的本地區塊鏈網路(Hardhat Network)是智慧合約開發的首選。

專案初始化

# 建立新專案目錄
mkdir my-ethereum-dapp
cd my-ethereum-dapp
npm init -y

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

# 初始化 Hardhat 專案
npx hardhat init
# 選擇 "Create a JavaScript project"
# 選擇 "Yes" 安裝依賴

Hardhat 配置文件

// hardhat.config.js
require("@nomicfoundation/hardhat-toolbox");
require("hardhat-deploy");

/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
  solidity: {
    version: "0.8.26",
    settings: {
      optimizer: {
        enabled: true,
        runs: 200
      },
      viaIR: true
    }
  },
  networks: {
    hardhat: {
      chainId: 31337,
      // 啟用本歷史模式,可以查詢歷史狀態
      forking: process.env.MAINNET_RPC_URL ? {
        url: process.env.MAINNET_RPC_URL,
        blockNumber: 19000000
      } : undefined
    },
    localhost: {
      url: "http://127.0.0.1:8545",
      chainId: 31337
    }
  },
  paths: {
    sources: "./contracts",
    tests: "./test",
    cache: "./cache",
    artifacts: "./artifacts"
  }
};

啟動本地区块链网络

# 啟動 Hardhat Network(自動創建測試帳戶)
npx hardhat node

# 輸出會顯示:
# Account #0: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (10000 ETH)
# Private Key: 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
# ...

在 Hardhat Network 上部署合約

// scripts/deploy.js
const hre = require("hardhat");

async function main() {
  // 獲取合約工廠
  const MyContract = await hre.ethers.getContractFactory("MyContract");
  
  // 部署合約
  const contract = await MyContract.deploy("Hello, Hardhat!");
  
  // 等待部署完成
  await contract.waitForDeployment();
  const address = await contract.getAddress();
  
  console.log("Contract deployed to:", address);
}

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

執行部署:

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

3.2 Anvil(Foundry)本地区块链

Foundry 的 Anvil 提供了更快、更輕量的本地区块链网络。

啟動 Anvil

# 啟動 Anvil,創建 10 個測試帳戶
anvil --accounts 10 --balance 100

# 輸出會顯示:
# Listening on 127.0.0.1:8545
# 
# Account 0: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (100 ETH)
# Private Key: 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80

使用 Cast 與合約交互

# 部署合約
forge create --rpc-url http://localhost:8545 \
  --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \
  contracts/MyContract.sol:MyContract

# 調用合約
cast send <CONTRACT_ADDRESS> "setValue(uint256)" 42 \
  --rpc-url http://localhost:8545 \
  --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80

# 讀取合約
cast call <CONTRACT_ADDRESS> "getValue()" \
  --rpc-url http://localhost:8545

3.3 Ganache 圖形化本地区塊鏈

Ganache 是 Truffle 套件中的本地区块链工具,提供圖形化介面。

# 安裝 Ganache CLI
npm install -g ganache

# 啟動 Ganache
ganache --host 127.0.0.1 --port 8545 --deterministic

# 或使用圖形化版本
# 下載 https://trufflesuite.com/ganache/

Ganache GUI 的特點:

3.4 模擬主網 Fork

開發時模擬主網狀態非常重要,可以測試與真實協議的交互。

使用 Hardhat Fork

// hardhat.config.js
module.exports = {
  networks: {
    hardhat: {
      forking: {
        url: process.env.MAINNET_RPC_URL, // 需要 Infura 或 Alchemy API Key
        blockNumber: 19000000
      }
    }
  }
};

啟動後,可以使用主網的完整狀態,但交易只在本機執行,不會影響主網。

# 啟動 Hardhat Node(自動 Fork 模式)
MAINNET_RPC_URL=https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY npx hardhat node

使用 Anvil Fork

# Fork 主網
anvil --fork-url https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY

# 可以指定 Fork 區塊號
anvil --fork-url https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY \
  --fork-block-number 19000000

第四章:開發工具配置

4.1 VS Code 與 Solidity 擴展

// .vscode/extensions.json
{
  "recommendations": [
    "hardinin.lucid-solidity",
    "tintinweb.solidity-auditor",
    "traetox.solidityvisualizer",
    "esby.vscode-solidity-flattener"
  ]
}

VS Code 配置

// .vscode/settings.json
{
  "solidity.compileUsingRemoteVersion": "v0.8.26",
  "solidity.defaultCompiler": "remote",
  "editor.formatOnSave": true,
  "files.associations": {
    "*.sol": "solidity"
  }
}

4.2 Git 與版本控制

# 初始化 Git 倉庫
git init

# 建立 .gitignore
cat > .gitignore <<EOF
node_modules/
cache/
artifacts/
typechain-types/
.env
coverage/
lcov.info
EOF

# 提交初始代碼
git add .
git commit -m "Initial commit: Hardhat project setup"

4.3 環境變數管理

# 建立 .env 檔案
cat > .env <<EOF
# RPC URLs
MAINNET_RPC_URL=https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY
SEPOLIA_RPC_URL=https://eth-sepolia.g.alchemy.com/v2/YOUR_KEY

# Private Keys(不要提交到版本控制!)
DEPLOYER_PRIVATE_KEY=0x...
TEST_ACCOUNT_PRIVATE_KEY=0x...

# API Keys
ETHERSCAN_API_KEY=YOUR_ETHERSCAN_KEY
COINMARKETCAP_API_KEY=YOUR_CMC_KEY
EOF

# 安裝 dotenv
npm install dotenv

# 在程式碼中載入
require("dotenv").config();

4.4 TypeScript 配置(可選)

# 初始化 TypeScript
npm install --save-dev typescript ts-node @types/node @types/mocha

# 建立 tsconfig.json
cat > tsconfig.json <<EOF
{
  "compilerOptions": {
    "target": "es2020",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "outDir": "./dist",
    "rootDir": "./",
    "declaration": true
  },
  "include": ["**/*.ts"],
  "exclude": ["node_modules", "dist"]
}
EOF

第五章:測試框架與實踐

5.1 Hardhat 測試框架

單元測試

// test/MyContract.ts
import { expect } from "chai";
import { ethers } from "hardhat";

describe("MyContract", function () {
  let myContract: any;
  let owner: any;
  let addr1: any;

  beforeEach(async function () {
    // 獲取測試帳戶
    [owner, addr1] = await ethers.getSigners();

    // 部署合約
    const MyContract = await ethers.getContractFactory("MyContract");
    myContract = await MyContract.deploy("Hello");
    await myContract.waitForDeployment();
  });

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

    it("Should store the greeting", async function () {
      expect(await myContract.greeting()).to.equal("Hello");
    });
  });

  describe("setGreeting", function () {
    it("Should update the greeting", async function () {
      const tx = await myContract.setGreeting("New Hello");
      await tx.wait();

      expect(await myContract.greeting()).to.equal("New Hello");
    });

    it("Should emit GreetingChanged event", async function () {
      await expect(myContract.setGreeting("New Hello"))
        .to.emit(myContract, "GreetingChanged")
        .withArgs("New Hello");
    });

    it("Should revert if called by non-owner", async function () {
      await expect(
        myContract.connect(addr1).setGreeting("New Hello")
      ).to.be.revertedWith("Ownable: caller is not the owner");
    });
  });
});

執行測試:

npx hardhat test

5.2 Foundry 測試框架

Foundry 的測試使用 Solidity 編寫,執行速度極快。

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

import "forge-std/Test.sol";
import "../src/MyContract.sol";

contract MyContractTest is Test {
    MyContract public myContract;
    address public owner;
    address public addr1;

    function setUp() public {
        // 模擬帳戶
        owner = makeAddr("owner");
        addr1 = makeAddr("addr1");

        // 部署合約
        myContract = new MyContract();
    }

    function testDeployment() public {
        assertEq(myContract.owner(), address(this));
    }

    function testSetGreeting() public {
        myContract.setGreeting("New Hello");
        assertEq(myContract.greeting(), "New Hello");
    }

    function testOnlyOwnerCanSetGreeting() public {
        vm.prank(addr1);
        vm.expectRevert("Ownable: caller is not the owner");
        myContract.setGreeting("New Hello");
    }

    // 模糊測試
    function testFuzzSetGreeting(string memory _greeting) public {
        myContract.setGreeting(_greeting);
        assertEq(myContract.greeting(), _greeting);
    }

    // Invariant 測試
    function testInvariantGreetingNotEmpty() public {
        // 確保 greeting 永遠不會是空字串
        for (uint i = 0; i < 100; i++) {
            vm.prank(address(uint160(i + 1)));
            myContract.setGreeting("test");
        }
    }
}

執行 Foundry 測試:

# 執行所有測試
forge test

# 執行特定測試
forge test --match-test testSetGreeting

# 顯示詳細輸出
forge test -vvv

# 生成覆蓋率報告
forge coverage

5.3 測試最佳實踐

測試金字塔

         /\
        /  \      端到端測試(少數)
       /----\    
      /      \   整合測試(適量)
     /--------\
    /          \  單元測試(大量)
   /------------\

測試覆蓋範圍

Gas 分析

// Hardhat Gas Reporter
require("hardhat-gas-reporter");

module.exports = {
  gasReporter: {
    enabled: true,
    currency: "USD",
    coinmarketcap: process.env.COINMARKETCAP_API_KEY
  }
};
# Foundry Gas 報告
forge test --gas-report

5.4 形式化驗證工具

Certora 是一個基於規則的形式化驗證工具:

// certora/conf/confs/MyContract.conf
{
  "smartContracts": [
    {
      "name": "MyContract",
      "path": "contracts/MyContract.sol"
    }
  ],
  "verification": {
    "rule": "rules/setGreetingOnlyOwner.svl"
  }
}
// rules/setGreetingOnlyOwner.svl
rule onlyOwnerCanSetGreeting(method f) {
    env e;
    calldataarg args;
    
    require e.msg.sender != currentContract.owner();
    
    f(e, args);
    
    assert false, "Only owner can call this function";
}

第六章:除錯與診斷工具

6.1 Hardhat 調試功能

console.log 支援

// Solidity 合約
pragma solidity ^0.8.26;

import "hardhat/console.sol";

contract MyContract {
    function myFunction(uint256 value) public {
        console.log("Function called with value:", value);
        
        if (value > 100) {
            console.log("Value is greater than 100");
        }
    }
}

交易追蹤

// 測試中的詳細錯誤訊息
it("Should fail with informative error", async function () {
  const tx = myContract.myFunction(42);
  
  await expect(tx).to.be.revertedWith("Value too small");
});

// 獲取交易回執中的除錯資訊
const receipt = await tx.wait();
console.log(receipt.logs);     // 事件日誌
console.log(receipt.gasUsed);  // Gas 使用量
console.log(receipt.blockNumber); // 區塊號

6.2 Tenderly 整合

Tenderly 提供專業的交易模擬和除錯功能:

# 安裝 Tenderly Hardhat 插件
npm install @tenderly/hardhat-tenderly

# 配置 hardhat.config.js
require("@tenderly/hardhat-tenderly");

module.exports = {
  tenderly: {
    project: "my-project",
    username: "my-username",
    privateVerification: false
  }
};
// 在測試中使用 Tenderly
const { expect } = require("chai");

describe("Tenderly Integration", function () {
  it("Should simulate transaction before execution", async function () {
    const simulation = await hre.tenderly.runTransaction({
      from: owner.address,
      to: contract.address,
      data: contract.interface.encodeFunctionData("myFunction", [42])
    });
    
    console.log("Simulation status:", simulation.status);
  });
});

6.3 Etherscan 驗證與除錯

合約驗證

# Hardhat 合約驗證
npx hardhat verify --network mainnet <CONTRACT_ADDRESS> <CONSTRUCTOR_ARGS>

# Foundry 合約驗證
forge verify-contract <CONTRACT_ADDRESS> \
  --compiler-version v0.8.26 \
  --optimizer-runs 200 \
  <SOURCE_FILE>:<CONTRACT_NAME>

驗證後,可以在 Etherscan 上:

6.4 Remix 除錯功能

Remix 提供強大的圖形化除錯工具:

  1. 逐步執行:可以一步步執行合約,查看每個步驟的狀態
  2. 變數檢視:即時查看所有狀態變數
  3. 堆疊追蹤:查看完整的呼叫堆疊
  4. 記憶體檢視:查看 EVM 記憶體內容
// 在 Remix 中使用除錯器
debugTransaction(txHash);

第七章:部署流程與配置

7.1 部署到測試網路

配置測試網路連線

// hardhat.config.js
module.exports = {
  networks: {
    sepolia: {
      url: process.env.SEPOLIA_RPC_URL,
      accounts: [process.env.DEPLOYER_PRIVATE_KEY],
      chainId: 11155111
    },
    holesky: {
      url: process.env.HOLESKY_RPC_URL,
      accounts: [process.env.DEPLOYER_PRIVATE_KEY],
      chainId: 17000
    }
  }
};

獲取測試網路 ETH

執行部署

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

# Foundry 部署
forge create --rpc-url $SEPOLIA_RPC_URL \
  --private-key $DEPLOYER_PRIVATE_KEY \
  contracts/MyContract.sol:MyContract

7.2 部署到主網

// 主網部署腳本
async function main() {
  const [deployer] = await ethers.getSigners();
  
  console.log("Deploying contracts with account:", deployer.address);
  console.log("Account balance:", (await deployer.provider.getBalance(deployer.address)).toString());
  
  const MyContract = await ethers.getContractFactory("MyContract");
  
  // 估算 Gas
  const estimatedGas = await MyContract.deploy.estimateGas("Hello");
  console.log("Estimated gas:", estimatedGas.toString());
  
  const contract = await MyContract.deploy("Hello", {
    gasLimit: estimatedGas.mul(120).div(100) // 增加 20% buffer
  });
  
  await contract.waitForDeployment();
  const address = await contract.getAddress();
  
  console.log("Contract deployed to:", address);
  
  // 驗證合約(如果使用 Etherscan)
  if (process.env.ETHERSCAN_API_KEY) {
    await hre.run("verify:verify", {
      address: address,
      constructorArguments: ["Hello"]
    });
  }
}

7.3 多重簽名部署

對於重要的合約,應該使用多重簽名錢包(如 Gnosis Safe)進行部署:

// 部署到 Safe
const Safe = await hre.ethers.getContractAt(
  "GnosisSafe",
  "0x..."
);

const tx = await Safe.execTransaction(
  to: myContractAddress,
  value: 0,
  data: encodedData,
  operation: 0, // CALL
  safeTxGas: 100000,
  baseGas: 0,
  gasPrice: 0,
  gasToken: ethers.ZeroAddress,
  refundReceiver: ethers.ZeroAddress,
  signatures: "0x..."
);

第八章:持續整合與自動化

8.1 GitHub Actions 配置

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

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Install Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      
      - name: Install Foundry
        uses: foundry-rs/foundry-action@v2
      
      - name: Install dependencies
        run: npm ci
        
      - name: Run Hardhat tests
        run: npx hardhat test
        
      - name: Run Foundry tests
        run: forge test
        
      - name: Run Slither
        run: |
          pip install slither-analyzer
          slither . --exclude-dependencies --json slither.json

8.2 自動化部署

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

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          
      - 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 }}
          DEPLOYER_PRIVATE_KEY: ${{ secrets.DEPLOYER_PRIVATE_KEY }}

結論

建立完善的本地開發環境是以太坊智慧合約開發的基礎。本指南涵蓋了從基礎軟體安裝、本地區塊鏈網路建置、測試框架選擇、除錯工具配置,到持續整合流程的完整內容。

關鍵要點:

  1. 選擇適合的開發框架(Hardhat 或 Foundry)
  2. 使用本地区块链网络加速開發週期
  3. 建立完整的測試覆蓋,包括單元測試和模糊測試
  4. 利用除錯工具快速定位問題
  5. 自動化測試和部署流程

隨著以太坊工具生態的持續演進,建議開發者定期更新工具版本,並關注新的最佳實踐。

參考資源

延伸閱讀與來源

這篇文章對您有幫助嗎?

評論

發表評論

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

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