以太坊第一個 DApp 開發完整教學:從零到部署
帶領讀者從零開始,開發並部署第一個以太坊 DApp。詳細講解智慧合約開發、前端整合、錢包連接、測試網部署等完整流程,涵蓋 Hardhat、Solidity、React、Ethers.js 等主流開發工具。
以太坊第一個 DApp 開發完整教學:從零到部署
概述
去中心化應用程式(Decentralized Application,簡稱 DApp)是建立在區塊鏈之上的應用程式,透過智慧合約實現業務邏輯,使用戶能夠在無需信任第三方的情況下進行交易和互動。以太坊是目前最成熟的 DApp 開發平台,擁有完整的開發工具生態系統和豐富的學習資源。
本文將帶領讀者從零開始,開發並部署第一個以太坊 DApp。我們將建立一個簡單的「留言板」應用程式,讓用戶可以在區塊鏈上發布和查看留言。這個專案雖然簡單,但涵蓋了 DApp 開發的核心概念:智慧合約編寫、前端介面開發、錢包連接、區塊鏈交互等。
本教學適合具備基礎程式設計經驗(JavaScript、Solidity 基礎)的讀者。我們將使用主流的開發工具和框架,包括 Hardhat、Solidity、Ethers.js、React,讓讀者能夠掌握業界實際使用的開發流程。
第一章:開發環境建置
1.1 必需工具安裝
在開始開發之前,需要安裝以下工具:
Node.js 與 npm:
# 檢查 Node.js 版本(需要 v14+)
node --version
# 檢查 npm 版本
npm --version
Git:
# 檢查 Git 版本
git --version
程式碼編輯器:
推薦使用 Visual Studio Code,並安裝以下擴充套件:
- Solidity(Nomic Foundation)
- Prettier - Code formatter
- ESLint
1.2 建立專案目錄
# 建立專案資料夾
mkdir my-first-dapp
cd my-first-dapp
# 初始化 npm 專案
npm init -y
1.3 安裝開發依賴
# 安裝 Hardhat - 以太坊開發環境
npm install --save-dev hardhat
# 安裝 Ethers.js - 以太坊區塊鏈交互庫
npm install ethers
# 安裝 Wagmi - React Hooks 庫
npm install wagmi viem
# 安裝 Vite - 前端建置工具
npm create vite@latest frontend -- --template react
cd frontend
npm install
npm install ethers wagmi viem @tanstack/react-query
1.4 初始化 Hardhat
# 回到專案根目錄
cd ..
# 初始化 Hardhat 專案
npx hardhat init
選擇「Create a JavaScript project」,Hardhat 會自動建立基本的專案結構:
my-first-dapp/
├── contracts/ # 智慧合約目錄
├── scripts/ # 部署腳本目錄
├── test/ # 測試目錄
├── hardhat.config.js # Hardhat 設定檔
└── package.json
第二章:智慧合約開發
2.1 撰寫留言板合約
在 contracts/ 目錄下建立 Guestbook.sol 檔案:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
/**
* @title Guestbook
* @dev 簡單的區塊鏈留言板合約
* @notice 允許用戶發布和查看留言
*/
contract Guestbook {
// 留言結構
struct Message {
address author; // 留言者地址
string content; // 留言內容
uint256 timestamp; // 留言時間
}
// 存儲所有留言的陣列
Message[] public messages;
// 記錄用戶的留言數量
mapping(address => uint256[]) public userMessages;
// 事件:當有新留言時觸發
event NewMessage(address indexed author, string content, uint256 timestamp);
/**
* @dev 發布新留言
* @param _content 留言內容
*/
function postMessage(string memory _content) public {
require(bytes(_content).length > 0, "留言內容不能為空");
require(bytes(_content).length <= 500, "留言內容不能超過 500 字元");
Message memory newMessage = Message({
author: msg.sender,
content: _content,
timestamp: block.timestamp
});
messages.push(newMessage);
userMessages[msg.sender].push(messages.length - 1);
emit NewMessage(msg.sender, _content, block.timestamp);
}
/**
* @dev 獲取所有留言數量
* @return 留言總數
*/
function getMessageCount() public view returns (uint256) {
return messages.length;
}
/**
* @dev 獲取指定範圍內的留言
* @param _start 起始索引
* @param _limit 最大數量
* @return 留言陣列
*/
function getMessages(uint256 _start, uint256 _limit)
public
view
returns (Message[] memory)
{
uint256 length = _limit;
if (_start + _limit > messages.length) {
length = messages.length - _start;
}
Message[] memory result = new Message[](length);
for (uint256 i = 0; i < length; i++) {
result[i] = messages[_start + i];
}
return result;
}
/**
* @dev 獲取用戶的所有留言
* @param _user 用戶地址
* @return 用戶的所有留言
*/
function getUserMessages(address _user)
public
view
returns (Message[] memory)
{
uint256[] storage indices = userMessages[_user];
Message[] memory result = new Message[](indices.length);
for (uint256 i = 0; i < indices.length; i++) {
result[i] = messages[indices[i]];
}
return result;
}
}
2.2 合約編譯
# 編譯智慧合約
npx hardhat compile
成功編譯後,會在 artifacts/ 目錄產生合約 ABI 和 Bytecode。
2.3 撰寫測試
在 test/ 目錄下建立 Guestbook.js 測試檔案:
const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("Guestbook", function () {
let guestbook;
let owner;
let user1;
let user2;
beforeEach(async function () {
// 取得測試帳戶
[owner, user1, user2] = await ethers.getSigners();
// 部署合約
const Guestbook = await ethers.getContractFactory("Guestbook");
guestbook = await Guestbook.deploy();
await guestbook.waitForDeployment();
});
describe("postMessage", function () {
it("應該允許用戶發布留言", async function () {
const tx = await guestbook.connect(user1).postMessage("Hello Web3!");
await tx.wait();
const messageCount = await guestbook.getMessageCount();
expect(messageCount).to.equal(1);
});
it("應該在內容為空時 revert", async function () {
await expect(
guestbook.connect(user1).postMessage("")
).to.be.revertedWith("留言內容不能為空");
});
it("應該在內容過長時 revert", async function () {
const longContent = "a".repeat(501);
await expect(
guestbook.connect(user1).postMessage(longContent)
).to.be.revertedWith("留言內容不能超過 500 字元");
});
it("應該正確記錄留言者地址", async function () {
await guestbook.connect(user1).postMessage("Test message");
const messages = await guestbook.getMessages(0, 1);
expect(messages[0].author).to.equal(user1.address);
});
});
describe("getMessages", function () {
beforeEach(async function () {
// 發布多條留言
await guestbook.connect(user1).postMessage("Message 1");
await guestbook.connect(user2).postMessage("Message 2");
await guestbook.connect(user1).postMessage("Message 3");
});
it("應該返回正確數量的留言", async function () {
const messages = await guestbook.getMessages(0, 10);
expect(messages.length).to.equal(3);
});
it("應該正確返回指定範圍的留言", async function () {
const messages = await guestbook.getMessages(1, 2);
expect(messages.length).to.equal(2);
expect(messages[0].content).to.equal("Message 2");
});
});
describe("getUserMessages", function () {
it("應該返回用戶的所有留言", async function () {
await guestbook.connect(user1).postMessage("User1 msg 1");
await guestbook.connect(user1).postMessage("User1 msg 2");
await guestbook.connect(user2).postMessage("User2 msg 1");
const user1Messages = await guestbook.getUserMessages(user1.address);
expect(user1Messages.length).to.equal(2);
});
});
});
執行測試:
npx hardhat test
第三章:本機部署與測試
3.1 啟動本機測試網路
# 啟動本機 Hardhat 節點
npx hardhat node
這會啟動一個本機以太坊測試網路,提供 20 個測試帳戶,每個帳戶有 10,000 ETH(測試用)。
3.2 部署腳本
建立 scripts/deploy.js 部署腳本:
const hre = require("hardhat");
async function main() {
console.log("開始部署 Guestbook 合約...");
// 取得合約工廠
const Guestbook = await hre.ethers.getContractFactory("Guestbook");
// 部署合約
const guestbook = await Guestbook.deploy();
// 等待部署完成
await guestbook.waitForDeployment();
// 取得合約地址
const contractAddress = await guestbook.getAddress();
console.log(`Guestbook 合約已部署至: ${contractAddress}`);
// 驗證合約
console.log("\n驗證合約功能...");
// 發布測試留言
const tx = await guestbook.postMessage("Hello from Hardhat!");
await tx.wait();
const messageCount = await guestbook.getMessageCount();
console.log(`留言數量: ${messageCount}`);
const messages = await guestbook.getMessages(0, 1);
console.log(`第一條留言: ${messages[0].content}`);
console.log(`留言者: ${messages[0].author}`);
console.log("\n部署成功!");
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
3.3 部署到本機網路
# 在另一個終端執行
npx hardhat run scripts/deploy.js --network localhost
第四章:前端開發
4.1 專案結構
在 frontend/src/ 目錄下建立以下檔案結構:
frontend/src/
├── components/
│ ├── ConnectWallet.jsx # 錢包連接元件
│ ├── MessageBoard.jsx # 留言板元件
│ └── MessageForm.jsx # 發布留言表單
├── hooks/
│ └── useGuestbook.js # 自定義鉤子
├── abi/
│ └── guestbook.json # 合約 ABI
├── App.jsx # 主應用程式
├── main.jsx # 進入點
└── index.css # 樣式
4.2 配置 Web3 提供者
修改 frontend/src/main.jsx:
import React from 'react'
import ReactDOM from 'react-dom/client'
import { WagmiProvider } from 'wagmi'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { http, createConfig } from 'wagmi'
import { mainnet, sepolia } from 'wagmi/chains'
import { injected } from 'wagmi/connectors'
import App from './App'
import './index.css'
// 配置 Wagmi
const config = createConfig({
chains: [mainnet, sepolia],
connectors: [
injected() // 瀏覽器錢包(如 MetaMask)
],
transports: {
[mainnet.id]: http(),
[sepolia.id]: http(),
},
})
const queryClient = new QueryClient()
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<WagmiProvider config={config}>
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
</WagmiProvider>
</React.StrictMode>,
)
4.3 建立 ABI 檔案
將編譯後的 ABI 複製到 frontend/src/abi/guestbook.json:
[
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "author",
"type": "address"
},
{
"indexed": false,
"internalType": "string",
"name": "content",
"type": "string"
},
{
"indexed": false,
"internalType": "uint256",
"name": "timestamp",
"type": "uint256"
}
],
"name": "NewMessage",
"type": "event"
},
{
"inputs": [
{
"internalType": "string",
"name": "_content",
"type": "string"
}
],
"name": "postMessage",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "getMessageCount",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_start",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_limit",
"type": "uint256"
}
],
"name": "getMessages",
"outputs": [
{
"components": [
{
"internalType": "address",
"name": "author",
"type": "address"
},
{
"internalType": "string",
"name": "content",
"type": "string"
},
{
"internalType": "uint256",
"name": "timestamp",
"type": "uint256"
}
],
"internalType": "struct Guestbook.Message[]",
"name": "",
"type": "tuple[]"
}
],
"stateMutability": "view",
"type": "function"
}
]
4.4 建立自定義 Hook
建立 frontend/src/hooks/useGuestbook.js:
import { useState, useEffect } from 'react'
import { useReadContract, useWriteContract, useAccount } from 'wagmi'
import { ethers } from 'ethers'
import guestbookABI from '../abi/guestbook.json'
const CONTRACT_ADDRESS = '0x5FbDB2315678afecb367f032d93F642f64180aa3' // 本機部署地址
export function useGuestbook() {
const { address, isConnected } = useAccount()
const [messages, setMessages] = useState([])
const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState(null)
// 讀取合約:取得留言數量
const { data: messageCount } = useReadContract({
address: CONTRACT_ADDRESS,
abi: guestbookABI,
functionName: 'getMessageCount',
})
// 讀取合約:取得留言列表
const { refetch: fetchMessages } = useReadContract({
address: CONTRACT_ADDRESS,
abi: guestbookABI,
functionName: 'getMessages',
args: [0n, 50n],
query: {
enabled: !!messageCount,
onSuccess: (data) => {
setMessages(data || [])
}
}
})
// 寫入合約:發布留言
const { writeContract: postMessage, isPending: isPosting } = useWriteContract()
const publishMessage = async (content) => {
if (!isConnected) {
setError('請先連接錢包')
return
}
setIsLoading(true)
setError(null)
try {
await postContract({
address: CONTRACT_ADDRESS,
abi: guestbookABI,
functionName: 'postMessage',
args: [content],
})
// 重新整理留言列表
await fetchMessages()
} catch (err) {
setError(err.message || '發布失敗')
} finally {
setIsLoading(false)
}
}
return {
messages,
messageCount: messageCount ? Number(messageCount) : 0,
isLoading,
isPosting,
error,
publishMessage,
refreshMessages: fetchMessages,
}
}
// 輔助函數:格式化地址
export function formatAddress(address) {
if (!address) return ''
return `${address.slice(0, 6)}...${address.slice(-4)}`
}
// 輔助函數:格式化時間
export function formatTimestamp(timestamp) {
if (!timestamp) return ''
const date = new Date(Number(timestamp) * 1000)
return date.toLocaleString('zh-TW')
}
4.5 建立錢包連接元件
建立 frontend/src/components/ConnectWallet.jsx:
import { useAccount, useConnect, useDisconnect } from 'wagmi'
import { injected } from 'wagmi/connectors'
export function ConnectWallet() {
const { address, isConnected } = useAccount()
const { connect } = useConnect({
connector: injected(),
})
const { disconnect } = useDisconnect()
if (isConnected) {
return (
<div className="wallet-info">
<span className="wallet-address">
{address.slice(0, 6)}...{address.slice(-4)}
</span>
<button onClick={() => disconnect()} className="disconnect-btn">
斷開連接
</button>
</div>
)
}
return (
<button onClick={() => connect()} className="connect-btn">
連接錢包
</button>
)
}
4.6 建立留言表單元件
建立 frontend/src/components/MessageForm.jsx:
import { useState } from 'react'
import { useAccount } from 'wagmi'
export function MessageForm({ onSubmit, isPosting }) {
const [content, setContent] = useState('')
const { isConnected } = useAccount()
const handleSubmit = (e) => {
e.preventDefault()
if (!content.trim()) return
onSubmit(content)
setContent('')
}
return (
<form onSubmit={handleSubmit} className="message-form">
<textarea
value={content}
onChange={(e) => setContent(e.target.value)}
placeholder={isConnected ? "寫下你的留言..." : "請先連接錢包"}
disabled={!isConnected || isPosting}
maxLength={500}
/>
<div className="form-footer">
<span className="char-count">{content.length}/500</span>
<button
type="submit"
disabled={!isConnected || !content.trim() || isPosting}
className="submit-btn"
>
{isPosting ? '發布中...' : '發布留言'}
</button>
</div>
</form>
)
}
4.7 建立留言板元件
建立 frontend/src/components/MessageBoard.jsx:
import { useGuestbook, formatAddress, formatTimestamp } from '../hooks/useGuestbook'
export function MessageBoard() {
const { messages, messageCount, isLoading, refreshMessages } = useGuestbook()
if (isLoading) {
return <div className="loading">載入中...</div>
}
return (
<div className="message-board">
<div className="board-header">
<h2>留言板</h2>
<span className="message-count">共 {messageCount} 條留言</span>
</div>
{messages.length === 0 ? (
<div className="empty-state">
尚無留言,成為第一個發布留言的人吧!
</div>
) : (
<div className="messages-list">
{messages.slice().reverse().map((message, index) => (
<div key={index} className="message-item">
<div className="message-header">
<span className="message-author">{formatAddress(message.author)}</span>
<span className="message-time">{formatTimestamp(message.timestamp)}</span>
</div>
<div className="message-content">{message.content}</div>
</div>
))}
</div>
)}
<button onClick={() => refreshMessages()} className="refresh-btn">
刷新
</button>
</div>
)
}
4.8 主應用程式
修改 frontend/src/App.jsx:
import { useAccount } from 'wagmi'
import { ConnectWallet } from './components/ConnectWallet'
import { MessageForm } from './components/MessageForm'
import { MessageBoard } from './components/MessageBoard'
import { useGuestbook } from './hooks/useGuestbook'
function App() {
const { isConnected } = useAccount()
const { publishMessage, isPosting, error } = useGuestbook()
return (
<div className="app">
<header className="header">
<h1>🌐 以太坊留言板</h1>
<ConnectWallet />
</header>
<main className="main">
<section className="form-section">
<MessageForm onSubmit={publishMessage} isPosting={isPosting} />
{error && <div className="error-message">{error}</div>}
</section>
<section className="board-section">
<MessageBoard />
</section>
</main>
<footer className="footer">
<p>這是一個部署在以太坊區塊鏈上的去中心化應用</p>
<p>所有留言都會被永久記錄在區塊鏈上</p>
</footer>
</div>
)
}
export default App
4.9 添加樣式
修改 frontend/src/index.css:
:root {
--primary: #627eea;
--background: #f5f5f5;
--card-bg: #ffffff;
--text: #333333;
--text-secondary: #666666;
--border: #e0e0e0;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: var(--background);
color: var(--text);
line-height: 1.6;
}
.app {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 0;
border-bottom: 1px solid var(--border);
}
.header h1 {
font-size: 1.5rem;
color: var(--primary);
}
.connect-btn, .disconnect-btn {
padding: 10px 20px;
border: none;
border-radius: 8px;
cursor: pointer;
font-weight: 600;
transition: all 0.2s;
}
.connect-btn {
background: var(--primary);
color: white;
}
.disconnect-btn {
background: #f0f0f0;
color: #666;
}
.main {
padding: 30px 0;
}
.form-section {
margin-bottom: 30px;
}
.message-form {
background: var(--card-bg);
border-radius: 12px;
padding: 20px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.message-form textarea {
width: 100%;
min-height: 100px;
padding: 15px;
border: 1px solid var(--border);
border-radius: 8px;
font-size: 1rem;
resize: vertical;
}
.form-footer {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 15px;
}
.char-count {
color: var(--text-secondary);
font-size: 0.875rem;
}
.submit-btn {
padding: 10px 24px;
background: var(--primary);
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
font-weight: 600;
}
.submit-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.error-message {
margin-top: 10px;
padding: 10px;
background: #fee;
color: #c00;
border-radius: 8px;
}
.message-board {
background: var(--card-bg);
border-radius: 12px;
padding: 20px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.board-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid var(--border);
}
.message-count {
color: var(--text-secondary);
}
.empty-state {
text-align: center;
padding: 40px;
color: var(--text-secondary);
}
.messages-list {
display: flex;
flex-direction: column;
gap: 15px;
}
.message-item {
padding: 15px;
background: #f9f9f9;
border-radius: 8px;
border-left: 4px solid var(--primary);
}
.message-header {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
font-size: 0.875rem;
}
.message-author {
color: var(--primary);
font-weight: 600;
}
.message-time {
color: var(--text-secondary);
}
.message-content {
white-space: pre-wrap;
word-break: break-word;
}
.refresh-btn {
margin-top: 20px;
width: 100%;
padding: 10px;
background: #f0f0f0;
border: none;
border-radius: 8px;
cursor: pointer;
}
.footer {
text-align: center;
padding: 30px 0;
color: var(--text-secondary);
font-size: 0.875rem;
}
第五章:部署上線
5.1 部署到 Sepolia 測試網路
首先,需要設定 Hardhat 設定檔以支援 Sepolia 網路:
// hardhat.config.js
require("@nomicfoundation/hardhat-toolbox");
require("dotenv").config();
/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
solidity: "0.8.19",
networks: {
sepolia: {
url: process.env.SEPOLIA_RPC_URL,
accounts: [process.env.PRIVATE_KEY],
},
},
};
建立 .env 檔案(請勿提交到 Git):
SEPOLIA_RPC_URL=your_sepolia_rpc_url_here
PRIVATE_KEY=your_wallet_private_key_here
取得 Sepolia 測試 ETH:
- 前往 https://faucet.sepolia.dev/ 或其他水龍頭
- 輸入錢包地址領取測試 ETH
部署到 Sepolia:
npx hardhat run scripts/deploy.js --network sepolia
部署成功後,會得到合約地址。將其更新到前端的 CONTRACT_ADDRESS 常數中。
5.2 建置前端
cd frontend
npm run build
5.3 部署到 Vercel
# 安裝 Vercel CLI
npm install -g vercel
# 部署
vercel
按照指示完成部署,幾分鐘後就可以透過 Vercel 提供的網址訪問你的 DApp。
第六章:開發安全須知
6.1 智慧合約安全檢查清單
在部署到主網之前,請確保:
- [ ] 合約通過完整測試覆蓋
- [ ] 使用知名審計服務審計(如有)
- [ ] 設定合理的 Gas 限制
- [ ] 考慮升級機制(可選)
- [ ] 設定 Emergency Withdrawal 功能(可選)
6.2 常見智慧合約漏洞
重入攻擊(Reentrancy):
// 不安全的寫法
function withdraw() external {
(bool success, ) = msg.sender.call{value: balance}("");
require(success);
balance[msg.sender] = 0;
}
// 安全的寫法(Checks-Effects-Interactions)
function withdraw() external {
uint256 amount = balance[msg.sender];
require(amount > 0, "No balance");
balance[msg.sender] = 0;
(bool success, ) = msg.sender.call{value: amount}("");
require(success);
}
整數溢位:
Solidity 0.8+ 內建整數溢位檢查,但仍需注意。
6.3 錢包安全
- 不要將私鑰提交到 Git
- 使用環境變數管理敏感資訊
- 使用硬體錢包進行實際資金操作
- 小額測試後再進行大額交易
結論
恭喜你!已經完成了第一個以太坊 DApp 的開發。這個留言板專案雖然簡單,但涵蓋了 DApp 開發的核心概念和流程:
- 智慧合約開發:使用 Solidity 編寫區塊鏈邏輯
- 本機測試:使用 Hardhat 進行開發和測試
- 前端整合:使用 Ethers.js/Wagmi 與區塊鏈交互
- 部署上線:將應用部署到測試網路和正式環境
這只是開始!以太坊生態系統還有更多值得探索的領域:
- 更複雜的 DeFi 協議
- NFT 鑄造和交易
- 去中心化自治組織(DAO)
- 跨鏈應用
建議下一步學習方向:
- 深入學習 Solidity 和智能合約安全
- 探索 OpenZeppelin 合約庫
- 學習 ERC-20、ERC-721 等代幣標準
- 實作更複雜的 DeFi 應用
參考資源
- Ethereum Foundation - 官方文檔
- Hardhat - 以太坊開發環境
- Solidity - 智慧合約語言
- Wagmi - React Hooks 庫
- OpenZeppelin - 智能合約庫
相關文章
- 以太坊智能合約開發實作教程:從環境搭建到部署上線完整指南 — 本教程帶領讀者從零開始,建立完整的以太坊開發環境,撰寫第一個智能合約,並將其部署到測試網絡和主網。我們使用 Solidity 作為合約語言,Hardhat 作為開發框架,提供一步一步的詳細操作指南。內容涵蓋 Hardhat 專案初始化、ERC-20 代幣合約撰寫、單元測試、Sepolia 測試網絡部署、以及主網部署等完整流程。
- 以太坊智能合約開發實戰:從基礎到 DeFi 協議完整代碼範例指南 — 本文提供以太坊智能合約開發的完整實戰指南,透過可直接運行的 Solidity 代碼範例,幫助開發者從理論走向實踐。內容涵蓋基礎合約開發、借貸協議實作、AMM 機制實現、以及中文圈特有的應用場景(台灣交易所整合、香港監管合規、Singapore MAS 牌照申請)。本指南假設讀者具備基本的程式設計基礎,熟悉 JavaScript 或 Python 等語言,並對區塊鏈概念有基本理解。
- Solidity 智慧合約開發基礎 — Solidity 是以太坊智慧合約的主要程式語言,專為區塊鏈上的去中心化應用設計。自 2014 年首次發布以來,Solidity 已經成為智慧合約開發的業界標準,被廣泛應用於 DeFi、NFT、DAO 等各種區塊鏈應用。
- Flashbots MEV-Boost 完整指南:以太坊 MEV 基礎設施深度解析 — Flashbots 是以太坊生態系統中最重要的 MEV(最大可提取價值)基礎設施之一。自 2020 年成立以來,Flashbots 從一個研究組織發展成為涵蓋 MEV 提取、交易隱私、去中心化排序等多個領域的綜合性平台。MEV-Boost 作為 Flashbots 的核心產品,已經成為以太坊網路中不可或缺的基礎設施,顯著改變了 MEV 的分配方式和區塊生產的生態格局。本文深入解析 Flashbot
- Solidity 智慧合約實戰範例完整指南:2026 年最新語法與最佳實踐 — Solidity 是以太坊智慧合約開發的主要程式語言,近年來持續演進。2025-2026 年,Solidity 語言在類型安全、Gas 優化、合約可升級性等方面都有重要更新。本文提供全面的 Solidity 實戰範例,涵蓋從基礎合約到進階模式的完整程式碼,幫助開發者快速掌握 2026 年最新的 Solidity 開發技術。
延伸閱讀與來源
- Ethereum.org Developers 官方開發者入口與技術文件
- EIPs 以太坊改進提案
這篇文章對您有幫助嗎?
請告訴我們如何改進:
評論
發表評論
注意:由於這是靜態網站,您的評論將儲存在本地瀏覽器中,不會公開顯示。
目前尚無評論,成為第一個發表評論的人吧!