以太坊智能合約開發入門實戰:從環境搭建到第一個部署合約
本文專為區塊鏈開發新手設計,提供以太坊智能合約開發的完整入門指南。我們從零開始,帶領讀者完成開發環境的搭建(Node.js、Hardhat、Foundry)、編寫第一個智能合約(ERC-20代幣)、部署到測試網絡,並通過實際案例講解智能合約的核心概念。本文涵蓋Solidity語言基礎、Hardhat配置、部署腳本編寫、測試用例設計等完整流程,是開發者入門以太坊智能合約開發的實用參考資料。
以太坊智能合約開發入門實戰:從環境搭建到第一個部署合約
概述
本文專為區塊鏈開發新手設計,提供以太坊智能合約開發的完整入門指南。我們將從零開始,帶領讀者完成開發環境的搭建、編寫第一個智能合約、部署到測試網絡,並通過實際案例講解智能合約的核心概念。本文適合具備基本程式設計經驗但尚未接觸區塊鏈開發的讀者,無需事先了解以太坊或Solidity語言。
智能合約是以太坊生態系統的核心,它是一種運行在區塊鏈上的自動執行程式。當預設條件被觸發時,合約會自動執行相關邏輯,無需任何中介機構的參與。這種「代碼即法律」的特點使得智能合約成為去中心化應用的基石。
本教程的目標是幫助讀者在本地機器上建立完整的開發環境,編寫並部署一個功能完整的ERC-20代幣合約,並理解智能合約開發的完整流程。通過完成本教程,讀者將具備獨立開發基本以太坊智能合約的能力。
前置技能要求:本教程假定讀者具備以下基本技能:熟悉至少一種程式語言(如JavaScript、Python、C++)、了解命令行基本操作、具有程式碼編輯器使用經驗。不需要任何區塊鏈或密碼學背景知識。
第一章:開發環境搭建
1.1 為什麼選擇這些工具
以太坊智能合約開發需要一套專門的工具鏈。本教程選擇的工具組合是目前業界最流行、文档最完善、社區支持最廣的方案。
Hardhat是以太坊智能合約開發的首選開發框架。它基於Node.js,提供了編譯、部署、測試、調試等完整功能。Hardhat的優勢在於:內置的本地區塊鏈網絡(Hardhat Network)讓部署變得快速簡單;詳盡的錯誤訊息幫助開發者快速定位問題;豐富的插件生態系統支持各種擴展功能。
Foundry是另一個流行的開發框架,使用Rust編寫,強調速度和安全性。Foundry的優勢在於:極快的測試執行速度、强大的Forge測試框架、便捷的Cast命令行工具。本教程以Hardhat為主,因為它對新手更友好,但會在適當時機介紹Foundry的相關功能。
Solidity是以太坊智能合約的官方編程語言,語法類似JavaScript,對新手比較友好。Solidity是一靜態類型語言,支援繼承、庫、 пользовательских類型等高級特性。2026年第一季度,Solidity的最新版本為0.8.28,引入了許多新特性包括更嚴格的類型檢查和更高效的位元操作。
1.2 安裝Node.js和開發工具
首先,我們需要在系統上安裝Node.js環境。Node.js是一個基於Chrome V8引擎的JavaScript運行時,是Hardhat等以太坊開發工具的基礎。
步驟一:安裝Node.js
打開終端,檢查系統是否已安裝Node.js:
node --version
如果顯示版本號(例如v20.11.0),表示已安裝Node.js。如果顯示「command not found」,則需要進行安裝。
在macOS或Linux上,可以使用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
# 使用Node.js 20
nvm use 20
# 驗證安裝
node --version
在Windows上,可以直接從Node.js官網下載安裝程式,或使用Windows Subsystem for Linux(WSL)獲得更好的開發體驗。
步驟二:安裝Hardhat
創建一個新的項目目錄,並初始化npm項目:
# 創建項目目錄
mkdir my-eth-project
cd my-eth-project
# 初始化npm項目
npm init -y
接下來安裝Hardhat和其他必要的開發依賴:
# 安裝Hardhat
npm install --save-dev hardhat
# 安裝OpenZeppelin contracts(安全的智能合約庫)
npm install @openzeppelin/contracts
# 安裝 ethers.js(以太坊JavaScript庫)
npm install ethers
# 安裝 dotenv(環境變量管理)
npm install dotenv
步驟三:初始化Hardhat項目
使用Hardhat的交互式初始化向導創建項目結構:
npx hardhat init
系統會提示選擇項目類型,選擇「Create a JavaScript project」:
? What do you want to do? …
❯ Create a JavaScript project
Create a TypeScript project
Create a TypeScript project (with Viem)
Create an empty Hardhat config
選擇後,Hardhat會自動創建項目結構並安裝必要的依賴。項目結構如下:
my-eth-project/
├── contracts/ # 智能合約原始碼
│ └── Lock.sol # 示例合約
├── scripts/ # 部署腳本
│ └── deploy.js
├── test/ # 測試文件
│ └── Lock.js
├── hardhat.config.js # Hardhat配置
├── package.json # npm依賴
└── README.md
1.3 配置Hardhat
打開hardhat.config.js文件,配置網絡和編譯器選項:
require("@nomicfoundation/hardhat-toolbox");
require("dotenv").config();
/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
solidity: {
version: "0.8.28",
settings: {
optimizer: {
enabled: true,
runs: 200
}
}
},
networks: {
// 本地測試網絡
localhost: {
url: "http://127.0.0.1:8545"
},
// Sepolia測試網絡
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/BscScan API用於驗證合約
etherscan: {
apiKey: {
sepolia: process.env.ETHERSCAN_API_KEY || ""
}
}
};
創建.env文件用於存儲敏感信息:
# .env文件示例
# 請複制此文件為.env並填入實際值
# 測試網絡RPC URL(從Alchemy或Infura獲取)
SEPOLIA_RPC_URL=https://eth-sepolia.g.alchemy.com/v2/YOUR_API_KEY
# 錢包私鑰(不要分享!)
# 建議使用測試帳戶的私鑰
PRIVATE_KEY=0xyour_private_key_here
# Etherscan API Key(用於合約驗證)
ETHERSCAN_API_KEY=your_etherscan_api_key
安全警告:千萬不要將包含真實資金的錢包私鑰提交到版本控制系統。在實際項目中,應使用硬體錢包或錢包管理工具(如Hardhat WalletConnect)進行簽名。
第二章:編寫第一個智能合約
2.1 Solidity語言基礎
在編寫智能合約之前,我們需要了解Solidity語言的基本語法。Solidity是一種類JavaScript的靜態類型語言,主要用於編寫以太坊智能合約。
合約結構
Solidity合約類似於面向對象語言中的類。每個合約可以包含變量、函數、事件和修飾符。以下是一個簡單的合約結構:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
// 這是一個簡單的合約示例
contract SimpleStorage {
// 狀態變量
uint256 private storedData;
// 事件 - 用於記錄區塊鏈上的重要操作
event DataStored(uint256 value, address indexed sender);
// 函數 - 設置值
function set(uint256 x) public {
storedData = x;
emit DataStored(x, msg.sender);
}
// 函數 - 獲取值
function get() public view returns (uint256) {
return storedData;
}
}
數據類型
Solidity支持多種基本數據類型:
| 類型 | 說明 | 示例 |
|---|---|---|
| uint256 | 無符號整數(256位) | uint256 a = 100; |
| int256 | 有符號整數 | int256 b = -50; |
| bool | 布爾值 | bool flag = true; |
| address | 以太坊地址 | address owner = 0x123...; |
| bytes32 | 固定長度字節數組 | bytes32 hash = keccak256(...); |
| string | 動態字符串 | string name = "Ethereum"; |
| mapping | 鍵值對映射 | mapping(address => uint256) balances; |
訪問控制
Solidity提供多種訪問控制修飾符:
contract AccessControl {
address public owner;
// 構造函數 - 合約部署時執行
constructor() {
owner = msg.sender;
}
// 函數修飾符 - 添加前置條件檢查
modifier onlyOwner() {
require(msg.sender == owner, "Not the owner");
_;
}
// 只有所有者可以調用的函數
function adminOnlyFunction() public onlyOwner {
// 僅管理員邏輯
}
}
2.2 編寫ERC-20代幣合約
現在,我們來編寫一個完整的ERC-20代幣合約。ERC-20是以太坊上代幣的標準接口,定義了代幣轉移、餘額查詢、授權等基本功能。
什麼是ERC-20
ERC-20是以太坊上同質化代幣的技術標準。任何符合ERC-20標準的代幣都可以在支持以太坊的錢包和交易所中使用。ERC-20定義了以下必需函數和事件:
totalSupply():代幣總供應量balanceOf(address):查詢某地址的餘額transfer(address, uint256):轉帳transferFrom(address, address, uint256):授權轉帳approve(address, uint256):授權allowance(address, address):查詢授權額度
完整代碼
在contracts目錄下創建新文件MyToken.sol:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
/**
* @dev 提供基本代幣功能的OpenZeppelin ERC20擴展
*/
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
/**
* @dev 可燃燒、可鑒造的ERC20代幣合約
*
* 這個合約實現了一個功能完整的代幣,包括:
* - 基本轉帳功能
* - 代幣燃燒功能(減少供應量)
* - 鑄造功能(增加供應量,只有所有者可以執行)
*/
contract MyToken is ERC20, ERC20Burnable, Ownable {
// 代幣精度(小數位數)
uint8 private _decimals;
// 代幣最大供應量
uint256 private _cap;
/**
* @dev 構造函數
* @param name 代幣名稱
* @param symbol 代幣符號
* @param decimalsValue 小數位數
* @param initialSupply 初始供應量
* @param capValue 最大供應量
*/
constructor(
string memory name,
string memory symbol,
uint8 decimalsValue,
uint256 initialSupply,
uint256 capValue
) ERC20(name, symbol) Ownable(msg.sender) {
require(capValue > 0, "ERC20Capped: cap is 0");
require(initialSupply <= capValue, "ERC20Capped: cap exceeded");
_decimals = decimalsValue;
_cap = capValue;
// 鑄造初始供應量
_mint(msg.sender, initialSupply);
}
/**
* @dev 返回小數位數
*/
function decimals() public view override returns (uint8) {
return _decimals;
}
/**
* @dev 返回最大供應量
*/
function cap() public view returns (uint256) {
return _cap;
}
/**
* @dev 鑄造新代幣(只有所有者可以調用)
* @param to 接收代幣的地址
* @param amount 鑄造數量
*/
function mint(address to, uint256 amount) public onlyOwner {
require(totalSupply() + amount <= cap(), "ERC20Capped: cap exceeded");
_mint(to, amount);
}
/**
* @dev 覆寫轉帳前後的鉤子函數
*/
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal override(ERC20) {
super._beforeTokenTransfer(from, to, amount);
}
}
合約解析
這個合約繼承了三個OpenZeppelin提供的合約:
- ERC20:實現基本的代幣功能,包括轉帳餘額管理等
- ERC20Burnable:允許持有者自願銷毀(燃燒)他們的代幣
- Ownable:實現基本的所有權訪問控制
合約的構造函數接受以下參數:
name:代幣的全稱,例如「My Token」symbol:代幣的簡稱,例如「MTK」decimals:代幣精度,通常為18initialSupply:初始鑄造的代幣數量cap:代幣的最大供應量
2.3 編譯智能合約
編譯過程會將Solidity代碼轉換為以太坊虛擬機(EVM)可以執行的字節碼。Hardhat提供了便捷的編譯命令:
npx hardhat compile
編譯成功後,會在artifacts目錄下生成合約的ABI(應用程序二進制接口)和字節碼。
ABI是以太坊智能合約的接口定義,描述了合約中所有可調用的函數、參數類型和返回值。它是客戶端(如Web應用程序)與區塊鏈上合約交互的橋樑。
編譯過程中可能出現的錯誤及解決方法:
錯誤1:Parser Error
原因:語法錯誤,例如缺少分號或括號不匹配
解決:檢查代碼語法
錯誤2:Type Error
原因:類型不匹配,例如嘗試將address賦值給uint256
解決:檢查變量類型
錯誤3:License Error
原因:缺少SPDX許可證聲明
解決:添加// SPDX-License-Identifier: MIT
第三章:部署智能合約
3.1 部署到本地測試網絡
首先,我們在本地測試網絡上部署合約。本地網絡是Hardhat內置的區塊鏈模擬器,無需連接到任何外部網絡。
啟動本地網絡
打開一個終端窗口,啟動Hardhat本地網絡:
npx hardhat node
這會啟動一個本地以太坊節點,默認在http://127.0.0.1:8545上運行。它會生成10個測試帳戶,每個帳戶包含100 ETH(測試代幣,無實際價值)。
編寫部署腳本
在scripts目錄下創建部署腳本deploy.js:
const hre = require("hardhat");
const fs = require("fs");
async function main() {
console.log("開始部署 MyToken 合約...");
// 獲取部署帳戶
const [deployer] = await hre.ethers.getSigners();
console.log("部署帳戶地址:", deployer.address);
// 部署合約
// 參數:名稱, 符號, 小數, 初始供應量, 最大供應量
// 初始供應量需要考慮小數位數:1000 * 10^18 = 1000000000000000000000
const initialSupply = hre.ethers.parseUnits("1000", 18);
const maxSupply = hre.ethers.parseUnits("10000", 18);
const MyToken = await hre.ethers.getContractFactory("MyToken");
const token = await MyToken.deploy(
"My Token", // 代幣名稱
"MTK", // 代幣符號
18, // 小數位數
initialSupply, // 初始供應量
maxSupply // 最大供應量
);
await token.waitForDeployment();
const contractAddress = await token.getAddress();
console.log("合約部署成功!");
console.log("合約地址:", contractAddress);
// 保存部署信息到文件
const deploymentInfo = {
network: hre.network.name,
contractAddress: contractAddress,
deployer: deployer.address,
timestamp: new Date().toISOString(),
initialSupply: initialSupply.toString(),
maxSupply: maxSupply.toString()
};
fs.writeFileSync(
"deployment-info.json",
JSON.stringify(deploymentInfo, null, 2)
);
// 驗證部署
console.log("\n部署驗證:");
console.log("- 代幣名稱:", await token.name());
console.log("- 代幣符號:", await token.symbol());
console.log("- 總供應量:", (await token.totalSupply()).toString());
console.log("- 最大供應量:", (await token.cap()).toString());
console.log("- 部署者餘額:", (await token.balanceOf(deployer.address)).toString());
}
// 執行主函數並處理錯誤
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
執行部署
在另一個終端窗口執行部署腳本:
npx hardhat run scripts/deploy.js --network localhost
成功部署後,終端會顯示:
開始部署 MyToken 合約...
部署帳戶地址: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
合約部署成功!
合約地址: 0x5FbDB2315678afecb367f032d93F642f64180aa3
部署驗證:
- 代幣名稱: My Token
- 代幣符號: MTK
- 總供應量: 1000000000000000000000
- 最大供應量: 10000000000000000000000
- 部署者餘額: 1000000000000000000000
3.2 部署到公共測試網絡
測試網絡(Testnet)是模擬以太坊主網的公共測試環境,與主網的區別在於測試代幣沒有實際價值。Sepolia是以太坊官方推薦的測試網絡。
獲取測試網絡代幣
在部署到Sepolia之前,需要從水龍頭(faucet)獲取測試ETH:
- 打開Sepolia水龍頭網站:https://sepoliafaucet.com
- 連接錢包(建議使用測試帳戶)
- 請求測試ETH
配置部署
確保.env文件中的私鑰是測試帳戶的私鑰(非真實資金)。然後執行部署:
npx hardhat run scripts/deploy.js --network sepolia
部署成功後,會得到一個Sepolia區塊鏈上的合約地址。例如:
合約部署成功!
合約地址: 0x5FbDB2315678afecb367f032d93F642f64180aa3
驗證合約
可以使用Etherscan Sepolia區塊瀏覽器驗證合約源代碼:
npx hardhat verify --network sepolia CONTRACT_ADDRESS "My Token" "MTK" 18 INITIAL_SUPPLY MAX_SUPPLY
3.3 與已部署合約交互
部署合約後,我們可以通過多種方式與它交互。以下是一些常見的操作:
使用Hardhat控制台交互
npx hardhat console --network localhost
在控制台中可以執行JavaScript代碼:
// 獲取合約實例
const token = await ethers.getContractAt(
"MyToken",
"0x5FbDB2315678afecb367f032d93F642f64180aa3"
);
// 查詢餘額
const balance = await token.balanceOf("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266");
console.log("餘額:", ethers.formatUnits(balance, 18));
// 轉帳
const tx = await token.transfer("0x70997970C51812dc3A010C7d01b50e0d17dc79C8", ethers.parseUnits("10", 18));
await tx.wait();
console.log("轉帳完成!");
創建交互腳本
在scripts目錄下創建interact.js:
const hre = require("hardhat");
async function main() {
// 從部署文件讀取合約地址
const fs = require("fs");
const deploymentInfo = JSON.parse(fs.readFileSync("deployment-info.json", "utf8"));
const contractAddress = deploymentInfo.contractAddress;
// 獲取合約
const token = await hre.ethers.getContractAt("MyToken", contractAddress);
// 獲取帳戶
const [account1, account2] = await hre.ethers.getSigners();
console.log("\n=== 與 MyToken 合約交互 ===\n");
// 查詢餘額
const balance1 = await token.balanceOf(account1.address);
console.log(`帳戶1 (${account1.address}) 餘額: ${hre.ethers.formatUnits(balance1, 18)} MTK`);
const balance2 = await token.balanceOf(account2.address);
console.log(`帳戶2 (${account2.address}) 餘額: ${hre.ethers.formatUnits(balance2, 18)} MTK`);
// 轉帳
const transferAmount = hre.ethers.parseUnits("50", 18);
console.log(`\n轉帳 ${hre.ethers.formatUnits(transferAmount, 18)} MTK 從帳戶1到帳戶2...`);
const tx = await token.transfer(account2.address, transferAmount);
await tx.wait();
console.log("轉帳完成!\n");
// 再次查詢餘額
const newBalance1 = await token.balanceOf(account1.address);
const newBalance2 = await token.balanceOf(account2.address);
console.log(`帳戶1 新餘額: ${hre.ethers.formatUnits(newBalance1, 18)} MTK`);
console.log(`帳戶2 新餘額: ${hre.ethers.formatUnits(newBalance2, 18)} MTK`);
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
第四章:智能合約測試
4.1 為什麼需要測試
智能合約一旦部署到區塊鏈上就很難修改,而且任何漏洞都可能導致資金損失。因此,充分的測試是智能合約開發中不可或缺的環節。
測試的好處包括:
- 發現漏洞:在部署前發現並修復問題
- 確保正確性:驗證合約邏輯符合預期
- 回歸測試:確保修改不會破壞現有功能
- 文檔作用:測試代碼可以作為合約行為的文檔
4.2 編寫測試用例
Hardhat使用JavaScript/TypeScript編寫測試,使用Mocha測試框架和Chai斷言庫。
在test目錄下創建測試文件MyToken.js:
const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("MyToken", function () {
let token;
let owner;
let addr1;
let addr2;
// 每個測試用例執行前部署合約
beforeEach(async function () {
[owner, addr1, addr2] = await ethers.getSigners();
const MyToken = await ethers.getContractFactory("MyToken");
// 部署合約:名稱、符號、小數、初始供應、最大供應
token = await MyToken.deploy(
"My Token",
"MTK",
18,
ethers.parseUnits("1000", 18), // 1000代幣
ethers.parseUnits("10000", 18) // 10000上限
);
await token.waitForDeployment();
});
// 測試:代幣名稱和符號
describe("Token Metadata", function () {
it("should have the correct name", async function () {
expect(await token.name()).to.equal("My Token");
});
it("should have the correct symbol", async function () {
expect(await token.symbol()).to.equal("MTK");
});
it("should have 18 decimals", async function () {
expect(await token.decimals()).to.equal(18);
});
});
// 測試:供應量
describe("Supply", function () {
it("should have correct total supply", async function () {
expect(await token.totalSupply()).to.equal(ethers.parseUnits("1000", 18));
});
it("should have correct cap", async function () {
expect(await token.cap()).to.equal(ethers.parseUnits("10000", 18));
});
});
// 測試:餘額查詢
describe("Balances", function () {
it("should assign initial supply to owner", async function () {
expect(await token.balanceOf(owner.address)).to.equal(
ethers.parseUnits("1000", 18)
);
});
it("should have zero balance for other accounts initially", async function () {
expect(await token.balanceOf(addr1.address)).to.equal(0);
});
});
// 測試:轉帳功能
describe("Transfers", function () {
it("should transfer tokens between accounts", async function () {
// 從owner轉帳50代幣到addr1
await token.transfer(addr1.address, ethers.parseUnits("50", 18));
expect(await token.balanceOf(addr1.address)).to.equal(
ethers.parseUnits("50", 18)
);
expect(await token.balanceOf(owner.address)).to.equal(
ethers.parseUnits("950", 18)
);
});
it("should fail when sender doesn't have enough tokens", async function () {
// 嘗試轉帳超出餘額的數量,應該失敗
await expect(
token.connect(addr1).transfer(owner.address, ethers.parseUnits("1", 18))
).to.be.revertedWith("ERC20: transfer amount exceeds balance");
});
it("should update balances after transfers", async function () {
const initialBalance = await token.balanceOf(owner.address);
// 轉帳100代幣到addr1
await token.transfer(addr1.address, ethers.parseUnits("100", 18));
// 轉帳50代幣從addr1到addr2
await token.connect(addr1).transfer(addr2.address, ethers.parseUnits("50", 18));
expect(await token.balanceOf(addr1.address)).to.equal(
ethers.parseUnits("50", 18)
);
expect(await token.balanceOf(addr2.address)).to.equal(
ethers.parseUnits("50", 18)
);
expect(await token.balanceOf(owner.address)).to.equal(
initialBalance - ethers.parseUnits("100", 18)
);
});
});
// 測試:授權功能
describe("Allowance", function () {
it("should approve tokens for delegated transfer", async function () {
await token.approve(addr1.address, ethers.parseUnits("100", 18));
expect(
await token.allowance(owner.address, addr1.address)
).to.equal(ethers.parseUnits("100", 18));
});
it("should transfer from approved account", async function () {
await token.approve(addr1.address, ethers.parseUnits("100", 18));
// addr1 代表 owner 轉帳給 addr2
await token
.connect(addr1)
.transferFrom(owner.address, addr2.address, ethers.parseUnits("50", 18));
expect(await token.balanceOf(addr2.address)).to.equal(
ethers.parseUnits("50", 18)
);
});
});
// 測試:鑄造功能(僅 owner)
describe("Minting", function () {
it("should allow owner to mint new tokens", async function () {
const initialSupply = await token.totalSupply();
await token.mint(addr1.address, ethers.parseUnits("100", 18));
expect(await token.totalSupply()).to.equal(
initialSupply + ethers.parseUnits("100", 18)
);
expect(await token.balanceOf(addr1.address)).to.equal(
ethers.parseUnits("100", 18)
);
});
it("should not allow non-owner to mint", async function () {
await expect(
token.connect(addr1).mint(addr1.address, ethers.parseUnits("100", 18))
).to.be.revertedWith("Ownable: caller is not the owner");
});
it("should not exceed cap when minting", async function () {
// 嘗試超過上限的鑄造,應該失敗
await expect(
token.mint(addr1.address, ethers.parseUnits("9001", 18))
).to.be.revertedWith("ERC20Capped: cap exceeded");
});
});
// 測試:燃燒功能
describe("Burning", function () {
it("should allow owner to burn tokens", async function () {
const initialSupply = await token.totalSupply();
const initialBalance = await token.balanceOf(owner.address);
// 燃燒100代幣
await token.burn(ethers.parseUnits("100", 18));
expect(await token.totalSupply()).to.equal(
initialSupply - ethers.parseUnits("100", 18)
);
expect(await token.balanceOf(owner.address)).to.equal(
initialBalance - ethers.parseUnits("100", 18)
);
});
});
});
4.3 執行測試
執行測試的命令:
npx hardhat test
成功執行後,會看到類似以下的輸出:
MyToken
Token Metadata
✔ should have the correct name
✔ should have the correct symbol
✔ should have 18 decimals
Supply
✔ should have correct total supply
✔ should have correct cap
Balances
✔ should assign initial supply to owner
✔ should have zero balance for other accounts initially
Transfers
✔ should transfer tokens between accounts
✔ should fail when sender doesn't have enough tokens
✔ should update balances after transfers
Allowance
✔ should approve tokens for delegated transfer
✔ should transfer from approved account
Minting
✔ should allow owner to mint new tokens
✔ should not allow non-owner to mint
✔ should not exceed cap when minting
Burning
✔ should allow owner to burn tokens
17 passing
第五章:2026年智能合約開發趨勢
5.1 最新技術發展
截至2026年第一季度,以太坊智能合約開發領域呈現以下趨勢:
Account Abstraction(帳戶抽象)
ERC-4337標準的普及使得智能合約錢包越來越流行。用戶不再需要保管複雜的私鑰,可以通過社交恢復、多因素認證等方式管理資產。主要錢包如Argent、Safe(原Gnosis Safe)都已支持ERC-4337。
Intent Architecture(意圖架構)
「意圖」正在成為區塊鏈交互的新範式。用戶只需表達「想要什麼」(例如「用1000 USDC換取ETH」),Solver網絡會自動找到最優執行路徑。這種模式大大降低了用戶的操作複雜度。
ZK-Rollup集成
ZK-Rollup技術的成熟使得更多應用開始部署到zkEVM兼容的Layer 2網絡。zkSync Era、Starknet等網絡提供了更低的Gas費用和更快的確認速度。
5.2 開發者資源推薦
官方文檔
- Ethereum Developer Documentation:https://ethereum.org/en/developers/
- Solidity官方文檔:https://docs.soliditylang.org/
- OpenZeppelin Contracts:https://docs.openzeppelin.com/contracts/
學習平台
- CryptoZombies:適合Solidity初學者
- Alchemy University:免費的區塊鏈開發課程
- Cyfrin Updraft:高質量的智能合約課程
開發工具
- Hardhat:https://hardhat.org/
- Foundry:https://book.getfoundry.sh/
- Remix IDE:https://remix.ethereum.org/
結論
本教程涵蓋了以太坊智能合約開發的完整流程:從環境搭建、編寫合約、部署測試到實際交互。通過完成本教程,讀者應該已經掌握了以下技能:
- 配置Hardhat開發環境
- 理解Solidity語言基礎
- 編寫符合ERC-20標準的代幣合約
- 部署合約到本地和公共測試網絡
- 編寫和執行智能合約測試
- 與已部署的合約進行交互
智能合約開發是一個持續學習的過程。建議讀者在完成本教程後,繼續探索以下方向:
- 學習更複雜的合約模式,如可升級合約
- 探索Layer 2網絡上的開發
- 深入研究DeFi協議的實現
- 關注以太坊的最新升級和EIP
區塊鏈技術仍在快速發展中,新的工具和標準不斷湧現。保持學習的熱情和好奇心,將幫助你在這個激動人心的領域中不斷前進。
附錄:常見問題解答
Q:如何處理「Insufficient Funds」錯誤?
A:確保錢包有足夠的ETH支付Gas費用。測試網絡可以使用水龍頭獲取測試ETH。
Q:合約部署失敗怎麼辦?
A:檢查錯誤訊息,常見原因包括:Gas不足、構造函數參數錯誤、網絡連接問題。
Q:如何升級已部署的合約?
A:使用代理模式(Proxy Pattern)。常見方案包括透明代理、可升級代理等。OpenZeppelin提供了完整的升級解決方案。
Q:智能合約可以修改嗎?
A:傳統合約部署後不可修改。但可以通過代理模式實現可升級合約,儘管這會增加複雜性和安全風險。
相關文章
- 以太坊智能合約開發實作教程:從環境搭建到部署上線完整指南 — 本教程帶領讀者從零開始,建立完整的以太坊開發環境,撰寫第一個智能合約,並將其部署到測試網絡和主網。我們使用 Solidity 作為合約語言,Hardhat 作為開發框架,提供一步一步的詳細操作指南。內容涵蓋 Hardhat 專案初始化、ERC-20 代幣合約撰寫、單元測試、Sepolia 測試網絡部署、以及主網部署等完整流程。
- 以太坊智能合約開發互動教程:從基礎到實際部署的完整指南 — 本文提供完整的以太坊智能合約開發互動教程,涵蓋開發環境設定、第一個智能合約、合約部署流程,並提供可直接在瀏覽器中運行的程式碼範例。我們從 Hardhat 開發框架的安裝和配置開始,逐步教學如何編寫、測試和部署智能合約,並展示如何使用 Web3.js 與合約進行互動。同時提供常見錯誤的調試方法和進階合約模式的實際應用。
- 以太坊智能合約開發實戰:從基礎到 DeFi 協議完整代碼範例指南 — 本文提供以太坊智能合約開發的完整實戰指南,透過可直接運行的 Solidity 代碼範例,幫助開發者從理論走向實踐。內容涵蓋基礎合約開發、借貸協議實作、AMM 機制實現、以及中文圈特有的應用場景(台灣交易所整合、香港監管合規、Singapore MAS 牌照申請)。本指南假設讀者具備基本的程式設計基礎,熟悉 JavaScript 或 Python 等語言,並對區塊鏈概念有基本理解。
- 以太坊第一個 DApp 開發完整教學:從零到部署 — 帶領讀者從零開始,開發並部署第一個以太坊 DApp。詳細講解智慧合約開發、前端整合、錢包連接、測試網部署等完整流程,涵蓋 Hardhat、Solidity、React、Ethers.js 等主流開發工具。
- 以太坊智能合約部署完整實戰指南:從環境建置到主網上線 — 本文提供以太坊智能合約部署的完整實戰教學,涵蓋從開發環境建置、本地測試、測試網部署、到主網上線的全部流程。我們以實際的 ERC-20 代幣合約為例,逐步講解每個環節的技術細節、可能遇到的問題、以及最佳實踐建議,幫助開發者掌握智能合約部署的核心技能。
延伸閱讀與來源
- Ethereum.org Developers 官方開發者入口與技術文件
- EIPs 以太坊改進提案
這篇文章對您有幫助嗎?
請告訴我們如何改進:
評論
發表評論
注意:由於這是靜態網站,您的評論將儲存在本地瀏覽器中,不會公開顯示。
目前尚無評論,成為第一個發表評論的人吧!