以太坊智能合約開發實戰完整教程:從環境建置到部署的工程實踐

本指南從工程師視角出發,提供完整的智能合約開發實戰教學。內容涵蓋主流開發框架 Hardhat 與 Foundry 的深度使用、測試驅動開發實踐、模糊測試工具應用、以及生產環境部署的最佳實踐,幫助開發者建立完善的智能合約開發工作流。

以太坊智能合約開發實戰完整教程:從環境建置到部署的工程實踐

概述

智能合約開發是以太坊生態系統的核心技能,本指南從工程師視角出發,提供完整的智能合約開發實戰教學。內容涵蓋主流開發框架 Hardhat 與 Foundry 的深度使用、測試驅動開發實踐、模糊測試(Fuzz Testing)工具應用、以及生產環境部署的最佳實踐。

本教程假定讀者已具備 JavaScript/TypeScript 基礎,並對 Solidity 語言有基本認識。通過本教程,你將能夠建立完整的智能合約開發環境、撰寫高品質的合約測試、發現並修復常見漏洞、最終將合約安全部署到以太坊主網或 Layer 2 網路。

截至 2026 年第一季度,智能合約開發工具鏈已高度成熟。Hardhat 佔據約 60% 的市場份額,其 JavaScript/TypeScript 生態系統對 Web2 開發者極為友好;Foundry 則以其 Rust 核心帶來的執行速度和原生模糊測試能力,獲得進階開發者的青睞。理解這兩種工具的優劣並正確選擇,是提升開發效率的關鍵。


第一章:開發環境建置與工具鏈概述

1.1 為何選擇正確的開發框架至關重要

智能合約開發與傳統軟體開發存在根本性差異:合約一旦部署便難以修改,任何漏洞都可能導致不可挽回的資產損失。這使得測試和開發工具的選擇變得尤為關鍵。

傳統開發中,開發者可以在上線後修復 Bug;但在區塊鏈世界,合約漏洞可能被利用導致數百萬美元損失,且無法「撤回」。2021 年的 Poly Network 攻擊損失 6.1 億美元、2022 年的 Ronin Bridge 攻擊損失 6.2 億美元、2023 年的 Euler Finance 攻擊損失 1.97 億美元——這些血的教訓都在提醒我們:投入足夠時間建立完善的開發和測試環境,是保護資產的第一步。

1.2 Hardhat 環境建置

Hardhat 是由 Nomic Labs 開發的智能合約開發框架,採用 Node.js 環境,對熟悉 JavaScript/TypeScript 的開發者極為友好。其核心優勢包括:

環境安裝步驟

首先,確保系統已安裝 Node.js(v18+)和 npm。然後建立項目目錄並初始化:

# 建立項目目錄
mkdir my-smart-contract-project
cd my-smart-contract-project

# 初始化 npm 項目
npm init -y

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

# 初始化 Hardhat 項目
npx hardhat init

初始化過程中,Hardhat 會詢問項目類型。選擇「Create a JavaScript project」可獲得最基本的項目結構:

? What do you want to do? …
❯ Create a JavaScript project
  Create a TypeScript project
  Create an empty hardhat.config.js
  Quit

選擇後,Hardhat 會自動創建以下目錄結構:

my-smart-contract-project/
├── contracts/           # 智能合約源代碼
│   └── Lock.sol        # 示例合約
├── scripts/            # 部署腳本
│   └── deploy.js
├── test/               # 測試文件
│   └── Lock.js
├── hardhat.config.js  # Hardhat 配置
└── package.json

配置文件詳解

hardhat.config.js 是整個項目的核心配置檔案:

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

