以太坊實戰-創建并運行一個最簡單的NFT合約
NFT理論部分
NFT,全稱為Non-Fungible Token,非同質化貨幣,了解非同質化貨幣之前,首先需要了解同質化貨幣和非同質化貨幣的區別,
| 語言 | 框架 |
|---|---|
| 同質化貨幣 | 功能就類似我們現實生活中使用的1元人民幣,雖然票面編號是不同的,但是價值是相同的, |
| 非同質化貨幣 | 功能則類似于我們現實生活中的車牌,不同車牌的編號是不同的,價值也不同,而且相差的價格十分大, |
非同質化代幣的產生是區塊鏈的一種落地方式,非同質化代幣對于促進藝術品,尤其是虛擬藝術品的流通,提供了極大的支持,
典型的非同質化代幣的應用專案:
迷戀貓:CryptoKitties | Catalogue

理論上來講,區塊鏈在同質化貨幣ERC-20的基礎上,保證其發行的所有代幣的編號唯一,就可以創造了非同質化貨幣,目前,以太坊提出了ERC-721合約介面支持上述功能,
創建通用NFT合約
針對非同質化貨幣,以太坊組織制定了ERC-721介面規范,
雖然官方沒有給出ERC-721介面規范的實作,但是網上已經有很多實作好的通用實作供我們參考或者直接繼承,
比如,下面這個開源專案就提供了ERC-20和ERC-721合約介面的通用實作,訪問地址:https://github.com/OpenZeppelin/openzeppelin-contracts
該專案已經將實作好的合約打包上傳到npm倉庫中,我們可以通過下面的命令來給專案引入依賴:
$ yarn add @openzeppelin/contracts
使用下面的命令創建卡羅牌合約TarotCard.sol
$ truffle create contract TarotCard
合約創建好的樣子如下:
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;
contract TarotCard {
constructor() public {
}
}
引入ERC-721的通用實作,并讓TarotCard合約繼承已經實作好的ERC-721合約,
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
contract TarotCard is ERC721 {
constructor() ERC721("Tarot","TAROT") {
}
}
到目前為止,我們已經創建好了一個NFT合約,合約的名稱為Tarot,合約的代幣為TAROT,在該NFT合約的基礎上,我們可以撰寫業務代碼來構建領域相關的NFT合約,
創建NFT專案
嗶哩嗶哩·卡羅牌專案
該專案將發布以下26個卡羅牌收藏品,每張卡羅牌都有編號,分別為0~25,
每張卡片只能被一個賬戶擁有,且該賬戶有權將該將收藏品轉移到其他賬戶,或者銷毀它,

專案合約
關于
ERC-721的通用實作是如何作業的,可以參考第6章,剖析ERC-721的通用實作,
任何一個合約中的代幣都有三個生命階段,下面分別撰寫這三個階段的代碼,
- 階段1:鑄幣,為了測驗方便,合約中的鑄幣介面直接將所有收藏品先發給第一個呼叫該合約函式的賬戶,
...
contract TarotCard is ERC721 {
uint8[] public tarots;
constructor() ERC721("Tarot","TAROT") {
}
function mint() public {
require(tarots.length <= 0, "only can mint once");
for (uint8 i = 0; i < 26; i++) {
tarots.push(i);
_mint(msg.sender, i);
}
}
}
...
通過下面的命令來創建測驗檔案tarot_card.js:
$ truffle create test TarotCard

