Scaffold-ETH 完整開發指南:從零開始構建以太坊應用
系統介紹 Scaffold-ETH 框架的設計理念、核心組件和實戰開發流程,從環境搭建、智慧合約開發到前端集成,幫助開發者快速構建全棧以太坊去中心化應用。
Scaffold-ETH 完整開發指南:從零開始構建以太坊應用
概述
Scaffold-ETH 是以太坊生態系統中最受歡迎的應用開發框架之一,由 Austin Griffith 創建並獲得廣泛社區支持。這個框架旨在幫助開發者快速搭建全棧以太坊應用,將智慧合約開發與前端介面整合在一起,大幅縮短從概念驗證到實際部署的時間。
本文深入介紹 Scaffold-ETH 的設計理念、核心組件、實戰開發流程,以及如何利用這個框架快速構建去中心化應用。我們將從環境搭建開始,逐步引導讀者完成一個完整的 DeFi 應用開發流程。
一、Scaffold-ETH 架構解析
1.1 設計理念
Scaffold-ETH 的設計圍繞「快速原型開發」這一核心目標:
- 前後端一體:將智慧合約(Solidity)與前端(React/Next.js)整合在同一專案中
- 即時反饋:修改合約後前端自動重新部署和刷新
- 開箱即用:預設配置涵蓋大多數常見需求
- 學習曲線平緩:適合區塊鏈新手快速上手
1.2 核心組件
Scaffold-ETH 由多個相互協作的組件構成:
Scaffold-ETH 架構:
┌─────────────────────────────────────────────────────────────┐
│ 前端層 (Next.js/React) │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Frontend │ │ App Routes │ │ Hooks & │ │
│ │ Components │ │ ( Wagmi ) │ │ Utils │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ 錢包連接層 (RainbowKit/Wagmi) │
├─────────────────────────────────────────────────────────────┤
│ 智慧合約層 (Hardhat/Foundry) │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Solidity │ │ Deploy │ │ Contract │ │
│ │ Contracts │ │ Scripts │ │ ABIs │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
1.3 版本選擇
Scaffold-ETH 目前有兩個主要版本:
Scaffold-ETH 2(當前主流):
- 使用 Next.js 14
- 整合 Wagmi 和 RainbowKit
- 支持 Hardhat 或 Foundry
- TypeScript 優先
Scaffold-ETH 1(經典版本):
- 使用 React + ethers.js
- 適合較舊的專案維護
建議新專案使用 Scaffold-ETH 2。
二、環境準備與專案初始化
2.1 系統要求
在開始之前,確保你的開發環境滿足以下要求:
- 作業系統:Linux、macOS 或 Windows(WSL2 推薦)
- Node.js:v18.17.0 或更高版本
- Git:用於版本控制
- 錢包:MetaMask 或其他 Web3 錢包
2.2 安裝流程
第一步:Clone 專案
# 進入工作目錄
cd ~/projects
# Clone Scaffold-ETH 2
git clone https://github.com/scaffold-eth/scaffold-eth-2.git my-dapp
# 進入專案目錄
cd my-dapp
第二步:安裝依賴
# 安裝所有依賴
npm install
這個過程會安裝:
- 前端依賴(Next.js、React、Wagmi)
- 合約依賴(Hardhat 相關套件)
- 開發工具依賴
第三步:配置環境變數
# 複製環境範例檔案
cp .env.example .env
# 編輯環境變數
nano .env
必要的環境變數:
# Alchemy 或其他 RPC 提供者
SEPOLIA_RPC_URL=your_rpc_url_here
MAINNET_RPC_URL=your_rpc_url_here
# 錢包私鑰(用於部署,注意安全)
DEPLOYER_PRIVATE_KEY=your_private_key_here
# Etherscan API Key(用於驗證合約)
ETHERSCAN_API_KEY=your_etherscan_key
2.3 專案結構
初始化後的專案結構如下:
my-dapp/
├── packages/
│ ├── nextjs/ # Next.js 前端應用
│ │ ├── app/ # App Router
│ │ │ ├── page.tsx # 主頁面
│ │ │ └── layout.tsx # 佈局
│ │ ├── components/ # React 元件
│ │ │ ├── scaffold/ # Scaffold 內建元件
│ │ │ └── ui/ # 自定義元件
│ │ ├── hooks/ # 自定義 Hooks
│ │ ├── utils/ # 工具函數
│ │ └── scaffold.config.ts # 前端配置
│ │
│ └── hardhat/ # Hardhat 合約項目
│ ├── contracts/ # Solidity 合約
│ │ └── YourContract.sol
│ ├── deploy/ # 部署腳本
│ ├── test/ # 測試檔案
│ └── hardhat.config.ts # Hardhat 配置
│
├── .env # 環境變數
├── package.json # 根 package.json
└── README.md # 專案說明
三、智慧合約開發
3.1 創建第一個合約
在 packages/hardhat/contracts/ 目錄下創建新的 Solidity 合約:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
/**
* @title SimpleToken
* @dev 簡單的 ERC20 代幣合約示例
*/
contract SimpleToken is ERC20 {
uint256 public constant INITIAL_SUPPLY = 1000000 * 10**18;
/**
* @dev 部署時鑄造全部供應量到部署者帳戶
*/
constructor() ERC20("Simple Token", "SIM") {
_mint(msg.sender, INITIAL_SUPPLY);
}
}
3.2 合約部署
使用 Hardhat 部署
# 部署到本地 Hardhat 網絡
cd packages/hardhat
npx hardhat run deploy/00_deploy_your_contract.ts --network localhost
部署腳本示例(deploy/00_deploy_your_contract.ts):
import { HardhatRuntimeEnvironment } from "hardhat/types";
import { DeployFunction } from "hardhat-deploy/types";
const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const { deployer } = await hre.getNamedAccounts();
const { deploy } = hre.deployments;
// 部署合約
await deploy("SimpleToken", {
from: deployer,
log: true,
autoMine: true,
});
};
export default func;
func.tags = ["SimpleToken"];
部署到測試網
# 部署到 Sepolia 測試網
npx hardhat run deploy/00_deploy_your_contract.ts --network sepolia
部署成功後,合約位址會自動同步到前端配置中。
3.3 合約交互
Scaffold-ETH 提供了便捷的合約交互方式。在前端代碼中:
// 使用 useScaffoldContract 鉤子獲取合約實例
import { useScaffoldContract } from "~~/hooks/scaffold-eth";
const { data: yourContract } = useScaffoldContract({
contractName: "SimpleToken",
});
// 調用合約方法
const mintTokens = async () => {
if (yourContract) {
const tx = await yourContract.mint(amount);
await tx.wait();
}
};
// 讀取合約數據
const balance = await yourContract.balanceOf(address);
四、前端開發
4.1 錢包連接
Scaffold-ETH 內建了錢包連接功能,使用 RainbowKit 和 Wagmi:
// packages/nextjs/app/components/Header.tsx
"use client";
import { useAccount, useConnectors } from "wagmi";
import { ArrowRightOnRectangleIcon, CircleStackIcon } from "@heroicons/react/24/outline";
export const Header = () => {
const { address, isConnected } = useAccount();
const { connectors, connect } = useConnectors();
return (
<div className="navbar bg-base-100">
<div className="flex-1">
<a className="btn btn-ghost normal-case text-xl">My DApp</a>
</div>
<div className="flex-none">
{isConnected ? (
<div className="dropdown dropdown-end">
<label tabIndex={0} className="btn btn-ghost btn-circle avatar">
<div className="w-10 rounded-full bg-neutral">
<CircleStackIcon className="h-6 w-6 text-white" />
</div>
</label>
<ul tabIndex={0} className="menu menu-sm dropdown-content mt-3 z-[1] p-2 shadow bg-base-100 rounded-box w-52">
<li>
<a>{address?.slice(0, 6)}...{address?.slice(-4)}</a>
</li>
</ul>
</div>
) : (
<button
className="btn btn-primary"
onClick={() => connect({ connector: connectors[0] })}
>
Connect Wallet
</button>
)}
</div>
</div>
);
};
4.2 讀取合約數據
使用 useScaffoldReadContract 鉤子讀取合約狀態:
import { useScaffoldReadContract } from "~~/hooks/scaffold-eth";
export const TokenBalance = ({ address }: { address: string }) => {
const { data: balance } = useScaffoldReadContract({
contractName: "SimpleToken",
functionName: "balanceOf",
args: [address],
});
return (
<div className="card bg-base-100 shadow-xl">
<div className="card-body">
<h2 className="card-title">Token Balance</h2>
<p className="text-2xl font-bold">
{balance ? Number(balance).toLocaleString() : "0"} SIM
</p>
</div>
</div>
);
};
4.3 寫入合約
使用 useScaffoldWriteContract 鉤子發起交易:
import { useScaffoldWriteContract } from "~~/hooks/scaffold-eth";
export const MintButton = () => {
const { writeContractAsync } = useScaffoldWriteContract("SimpleToken");
const handleMint = async () => {
try {
writeContractAsync({
functionName: "mint",
args: [BigInt(1000 * 10**18)],
});
} catch (err) {
console.error("Mint failed:", err);
}
};
return (
<button className="btn btn-primary" onClick={handleMint}>
Mint 1000 SIM
</button>
);
};
4.4 交易歷史
使用 useWatchContractEvent 監聽合約事件:
import { useWatchContractEvent } from "wagmi";
import { useScaffoldContractRead } from "~~/hooks/scaffold-eth";
export const TransactionHistory = () => {
const [events, setEvents] = useState<any[]>([]);
useWatchContractEvent({
address: "0x...", // 合約地址
abi: [...], // 合約 ABI
eventName: "Transfer",
onLogs(logs) {
setEvents((prev) => [...prev, ...logs]);
},
});
return (
<div>
<h3>Transaction History</h3>
{events.map((event, i) => (
<div key={i}>
From: {event.args.from} To: {event.args.to} Amount: {event.args.value.toString()}
</div>
))}
</div>
);
};
五、進階功能
5.1 自定義網絡配置
在 scaffold.config.ts 中配置自定義網絡:
import type { ScaffoldConfig } from "./scaffold.config";
const config: ScaffoldConfig = {
targetNetworks: [
// 本地網絡
{
id: 31337,
name: "Localhost",
httpUrl: "http://localhost:8545",
wsUrl: "ws://localhost:8545",
},
// Sepolia 測試網
{
id: 11155111,
name: "Sepolia",
httpUrl: process.env.NEXT_PUBLIC_SEPOLIA_RPC_URL || "",
wsUrl: process.env.NEXT_PUBLIC_SEPOLIA_RPC_URL?.replace("https", "wss"),
},
],
// 其他配置...
};
export default config;
5.2 事件監控儀表板
利用 Scaffold-ETH 構建簡單的事件監控儀表板:
"use client";
import { useState, useEffect } from "react";
import { usePublicClient } from "wagmi";
import { getContract } from "wagmi/actions";
export const EventDashboard = () => {
const [recentEvents, setRecentEvents] = useState<any[]>([]);
const publicClient = usePublicClient();
useEffect(() => {
const fetchEvents = async () => {
const logs = await publicClient.getContractEvents({
address: "0x...",
abi: [...],
eventName: "Transfer",
fromBlock: 0n,
toBlock: "latest",
});
setRecentEvents(logs.slice(-10).reverse());
};
fetchEvents();
}, [publicClient]);
return (
<div className="overflow-x-auto">
<table className="table">
<thead>
<tr>
<th>Block</th>
<th>From</th>
<th>To</th>
<th>Amount</th>
</tr>
</thead>
<tbody>
{recentEvents.map((event) => (
<tr key={event.logIndex}>
<td>{event.blockNumber.toString()}</td>
<td>{event.args.from?.slice(0, 10)}...</td>
<td>{event.args.to?.slice(0, 10)}...</td>
<td>{event.args.value?.toString()}</td>
</tr>
))}
</tbody>
</table>
</div>
);
};
5.3 多簽錢包集成
集成 Safe(原 Gnosis Safe)多簽錢包:
import { useSafeAppsSDK } from "@safe-global/safe-apps-react-sdk";
export const SafeMultiSig = () => {
const { safe, sdk } = useSafeAppsSDK();
const executeTransaction = async () => {
const tx = {
to: "0x...",
data: "0x...",
value: "0",
};
const safeTxHash = await sdk.txs.signMessage(tx);
// 實際執行需要多簽審批
};
return (
<button onClick={executeTransaction} className="btn btn-secondary">
Execute via Safe
</button>
);
};
5.4 客製化主題
Scaffold-ETH 使用 Tailwind CSS,輕鬆實現主題定制:
/* styles/globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--primary: #6366f1;
--secondary: #ec4899;
--accent: #10b981;
}
@layer components {
.btn-primary {
@apply bg-[var(--primary)] hover:bg-[var(--primary)]/90 text-white;
}
}
六、本地開發工作流
6.1 啟動開發伺服器
# 同時啟動前端和 Hardhat 節點
npm run start
# 或者分開啟動
# 終端 1:啟動 Hardhat 本地節點
cd packages/hardhat
npx hardhat node
# 終端 2:啟動前端開發伺服器
cd packages/nextjs
npm run dev
6.2 熱重載
Scaffold-ETH 支援智慧合約修改後的熱重載:
- 修改 Solidity 合約代碼
- 重新部署合約:
cd packages/hardhat
npx hardhat deploy --network localhost
- 前端自動感知合約變化並刷新
6.3 除錯技巧
合約除錯:
// 在合約中添加事件用於除錯
event DebugEvent(string message, uint256 value);
function someFunction(uint256 input) public {
emit DebugEvent("Function called with value:", input);
// 函數邏輯
}
前端除錯:
// 使用 wagmi 的除錯功能
const { data, error } = useScaffoldReadContract({
contractName: "YourContract",
functionName: "yourFunction",
});
// 檢查錯誤
if (error) {
console.error("Contract call error:", error);
}
七、部署上線
7.1 部署到主網
# 部署到以太坊主網
cd packages/hardhat
npx hardhat run deploy/00_deploy_your_contract.ts --network mainnet
7.2 前端部署
使用 Vercel 或類似平台部署 Next.js 前端:
# 使用 Vercel CLI 部署
npm i -g vercel
vercel
# 或連接到 GitHub 倉庫進行 CI/CD 部署
7.3 合約驗證
部署後在 Etherscan 上驗證合約:
npx hardhat verify --network mainnet CONTRACT_ADDRESS "constructor arguments"
八、效能優化
8.1 減少 Gas 成本
在 Solidity 合約中優化 Gas 消耗:
// 優化前
function processArray(uint256[] calldata arr) public view returns (uint256) {
uint256 sum = 0;
for (uint256 i = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
}
// 優化後:使用 assembly
function processArrayOptimized(uint256[] calldata arr) public view returns (uint256 sum) {
assembly {
for { let i := 0 } lt(i, arr.length) { i := add(i, 1) } {
sum := add(sum, calldataload(add(arr.offset, mul(i, 0x20))))
}
}
}
8.2 前端載入優化
使用 React 懶加載:
import dynamic from "next/dynamic";
const HeavyComponent = dynamic(
() => import("./HeavyComponent"),
{ loading: () => <p>Loading...</p> }
);
8.3 快取策略
利用 React Query 快取合約數據:
import { useQuery } from "@tanstack/react-query";
const { data } = useQuery({
queryKey: ["tokenBalance", address],
queryFn: () => contract.balanceOf(address),
staleTime: 60000, // 1 分鐘內不重新獲取
});
九、常見問題與解決方案
9.1 合約部署失敗
問題:部署時出現 "insufficient funds" 錯誤
解決方案:
- 檢查錢包餘額是否充足
- 確認 .env 中的私鑰正確
- 檢查 RPC URL 是否正確
9.2 前端無法連接錢包
問題:錢包連接按鈕無響應
解決方案:
- 確保 MetaMask 已安裝
- 檢查瀏覽器擴展是否啟用
- 清除瀏覽器緩存並重試
9.3 合約方法調用失敗
問題:調用合約時出現 revert
解決方案:
- 使用 try-catch 捕獲錯誤
- 檢查合約函數參數類型
- 確認合約已正確部署
9.4 Hardhat 節點連接問題
問題:無法連接到本地 Hardhat 節點
解決方案:
- 確保 Hardhat 節點正在運行
- 檢查 port 8545 是否被佔用
- 確認 network 配置正確
十、總結
Scaffold-ETH 為以太坊應用開發提供了完整的解決方案。通過整合 Hardhat、Next.js、Wagmi 等工具,它大幅降低了 Web3 開發的門檻,使開發者能夠專注於業務邏輯而非基礎設施。
掌握 Scaffold-ETH 意味著:
- 能夠快速搭建 DeFi、NFT、DAO 等各類應用
- 理解全棧以太坊開發工作流
- 為未來更複雜的 Web3 項目奠定基礎
建議開發者從簡單的項目開始,逐步掌握框架的各個方面,然後挑戰更複雜的去中心化應用。隨著經驗積累,你將能夠充分利用 Scaffold-ETH 的靈活性,構建真正創新的區塊鏈應用。
相關文章
- 智能合約部署實戰:從環境搭建到主網上線 — 涵蓋從開發環境準備、本地測試、測試網部署、正式環境部署的完整流程,深入探討 Gas 優化、安全審計、合約升級等進階主題,提供開發者從新手到專業的實戰指南。
- Noir 隱私合約開發完整指南:從零知識證明到實際應用 — 深入介紹 Noir 語言的開發環境、語法特性與實際應用,包括零知識證明基礎、承諾方案實現、以及如何在以太坊上構建隱私保護應用,提供完整的程式碼範例與開發實踐指南。
- Solidity 隱私合約開發進階指南:承諾、Merkle 樹與零知識證明整合 — 全面解析使用 Solidity 構建隱私保護智能合約的核心技術,包括 Pedersen 承諾、同態加密 Merkle 樹、稀疏 Merkle 證明、以及 Groth16 和 PLONK 驗證合約的整合,並提供完整的實際應用案例與程式碼範例。
- EIP-1559 深度解析:以太坊費用市場的範式轉移 — 深入解析 EIP-1559 的費用結構、ETH 燃燒機制、經濟學意涵,以及對用戶、驗證者和生態系統的影響。
- EIP-7702 帳戶抽象完整指南 — 深入介紹 EIP-7702 讓 EOA 臨時獲得合約功能的技术原理,涵蓋社交恢復錢包、自動化交易、權限委托等應用場景。
延伸閱讀與來源
- Ethereum.org Developers 官方開發者入口與技術文件
- EIPs 以太坊改進提案
這篇文章對您有幫助嗎?
請告訴我們如何改進:
評論
發表評論
注意:由於這是靜態網站,您的評論將儲存在本地瀏覽器中,不會公開顯示。
目前尚無評論,成為第一個發表評論的人吧!