三個步驟:寫程式 → 編譯程式碼 → 發佈智能合約
不管如何,要開發 NFT 專案一定要有這三個步驟:撰寫程式碼 → 編譯程式碼 → 在區塊鏈上發佈智能合約。
前面的文章中我們已經成功上傳了 NFT 的 Metadata,以及 NFT 的圖片至 IPFS 了,再來就可以拿這個 IPFS 的網址開始寫 NFT 專案的程式碼啦!
這次開發是使用「Remix」 ,它是一個用來開發 Solidity 智能合約程式語言的線上編輯環境,我們所有的程式開發與部屬程式碼到區塊鏈上都會透過 Remix 來進行操作,這樣可以捨去非常多在自己電腦上安裝環境的事情,大幅降低開發智能合約的門檻,也能讓一些新手讀者在真正去實作的時候更容易有「第一次動手寫程式打造一項產品」的體驗。
但除了 Solidity 語言以及 Remix 開發環境以外,這次還要介紹一個很重要的智能合約函式庫叫做「OpenZeppelin」,透過這套工具將大大加速開發所需的時間,而且這個函式庫對於資安的部分一直都有在進化,比起我們自己撰寫程式碼會更加安全。
OpenZeppelin
OpenzZeppelin 本身是一間公司,它們的主要業務大概是提供工具讓開發者更方便開發與管理智能合約以及提供智能合約程式碼資安方面的審計,在官網上可以看到一些它們參與過很有名的專案開發或協助審計,例如 Brave 瀏覽器、Compound 借貸平台,官方網站網址是:
https://openzeppelin.com/
他們提供了一套開源又相對安全的智能合約開發函式庫供智能合約開發者使用,翻譯成白話就是他們打造了一個工具箱,並且開放讓所有人能夠隨意領取這個免費的工具箱,領取了工具箱就可以用裡面的工具去打造自己的產品,使用 OpenZeppelin 的工具箱做為專案開發的根基,我們想要打造車子時就不再需要從輪子開始造起了,是不是佛心公司。
這包超大包家庭號原始碼大致上可以分為下列幾種功用:
1. Access control: 誰擁有這個智能合約或是整個系統,且可以對其進行操作。
2. Token: 鑄造自己的代幣和 NFT,目前看起來總共提供五種標準,包含代幣標準(ERC-20、ERC-777), NFT 標準(ERC-721、ERC-1155)以及 NFT 的稅收標準(ERC-2981),直接使用的話那些官方文件規定的接口都已經打造完成,我們不用額外再處理了 。
3. Gas Station Network: 讓用戶在跟我們的智能合約互動時,可以不需要自己付費。
4. Utilities: 一些通用的工具,例如能夠避免溢位的數學變數、數位簽章驗證、無須信任機制的支付系統等。
要使用 OpenZeppelin 的工具蠻容易的不用下載下來,在 Remix 上直接 import 要使用的工具即可,底下的程式碼會介紹到。
Remix 的初始設定
至於瀏覽器的部分,會需要使用有安裝 Metamask 錢包或是其他錢包擴充套件的瀏覽器,如果你的 Metamask 安裝在 Chrome 上的話就用 Chrome 開發吧,因為後續當我們要將智能合約發至測試網時,就需要它裡面的測試幣來支付鏈上手續費。
第一次打開,它或許會跳出小視窗顯示”Help us to improve Remix”,就看你要不要同意了,意思是當程式發生一些例外狀況時,會將當下這個狀況回報給以太坊基金會的開發者,幫助他們發現問題進而優化系統。
就像使用一般檔案總管那樣,我們先點開contracts的資料夾,並點選上方折一角的紙張icon,在contracts資料夾中建立一個新的.sol檔,例如abcd.sol、NFT1155.sol,.sol就是solidity程式語言的副檔名。
使用 OpenZeppelin 快速開發第一個 NFT 程式專案
由於這次要發行的是 ERC-1155 協議的 NFT,我會將檔名命名為 Basic-1155.sol,你也可以取個自己喜歡的名字,但是副檔名必須是 .sol。
第一個 NFT 專案會先寫一段最簡單的程式碼,匯入 OpenZeppelin,再嘗試在本機端編譯、發佈智能合約,確定可以發佈就表示成功了。
Basic-1155.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;
import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
contract Basic_1155 is ERC1155 {
constructor() ERC1155("ipfs://QmdnBQrQa6KXVeeNr7fpc9uxQLtcCyknjcWqLXtoG1ahTK") {}
}
第一行最開始我們需要給一個”SPDX”許可標示符號,這個符號的意思就是這個程式碼要不要開源授權給他人使用,開源的程度就與不同的許可有關係,”// SPDX-License-Identifier: MIT”代表MIT開源許可;”// SPDX-License-Identifier: unlicensing”代表不開源,想要使用其他不同許可可以去
SPDX 看看詳細內容。
第二行 pragma solidity 就是指定solidity的版本,在這篇文章中我們設定版本號是 0.8.x,編譯器會使用當下最新的 Solidity 0.8.14 版本,由於 Solidity 仍是一個更新非常頻繁的程式語言,若未來當你看到這篇文章,已經有更新版本的 Solidity,而你執行發現程式碼有點問題,不妨試著將版本降回 0.8.14 再執行看看。
接著第三行 import 的意思是要匯入 OpenZeppelin 的工具箱 ERC1155.sol,這樣我們底下的程式碼都可以使用它了。
下面的 contract 就是主角智能合約了,Basic_1155 是我自己命名的名稱,is ERC1155 就是使用到 OpenZeppelin 開發好的 ERC1155,contract 內 constructor 的用途是初次部屬智能合約時,會先執行的地方,而且只能被執行一次,所以在我的理解中,constructor是一種設定程式初始參數的概念,後面需要填上一個NFT 所需的 URI 欄位,也就是放上前一篇文章中我們發佈在 IPFS 的 .json 檔對應到的 IPFS 網址,還記得上一篇文章的最後面我做了一張圖:
也就是說,當 NFT 的顯示器在嘗試顯示這張 NFT 的樣貌時會先透過我們這邊設定的 URI 網址連到 IPFS 上面的 .json 檔,並在解析 .json 檔底下的 Metadata 時,找到 image 欄位,再透過 image 欄位裡面的 IPFS 網址去連結到真正的圖片將 NFT 完整地顯示出來。
編譯與發佈智能合約
最左邊的第一張圖是編譯,選好 COMPILER 的版本將要編譯的 .sol 檔編譯,看到變成綠色勾勾就可以了。
中間第二張圖是發佈,要先將 ENVIRONMENT 切換成「Injected Web3」,並將 Metamask 切換成這次使用的 Rinkeby 測試網路,底下 CONTRACT 記得換成要發佈的合約,如果是用我的程式碼,這裡應該要選擇「Basic_1155」,由於這次我們使用了 OpenZeppelin ,選擇會比較多不要選錯了,接著按 Deploy 錢包就會跳出來要求你確認交易,確認交易之後因為要上傳任何東西到區塊鏈都需要花點時間,等一下就能看到第三張圖了。
如果你做到最右邊的第三張圖有看到這些東西,就表示你已經成功完成並發佈第一個 NFT 的專案了,哦不過它現在甚麼事情都做不到,也還沒有可以鑄造 NFT 的功能,我們繼續來改寫它吧。
可以被擁有的智能合約(Owner and ownable)
這些我們在區塊鏈上創造的代幣或 NFT 數位資產,就像是現實生活中的鈔票、硬幣、不動產,非常直覺都是可以被擁有視作是個人的獨立資產。而智能合約本身也可以被歸屬於某個人的錢包哦!
智能合約就像是一台路邊的自動販賣機一樣,需要輸入甚麼數據(投錢、選擇商品按鈕),將會產生甚麼結果(掉出飲料),程式碼編寫會使得這台自動販賣機的功能不同,除了這些相似之處,智能合約與自動販賣機一樣可以被人擁有,只有擁有者才具有權限進行一些參數設定或修改,可以被擁有的智能合約被稱作「Ownable」擁有者被稱作「Owner」。
那我們就直接改寫剛剛的 Basic-1155.sol,增加 Ownable 讓智能合約能夠被擁有,並增加一個 function 函式命名為 setURI,讓智能合約的擁有者能夠設定新的網址。
Ownable-1155.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;
import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract Ownable_1155 is ERC1155, Ownable {
constructor() ERC1155("ipfs://QmdnBQrQa6KXVeeNr7fpc9uxQLtcCyknjcWqLXtoG1ahTK") {}
function setURI(string memory newuri) public onlyOwner {
_setURI(newuri);
}
}
這邊使用 OpenZeppelin 提供的 Ownable 工具,所以需要在前面加上一行 import 將工具匯入。
第二個要修改的地方是在 contract 的右邊,重新將原本的 Basic_1155 改名成 Ownable_1155 避免自己混淆,並加入「, Ownable」,表示這個合約要同時繼承 OpenZeppelin 開發好的 ERC1155 與 Ownable。
function setURI 中有個 newuri,就是未來當呼叫智能合約的 setURI 函式時,需要代入一個網址參數,這個網址參數將會被設定成 NFT 的新網址,所以同一個 NFT 在設定後可能會有不同的圖片外觀。
setURI 函式還必須設定為只有智能合約擁有者能夠更動,否則路邊隨便一個路人都能來設定我們 NFT 的網址會很不妙,設定的方式也很簡單,直接在 function 的右邊加上 onlyOwner 就可以了。
_setURI 則是我們從 OpenZeppelin 匯入 ERC1155 的接口,呼叫它並丟給它一串網址就能夠重新設定 NFT 的網址了。
測試與認識 Ownable
程式改完就跟前面一樣,重新編譯跟發佈新的智能合約,在這裡我們要來實際測試甚麼叫做「可以被擁有的智能合約」,經過一番探索一定會更懂一些。
發佈成功之後,點開這個 OWNABLE_1155 的智能合約就會看到超多東西,橘色方塊是需要放到區塊鏈上執行的交易,需要付瓦斯費的,藍色則是可以直接獲取對應資料的方塊,這次我們寫了一個 setURI 的函式,讓它是 onlyOwner,這表示只有合約的擁有人才能使用 setURI 的函式,我們現在就試試看吧!
先用一次藍色按鈕「uri」,再按下「call」的按鈕去使用它,運作的結果會是下方的 ipfs 網址,這個網址就是我們最初在建立 ERC1155 時,給定的網址。
接著換用上面橘色按鈕「setURI」,在「newuri」欄位隨便打上另外一個網址,這個網址會是這個 NFT 的新樣貌,接著按下「transact」把這筆交易放上鏈。
過一下子等到交易上鏈之後,我們再查一次藍色按鈕「uri」,就發現網址已經被改成剛剛輸入的新網址了。
這只能知道我們寫的 setURI 可以正常運作啊!那怎麼證明只有智能合約的擁有人才能使用 setURI 的函式呢?
這時候就要回到 Metamask 去切換成另一個不同的錢包,重新與 Remix 連結,這時候再用新錢包去嘗試使用 setURI 這個函式就會出現以下錯誤,錯誤的內容就是打算使用「setURI」這個函式的錢包地址不是這個智能合約的擁有者,所以無法使用。
瞭解 Ownable 大概的運作原理後,未來在開發程式時就會變得更靈活,我們可以自行設計哪些函式是給使用者使用的,哪些函式是我們自己才能使用的,不過畢竟區塊鏈講求的是去中心化的概念,當我們的專案使用越多 onlyOwner 的東西,就表示專案項目的負責人能夠私自更動的東西越多,進而可能讓用戶開始對專案產生是否過度中心化的疑慮。
最後再補充一個我覺得蠻重要的東西, Ownable 之中還有一個「renounceOwnership」的函式,它的功用是釋放所有智能合約的擁有者角色,只有合約的擁有者才能執行,執行以後這個智能合約就不再有擁有者了,那些使用到 onlyOwner 的函式就沒有人能夠更動它了,所以如果專案的設定確定都正確未來也不會更動了,可以使用 renounceOwnership 來讓你的專案更加去中心化。
結語
你可能會發現做到現在連個 NFT 的影子都沒有,到底在搞甚麼… 但考量到篇幅問題,我想把 NFT 的生產鑄造這個最重要的部分放到下一篇文章中,這篇文章主要專注在如何使用名為「OpenZeppelin」的工具箱,以及撰寫完程式碼後的一些基本編譯及發佈智能合約的部分。
總之祝大家端午佳節安康,先前填過領取 NFT 表單的讀者們我會盡快將端午節 NFT 發送出去,到時候就會在 Polygon 上看到資產了哦!
延伸閱讀
最後,謝謝你在百忙之中願意抽空來花時間來看我的文章,如果還喜歡這些內容的話希望能獲得你的追蹤及小額贊助支持,讓我更有繼續寫下去的動力,EVM 兼容錢包地址為 0xae1dd06d57f582999a9c50b86ba913eecd7155ce。
下次見嚕 o((>ω< ))o~