在 NEO 上使用智慧合約釋出 NFT

NEOWest發表於2018-12-11

作者: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 合夥人證書


相關文章