1. 程式人生 > >ERC721的簡單剖析——繼ERC20之後的新的以太坊協議

ERC721的簡單剖析——繼ERC20之後的新的以太坊協議

       ERC20出現之後,我開始去了解什麼是ERC20。但是在2017年,一種名為CryptoKitties(加密貓)的以太坊遊戲在頃刻間吸引了全球眾多虛擬遊戲世界裡面的玩家,佔據了各大媒體以及網站的頭條,一隻加密貓的價格最高峰竟然達到1億RMB,究竟是什麼東西的存在使得這款遊戲變得如此瘋狂,使得眾多遊戲玩家為之而痴迷,接下來就有我來給大家簡單的剖析一下CryptoKitties(加密貓)遊戲背後的主謀吧。首先CryptoKitties(加密貓)是一款玩家可以在遊戲裡進行加密貓的買、賣、交易以及培養出新的加密貓的區塊鏈遊戲。但是,我們童年時代曾經玩過的數碼寶貝也是有這樣類似的功能,只不過存在於單機遊戲裡面,包括現在有很多遊戲也有這樣的遊戲體驗功能。能夠使得CryptoKitties(加密貓)變得如此有魅力,如此讓玩家為之投放RMB是因為其背後的ERC721所起到的作用。如果ERC721換成了ERC20,我相信這款遊戲的吸引力應該不會有巔峰時那麼高,雖然CryptoKitties(加密貓)是以太坊推出的首款區塊鏈遊戲。

 

         由於有ERC721的支撐,CryptoKitties(加密貓)變得更有收藏價值,更有意義,因為每隻加密貓都有一個ERC721 Token,而這個token是唯一且不能重複生成,好比與每個人的基因,是獨一無二的。而ERC721 Token的id就好比加密碼中的DNA,所以每個加密貓都是獨一無二的。正是唯一性以及稀有性才促使加密貓收藏價值的形成。

            回溯歷史,收藏已經成為個別人的一種愛好,人們喜歡收藏是源自於商品的稀缺性以及唯一性(不可再生性——此處不談及仿製品),同樣地,我們通過採用以太坊的ERC721 Token對某些商品進行標記並使其產生稀缺性。與ERC20相似,ERC721是一種標準的協議且充當交易Token能夠實現智慧合約以及外部應用交易。

            可替代性——是商品中的一種性質(金錢和商品),能夠讓其他等價或等性質的商品來替代。一般而言,可替代性是貨幣或者Token的一種性質,去確定相同或相似型別的物品或數量在交換或使用過程中是否可以實現互換。舉個例子,我們可以用RMB在現實生活中的某一家便利店購買一瓶蘇打水。

 

             但是,當我們用一件科比的秋衣去超市兌換一件商品,也許老闆不會賣給你,有人會問為什麼呢?雖然科比

 

的球衣是你用300元買回來的,而且你覺得這球衣對於你來說是有收藏價值的。但是,超市的老闆也許連科比都不知道是誰,他更不認為一件球服具有收藏價值。

            就收藏品而言,如果收藏品具有不同的特徵,則收藏品中的兩件物品不可替代。 對於物理硬幣,金幣不能與銅幣相互替換,因為它們的不同特徵賦予了它們與收藏者不同的價值。

            ERCToken 可以用到任何交易,但其價值是由每個Token的唯一性和罕見性所致。這一標準定義了相關函式:name , symbol , totalSupply , balanceOf , ownerOf , approve , takeOwnership , transfer , tokenOfOwnerByIndex 以及定義了兩個時間:Transfer and Approval。下面就簡單的介紹介紹ERC721:

contract ERC721 {
   // ERC20 compatible functions
   function name() constant returns (string name);
   function symbol() constant returns (string symbol);
   function totalSupply() constant returns (uint256 totalSupply);
   function balanceOf(address _owner) constant returns (uint balance);
   // Functions that define ownership
   function ownerOf(uint256 _tokenId) constant returns (address owner);
   function approve(address _to, uint256 _tokenId);
   function takeOwnership(uint256 _tokenId);
   function transfer(address _to, uint256 _tokenId);
   function tokenOfOwnerByIndex(address _owner, uint256 _index) constant returns (uint tokenId);
   // Token metadata
   function tokenMetadata(uint256 _tokenId) constant returns (string infoUrl);
   // Events
   event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
   event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);
}