/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
  solidity: {
    version: "0.8.24",
    settings: {
      optimizer: {
        enabled: true,
        runs: 200
      },
      viaIR: true
    }
  },
  networks: {
    hardhat: {
      // 本地測試網絡配置
      chainId: 31337
    },
    sepolia: {
      url: process.env.SEPOLIA_RPC_URL || "",
      accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
      chainId: 11155111
    },
    mainnet: {
      url: process.env.MAINNET_RPC_URL || "",
      accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
      chainId: 1
    }
  },
  etherscan: {
    apiKey: {
      sepolia: process.env.ETHERSCAN_API_KEY || "",
      mainnet: process.env.ETHERSCAN_API_KEY || ""
    }
  },
  gasReporter: {
    enabled: true,
    currency: "USD",
    coinmarketcap: process.env.COINMARKETCAP_API_KEY
  }
};

1.3 Foundry 環境建置

Foundry 是由 Paradigm 團隊開發的智能合約開發框架,採用 Rust 編寫核心,以其極速的測試執行和原生模糊測試能力著稱。對於追求極致性能和安全性的大型項目,Foundry 是首選。

環境安裝步驟

Foundry 可以通過多種方式安裝,最推薦的是使用 foundryup:

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

# 初始化 Foundry 工具鏈
foundryup

安裝完成後,可使用以下命令驗證:

# 檢查版本
forge --version
cast --version
anvil --version

# 輸出類似:
# forge 0.3.0 (e20f9b9 2024-12-15 00:00:00)
# cast 0.3.0 (e20f9b9 2024-12-15 00:00:00)
# anvil 0.3.0 (e20f9b9 2024-12-15 00:00:00)

Foundry 工具鏈詳解

Foundry 包含三個核心工具:

建立 Foundry 項目

# 使用 Forge 初始化項目
forge init my-foundry-project

# 項目結構
my-foundry-project/
├── src/              # Solidity 源代碼
│   └── Counter.sol
├── test/             # 測試文件
│   └── Counter.t.sol
├── script/           # 部署腳本
│   └── Counter.s.sol
├── lib/              # 依賴庫(Foundry 會自動管理)
├── foundry.toml      # Foundry 配置
└── .gitignore

foundry.toml 配置

[profile.default]
src = "src"
out = "out"
libs = ["lib"]
solc = "0.8.24"
optimizer = true
optimizer_runs = 200

[rpc_endpoints]
sepolia = "${SEPOLIA_RPC_URL}"
mainnet = "${MAINNET_RPC_URL}"

[etherscan]
sepoli = { key = "${ETHERSCAN_API_KEY}" }
mainnet = { key = "${ETHERSCAN_API_KEY}" }

[profile.ci]
verbosity = 4

1.4 開發工具選擇策略

選擇 Hardhat 還是 Foundry,需要根據團隊背景和項目需求綜合考量:

特性HardhatFoundry
語言生態JavaScript/TypeScriptRust
測試速度中等(每秒數十個測試)極快(每秒數千個測試)
模糊測試需要額外工具(如 Certora)原生支持
調試體驗優秀的堆疊追蹤基本
Web2 集成簡單需要適應
插件生態豐富較少
學習曲線較平緩較陡峭

建議的選擇策略


第二章:Hardhat 深度實戰教程

2.1 第一個智能合約:SimpleStorage

讓我們從頭開始,使用 Hardhat 開發、測試和部署一個簡單的存儲合約。

合約代碼

在 contracts 目錄下創建 SimpleStorage.sol:

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

contract SimpleStorage {
    uint256 private storedValue;
    address public owner;
    
    // 事件:記錄值變更
    event ValueChanged(uint256 newValue);
    event OwnerChanged(address newOwner);
    
    constructor() {
        owner = msg.sender;
    }
    
    // 存儲數值
    function setValue(uint256 _value) public {
        require(msg.sender == owner, "Not the owner");
        storedValue = _value;
        emit ValueChanged(_value);
    }
    
    // 讀取存儲的數值
    function getValue() public view returns (uint256) {
        return storedValue;
    }
    
    // 轉移擁有權
    function transferOwnership(address _newOwner) public {
        require(msg.sender == owner, "Not the owner");
        require(_newOwner != address(0), "Invalid address");
        owner = _newOwner;
        emit OwnerChanged(_newOwner);
    }
}

2.2 測試驅動開發實踐