下面是測驗檔案的基礎內容,該內容可以用來測驗TarotCard合約是否正常發布,
const TarotCard = artifacts.require("TarotCard");
contract("TarotCard", function (/* accounts */) {
it("should assert true", async function () {
await TarotCard.deployed();
return assert.isTrue(true);
});
});
下面,補充測驗內容來驗證鑄幣函式的正確性,
const TarotCard = artifacts.require("TarotCard");
contract("TarotCard", function (/* accounts */) {
// 測驗合約是否正常發布
it("should assert true", async function () {
await TarotCard.deployed();
return assert.isTrue(true);
});
// 測驗合約鑄幣函式邏輯是否正確
it("verify mint call", async function () {
await TarotCard.deployed();
return assert.isTrue(true);
});
});
- 階段2:流通
流通簡單來說就是轉讓收藏品,
function transfer(address to, uint8 tokenId) public {
uint8[] storage tokenIds = addr2tokenIds[msg.sender];
bool isExist = false;
uint i = 0;
while(i < tokenIds.length) {
if (tokenIds[i] == tokenId) {
isExist = true;
break;
}
i += 1;
}
require(isExist, "account should contains tokenId");
for(uint j = i; j < tokenIds.length - 1; j++) {
tokenIds[j] = tokenIds[j+1];
}
tokenIds.pop();
addr2tokenIds[to].push(tokenId);
transferFrom(msg.sender, to, tokenId);
}
- 階段3:銷毀
銷毀,很簡單,永久的將收藏洗掉,
function burn(uint8 tokenId) public {
require(ERC721.ownerOf(tokenId) == msg.sender, "ERC721: transfer of token that is not own");
uint8[] storage tokenIds = addr2tokenIds[msg.sender];
bool isExist = false;
uint i = 0;
while(i < tokenIds.length) {
if (tokenIds[i] == tokenId) {
isExist = true;
break;
}
i += 1;
}
require(isExist, "account should contains tokenId");
for(uint j = i; j < tokenIds.length - 1; j++) {
tokenIds[j] = tokenIds[j+1];
}
tokenIds.pop();
_burn(tokenId);
}
專案界面
為了方便演示,我們將上述三個階段的按鈕全部放在了用戶操作頁面上,分別為鑄幣,轉讓(流通)和銷毀,
- 階段1:鑄幣
在真實環境中,鑄幣操作大多都在合約初始化的時候完成,
在用戶操作界面放置鑄幣按鈕,僅僅是為了方便展示,該按鈕可以將塔羅牌中全套收藏品ID一次性賦予第一個呼叫該合約鑄幣方法(mint)的賬戶,讓該賬戶擁有全部NFT收藏品,

- 階段2:流通
在真實環境中,流通操作或者叫做轉賬操作是最常見的操作,通過該操作,一個賬戶中的NFT收藏品可以轉移到另一個賬戶中,
在用戶操作界面中,用戶需要提供轉讓地址和轉讓NFT收藏品的ID,才能點擊轉讓按鈕來轉讓該NFT收藏品,

- 階段3:銷毀
在真實環境中,銷毀操作也是可能會發生的,如果用戶真的想讓某個NFT收藏品消失的話,
在用戶操作界面中,用戶需要提供銷毀NFT收藏品的ID,才能點擊銷毀按鈕來銷毀該NFT收藏品,
銷毀操作在合約中已經完成,但是按鈕并沒有添加,有興趣的可以添加一下,試一試效果,
專案總結
專案繼承了ERC721的實作合約,在其基礎上增加了我們本身的鑄幣,轉賬和銷毀合約,非常方便的實作了NFT的一個應用程式,NFT作為區塊鏈的落地方式,其核心的價值就是,虛擬資產一旦被轉移后,歸屬權就會本質變更,而不需要任何人證明,
補充
- 合約編譯的時候指定合約生成目錄
在部署合約的時候,生成的內容*.json檔案會放到一個目錄中,這個目錄的位置可以指定,指定的目的是,讓別人參考你這些生成的檔案的時候,不會因為你每次部署的內容不同,人家專案啟動后,參考的東西是你上次部署的內容,
咱們舉個例子,比如說,你首次部署的時候,放在了檔案build中,然后別人把你build檔案夾中的東西拷貝出來,放到他們的目錄中來使用,當你再次部署的時候,你最新生成的確實還在build中,但是別人的目錄中的那份檔案就是陳舊的了,不能使用了,所以,我們要直接把生成的檔案的位置放到別人需要的目錄中去,這樣你部署一次,就可以把最新的直接給到別人,
想要實作上面這個目標,該怎么做呢,其實就是配置truffle.config中的內容就可以了,
https://trufflesuite.com/docs/truffle/reference/configuration.html#contracts_build_directory

- MetaMask賬戶切換的時候重繪頁面
很多情況下,MetaMask賬戶切換并不能帶來頁面重繪,這就造成了假死或者被人誤以為BUG,所以,在可能的情況下,加一個切換賬戶,頁面重繪是一個很好的主意,
https://ethereum.stackexchange.com/questions/42768/how-can-i-detect-change-in-account-in-metamask/49008
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/402486.html
標籤:區塊鏈
上一篇:umee經濟學
