在之前的文章中,我們已經完成了智能合約的部分並且可以直接在etherscan上對合約進行交互,然而對一般使用者而言,有個網站能直接mint不需自己輸入參數在使用者體驗上是比較友善的,接下來我們將會透過ethers.js
這個庫從前端進入web3的世界。
demo網站: github page
source code: github
我的demo網站主要是使用react
, redux
, styled-component
及typescript
來撰寫,本篇不會講解以上前端技術的使用,而是著重在分享跟區塊鏈交互的部分。
目前我所知道前端比較多人是透過web3.js及ethers.js與以太坊網路進行交互,這邊我選用的是後者。
要連接以太坊網路首先我們需要一個Provider
,ethers.js的文檔中建議使用getDefaultProvider
,但我將網站設計成一定要有metamask
才能使用,為了方便這邊就直接使用Web3Provider
跟metamask交互。
雖然官方有預設的API KEY,但官方強烈建議我們還是要自行申請API KEY帶入,因為預設的API KEY可能被大量的人使用導致超出rate limit或是速度較慢,我們在上一篇已經免費申請過infura,所以這邊要用的話也可以帶入自己的infura project id,更多參數請見官方文檔。
const provider = ethers.getDefaultProvider(4, {
infura: YOUR_INFURA_PROJECT_ID,
});
若我們有安裝metamask的chrome extension,metamask會將web3 provider以window.ethereum
注入到網頁中,而ethers.providers.Web3Provider
接收的第一個參數就是該provider,而metamask在背後實際上也是使用了infura的服務。
const provider = new ethers.providers.Web3Provider(window.ethereum, 4)
await provider.send('eth_requestAccounts', []) // 會輸出錢包地址如: ['0xabc...xyz']
上面兩種創建Provider的方法我都有帶入一個參數network
,並且值為4
。
這個參數也可以是字串形式的network name,預設會是homestead
主網(帶1也可以),由於我們是要連接rinkeby測試網所以傳了4。
Network | Chain ID |
---|---|
mainnet | 1 |
ropsten | 3 |
rinkeby | 4 |
goerli | 5 |
const switchToRinkeby = async () => {
const chainId = 4
const hexedChainId = '0x4'
if (window.ethereum.networkVersion === chainId) {
return true
}
try {
await window.ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: hexedChainId }],
})
return true
} catch (err) {
alert('Please check if you have added Rinkeby testnet to your metamask')
return false
}
}
const initMetamask = async () => {
if (!window.ethereum) {
alert('Please install metamask')
return
}
const switched = await switchToRinkeby()
if (!switched) return
provider = new ethers.providers.Web3Provider(window.ethereum, 4)
return await provider.send('eth_requestAccounts', [])
}
呼叫上面的initMetamask()
將會跳出metamask連接視窗並切換到rinkeby測試網,要是成功連接就會return連接的錢包地址。
至於wallet_switchEthereumChain
之類的method name我們可以參考metamask文檔,這些method是由EIP提出而metamask將它實作出來,在開發web3 app時可能會需要去閱讀一些EIP文件。
在我的完整程式碼中,連接成功後我將已連結的flag及address存入了redux
中方便往後有其他頁面要使用。
而接下來我們只要成功與合約進行交互,就能取得目前mint的數量,並且也可以從前端進行mint操作。
與合約交互使用的是ethers.Contract
,需要傳入3個參數:
因為我們有開源,所以ABI
我們可以在etherscan上查看,或是你用remix IDE compile也可以產出ABI。
這邊我是用另一隻檔案管理合約地址以及ABI,引入後當作參數帶入。
let HHGContract: ethers.Contract
const handleCheckContract = async () => {
const signer = provider.getSigner()
HHGContract = new ethers.Contract(addr, ABI, signer)
try {
const [current, total] = await Promise.all([
HHGContract.totalSupply(),
HHGContract.MAX_SUPPLY(),
])
setMintedObj({
current: current.toNumber(),
total: total.toNumber(),
})
} catch (error) {
alert('something went wrong')
}
}
呼叫ethers.Contract
後會返回一個Contract的實例
,之後我們可以透過Contract.METHOD_NAME
呼叫合約中的方法,並且會返回Promise。
這邊我分別用了totalSupply()
及MAX_SUPPLY()
去取得目前已mint數量以及合約總供應量。
上面有提到第三個參數可以傳Signer或Provider,但我們此時不傳Provider,原因是我們除了呼叫上面兩個
read
method之外,待會進行mint操作時需要呼叫write
method發送transaction,此時就必須要有signer的存在否則會報錯。
接下來實作mint function。
const handleMint = async () => {
// 我的mint price是0.01ETH,這邊也可以從合約取得,但我把mint price當常量不會更改所以前端寫死
const options = { value: ethers.utils.parseEther('0.01') }
try {
// 我合約safeMint第一個參數是to address,我呼叫完initMetamask()後便把返回的錢包地址存入wallet.address
const res = await HHGContract.safeMint(wallet.address[0], options)
setMinting(true)
const comfirmed = await provider.waitForTransaction(res.hash)
// The status of a transaction is: 1 is successful or 0 if it was reverted
if (comfirmed.status === 1) {
alert('Minted successfully!')
setMinting(false)
// 刷新頁面上數據
handleCheckContract()
}
} catch (error) {
alert('Mint failed')
setMinting(false)
}
}
首先,送出mint後便把minting state改為true,使畫面mint按鈕更改為minting。
接著我們用了waitForTransaction來監聽mint transaction的狀態,待交易被確認就會返回status = 1
,此時跳出成功提示並刷新頁面數據。
對一個PFP NFT項目而言,販售盲盒後有一件最重要的事就是reveal開圖
,所有的買家們滿心期待自己會開到喜歡的圖片。
現在我們已經mint了很多自己的NFT,接下來準備開圖!
我們在上一篇有提過,開圖過程其實非常簡單,只要合約owner操作setBaseURI()
將metadata資源路徑改為開圖後的路徑(之前已上傳到pinata),並且更改revealed狀態就好。
開圖還有可以琢磨的細節,畢竟metadata是由項目方上傳的,要如何防止項目方自己偷看metadata把較稀有NFT收入囊中。有聽過項目是在開圖前讓holder社群票選出一個數字,再把token id對應的metadata依照該數字打亂,但此做法也無法真正防止項目方作球,實際上很多NFT項目都是沒有特別處理開圖,項目方存在很多黑箱的空間,這也是目前很值得探討的一個議題。
baseURI更改完成後隨即送出setRevealed
,這邊我都是直接在etherscan上操作。
以上步驟都完成後,我們可以到opensea上該NFT的頁面按下refresh metadata
按鈕加速opensea開圖的時間,過一小段時間就可以看到NFT圖片刷新囉!
以上就是盲盒開圖的步驟,本系列文章只是一個粗淺的介紹,實際上很多項目都有更特殊的機制與玩法。如果實作上有遇到什麼困難,或是有其他想討論的話題都可以留言給我,如果覺得我的文章有幫助也歡迎大家幫我的github repo按個star~