初探ERC-721

ERC-721協議是以太坊開發中最常使用的第二大協議,第一大協議當然是我們的ERC-20。

ERC-721官方簡要解釋是Non-Fungible Tokens,簡寫爲NFTs,多翻譯爲非同質代幣。

ERC-721最早進入大家的視野都是因爲 《CryptoKitties》 加密貓的功勞。所以我下面會結合加密貓遊戲的案例來說說我們的ERC-721。

ERC-721特點:

1.無法分割,ERC-20Token可以無限細分爲10^18份,而ERC721的Token最小的單位爲1,無法再分割。

2.獨一無二,每一個Token完全不同,並且每個Token對不同的用戶都有不同的價值。

回想一下,加密貓最小單位爲 1只,並且每一隻貓的基因長相也完全不同。

下面我會附上以太坊EIP721接口標準。來自:https://eips.ethereum.org/EIPS/eip-721

pragma solidity ^0.4.20;

/// @title ERC-721 Non-Fungible Token Standard
/// @dev See https://eips.ethereum.org/EIPS/eip-721
///  Note: the ERC-165 identifier for this interface is 0x80ac58cd.
interface ERC721 /* is ERC165 */ {
    /// @dev This emits when ownership of any NFT changes by any mechanism.
    ///  This event emits when NFTs are created (`from` == 0) and destroyed
    ///  (`to` == 0). Exception: during contract creation, any number of NFTs
    ///  may be created and assigned without emitting Transfer. At the time of
    ///  any transfer, the approved address for that NFT (if any) is reset to none.
    event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);

    /// @dev This emits when the approved address for an NFT is changed or
    ///  reaffirmed. The zero address indicates there is no approved address.
    ///  When a Transfer event emits, this also indicates that the approved
    ///  address for that NFT (if any) is reset to none.
    event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);

    /// @dev This emits when an operator is enabled or disabled for an owner.
    ///  The operator can manage all NFTs of the owner.
    event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);

    /// @notice Count all NFTs assigned to an owner
    /// @dev NFTs assigned to the zero address are considered invalid, and this
    ///  function throws for queries about the zero address.
    /// @param _owner An address for whom to query the balance
    /// @return The number of NFTs owned by `_owner`, possibly zero
    function balanceOf(address _owner) external view returns (uint256);

    /// @notice Find the owner of an NFT
    /// @dev NFTs assigned to zero address are considered invalid, and queries
    ///  about them do throw.
    /// @param _tokenId The identifier for an NFT
    /// @return The address of the owner of the NFT
    function ownerOf(uint256 _tokenId) external view returns (address);

    /// @notice Transfers the ownership of an NFT from one address to another address
    /// @dev Throws unless `msg.sender` is the current owner, an authorized
    ///  operator, or the approved address for this NFT. Throws if `_from` is
    ///  not the current owner. Throws if `_to` is the zero address. Throws if
    ///  `_tokenId` is not a valid NFT. When transfer is complete, this function
    ///  checks if `_to` is a smart contract (code size > 0). If so, it calls
    ///  `onERC721Received` on `_to` and throws if the return value is not
    ///  `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
    /// @param _from The current owner of the NFT
    /// @param _to The new owner
    /// @param _tokenId The NFT to transfer
    /// @param data Additional data with no specified format, sent in call to `_to`
    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;

    /// @notice Transfers the ownership of an NFT from one address to another address
    /// @dev This works identically to the other function with an extra data parameter,
    ///  except this function just sets data to "".
    /// @param _from The current owner of the NFT
    /// @param _to The new owner
    /// @param _tokenId The NFT to transfer
    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;

    /// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
    ///  TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE
    ///  THEY MAY BE PERMANENTLY LOST
    /// @dev Throws unless `msg.sender` is the current owner, an authorized
    ///  operator, or the approved address for this NFT. Throws if `_from` is
    ///  not the current owner. Throws if `_to` is the zero address. Throws if
    ///  `_tokenId` is not a valid NFT.
    /// @param _from The current owner of the NFT
    /// @param _to The new owner
    /// @param _tokenId The NFT to transfer
    function transferFrom(address _from, address _to, uint256 _tokenId) external payable;

    /// @notice Change or reaffirm the approved address for an NFT
    /// @dev The zero address indicates there is no approved address.
    ///  Throws unless `msg.sender` is the current NFT owner, or an authorized
    ///  operator of the current owner.
    /// @param _approved The new approved NFT controller
    /// @param _tokenId The NFT to approve
    function approve(address _approved, uint256 _tokenId) external payable;

    /// @notice Enable or disable approval for a third party ("operator") to manage
    ///  all of `msg.sender`'s assets
    /// @dev Emits the ApprovalForAll event. The contract MUST allow
    ///  multiple operators per owner.
    /// @param _operator Address to add to the set of authorized operators
    /// @param _approved True if the operator is approved, false to revoke approval
    function setApprovalForAll(address _operator, bool _approved) external;

    /// @notice Get the approved address for a single NFT
    /// @dev Throws if `_tokenId` is not a valid NFT.
    /// @param _tokenId The NFT to find the approved address for
    /// @return The approved address for this NFT, or the zero address if there is none
    function getApproved(uint256 _tokenId) external view returns (address);

    /// @notice Query if an address is an authorized operator for another address
    /// @param _owner The address that owns the NFTs
    /// @param _operator The address that acts on behalf of the owner
    /// @return True if `_operator` is an approved operator for `_owner`, false otherwise
    function isApprovedForAll(address _owner, address _operator) external view returns (bool);
}

