作者:39acd4af77
用 NEO 智慧合約實現 NFT
NFT 是什麼
NFT(Non-Fungible Token)即非同質化代幣,我們經常接觸的數字貨幣 BTC、ETH 等都可以稱為同質化代幣,因為每個 BTC 都是一樣的,價值和屬性都一樣;如果我們想用數字貨幣做更多事情,比如貨幣功能以外的資產登記、權證記錄等,這時候需要的每一份“資產”都是獨一無二的,它可以有自己的屬性,可以單獨修改,因此便有了NFT 的概念,即非同質化代幣。
這裡就講講如何在 NEO 區塊鏈上使用智慧合約實現 NFT。
需求
實現一套會員系統,成為會員後可以擁有一個 NFT 的證書,每個證書有唯一的編號標識,該證書可以記錄會員的貢獻值,貢獻值達到一定數值可以進行升級,證書還可以轉讓出去,賣出後新的擁有者會享有證書附帶的特權。
實現
設計合約時,可以按這個思路出發:從需求中提取合約要實現的功能,由功能來決定合約要提供的介面,然後細化每個介面的實現思路和許可權等。
根據前面描述的需求,我們的證書合約需要具備發行證書、給證書加積分、證書升級、轉賣證書的功能,由此可以得出合約要實現的介面:
- 發行/購買證書:Deploy
- 給證書加積分:AddPoint
- 證書升級:Upgrade
- 轉賣證書:Exchange
除了以上基本功能外,我們還需要一些輔助功能,比如查詢某個賬戶上有沒有證書;所擁有的證書有多少積分,等級是多少;已經發行了多少個證書等:
- 查詢證書資訊:GetNftInfo
- 查詢發行的數量:GetNftCount
- 查詢轉賣資訊:GetTxInfo
有了以上的設計,基本思路和實現方案已經清楚了,接下來就要開發合約了,開發前的配置準備工作可以參考 NEO 的智慧合約開發文件。各介面實現程式碼和說明如下:
public class NFT : SmartContract
{
public static object Main(string method, object[] args)
{
var magicstr = "NFT";
if (Runtime.Trigger == TriggerType.Verification)
{
return false;
}
else if (Runtime.Trigger == TriggerType.VerificationR)
{
return true;
}
else if (Runtime.Trigger == TriggerType.Application)
{
//入口處取到呼叫指令碼
var callscript = ExecutionEngine.CallingScriptHash;
//傳入address獲取nft資訊
if (method == "GetNftInfo")
{
byte[] address = (byte[]) args[0];
if (address.Length == 0) return false;
StorageMap addressMap = Storage.CurrentContext.CreateMap("addressMap");
byte[] tokenId = addressMap.Get(address);
if (tokenId.Length == 0) return false;
return GetNftByTokenId(tokenId);
}
//傳入txid 獲取nft交易資訊
if (method == "GetTxInfo")
{
byte[] txid = (byte[]) args[0];
if (txid.Length == 0) return false;
return GetTxInfoByTxid(txid);
}
//獲取已發行的nft數量
if (method == "GetNftCount")
{
StorageMap nftCountMap = Storage.CurrentContext.CreateMap("nftCountMap");
return nftCountMap.Get("nftCount").AsBigInteger();
}
//給一個address發行nft
if (method == "Deploy")
{
byte[] address = (byte[]) args[0];
if (address.Length == 0) return false;
StorageMap addressMap = Storage.CurrentContext.CreateMap("addressMap");
//用當前交易的交易二次hash作為nft的id
var tokenId = Hash256((ExecutionEngine.ScriptContainer as Transaction).Hash);
var newNftInfo = CreateNft(address, tokenId);
if (SaveNftInfo(newNftInfo))
{
addressMap.Put(address, tokenId);
AddNftCount();
SetTxInfo(null, address, tokenId);
return true;
}
return false;
}
//轉手交易
if (method == "Exchange")
{
byte[] from = (byte[]) args[0];
byte[] to = (byte[]) args[1];
if (from.Length == 0 || to.Length == 0)
return false;
StorageMap addressMap = Storage.CurrentContext.CreateMap("addressMap");
byte[] toTokenId = addressMap.Get(to);
byte[] fromTokenId = addressMap.Get(from);
var fromNftInfo = GetNftByTokenId(fromTokenId);
fromNftInfo.Owner = to;
if (SaveNftInfo(fromNftInfo))
{
addressMap.Delete(from);
addressMap.Put(to, fromTokenId);
SetTxInfo(from, to, fromTokenId);
return true;
}
return false;
}
//升級
if (method == "Upgrade")
{
byte[] address = (byte[]) args[0];
StorageMap addressMap = Storage.CurrentContext.CreateMap("addressMap");
byte[] tokenId = addressMap.Get(address);
if (tokenId.Length == 0) return false;
var nftInfo = GetNftByTokenId(tokenId);
nftInfo.Rank += 1;
SaveNftInfo(nftInfo);
return true;
}
//加分
if (method == "AddPoint")
{
byte[] address = (byte[]) args[0];
BigInteger pointValue = (BigInteger) args[1];
if (address.Length == 0) return false;
StorageMap addressMap = Storage.CurrentContext.CreateMap("addressMap");
byte[] tokenId = addressMap.Get(address);
if (tokenId.Length == 0) return false;
var nftInfo = GetNftByTokenId(tokenId);
nftInfo.Point += pointValue;
if (SaveNftInfo(nftInfo))
return true;
return false;
}
}
return false;
}
//new一個新nft
public static NFTInfo CreateNft(byte[] owner, byte[] tokenId)
{
var nftInfo = new NFTInfo();
nftInfo.TokenId = tokenId;
nftInfo.Owner = owner;
nftInfo.Point= 0;
nftInfo.Rank = 1;
return nftInfo;
}
//增加已發行的數量
public static void AddNftCount()
{
StorageMap nftCountMap = Storage.CurrentContext.CreateMap("nftCountMap");
var oldCount = nftCountMap.Get("nftCount").AsBigInteger();
nftCountMap.Put("nftCount", oldCount + 1);
}
//儲存nft資訊
public static bool SaveNftInfo(NFTInfo nftInfo)
{
StorageMap userNftInfoMap = Storage.CurrentContext.CreateMap("userNftInfoMap");
byte[] nftInfoBytes = Helper.Serialize(nftInfo);
userNftInfoMap.Put(nftInfo.TokenId, nftInfoBytes);
return true;
}
//傳入TokenID獲得nft資訊
public static NFTInfo GetNftByTokenId(byte[] tokenId)
{
StorageMap userNftInfoMap = Storage.CurrentContext.CreateMap("userNftInfoMap");
byte[] data = userNftInfoMap.Get(tokenId);
var nftInfo = new NFTInfo();
if (data.Length > 0)
nftInfo = data.Deserialize() as NFTInfo;
return nftInfo;
}
//儲存交易資訊
public static void SetTxInfo(byte[] from, byte[] to, byte[] tokenId)
{
ExchangeInfo info = new ExchangeInfo();
info.@from = from;
info.to = to;
info.tokenId = tokenId;
byte[] exInfo = Neo.SmartContract.Framework.Helper.Serialize(info);
//當前執行的ScriptContainer的Transaction Hash作為txid
var txid = (ExecutionEngine.ScriptContainer as Transaction).Hash;
StorageMap ExchangeInfoMap = Storage.CurrentContext.CreateMap("txInfoMap");
ExchangeInfoMap.Put(txid, exInfo);
}
//通過txid獲取交易資訊
public static ExchangeInfo GetTxInfoByTxid(byte[] txid)
{
ExchangeInfo info = new ExchangeInfo();
StorageMap ExchangeInfoMap = Storage.CurrentContext.CreateMap("txInfoMap");
var data = ExchangeInfoMap.Get(txid);
if (data.Length > 0)
info = data.Deserialize() as ExchangeInfo;
return info;
}
}
//交易資訊
public class ExchangeInfo
{
public byte[] from;
public byte[] to;
public byte[] tokenId;
}
//證書資訊
public class NFTInfo
{
public byte[] TokenId; //tokenid 證書ID
public byte[] Owner; //所有者 address
public BigInteger Rank; //等級
public BigInteger Point; //積分值
}
複製程式碼
這樣就實現了我們前面設計的介面,使用時編譯釋出合約到 NEO 網路上就可以了。
本文主要介紹如何結合 NEO 智慧合約實現一個 NFT 合約以及開發 NEO 智慧合約的基本思路和技術,省略了許多檢查的部分,實際使用中我們可以結合需求來隨意擴充套件,比如限制等級,控制數量,付費購買檢查等。這裡是一個更完善的 NEO 上 NFT 合約實現:BlaCat 合夥人證書。