Hardhat 使用 JavaScript/TypeScript 編寫測試,測試框架基於 ethers.js 和 Mocha。

單元測試示例

在 test 目錄下創建 SimpleStorage.js:

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

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

  // 每個測試前部署新的合約實例
  beforeEach(async function () {
    const SimpleStorage = await ethers.getContractFactory("SimpleStorage");
    [owner, otherAccount] = await ethers.getSigners();
    simpleStorage = await SimpleStorage.deploy();
  });

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

    it("should initialize with zero value", async function () {
      expect(await simpleStorage.getValue()).to.equal(0);
    });
  });

  describe("setValue", function () {
    it("should revert when called by non-owner", async function () {
      const value = 42;
      await expect(
        simpleStorage.connect(otherAccount).setValue(value)
      ).to.be.revertedWith("Not the owner");
    });

    it("should store the value when called by owner", async function () {
      const value = 42;
      await simpleStorage.setValue(value);
      expect(await simpleStorage.getValue()).to.equal(value);
    });

    it("should emit ValueChanged event", async function () {
      const value = 100;
      await expect(simpleStorage.setValue(value))
        .to.emit(simpleStorage, "ValueChanged")
        .withArgs(value);
    });
  });

  describe("transferOwnership", function () {
    it("should transfer ownership correctly", async function () {
      await simpleStorage.transferOwnership(otherAccount.address);
      expect(await simpleStorage.owner()).to.equal(otherAccount.address);
    });

    it("should revert when called by non-owner", async function () {
      await expect(
        simpleStorage.connect(otherAccount).transferOwnership(owner.address)
      ).to.be.revertedWith("Not the owner");
    });

    it("should revert for zero address", async function () {
      await expect(
        simpleStorage.transferOwnership(ethers.ZeroAddress)
      ).to.be.revertedWith("Invalid address");
    });
  });
});

運行測試

# 運行所有測試
npx hardhat test

# 運行特定測試文件
npx hardhat test test/SimpleStorage.js

# 運行測試並顯示詳細輸出
npx hardhat test --verbose

2.3 Hardhat 網絡與本地面臨

Hardhat 內建了一個強大的本地面臨網絡(Hardhat Network),它模擬以太坊節點行為,支持快速測試迭代。

使用本地面臨

// 直接使用默認配置
const { ethers } = require("hardhat");

async function main() {
  // Hardhat 會自動使用本地面臨
  const [signer] = await ethers.getSigners();
  console.log("Deploying with account:", signer.address);
}

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

fork 主網進行測試

有時需要在隔離的主網狀態下測試合約,Hardhat 支持 fork 主網:

// hardhat.config.js
module.exports = {
  networks: {
    hardhat: {
      forking: {
        url: process.env.MAINNET_RPC_URL,
        blockNumber: 19000000  // 可選:指定區塊號
      }
    }
  }
};

這種方式允許你在本地環境中與真實的主網合約交互,例如測試與 Uniswap、Aave 等主流 DeFi 協議的集成。

2.4 Hardhat 部署腳本

部署是將合約發布到區塊鏈的過程。以下是完整的部署腳本:

// scripts/deploy.js
const { ethers } = require("hardhat");

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

  // 獲取合約工廠
  const SimpleStorage = await 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)
  // await run("verify:verify", {
  //   address: address,
  //   constructorArguments: []
  // });
}

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

部署到不同網絡

# 部署到本地面臨
npx hardhat run scripts/deploy.js

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

# 部署到主網(請謹慎!)
npx hardhat run scripts/deploy.js --network mainnet

2.5 Hardhat 插件生態

Hardhat 的強大之處在於其插件系統。以下是一些必備插件:

Gas 報告插件

npm install --save-dev hardhat-gas-reporter
// hardhat.config.js
require("hardhat-gas-reporter");

module.exports = {
  gasReporter: {
    enabled: true,
    currency: "USD",
    coinmarketcap: process.env.COINMARKETCAP_API_KEY,
    token: "ETH",
    gasPriceApi: "https://api.etherscan.io/api?module=proxy&action=eth_gasPrice"
  }
};

