ERC721藏品合約詳解,附代碼實現

咱們能夠用以太坊智能合約來模擬稀有的收藏品,每個通證都遵循以太坊ERC-721標準, 它是DieterShirley在2017年底提出的以太坊改進建議書。ERC721可使智能合約像相似於 ERC20代幣同樣進行交易, 區別在於,ERC721通證是獨一無二的,每個都有惟一的ID,具備不可取代性。git

若是你但願立刻開始學習以太坊智能合約和應用開發,能夠訪問匯智網提供的出色的在線互動教程:github

ERC721約定了一些接口函數,使它在必定程度上符合ERC20代幣標準。這麼作是爲了讓現有的 錢包更容易顯示代幣的基本信息。這些函數可讓符合ERC721標準的智能合約像比特幣或者 以太幣這樣普通的數字加密幣同樣,經過智能合約編程的方式定義一些功能讓用戶實現向他人 發送代幣或檢查帳戶餘額等操做。編程

這是一個簡明的ERC721智能合約聲明:app

contract ERC721 {
   //與ERC20兼容的接口
   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);
   //全部權相關的接口
   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);
   //通證元數據接口
   function tokenMetadata(uint256 _tokenId) constant returns (string infoUrl);
   //事件
   event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
   event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);
}

name - 名稱

該函數應當返回通證的名稱。 例如:函數

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

Symbol - 符號

該函數應當返回通證的符號,它有助於提升與ERC20的兼容性。例如:學習

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

區塊鏈開發課程精選

totalSupply - 總髮行量

該函數應當返回區塊鏈上供應的通證總數量,該數量不必定是固定不變的。 例如:區塊鏈

contract MyNFT {
  //想發行多少取決於你 ;)
  uint256 private totalSupply = 1000000000;

  function totalSupply() constant returns (uint256supply){
    return totalSupply;
  }
}

balanceOf - 餘額

該函數用於查詢某一地址裏的通證餘額。例如:ui

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

下面這些函數定義了合約如何處理通證的全部權及如何轉移全部權。其中最重要的兩個函數 是獲取(takeOwnership)和轉帳(transfer),用來實現用戶之間的通證流轉,就像銀行的提款 和匯款功能。加密

ownerOf - 持幣人

該函數返回通證持有人的地址。由於每個ERC721通證都是不可替代的,所以能夠在區塊鏈上 惟一的地址找到,咱們能夠用通證的ID來肯定其持有人。3d

contract MyNFT {
  mapping(uint256 => address) privatetokenOwners;
  mapping(uint256 => bool) private tokenExists;

  function ownerOf(uint256 _tokenId) constant returns (address owner) {
    require(tokenExists[_tokenId]);
    return tokenOwners[_tokenId];
  }
}

approve - 受權

該函數用來受權給另外一主體表明持有人進行通證轉移操做。例如,假設Alice有一個ERC721通證,她能夠 調用approve函數來受權給她的朋友Bob,而後Bob就能夠表明Alice行使通證持有人的權利。

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 - 獲取

該函數相似於取款功能,一個外部主體經過調用takeOwnership函數來從另外一個用戶的帳戶 中提取ERC721通證。

所以,在一個用戶被(其餘人)受權擁有必定數量的通證的狀況下,能夠經過該功能將這部分 通證從另外一個用戶的帳戶中提取出來。

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[oldOwner] += 1;
    Transfer(oldOwner, newOwner,_tokenId);
  }
}

transfer - 轉帳

另外一種轉移通證的方法時使用transfer函數。轉帳(transfer)功能可讓用戶將通證發給另外一個用戶, 相似於操做比特幣這樣的加密數字貨幣。然而,只有在匯出帳戶以前受權過匯入帳戶持有其通證的 狀況下,才能夠進行轉帳。

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 - 通證檢索

這個函數是可選的,但推薦你實現它。

每個ERC721通證的持有者能夠同時持有不止一個通證,由於每一個通證都有惟一的ID,可是,要跟蹤某個用戶持有的 通證可能就會比較困難。爲此,合約須要記錄每一個用戶持有的每一個通證。經過這種方式,用戶能夠 經過索引清單檢索其擁有的通證。通證檢索(tokenOfOwnerByIndex)函數能夠經過這種方式追溯某一特定的通證。

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

tokenMetaData - 通證元數據

就像咱們以前所說的,使物品具備不可替代性的是它們獨一無二的特質。美圓和網球卡不可替代, 由於它們的特徵不一樣。可是,在區塊鏈上將這些區分每一個通證的特徵儲存下來成本很高,也不推薦這麼作。 爲了解決這個問題,咱們能夠儲存每一個通證的引用(references),例如IPFS哈希或HTTP(S)連接,這些 引用,被稱做元數據。元數據是可選的。

tokenMetaData函數應當返回通證的元數據,或者通證數據的連接。

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

當調用合約方法的時候,事件將會被觸發,而且一旦被觸發就會向監聽系統傳播。外部應用能夠監聽區塊鏈 中的事件,一旦接收到區塊鏈中的事件被觸發,監聽系統就能夠經過事件中包含的信息執行邏輯程序。 ERC721標準定義了下面兩個事件。

Transfer - 轉帳

當一個通證的全部權從一個用戶轉移到另外一個時,將觸發該事件,事件的信息包括匯出帳戶、匯入帳戶和通證ID。

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

}

Approval - 批准

當一個用戶容許另外一個用戶持有其通證的時候(例如啓用「受權」功能的時候),該事件就會被觸發,事件的信息中 包含這些通證如今的持有帳戶、被受權帳戶以及通證ID。

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

完整的源代碼在這裏

原文:ERC721剖析:收藏品類的以太坊智能合約

相關文章
相關標籤/搜索