以太坊智能合約開發實戰完整教程:從環境建置到部署的工程實踐
本指南從工程師視角出發,提供完整的智能合約開發實戰教學。內容涵蓋主流開發框架 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 的開發者極為友好。其核心優勢包括:
- 內建的 Solidity 編譯器整合
- 強大的本地區塊鏈測試網絡(Hardhat Network)
- 可擴展的插件生態系統
- 优秀的調試功能和錯誤訊息
環境安裝步驟:
首先,確保系統已安裝 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 包含三個核心工具:
- Forge:智能合約測試、構建和部署框架
- Cast:命令行互動工具,用於與以太坊合約交互
- Anvil:本地以太坊節點,用於開發和測試
建立 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,需要根據團隊背景和項目需求綜合考量:
| 特性 | Hardhat | Foundry |
|---|---|---|
| 語言生態 | JavaScript/TypeScript | Rust |
| 測試速度 | 中等(每秒數十個測試) | 極快(每秒數千個測試) |
| 模糊測試 | 需要額外工具(如 Certora) | 原生支持 |
| 調試體驗 | 優秀的堆疊追蹤 | 基本 |
| Web2 集成 | 簡單 | 需要適應 |
| 插件生態 | 豐富 | 較少 |
| 學習曲線 | 較平緩 | 較陡峭 |
建議的選擇策略:
- Web2 背景團隊、短期項目:選擇 Hardhat
- 大型項目、高安全性要求:選擇 Foundry
- 混合策略:使用 Foundry 進行核心測試,Hardhat 進行前端集成
第二章: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 Sepolia | L2 測試 | 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
結論:建立完善的開發工作流
智能合約開發需要嚴謹的測試和部署流程。本教程涵蓋的關鍵要點包括:
- 選擇適合的工具:根據團隊背景選擇 Hardhat 或 Foundry
- 測試驅動開發:先寫測試,再寫合約代碼
- 全面測試覆蓋:單元測試、集成測試、模糊測試
- 安全第一:時刻警惕重入、溢位等常見漏洞
- 多階段部署:從本地面臨到測試網再到主網
- 持續集成:將測試自動化,防止漏洞引入
記住:智能合約的漏洞修復成本極高,投資時間建立完善的開發和測試環境,是保護項目和用戶資產的最佳策略。祝你的智能合約開發之旅順利!
相關文章
- 以太坊智能合約開發實作教程:從環境搭建到部署上線完整指南 — 本教程帶領讀者從零開始,建立完整的以太坊開發環境,撰寫第一個智能合約,並將其部署到測試網絡和主網。我們使用 Solidity 作為合約語言,Hardhat 作為開發框架,提供一步一步的詳細操作指南。內容涵蓋 Hardhat 專案初始化、ERC-20 代幣合約撰寫、單元測試、Sepolia 測試網絡部署、以及主網部署等完整流程。
- Solidity 智慧合約完整實作指南:從 ERC-20 到可升級合約的工程實踐 — 本文從工程實踐角度深入講解 Solidity 智慧合約的完整開發流程,涵蓋 ERC-20 代幣合約的完整實現、基於角色的存取控制系統、可升級代理模式、以及使用 Foundry 框架的全面測試策略。我們提供了可直接用於生產環境的程式碼範例,包括完整的 ERC20 實現、AccessControl 角色管理、透明代理合約、以及包含模糊測試的測試套件。透過本文,開發者將掌握編寫安全、高效、可升級智慧合約的核心技能。
- 以太坊智能合約開發實戰:從基礎到 DeFi 協議完整代碼範例指南 — 本文提供以太坊智能合約開發的完整實戰指南,透過可直接運行的 Solidity 代碼範例,幫助開發者從理論走向實踐。內容涵蓋基礎合約開發、借貸協議實作、AMM 機制實現、以及中文圈特有的應用場景(台灣交易所整合、香港監管合規、Singapore MAS 牌照申請)。本指南假設讀者具備基本的程式設計基礎,熟悉 JavaScript 或 Python 等語言,並對區塊鏈概念有基本理解。
- 以太坊第一個 DApp 開發完整教學:從零到部署 — 帶領讀者從零開始,開發並部署第一個以太坊 DApp。詳細講解智慧合約開發、前端整合、錢包連接、測試網部署等完整流程,涵蓋 Hardhat、Solidity、React、Ethers.js 等主流開發工具。
- 以太坊智能合約部署完整實戰指南:從環境建置到主網上線 — 本文提供以太坊智能合約部署的完整實戰教學,涵蓋從開發環境建置、本地測試、測試網部署、到主網上線的全部流程。我們以實際的 ERC-20 代幣合約為例,逐步講解每個環節的技術細節、可能遇到的問題、以及最佳實踐建議,幫助開發者掌握智能合約部署的核心技能。
延伸閱讀與來源
- Ethereum.org Developers 官方開發者入口與技術文件
- EIPs 以太坊改進提案
這篇文章對您有幫助嗎?
請告訴我們如何改進:
評論
發表評論
注意:由於這是靜態網站,您的評論將儲存在本地瀏覽器中,不會公開顯示。
目前尚無評論,成為第一個發表評論的人吧!