Contract Sizer 插件

npm install --save-dev hardhat-contract-sizer
require("hardhat-contract-sizer");

module.exports = {
  contractSizer: {
    alphaSort: true,
    disambiguatePaths: false,
    runOnCompile: true,
    strict: true
  }
};

第三章:Foundry 深度實戰教程

3.1 Foundry 測試框架詳解

Foundry 使用 Solidity 編寫測試,這是與 Hardhat 的根本區別。這種方式允許測試直接訪問合約,無需通過 JSON-RPC 層,帶來顯著的性能優勢。

基本測試結構

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

import { Test, console } from "forge-std/Test.sol";
import { SimpleStorage } from "../src/SimpleStorage.sol";

contract SimpleStorageTest is Test {
    SimpleStorage public simpleStorage;
    address public owner;
    address public otherAccount;

    function setUp() public {
        simpleStorage = new SimpleStorage();
        owner = address(this);
        otherAccount = makeAddr("otherAccount");
    }

    function test_InitialValue() public {
        assertEq(simpleStorage.getValue(), 0);
    }

    function test_SetValueByOwner() public {
        simpleStorage.setValue(42);
        assertEq(simpleStorage.getValue(), 42);
    }

    function test_RevertWhenNonOwnerSetsValue() public {
        vm.prank(otherAccount);
        vm.expectRevert("Not the owner");
        simpleStorage.setValue(42);
    }

    function test_EmitEvent() public {
        vm.expectEmit(true, true, false, true);
        emit ValueChanged(42);
        simpleStorage.setValue(42);
    }
}

3.2 Foundry 強大功能:Cheat Codes

Foundry 的核心優勢之一是「Cheat Codes」,允許測試中操縱區塊鏈狀態:

import { Test, console, vm } from "forge-std/Test.sol";

// 操縱時間
function test_TimeManipulation() public {
    vm.warp(1 days);  // 設置區塊時間戳
    vm.roll(100);      // 設置區塊高度
    skip(1 hours);     // 快進 1 小時
    rewind(1 hours);  // 回退 1 小時
}

// 操縱地址餘額
function test_BalanceManipulation() public {
    address testAddr = makeAddr("richGuy");
    vm.deal(testAddr, 1000 ether);
    assertEq(testAddr.balance, 1000 ether);
}

// 模擬合約調用
function test_Prank() public {
    vm.prank(otherAccount);
    simpleStorage.setValue(100);  // 以 otherAccount 身份調用
}

// 持續模擬(多個調用)
function test_StartPrank() public {
    vm.startPrank(otherAccount);
    simpleStorage.setValue(100);
    simpleStorage.transferOwnership(owner);
    vm.stopPrank();
}

// 模擬區塊鏈狀態
function test_Mock() public {
    vm.mockContract(
        address(priceOracle),
        abi.encodeWithSignature("latestAnswer()", 3000e8)
    );
}

3.3 模糊測試實戰

模糊測試(Fuzz Testing)是發現智能合約漏洞的強大工具。Foundry 原生支持模糊測試,無需額外配置。

基本模糊測試

// 自動生成隨機輸入測試函數
function testFuzz_SetValue(uint256 _value) public {
    simpleStorage.setValue(_value);
    assertEq(simpleStorage.getValue(), _value);
}

// 模糊測試帶有邊界條件
function testFuzz_SetValueBoundaries(uint8 _value) public {
    simpleStorage.setValue(_value);
    assertEq(simpleStorage.getValue(), _value);
}

// 模糊測試地址
function testFuzz_TransferOwnership(address _newOwner) public {
    vm.assume(_newOwner != address(0));  // 排除零地址
    simpleStorage.transferOwnership(_newOwner);
    assertEq(simpleStorage.owner(), _newOwner);
}

複雜模糊測試:AMM 測試

模糊測試在發現邊界條件和意外輸入方面特別有效。以下是測試 AMM 合約的示例:

