昨天晚上受到 LXDAO Vdel 老師的邀請給 web3 實習計劃的同學們講一下 DAPP 開發。乍一想,其實連錢包的需要鏈上交互的不就是 DAPP 呗,也沒多複雜,更何況現在的 AI 這麼強,想寫個 DAPP 不是分分鐘的事情。於是我便答應了下來,心想,這不就是個 web3 的 hello world 嗎?
後來,忙完工作後想了下,我可能是受到 "知識的詛咒" 了。回憶了一下自己第一次打黑客松,做 LXDAO Marry3 的仿品 Ring3 時的迷茫無助,想想這幾年打黑客松、做項目在不同的鏈摸爬滾打的經歷,突然覺得還是值得一說的。同時,這也是一個非常好的機會整理下我這幾年的經驗,準備課程的同時,寫一篇博客。
什麼是 DAPP?#
通常的定義是:DAPP,全稱為 Decentralized Application,中文翻譯為去中心化應用,是運行在區塊鏈上的應用,與傳統的中心化應用不同,DAPP 不依賴於中心化的伺服器,而是依賴於區塊鏈的去中心化特性,因此 DAPP 具有去中心化、去信任、去中介的特性。
以上是一個非常官方的介紹,但實際上如果嚴格按著官方伺服器去做 DAPP,讓 everything decentralized 用戶體驗會非常差,比如難以訪問、速度慢、流程複雜等等。事實上,就算是區塊鏈最偉大的發明之一 —— 去中心化交易所 Uniswap,也不是完全去中心化的,具體來說,比如 uniswap 上的代幣列表,如果每次都從區塊鏈上讀,可能得等到猴年馬月;還有一些價格、滑點、交易量、TVL、流動性池等等諸如此類偏數據查詢的數據都是通過中心化服務檢索整理存儲區塊鏈數據後,提供 web api/GraphQL 接口,再通過前端調用這些接口,提供給用戶使用的。
那麼話又說回來,到底什麼是去中心化應用(DAPP)呢?用我的一句話來說,需要鏈上交互的,進行寫入 / 讀取操作的,都可以算作是 DAPP,只不過有著程度之分。DAPP 和我們傳統的 web 開發不是對立的,還是可以彼此兼容的,我們可以通過維恩圖來分析一個產品的去中心化程度:
整個紫色區域我認為是可以被定義為 DAPP 的區域,重點關注幾個區域:
- 棕色區域是絕大多數 DAPP:如 uniswap、pendle、opensea、ens 等大多數知名的 DAPP 都是前端後端既有去中心化技術也有中心化技術的。前端通常不會使用 IPFS 這樣的技術部署服務,但可能通過去中心化的技術 CDN 去提升用戶體驗。後端服務的底層是基於去中心化的區塊鏈技術的,核心服務用戶是直接與鏈交互的,但都會通過中心化的數據庫伺服器同步區塊鏈上數據,形成索引,提升數據查詢速度,本質也是提升用戶體驗。
- 再看棕色區域下面的 "簡單 DAPP" 區域,這個區域的應用通常功能相對簡單,具體來說可能只需要讀取某個地址的資產情況,或者進行一些簡單的寫入操作,比如 mint nft、transfer nft 等等。這類 DAPP 在 2021 年左右尤為盛行,當時是 NFT 的旺季,很多項目方會做一些酷炫的網頁來提升自己 NFT 的品牌形象,以賣出更多的 NFT。這些 DAPP 會去判斷用戶有沒有資格 Mint,能 mint 幾個 NFT,然後讓用戶調用智能合約進行 Mint,再將 Mint 後的 NFT 展示給用戶。順便一提,LXDAO 的 0 號項目良心 NFT 也是誕生在那個時期的,但良心 NFT 在整個市場中獨樹一幟,網頁風格樸實但功能完善,客觀介紹 NFT 的功能,沒有花裡胡哨的噱頭,可以理解為是一場單純的藝術品銷售網站。當時的價格只要 0.01ETH 一個,現在一個難求
- 然後看棕色區域的 "極端 DAPP" 區域。在這裡咱們先不討論 CDN 的內容分發網絡,這服務本身確實是去中心化的,但仍然面臨 CDN 服務商中心化的問題,但大多數時候我們去中心化應用可以接受沒有交互界面的前提下使用。這裡討論的去中心化前端就是想避免被網絡服務商中心化的問題,比如:Tornado.cash,這個說白了就是利用區塊鏈技術洗錢的應用,誰來維護他就會有法律風險,任何伺服器提供商都不會希望自己的伺服器會用來部署這樣的東西的。但技術是無罪的,社區就想到了個絕妙的主意把 Tornado.cash 的前端代碼部署到了 IPFS 上(下一章有介紹),簡單地說像把商店複製成傳單,分發給很多人保存,封了個地方,其他地方還有。當然代價就是這個網站訪問起來巨慢無比。所以這也不是常見的方案。
- 寬泛的說,其實 DAPP 的核心就是一整套的智能合約,只有智能合約就可以構成一個 DAPP,但區塊鏈不是只有程序員,想要有更多的用戶,還是得有基礎的交互界面。
總結一下,有沒有和鏈交互、核心是不是圍繞智能合約展開是判斷一個 DAPP 的最基本標準,中心化的前端後端是可以與 DAPP 兼容的,對提升用戶體驗有非常重要的作用。
DAPP 的基本架構#
綜合我們上文對 DAPP 的理解,其實 DAPP 的基本架構簡單的來看可以分為前端、後端、智能合約三個部分,前端和後端是中心化的,智能合約是去中心化的。哈哈,當然沒這麼簡單啦,要這麼簡單就沒什麼可以寫的了。除了上文的內容,還有一些重要的基礎服務支持 DAPP 的運行,我給出了詳細的解釋,他們的關係如下圖,也沒多少好說的:
IPFS#
中文名叫星際文件系統,可以理解為一個去中心化的文件存儲系統,但沒有區塊鏈複雜的共識機制,會通過核對哈希值的方式區別 / 檢索文件。
具體但簡單的說,你可以理解為,有一群好心人在互聯網上維護了一套免費的存儲設施,上傳文件會被分佈式的存儲在很多台全球各地的存儲設施上(就是部分伺服器上都有一份),通過計算文件哈希值來避免重複存儲,同時用戶也可以通過文件哈希值找到對應文件的完整數據。
當然天下沒有那麼好的事情,免費的代價就是你的文件可能隨時被刪除,但是,IPFS 也提供了一些付費的存儲服務,通過 Filecoin(區塊鏈)來保證文件的存儲。
RPC 服務商#
事實上普通應用無法也無需直接與以太坊網絡進行交互,詳細的區塊鏈底層知識就不在這裡細說了,有興趣的朋友可以自己查一下。
DAPP 只需要 "使用" 區塊鏈,不需要 "維護" 區塊鏈,"維護" 區塊鏈才是要與鏈直接交互的操作,也就是參與區塊鏈的挖礦 / 共識,這需要同步龐大的區塊鏈數據,但 "使用" 區塊鏈不需要關心那麼龐大的數據,和普通的 web 服務相似,只需要開放一些接口就可以了。
RPC(遠程過程調用 remote procedure call)服務商本質上就是提供一個 API 接口,只不過有一點特殊的格式要求,RPC 服務商通常運行著不同類型的以太坊節點(包括全節點、輕節點或歸檔節點),並將節點功能通過 JSON-RPC 接口暴露出來。這些接口遵循以太坊的 JSON-RPC 規範,讓開發者可以通過 HTTP 或 WebSocket 請求來查詢區塊鏈數據、發送交易、調用智能合約等。
索引服務商#
可以先看看 RPC 再來理解索引服務。這需要說得再細一點,當你用 RPC 去讀取區塊鏈數據時,你不能像使用數據庫一樣自由地查詢你需要的數據。通常你只能讀取一個合約或一個錢包當前的狀態(比如餘額),或者查詢單個交易 / 區塊的詳細信息,或者監聽一些事件的發生(比如有沒有新的 NFT 被 mint,有沒有新的交易)。
但作為一個 DApp,通常會需要查詢某個合約從部署到現在所有的交易情況。具體點說,可能要查某個 NFT 合約的所有持倉者。如果沒有索引服務,你就需要從合約部署的區塊開始,一個區塊一個區塊地去記錄相關 NFT 的 mint、transfer、burn 等交易。
理論上講這是一個沒那麼 "必要" 的服務,如果你的項目體量不大,完全可以自己做這件事情。但是這種服務的需求是相似的、通用的,所以就有了索引服務。它們檢查每一個區塊,把相關交易記錄到數據庫裡,方便開發者用常規的數據查詢語句(通常會提供 GraphQL API)進行查詢,幫大家減少開發的負擔。
預言機(Oracle)#
Oracle 這個名字聽起來很高大上,但其實就是區塊鏈與現實世界的橋梁。思考一個問題,區塊鏈怎麼知道當前 ETH 的價格是多少呢?直觀的說是不是可以部署一個智能合約,然後找一個可以信任的角色把價格告訴智能合約,智能合約再根據這個價格進行後續操作。但問題來了,誰來保證這個角色是可信的呢?我們當然不能只根據一個角色的消息就判斷消息的真假,結果的驗證也得去中心化,預言機就應運而生了。
預言機的基本原理是通過多個數據源和多個數據提供者來確保數據的可靠性。以 ETH 價格為例,預言機網絡會從 Coinbase、Binance、Kraken 等多個交易所獲取 ETH/USD 的價格數據,然後通過加權平均算法計算出一個最終價格。如果某個交易所提供的價格與其他大多數交易所差異過大,會被系統識別並排除。同時,數據提供者(預言機節點)需要質押代幣作為保證金,如果提供錯誤的價格數據會被罰沒代幣,這樣就形成了經濟激勵來保證數據準確性。
最著名的預言機項目是 Chainlink,它建立了一個龐大的預言機網絡,為幾千個 DeFi DAPP 提供實時的價格數據。當你在 Uniswap 上交易時,或者在 Aave 上借貸時,這些協議都需要知道各種代幣的準確價格來計算交易比率或清算條件,而這些價格數據就是通過 Chainlink 等預言機提供的。
工具鏈推薦#
在這一部分我會首先介紹比較通用的前後端開發工具鏈,再介紹我接觸到的一些鏈的工具鏈。
通用技術#
個人非常推薦搞 DApp 開發的優先考慮學好 JS,因為你會了 JS 幾乎就可以一個人跑通全流程了。
前端技術棧:
- 前端框架:Next.js
- 樣式庫:Tailwind CSS
- UI 庫:shadcn/ui
- 替代樣式方案:bruce 老師的推文
- 狀態管理:Zustand
- 表單驗證:Zod
- 新興技術棧:TanStack
部署和工具:
- 簡單部署:Vercel
- 運維方案:Coolify
- 設計工具:Figma
- 原型工具:Excalidraw
後端技術棧:
- 簡單後端:Next.js 的 API Routes + Vercel serverless 函數
- 複雜後端:NestJS
- ORM:Prisma
- 數據庫:PostgreSQL
- 部署平台:NorthFlank
存儲服務:
成本優化:
- 推薦搜索 "獨立開發 窮鬼套餐" 獲取更多經濟方案
生態工具鏈#
區塊鏈 | 錢包插件鏈接 | 合約 / 鏈交互庫 | 合約開發框架 / 語言 | RPC 提供商 | 索引服務 |
---|---|---|---|---|---|
ETH | RainbowKit, ConnectKit, Web3Modal | ethers.js, viem, web3.js | Foundry, Hardhat, Truffle (Solidity 語言) | Alchemy, Infura, QuickNode | The Graph, Moralis, Alchemy |
Solana | @solana/wallet-adapter | @solana/web3.js, @solana/spl-token | Anchor (框架), Rust (語言) | Helius, QuickNode, Alchemy | Helius, Simple Hash |
BTC | UniSat, Xverse | bitcoinjs-lib, @scure/btc-signer | Bitcoin Script (腳本語言) | BlockCypher, Blockstream API | Ordiscan, Blockstream |
Cosmos | @cosmos-kit/react | @cosmjs/stargate, cosmjs | Cosmos SDK (框架), CosmWasm (Rust 語言) | All Nodes, Stakely | Mintscan, Big Dipper |
Aptos | @aptos-labs/wallet-adapter | @aptos-labs/ts-sdk | Move (語言), Aptos Framework | Aptos Labs, Nodereal | Aptos Labs, Aptoscan |
TON | @tonconnect/ui-react | @ton/ton, @ton/crypto | FunC (語言), Blueprint (框架) | TonCenter, GetBlock | TonAPI, Toncenter |
不管怎麼說以太坊是目前最成熟的鏈,咱們接下來的章節會以以太坊為例,介紹 DAPP 開發的全流程。
NFT mint DAPP Demo#
合約開發及部署(以下選擇其一即可)#
開發前準備#
- 製作圖片:可以考慮用自己的頭像,也可以找個 AI 生成一個
- 上傳到Pinata IPFS
- 註冊登錄
- 上傳文件
- 記錄 CID like this:bafkreigfpdewysnl5fq2ir57r26fhxpa6bg3qoy3sbkxlajq6dkf4wye3u
- 你可以通過 IPFS Gateway 訪問圖片,like this:https://ipfs.io/ipfs/bafkreigfpdewysnl5fq2ir57r26fhxpa6bg3qoy3sbkxlajq6dkf4wye3u
- 準備字符串 ipfs://{CID},like this:ipfs://bafkreigfpdewysnl5fq2ir57r26fhxpa6bg3qoy3sbkxlajq6dkf4wye3u
- 準備 metadata
- 參考下面的內容,寫一個自己的 Metadata,名字(name)、描述(description)、屬性 (attributes) 隨便寫 (注意:屬性是一個固定內容為 trait_type 和 value 的數組)
- 複製剛剛的內容,做成一個 json 文件
- 參考之前的步驟上傳到 ipfs
- 記錄 CID,準備 IPFS URL,like this:ipfs://bafkreid7msiyufvgilrlkt6244psudmaycbbzika2aq57kou3xha5u36pe
{
"name": "My First Handmade NFT",
"description": "My First Handmade NFT by @hardman_eth",
"image": "ipfs://bafkreigfpdewysnl5fq2ir57r26fhxpa6bg3qoy3sbkxlajq6dkf4wye3u",
"attributes": [
{
"trait_type": "IQ",
"value": "80"
},
{
"trait_type": "Hat",
"value": "Pot"
}
]
}
- 準備 etherscan API key
- https://etherscan.io/login 註冊登錄,主網 API key 和測試網通用
- 把 API key 複製下來,放到環境變量.env 裡
- 加上計劃用來部署的私鑰 (可以考慮自己用腳本生成一個 / 直接創建一個,不建議用線上工具生成,有安全問題)
# Private key for deployment
PRIVATE_KEY=your_private_key_here
# Public Sepolia RPC URL, You can change it to your own private RPC
SEPOLIA_RPC_URL=https://ethereum-sepolia-rpc.publicnode.com
# Etherscan API key for contract verification (free from etherscan.io)
ETHERSCAN_API_KEY=your_etherscan_api_key
- 領水(測試幣)
區塊鏈上的任何寫入操作都需要支付 Gas 費用,咱們使用測試網,可以領取一些測試幣,也就是 "領水"。
https://www.alchemy.com/faucets/ethereum-sepolia
也可以關注我推特,私信地址,送你 0.1 個。
基於 foundry 框架#
- 安裝 foundry,我就不詳細寫了,大家看官方文檔
- 初始化 foundry 項目
forge init foundry-nft
- 把剛才準備好的.env 複製到文件目錄裡
- 安裝依賴(openzeppelin)
forge install OpenZeppelin/openzeppelin-contracts
- 編寫合約,可以直接叫 AI 生成,提示詞參考 “寫一個簡單的 nft 合約,免費 mint,限量 999 個,ipfs://bafkreid7msiyufvgilrlkt6244psudmaycbbzika2aq57kou3xha5u36pe”,也可以直接參考 foundry-simple-nft 倉庫 中的合約代碼
- 編寫單元測試,參考 foundry-nft/test/SimpleNFT.t.sol
- 準備部署腳本,參考 foundry-nft/script/DeploySimpleNFT.s.sol
- 部署合約,驗證合約
# 1. 複製環境變量文件並填寫私鑰
cp .env.example .env
# 編輯 .env 文件,填入你的私鑰
# 2. 部署到 Sepolia 測試網
source .env && forge script script/DeploySimpleNFT.s.sol --rpc-url $SEPOLIA_RPC_URL --broadcast --verify
# 或者使用公共 RPC 直接部署
forge script script/DeploySimpleNFT.s.sol --rpc-url https://ethereum-sepolia-rpc.publicnode.com --broadcast
# 3. 或者分步執行
# 先部署
source .env && forge script script/DeploySimpleNFT.s.sol --rpc-url $SEPOLIA_RPC_URL --broadcast
# 後驗證 (需要合約地址)
source .env && forge verify-contract <CONTRACT_ADDRESS> src/SimpleNFT.sol:SimpleNFT \
--etherscan-api-key $ETHERSCAN_API_KEY \
--chain sepolia
前端開發#
https://github.com/0xhardman/handmade-nft-frontend master 分支
集成錢包插件#
- 安裝 rainbowkit,wagmi,viem
npm install @rainbow-me/rainbowkit wagmi [email protected] @tanstack/react-query
- 配置測試網網絡
- 封裝 provider
- 簡單了解 RainbowKit 的配置
粗略了解常用 wagmi hook#
- useReadContract:讀取單個合約的數據
- useWriteContract:向合約裡寫入數據,如 mint transfer
- useAccount:獲取連接賬戶的基本信息(地址,當前所在的網絡)
- useSignMessage:給消息簽名
- useBalance:獲取賬戶的 gas token 餘額,或 erc20 代幣餘額
- useWaitForTransactionReceipt:等待確認交易成功
- useSwitchChain:切換所在的鏈
- useSwitchAccount:切換賬戶
獲取 abi 文件#
- 如果合約已驗證,可以從區塊鏈瀏覽器上下載
- 如果是自己部署的,可以從編譯文件裡找
- 如 foundry 的可以從 out/SimpleNFT.out/SimpleNFT.json 取到
功能實現#
- 檢查網絡,並提示切換。
- 檢查錢包餘額,提示用戶充值。
- 點擊按鈕,調用合約 mint。
- 等待交易確認,提示成功。
- 我的 nft 頁面展示
其他小白可能不知道的概念#
Mint#
可以理解為初次購買,比如購買一件藝術品,你直接從藝術家手上買就叫 Mint,你的錢會直接打給藝術家,以支持藝術家,區別從二手商或者其他買家手上買,二手商或者買家會賺取差價。從技術角度說,mint 是指在區塊鏈網絡上從零地址 (0x0000...) 創造新的數字資產並分配給用戶,區別於 transfer,transfer 是將一個資產從一個地址轉移到另一個地址。
什麼是 Gas? 為什麼要 Gas?#
這個問題非常經典,我第一次接觸區塊鏈的時候也是一臉懵逼,為啥轉個賬還要手續費?而且這個手續費還忽高忽低的,有時候幾塊錢有時候幾十塊錢,簡直比銀行還黑!
其實 Gas 就是以太坊網絡的 "手續費",但它不是簡單的手續費這麼簡單。想像一下,以太坊就像一個全球共享的超級計算機,你要讓這台計算機幫你做事(比如轉賬、執行智能合約),就需要付費給那些維護這台計算機的人(礦工 / 驗證者)。
技術的說,每個操作都需要消耗計算資源,Gas 就是衡量這些資源消耗的單位。轉個賬可能只需要 21,000 Gas,但如果你要執行一個複雜的智能合約(比如在 Uniswap 上交易),可能就需要幾十萬 Gas。Gas 價格(Gas Price)會根據網絡擁堵程度實時變化,網絡忙的時候價格就高,閒的時候價格就低,就像打車的峰時價格一樣。
所以為什麼要 Gas?簡單說就是防止有人惡意攻擊網絡(比如無限循環的代碼),同時激勵礦工 / 驗證者維護網絡安全。沒有 Gas,整個網絡早就被各種垃圾交易搞癱瘓了。
為什麼要等待區塊確認?#
為什麼我點了按鈕,還要等個十幾秒甚至幾分鐘才能看到結果?這體驗也太差了吧!
但其實這是區塊鏈去中心化的必然結果。傳統的中心化系統,比如支付寶,你轉賬的時候只需要支付寶的伺服器確認一下就行了,秒到賬。但區塊鏈不一樣,它沒有一個中央伺服器,而是由全世界成千上萬個節點共同維護的。
具體來說,當你發起一筆交易時,這筆交易會被廣播到整個網絡,然後等待被礦工(或驗證者)打包到新的區塊裡。以太坊大概每 12-15 秒產生一個新區塊,所以你的交易最快也要等一個區塊時間才能被確認。
但這還沒完,為了防止鏈重組(簡單說就是區塊鏈的分叉回滾,為什麼鏈會重組就說來話長了... 大家可以自己查一下吧),通常建議等待多個區塊確認。比如交易所通常要求 12 個區塊確認才認為一筆交易是最終的,這就是為什麼有時候要等幾分鐘的原因。
不過對於 DAPP 來說,通常等 1-2 個區塊確認就夠了,大概就是十幾秒到一分鐘的時間。雖然比中心化系統慢,但換來的是去中心化、抗審查、全球無界的特性,我覺得這個 trade-off 還是值得的。
推薦資源#
EVM 生態 DApp 腳手架
Learn Blockchain, Solidity, and Full Stack Web3 Development with JavaScript
web3 實習計劃
關於將助記詞轉換為 ETH 地址,你所需要知道的一切
結語#
其實想說的還是很多的,但圍繞 “入門” 展開的話又說不了太深。想入門區塊鏈沒那麼簡單,半小時只能帶大家 “略讀” 一下 DApp 開發這本厚書,還是得多實戰,多嘗試!
歡迎私信我的推特,一起交流!