以太坊實戰-創建並運行一個最簡單的NFT合約

星雲區塊鏈 2022-01-08 03:12:20 阅读数:861

以太 nft

以太坊實戰-創建並運行一個最簡單的NFT合約

NFT理論部分

NFT,全稱為Non-Fungible Token,非同質化貨幣。了解非同質化貨幣之前,首先需要了解同質化貨幣和非同質化貨幣的區別。

語言 框架
同質化貨幣 功能就類似我們現實生活中使用的1元人民幣,雖然票面編號是不同的,但是價值是相同的。
非同質化貨幣 功能則類似於我們現實生活中的車牌,不同車牌的編號是不同的,價值也不同,而且相差的價格十分大。

非同質化代幣的產生是區塊鏈的一種落地方式。非同質化代幣對於促進藝術品,尤其是虛擬藝術品的流通,提供了極大的支持。

典型的非同質化代幣的應用項目:

迷戀猫:CryptoKitties | Catalogue

image-20211213195359455

理論上來講,區塊鏈在同質化貨幣ERC-20的基礎上,保證其發行的所有代幣的編號唯一,就可以創造了非同質化貨幣。目前,以太坊提出了ERC-721合約接口支持上述功能。

創建通用NFT合約

針對非同質化貨幣,以太坊組織制定了ERC-721接口規範。

雖然官方沒有給出ERC-721接口規範的實現,但是網上已經有很多實現好的通用實現供我們參考或者直接繼承。

比如,下面這個開源項目就提供了ERC-20ERC-721合約接口的通用實現。訪問地址:https://github.com/OpenZeppelin/openzeppelin-contracts

image-20211215100955756

該項目已經將實現好的合約打包上傳到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

每張卡片只能被一個賬戶擁有,且該賬戶有權將該將收藏品轉移到其他賬戶,或者銷毀它。

image-20211225145838742

項目合約

關於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

image-20211225143355319

下面是測試文件的基礎內容,該內容可以用來測試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收藏品。

image-20211230212118910

  • 階段2:流通

在真實環境中,流通操作或者叫做轉賬操作是最常見的操作,通過該操作,一個賬戶中的NFT收藏品可以轉移到另一個賬戶中。

在用戶操作界面中,用戶需要提供轉讓地址和轉讓NFT收藏品的ID,才能點擊轉讓按鈕來轉讓該NFT收藏品。

image-20211230212051566

  • 階段3:銷毀

在真實環境中,銷毀操作也是可能會發生的,如果用戶真的想讓某個NFT收藏品消失的話。

在用戶操作界面中,用戶需要提供銷毀NFT收藏品的ID,才能點擊銷毀按鈕來銷毀該NFT收藏品。

銷毀操作在合約中已經完成,但是按鈕並沒有添加,有興趣的可以添加一下,試一試效果。

項目總結

項目繼承了ERC721的實現合約,在其基礎上增加了我們本身的鑄幣,轉賬和銷毀合約,非常方便的實現了NFT的一個應用程序。NFT作為區塊鏈的落地方式,其核心的價值就是,虛擬資產一旦被轉移後,歸屬權就會本質變更,而不需要任何人證明。

補充

  1. 合約編譯的時候指定合約生成目錄

在部署合約的時候,生成的內容*.json文件會放到一個目錄中,這個目錄的比特置可以指定,指定的目的是,讓別人引用你這些生成的文件的時候,不會因為你每次部署的內容不同,人家項目啟動後,引用的東西是你上次部署的內容。

咱們舉個例子,比如說,你首次部署的時候,放在了文件build中,然後別人把你build文件夾中的東西拷貝出來,放到他們的目錄中來使用,當你再次部署的時候,你最新生成的確實還在build中,但是別人的目錄中的那份文件就是陳舊的了,不能使用了。所以,我們要直接把生成的文件的比特置放到別人需要的目錄中去,這樣你部署一次,就可以把最新的直接給到別人。

想要實現上面這個目標,該怎麼做呢,其實就是配置truffle.config中的內容就可以了。

https://trufflesuite.com/docs/truffle/reference/configuration.html#contracts_build_directory

image-20211229191500467

  1. MetaMask賬戶切換的時候刷新頁面

很多情况下,MetaMask賬戶切換並不能帶來頁面刷新,這就造成了假死或者被人誤以為BUG。所以,在可能的情况下,加一個切換賬戶,頁面刷新是一個很好的主意。

https://ethereum.stackexchange.com/questions/42768/how-can-i-detect-change-in-account-in-metamask/49008

版权声明:本文为[星雲區塊鏈]所创,转载请带上原文链接,感谢。 https://gsmany.com/2022/01/202201080312201760.html