contract AMMFuzzTest is Test {
    Token public tokenA;
    Token public tokenB;
    AMM public amm;
    
    uint256 constant INITIAL_LIQUIDITY = 1000e18;
    
    function setUp() public {
        tokenA = new Token("Token A", "TKA");
        tokenB = new Token("Token B", "TKB");
        amm = new AMM(address(tokenA), address(tokenB));
        
        tokenA.mint(address(this), INITIAL_LIQUIDITY);
        tokenB.mint(address(this), INITIAL_LIQUIDITY);
        
        tokenA.approve(address(amm), type(uint256).max);
        tokenB.approve(address(amm), type(uint256).max);
        
        amm.addLiquidity(INITIAL_LIQUIDITY, INITIAL_LIQUIDITY);
    }
    
    function testFuzz_SwapExactInput(
        uint256 amountIn,
        bool aToB
    ) public {
        vm.assume(amountIn > 0 && amountIn < INITIAL_LIQUIDITY / 10);
        
        uint256 balanceBefore = aToB 
            ? tokenB.balanceOf(address(this)) 
            : tokenA.balanceOf(address(this));
        
        if (aToB) {
            tokenA.mint(address(this), amountIn);
            tokenA.approve(address(amm), amountIn);
            amm.swapExactInput(address(tokenA), amountIn);
        } else {
            tokenB.mint(address(this), amountIn);
            tokenB.approve(address(amm), amountIn);
            amm.swapExactInput(address(tokenB), amountIn);
        }
        
        uint256 balanceAfter = aToB 
            ? tokenB.balanceOf(address(this)) 
            : tokenA.balanceOf(address(this));
        
        assertGt(balanceAfter, balanceBefore);  // 交易後餘額應增加
    }
    
    function testFuzz_AddLiquidity(
        uint256 amountA,
        uint256 amountB
    ) public {
        vm.assume(amountA > 0 && amountB > 0);
        vm.assume(amountA < INITIAL_LIQUIDITY);
        vm.assume(amountB < INITIAL_LIQUIDITY);
        
        tokenA.mint(address(this), amountA);
        tokenB.mint(address(this), amountB);
        
        uint256 lpTokensBefore = amm.balanceOf(address(this));
        amm.addLiquidity(amountA, amountB);
        uint256 lpTokensAfter = amm.balanceOf(address(this));
        
        assertGt(lpTokensAfter, lpTokensBefore);
    }
}

3.4 Foundry 部署腳本

Foundry 使用 Solidity 編寫部署腳本:

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

import { Script, console } from "forge-std/Script.sol";
import { SimpleStorage } from "../src/SimpleStorage.sol";

contract SimpleStorageScript is Script {
    function run() external {
        uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
        vm.startBroadcast(deployerPrivateKey);
        
        SimpleStorage simpleStorage = new SimpleStorage();
        
        console.log("SimpleStorage deployed to:", address(simpleStorage));
        
        vm.stopBroadcast();
    }
}

部署命令

# 部署到 Anvil 本地面臨
forge script script/SimpleStorage.s.sol

# 部署到 Sepolia
forge script script/SimpleStorage.s.sol --rpc-url sepolia --broadcast --verify

# 部署到主網
forge script script/SimpleStorage.s.sol --rpc-url mainnet --broadcast --verify

3.5 Cast 工具深度使用

Cast 是 Foundry 的命令行工具,用於與已部署的合約交互:

# 調用只讀函數
cast call <CONTRACT_ADDRESS> "getValue()" --rpc-url https://eth-sepolia.g.alchemy.com/v2/<KEY>

# 發送交易
cast send <CONTRACT_ADDRESS> "setValue(uint256)" 42 --rpc-url <RPC_URL> --private-key <PRIVATE_KEY>

# 獲取錢包餘額
cast balance <ADDRESS> --rpc-url <RPC_URL>

# 估算 Gas
cast estimate <CONTRACT_ADDRESS> "setValue(uint256)" 42 --rpc-url <RPC_URL>