細分如下:(這些例子僅在發文章時候引用,沒有測試過,請不要把這些運用到實際開發中去)

類ERC20的函式

                ERC721定義的某些函式也遵循了ERC20的標準,這樣使得現有的錢包能夠更容易顯示token簡單的資訊,這些功能可以讓符合這個標準的智慧合約充當像比特幣或以太坊這樣的通用加密貨幣,通過定義允許使用者執行諸如向他人傳送令牌和檢查賬戶餘額的功能。

name:

這個函式作用於告訴外部智慧合約以及應用Token的名字,如下是該函式使用的一個例子:

contract MyNFT {
  function name() constant returns (string name){
    return "My Non-Fungible Token";
  }
}

symbol:

該功能還有助於提供與ERC20令牌標準的相容性。 它為外部程式提供令牌的簡寫名稱或符號。

contract MyNFT {
  function symbol() constant returns (string symbol){
    return "MNFT";
  }
}

totalSupply:

該函式返回區塊鏈上可用的硬幣總數。 

contract MyNFT {
  // This can be an arbitrary number
  uint256 private totalSupply = 1000000000;
  function totalSupply() constant returns (uint256 supply){
    return totalSupply;
  }
}

balanceOf:

該函式是使用者提供錢包地址去搜索賬戶中的Token的數目。

contract MyNFT {
  mapping(address => uint) private balances;
  function balanceOf(address _owner) constant returns (uint balance)
  {
    return balances[_owner];
  }
}

Ownership Functions

該函式定義了智慧合約如何去處理一個token的所有者,而且所有者如何轉換。最為顯著的函式是:takeOwnership和transfer二者起到的是提款以及賬戶與賬戶之間轉賬的作用。

ownerOf:

該函式是返回Token擁有者的地址,由於每個ERC721令牌都是不可替代的,因此它們是唯一的,它通過唯一ID在區塊鏈中引用,我們可以使用其ID來確定Token的所有者。

contract MyNFT {
  mapping(uint256 => address) private tokenOwners;
  mapping(uint256 => bool) private tokenExists;
  function ownerOf(uint256 _tokenId)
  constant returns (address owner) {
    require(tokenExists[_tokenId]);
    return tokenOwners[_tokenId];
  }
}

approve:

該功能批准或授予另一實體代表所有者轉讓代幣的許可權。 例如,如果愛麗絲擁有1個NFT,她可以為她的朋友鮑勃撥打批准功能。 通話成功後,鮑勃可以代表愛麗絲以後對該令牌進行所有權或執行操作。 

contract MyNFT {
  mapping(address => mapping (address => uint256)) allowed;
  function approve(address _to, uint256 _tokenId){
    require(msg.sender == ownerOf(_tokenId));
    require(msg.sender != _to);
    allowed[msg.sender][_to] = _tokenId;
    Approval(msg.sender, _to, _tokenId);
  }
}

takeOwnership:

這個功能就像一個提款功能,因為外部使用者可以呼叫它來從另一個使用者的帳戶中取出Token。 因此,當用戶被批准擁有一定數量的Token(該Token屬於當前使用者但存於另一使用者)並希望從另一使用者的餘額中提取所述Token時,可以takeOwnership函式。

contract MyNFT {
  function takeOwnership(uint256 _tokenId){
    require(tokenExists[_tokenId]);
    address oldOwner = ownerOf(_tokenId);
    address newOwner = msg.sender;
    require(newOwner != oldOwner);
    require(allowed[oldOwner][newOwner] == _tokenId);
    balances[oldOwner] -= 1;
    tokenOwners[_tokenId] = newOwner;
    balances[newOwner] += 1;
    Transfer(oldOwner, newOwner, _tokenId);
  }
}

transfer:

該方法是用於轉換Token,transfer讓Token的所有者將其傳送給其他使用者,類似於獨立的加密貨幣。但是,只有當接收賬戶先前已被髮送賬戶批准擁有該Token時才能啟動轉賬。

contract MyNFT {
  mapping(address => mapping(uint256 => uint256)) private ownerTokens;
  function removeFromTokenList(address owner, uint256 _tokenId) private {
    for(uint256 i = 0;ownerTokens[owner][i] != _tokenId;i++){
      ownerTokens[owner][i] = 0;
    }
  }
  function transfer(address _to, uint256 _tokenId){
    address currentOwner = msg.sender;
    address newOwner = _to;
    require(tokenExists[_tokenId]);
    require(currentOwner == ownerOf(_tokenId));
    require(currentOwner != newOwner);
    require(newOwner != address(0));
    removeFromTokenList(_tokenId);
    balances[oldOwner] -= 1;
    tokenOwners[_tokenId] = newOwner;
    balances[newOwner] += 1;
    Transfer(oldOwner, newOwner, _tokenId);
  }
}

tokenOfOwnerByIndex (優先推薦):

每個人同時可以擁有一個或多個Token,因為每個Token的ID都是唯一的,所以很難跟蹤到每個人所擁有的Token的ID。為此,該智慧合約記錄了每個人所擁有Token的ID,因此,使用者擁有的每個單獨的Token都可以通過其索引在使用者擁有的Token列表(陣列)中檢索。tokenOfOwnerByIndex可以幫助我們檢索出任意令牌。

contract MyNFT {
  mapping(address => mapping(uint256 => uint256)) private ownerTokens;
  function tokenOfOwnerByIndex(address _owner, uint256 _index) constant returns (uint tokenId){
    return ownerTokens[_owner][_index];
  }
}

MetaData Function

正如我們之前所說的,一張人民幣和一件科比的球服之間是不可替換的,因為它們有著各自不同的屬性。在可以告知Token屬性的區塊鏈上儲存資料是相當的昂貴以及不推薦此方法,為了解決這個問題,我們可以將引用(如IPFS雜湊或HTTP(S)連結)儲存到鏈上的每個Token的屬性,以便鏈外的程式可以執行邏輯來查詢有關Token的更多資訊。

tokenMetadata(優先推薦):

這方法可以讓我們檢視Token的元資料或者資料的連結。

contract MyNFT {
  mapping(uint256 => string) tokenLinks;
  function tokenMetadata(uint256 _tokenId) constant returns (string infoUrl) {
    return tokenLinks[_tokenId];
  }
}

Event

只要合同呼叫,事件都會被出發。而且一旦觸發事件,將會廣播到所有的監聽中去。外部程式監聽區塊鏈事件,以便在事件觸發後使用事件提供的資訊執行邏輯。ERC712定義的兩個事件如下:

Transfer:

只要Token的持有者發生改變,都會觸發此事件,同時會被廣播。 廣播裡會詳細說明哪個帳戶傳送Token,哪個帳戶收到Token,以及哪個Token(通過ID)被傳送。

contract MyNFT {
  event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
}

Approval:

只要Token的所有者同意另一使用者獲得Token的所有權,事件將會被觸發。它詳細說明哪個帳戶當前擁有該Token,哪個帳戶將來擁有該Token,以及哪個Token(通過Token的ID)被批准轉讓其所有權。

contract MyNFT {
  event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);
}

與ERC20類似,新提出的ERC721標準為新的智慧合約開闢了一個通道,作為不可互換的東西。 正如在CryptoKitties(ERC721),Decentraland(ERC20),CryptoPunks(ERC20)和其他應用程式中可以看到的,不可互換的令牌已被證明是非常高需求的產品。 即使維基解密擁有幾個高價值的CryptoKitties! 但是,最終這一標準將進一步擴大加密貨幣經濟並有助於推動這一領域的進一步發展。

本篇文章作者大部分內容引用了文章,也加入了自己的一些看法以及理解,本篇文章僅代表個人意見,如有不得之處請在評論區告知。