interface ERC165 {
    /// @notice Query if a contract implements an interface
    /// @param interfaceID The interface identifier, as specified in ERC-165
    /// @dev Interface identification is specified in ERC-165. This function
    ///  uses less than 30,000 gas.
    /// @return `true` if the contract implements `interfaceID` and
    ///  `interfaceID` is not 0xffffffff, `false` otherwise
    function supportsInterface(bytes4 interfaceID) external view returns (bool);
}

 

 

接口說明:

  • balanceOf(): 返回由_owner 持有的NFTs的數量。

  • ownerOf(): 返回tokenId代幣持有者的地址。

  • approve(): 授予地址_to具有_tokenId的控制權,方法成功後需觸發Approval 事件。

  • setApprovalForAll(): 授予地址_operator具有所有NFTs的控制權,成功後需觸發ApprovalForAll事件。

  • getApproved()、isApprovedForAll(): 用來查詢授權。

  • safeTransferFrom(): 轉移NFT所有權,一次成功的轉移操作必鬚髮起 Transer 事件。安全是指如果目標地址爲合約地址,則執行onERC721Received進行資產轉移,否則的話交易會回滾;如果目標地址爲外部賬號地址,則正常轉移。

細心的朋友應該有發現 ERC-721協議繼承自 ERC-165,ERC-165同樣也是一個合約標準,這個標準要求合約提供其實現了哪些接口,這樣在與合約進行交互的時候可以先調用此接口進行查詢。

下面我會截取一些加密貓的源碼分析給大家看。

你也可以在Etherscan上查看完整源碼:https://etherscan.io/address/0x06012c8cf97bead5deae237070f9587f8e7a266d#contracts

struct Kitty {

        uint256 genes;

        uint64 birthTime;

        uint64 cooldownEndBlock;

        uint32 matronId;

        uint32 sireId;

        uint32 siringWithId;

        uint16 cooldownIndex;

        uint16 generation;
    }

首先,聲明瞭Kitty結構體,其中包含

genes — 是256位的整數,主要作爲貓的遺傳密碼,是決定貓外貌的核心數據;
birthTime — 貓出生時所打包的區塊的時間戳;
cooldownEndBlock — 貓可以再次繁殖的最小時間;
matronId 和 sireId — 分辨是貓母親的ID號和貓父親的ID號;
siringWithId — 如果貓懷孕了,則設置爲父親的ID,否則爲零;
cooldownIndex — 貓繁殖所需的冷卻時間(貓需要等待多久才能繁殖);
generation — 貓的“世代號”(指明這是第幾代貓)。合約創造的第一隻貓是0代,新一代貓的“世代號”是其父母中較大的一代再加1。

Kitty[] kitties;

Kitty數組,存在“鏈上”所有貓咪數據。可通過ID號獲取貓咪具體信息,也可通過kitties.Length獲取貓咪數量。

    mapping (uint256 => address) public kittyIndexToOwner;

    mapping (address => uint256) ownershipTokenCount;

kittyIndexToOwner  所有貓咪對應的用戶地址。

ownershipTokenCount  用戶地址下的貓咪數量。

 

    function _transfer(address _from, address _to, uint256 _tokenId) internal {
        // Since the number of kittens is capped to 2^32 we can't overflow this
        ownershipTokenCount[_to]++;
        // transfer ownership
        kittyIndexToOwner[_tokenId] = _to;
        // When creating new kittens _from is 0x0, but we can't account that address.
        if (_from != address(0)) {
            ownershipTokenCount[_from]--;
            // once the kitten is transferred also clear sire allowances
            delete sireAllowedToAddress[_tokenId];
            // clear any previously approved ownership exchange
            delete kittyIndexToApproved[_tokenId];
        }
        // Emit the transfer event.
        Transfer(_from, _to, _tokenId);
    }

這是交易函數

 

 function _createKitty(
        uint256 _matronId,
        uint256 _sireId,
        uint256 _generation,
        uint256 _genes,
        address _owner
    )
        internal
        returns (uint)
    {
        require(_matronId == uint256(uint32(_matronId)));
        require(_sireId == uint256(uint32(_sireId)));
        require(_generation == uint256(uint16(_generation)));

        // New kitty starts with the same cooldown as parent gen/2
        uint16 cooldownIndex = uint16(_generation / 2);
        if (cooldownIndex > 13) {
            cooldownIndex = 13;
        }

        Kitty memory _kitty = Kitty({
            genes: _genes,
            birthTime: uint64(now),
            cooldownEndBlock: 0,
            matronId: uint32(_matronId),
            sireId: uint32(_sireId),
            siringWithId: 0,
            cooldownIndex: cooldownIndex,
            generation: uint16(_generation)
        });
        uint256 newKittenId = kitties.push(_kitty) - 1;

        require(newKittenId == uint256(uint32(newKittenId)));

        // emit the birth event
        Birth(
            _owner,
            newKittenId,
            uint256(_kitty.matronId),
            uint256(_kitty.sireId),
            _kitty.genes
        );

        // This will assign ownership, and also emit the Transfer event as
        // per ERC721 draft
        _transfer(0, _owner, newKittenId);

        return newKittenId;
    }

新的貓咪的產生函數

 

總結

    隨着區塊鏈技術的發展,初創公司和開發者開始對ERC721越來越感興趣,這是一種讓加密數字資產更容易普及的方式。

    ERC721可能會比ERC20應用的更加廣泛。虛擬資產預計將來會有幾萬億的市場,屆時,將是ERC721標準大放異彩的時候。

 

下一篇文章我會爲大家詳細解析一個非常冷門的Token協議,ERC918

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章