# 編碼函數調用數據
cast calldata "setValue(uint256)" 42
# 輸出:0x55241077000000000000000000000000000000000000000000000000000000000000002a

# 解碼返回數據
cast 4byte 0x55241077
# 輸出:setValue(uint256)

第四章:智能合約安全性測試

4.1 常見漏洞類型回顧

在編寫測試時,需要特別關注以下常見漏洞:

重入攻擊(Reentrancy Attack)

// 漏洞合約
contract VulnerableBank {
    mapping(address => uint256) public balances;
    
    function deposit() external payable {
        balances[msg.sender] += msg.value;
    }
    
    function withdraw() external {
        uint256 amount = balances[msg.sender];
        // 漏洞:將 ETH 發送後才更新狀態
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success, "Transfer failed");
        balances[msg.sender] = 0;  // 狀態更新在轉帳之後
    }
}

// 測試重入攻擊
contract ReentrancyTest is Test {
    VulnerableBank public bank;
    Attacker public attacker;
    
    function test_ReentrancyAttack() public {
        vm.deal(address(bank), 100 ether);
        vm.deal(address(attacker), 1 ether);
        
        // 攻擊者存款
        attacker.attack();
        
        // 由於重入漏洞,攻擊者可以提取超過其存款的資金
        assertGt(attacker.balance, 1 ether);
    }
}

整數溢位(Integer Overflow/Underflow)

Solidity 0.8+ 內建了溢位檢查,但早期版本需要使用 SafeMath:

// 使用 SafeMath 防止溢位
import "@openzeppelin/contracts/utils/math/SafeMath.sol";

contract SafeToken is ERC20 {
    using SafeMath for uint256;
    
    function transfer(address to, uint256 amount) public override returns (bool) {
        _balances[msg.sender] = _balances[msg.sender].sub(amount);
        _balances[to] = _balances[to].add(amount);
        emit Transfer(msg.sender, to, amount);
        return true;
    }
}

4.2 安全測試最佳實踐

完整的安全測試應涵蓋

contract ComprehensiveTest is Test {
    MyContract public contract_;
    
    function setUp() public {
        contract_ = new MyContract();
    }
    
    // 1. 權限控制測試
    function test_OnlyOwnerCanCall() public {
        vm.prank(address(0));
        vm.expectRevert();
        contract_.adminFunction();
    }
    
    // 2. 邊界條件測試
    function test_ZeroAddress() public {
        vm.expectRevert();
        contract_.setAddress(address(0));
    }
    
    // 3. 數值邊界測試
    function test_Overflow() public {
        vm.expectRevert();
        contract_.setValue(type(uint256).max);
    }
    
    // 4. 狀態一致性測試
    function test_StateConsistency() public {
        // 多個操作的狀態一致性
    }
    
    // 5. 事件測試
    function test_Events() public {
        vm.expectEmit(true, true, false, true);
        emit ExpectedEvent(expectedData);
        contract_.doSomething();
    }
    
    // 6. 訪問控制矩陣測試
    function test_AccessControlMatrix() public {
        address[] memory users = new address[](3);
        // 測試每個用戶的權限
    }
}

4.3 OpenZeppelin 測試輔助

OpenZeppelin 提供了測試輔助庫,簡化常見測試場景:

import "@openzeppelin/contracts/utils/Arrays.sol";
import "@openzeppelin/contracts/utils/Strings.sol";

contract AdvancedTest is Test {
    using Arrays for uint256[];
    using Strings for uint256;
    
    function test_ArrayOperations() public {
        uint256[] memory arr = new uint256[](5);
        for (uint256 i = 0; i < 5; i++) {
            arr[i] = i;
        }
        
        // 排序
        arr.sort();
        assertEq(arr[0], 0);
        assertEq(arr[4], 4);
    }
}

第五章:測試網絡與部署策略

5.1 測試網絡選擇

開發過程中,需要在不同測試網絡上驗證合約:

網絡特性適用場景
Hardhat/Anvil 本地網絡快速、免費、隔離開發調試
Sepolia主流測試網絡集成測試
Holesky更接近主網最終驗證
Arbitrum SepoliaL2 測試L2 部署

獲取測試網 ETH

# Sepolia 水龍頭
# https://sepolia.etherscan.io/faucet

# Holesky 水龍頭
# https://holesky.etherscan.io/faucet

5.2 多階段部署流程

生產環境部署應遵循嚴格的多階段流程:

// scripts/deploy.js
async function main() {
  const [deployer] = await ethers.getSigners();
  console.log("Deploying contracts with account:", deployer.address);
  console.log("Account balance:", (await deployer.getBalance()).toString());

  // 第一階段:部署合約
  const SimpleStorage = await ethers.getContractFactory("SimpleStorage");
  const simpleStorage = await SimpleStorage.deploy();
  await simpleStorage.waitForDeployment();
  
  const address = await simpleStorage.getAddress();
  console.log("Contract deployed to:", address);

  // 第二階段:驗證(等待幾個區塊確認)
  console.log("Waiting for confirmations...");
  await simpleStorage.deploymentTransaction()?.wait(5);

  // 第三階段:初始化(如果需要)
  // await simpleStorage.initialize(params);

  // 第四階段:驗證合約
  if (process.env.ETHERSCAN_API_KEY) {
    await run("verify:verify", {
      address: address,
      constructorArguments: []
    });
  }
}

5.3 升級合約模式

對於需要持續迭代的項目,應使用可升級代理模式:

// 使用 OpenZeppelin Upgrades 插件
const { ethers, upgrades } = require("hardhat");

async function main() {
  const SimpleStorageV1 = await ethers.getContractFactory("SimpleStorageV1");
  
  // 部署可升級合約
  const proxy = await upgrades.deployProxy(SimpleStorageV1, [42], {
    initializer: "initialize"
  });
  
  console.log("Proxy deployed to:", await proxy.getAddress());
  
  // 升級到 V2
  const SimpleStorageV2 = await ethers.getContractFactory("SimpleStorageV2");
  const upgraded = await upgrades.upgradeProxy(
    await proxy.getAddress(),
    SimpleStorageV2
  );
  
  console.log("Upgraded to V2 at:", await upgraded.getAddress());
}

第六章:持續集成與自動化測試

6.1 GitHub Actions 集成

將測試集成到 CI/CD 流程中:

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

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [18.x, 20.x]

    steps:
    - uses: actions/checkout@v4
    
    - name: Setup Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v4
      with:
        node-version: ${{ matrix.node-version }}
        cache: 'npm'
    
    - name: Install dependencies
      run: npm ci
    
    - name: Compile contracts
      run: npx hardhat compile
    
    - name: Run tests
      run: npx hardhat test
    
    - name: Run coverage
      run: npx hardhat coverage

6.2 Foundry CI 集成

# .github/workflows/foundry.yml
name: Foundry Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive
      
      - name: Install Foundry
        uses: foundry-rs/foundry-toolchain@v1
        with:
          version: nightly
      
      - name: Run Forge Build
        run: forge build
      
      - name: Run Forge Test
        run: forge test
      
      - name: Run Fuzz Tests
        run: forge test --fuzz-runs 10000

結論:建立完善的開發工作流

智能合約開發需要嚴謹的測試和部署流程。本教程涵蓋的關鍵要點包括:

  1. 選擇適合的工具:根據團隊背景選擇 Hardhat 或 Foundry
  2. 測試驅動開發:先寫測試,再寫合約代碼
  3. 全面測試覆蓋:單元測試、集成測試、模糊測試
  4. 安全第一:時刻警惕重入、溢位等常見漏洞
  5. 多階段部署:從本地面臨到測試網再到主網
  6. 持續集成:將測試自動化,防止漏洞引入

記住:智能合約的漏洞修復成本極高,投資時間建立完善的開發和測試環境,是保護項目和用戶資產的最佳策略。祝你的智能合約開發之旅順利!

延伸閱讀與來源

這篇文章對您有幫助嗎?

評論

發表評論

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

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