1. 程式人生 > >第十七課 【ERC721實踐】迷戀貓從玩耍到開發

第十七課 【ERC721實踐】迷戀貓從玩耍到開發

**CryptoKitties(中文名:迷戀貓)**是一款在以太坊區塊鏈上的虛擬養貓遊戲,一經推出就以病毒式的快速擴散,橫掃整個以太坊市場。而這款可愛的遊戲於2018年 2 月 16 日(農曆大年初一)登陸 iOS國區,中文名稱的 “迷戀貓”,皆因 “迷戀” 與 “密鏈” 同音,亦有 “加密區塊鏈” 的意思,而且為了慶祝中國農曆新年,所有在節日期間出生的新貓咪都將會有中國的背景故事。 迷戀貓是一款由 Axiom Zen 和以太坊智慧合作開發的區塊鏈寵物養成遊戲。在遊戲中,玩家使用以太幣(ETH)進行電子貓的購買、餵食、照料與交配等,但其最為核心是玩法是將以太坊區塊鏈中的電子貓進行出售。撇開眾人對虛擬小貓的狂熱不談,畢竟絕對不只是貓很可愛這種原因,這個遊戲已造成過大流量,甚至因流量太大導致遊戲困難,讓很多交易耗費比原本更長的時間,官方甚至為此讓生育費漲價以提供礦工更高的誘因。目前 CryptoKitties 已經成為以太坊最流行的智慧合約,數量約莫是第二名的兩倍。

1. 迷戀貓註冊/玩法攻略

玩迷戀貓遊戲,玩家需要在以太坊區塊鏈上下載到這款遊戲的APP,遊戲開始系統會贈送玩家一隻喵。剛推出時是送貓的,現在只有活動時才贈送。它讓你沉迷於吸貓,然後當你無法自拔後,就會自願掏出大把的以太幣去氪金了,滿滿的套路啊!

1.1 在谷歌瀏覽器中安裝MetaMask

1.1.1 下載安裝谷歌瀏覽器(Chrome)

百度搜索"谷歌瀏覽器"即可進行下載安裝(谷歌瀏覽器可以完美支援第三方外掛和擴充套件程式)。

1.1.2 安裝MetaMask錢包外掛

MetaMask是一款執行穩定的以太坊錢包,我們和其他交易所的區別之一在於在這裡您可以自主管理您的錢包,並且錢包所有的資產變動都會上區塊鏈確認,完全是開放和透明的,而在其他交易所,您的錢包實際是平臺在管理,無法完全杜絕資金風險以及被限制充幣提幣等行為。 1)國內使用者可使用 MetaMask_v3.9.11.crx下載MetaMask並解壓,然後在擴充套件程式設定頁面chrome://extensions/

將下載好的檔案拖進來即可安裝,安裝後點擊啟用,即可在瀏覽器右上角看到MetaMask圖示。如下圖:

image

2)翻牆或者國外的使用者可以點選 這裡在谷歌外掛商店進行安裝。

image

1.2 建立MetaMask賬號

1)接下來,點選MetaMask圖示建立自己的賬戶。 2)對MetaMask錢包外掛進行設定,首先需要連結到主網。點選MetaMask介面的左上角,選擇Main Ethereum Network

image

1.3 購買迷戀貓

開啟官方網站(www.cryptokitties.co ),用MetaMask的錢包地址登入,然後到市場上用ETH去購買貓,新手登入將免費獲得一隻。需要擁有以太幣(ETH),用於養貓的花費,沒有的話要先去交易所買點以太幣ETH。 然後到你的貓咪個人頁面去餵養、培育、銷售它。每一個貓咪都可以選擇扮演母親或者父親的角色,和其他異性貓咪組成家庭,最後按繁殖按鈕生下小貓咪。如果想和其他人的貓咪繁育,則需要支付其他玩家相應的ETH。生下的小貓咪則可以去市場上交易。貓咪每多繁殖一次,培育小貓咪所需時間都會相應增長,目前官網上每5分鐘就誕生一個小貓咪。主人可以將自己的貓咪打扮各種風格的花式貓。由於區塊鏈是分散的,每個交易都是通過多臺計算機分配的,而不是中央伺服器,所以玩家支付的ETH相當於區塊鏈網路中支援交易或合約的成本。 當你已在瀏覽器安裝MetaMask外掛並手握MetaMask賬號後

,擼貓的一切準備工作已經完畢,登陸迷戀貓**(CryptoKitties)官網**,準備選購你的第一隻貓咪吧。

在以太貓這款遊戲中,玩家必須至少擁有一隻貓才可進行遊戲,而獲得貓主要可以通過以下三種方式: 1.測試玩家,獲贈免費的貓 2.普通玩家,在遊戲內市場購買自己的第一隻貓 3.普通玩家,在Twitch、Reddit等平臺等待主播或其他玩家贈送(適合體驗遊戲) 在這裡我們僅介紹第2種,通過在市場中購買貓的方式開始遊玩:

image.png

當您已在瀏覽器上安裝並登陸MetaMask錢包,迷戀貓官網會自動同步您的登陸資訊。在此過程中您可能會遇到賬號被鎖定的阻礙,這是正常現象,輸入密碼就可解鎖。

初始頁面,左上為您的個人資訊,右側頂部的三個標籤欄依次為:“我的貓咪”(My Kitties)、“市場”(Marketplace)還有“邀請”(Invite)。但由於上圖中的賬號未擁有貓,所以頁面中顯示的是市場推薦。 讓我們點進“市場”看看具體情況。 玩家可以在市場中交易或給自己的貓配種。頁面中上的四個標籤分別為:“正在售賣”(For Sale)、“提供配種”(Siring)、“零代貓”(Gen 0)以及“全部”(All Kitts)。 顧名思義,通過“正在售賣”標籤玩家可以快速檢視可以購買的物件。 “提供配種”標籤下顯示的全是可以用來與自己貓交配的“公貓”物件。 “零代貓”的概念比較特殊,在2018年11月前,系統每隔15分鐘都會產出一批零代貓,它們擁有最短的初始生殖間隔。新零代貓的初始價格比最近市場中售賣出的五隻貓的價格平均值高50%。 通過“全部”標籤,玩家可以檢視遊戲裡的所有貓。無論是“市場”還是“我的貓咪”頁面,玩家都可通過右側下拉框簡單篩選顯示規則。規則大體為“最新一代”、“最初始一代”以及“最便宜”和“最貴”。 購買貓時,玩家除要選擇帶有“For sale”(正在售賣)標籤的物件,還需仔細查該物件的各項屬性。 這裡要注意幾點,包括貓的代數,貓的交配間隔以及它的外觀和屬性。 通常來說,零代貓擁有最短的初始交配間隔,但隨著玩家讓其配種次數的增多,貓咪的交配間隔會愈發延長。 以今天創下新交易記錄的4號貓“Fluffy”為例,它的交配間隔已經降至第二檔,意味著它只能每隔2-5分鐘交配一次。

由玩家培育出的貓咪的交配間隔不會低於它的父母。目前最慢一檔的交配間隔為一週一次。而僅在目前看來,交配間隔越短的貓咪在市場上的價值也就越高。 已有不少玩家把迷戀貓當成“生殖農場”,所以如果您是初次進入遊戲,一定要觀察購買物件是否處在交配CD,以及它的交配慾望。 除了交配相關,貓的外觀、性狀以及父母和子女可能都是買貓者要考慮在內的問題。

從眼睛到尾巴再到花色甚至是背景,這些都是能夠直觀觀察到的。而同過“Cattributes”一欄,你還可以進一步看到更多關於這隻貓咪由基因排列體現出的性狀特徵。 題外話,迷戀貓裡的貓咪均由256位基因編碼組成,父母的基因在一定程度上會決定孩子的特徵。零代貓由於是系統生成,無父母一欄。 看到了吧,在迷戀貓裡買貓的門道非常多,入坑需謹慎。 在選擇好想要購買的貓咪後,點選“Buy now”按鈕並使用以太幣購買貓咪。 以上就是就是以太坊買貓攻略。需要注意的是,遊戲中玩家自行配種以及更多涉及市場的操作均需向平臺提交手續費。所以不僅是想在迷戀貓裡賺錢,就算想要維持持續運作也不是一件容易事。當然,如果你手握一大堆以太幣那就簡單了。

2. 謎戀貓原始碼詳解

要想快速瞭解這個遊戲的工作原理,最好的方法就是直接閱讀原始碼。 CryptoKitties 的原始碼大部分是開源的(也有小部分沒有開源,主要是是交配基因更新這部分)。CryptoKitties 原始碼大約有2000行,本文將主要講解其中相對重要的部分。

CryptoKitties遊 戲的原始碼分成了一個個的子合約,而非一個包含所有邏輯的單一檔案。 子合約通過下述方式繼承主合約:

contract KittyAccessControl
contract KittyBase is KittyAccessControl
contract KittyOwnership is KittyBase, ERC721
contract KittyBreeding is KittyOwnership
contract KittyAuction is KittyBreeding
contract KittyMinting is KittyAuction
contract KittyCore is KittyMinting

最終應用程式指向的合約是 KittyCore 合約,這個合約繼承了前面所有合約的屬性和方法。接下來,讓我們一個一個地來分析這些合約。

##2.1 KittyAccessControl:誰控制合約? 該合約負責管理各種不同的地址,同時定義了各種僅限於特定角色執行的限制性操作,這些角色被命名為“CEO”、“CFO”和“COO”。 KittyAccessControl 合約主要功能是管理其他合約,所以它不涉及遊戲的具體邏輯。該合約定義了以太坊地址“CEO”、“CFO”和“COO”的使用方法,它們對該合約有著特殊的所有權以及控制權限。 KittyAccessControl 合約還定義了一些函式修飾符,如 onlyCEO (該函式只有“CEO”才能執行),同時該合約還定義了一些暫停/恢復合約的方法以及提現方法。

 modifier onlyCLevel() {
     require(
         msg.sender == cooAddress ||
         msg.sender == ceoAddress ||
         msg.sender == cfoAddress
     );
     _;
 }

 //...some other stuff

// Only the CEO, COO, and CFO can execute this function:
function pause() external onlyCLevel whenNotPaused {
    paused = true;
}

pause() 函式主要是方便開發人員進行版本更新,以防那些預見不到的 bug。但我同事 Luke 指出,這個方法可以讓開發人員有權完全凍結合約,令所有人都無法轉讓、出售或繁殖他們的貓咪!當然,開發人員並不想這麼做。但真正有趣的地方確實,大多數人僅僅因為該遊戲執行在以太坊上,就會以為它是一款完全是去中心化的遊戲。 我們接著來看其他合約。

##2.1 KittyBase:數字貓是什麼貓? KittyBase 合約是我們定義最底層的程式碼的地方,這些程式碼會用到整個遊戲的所有核心功能上,包括資料儲存結構、相關常量與資料型別,以及管理這些資料的內部函式。 KittyBase 合約定義了應用程式的很多核心資料。首先它將Kitty定義為結構體型別:

struct Kitty {
    uint256 genes;
    uint64 birthTime;
    uint64 cooldownEndBlock;
    uint32 matronId;
    uint32 sireId;
    uint32 siringWithId;
    uint16 cooldownIndex;
    uint16 generation;
}

從這裡可以看到,一隻貓咪的本質就是一長串的無符號整數。

接下來一一介紹貓咪的每個屬性的具體引數:

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

需要注意的是,在 CryptoKitties 遊戲中,貓是無性別之分的,任意2只貓都可以一起繁殖。 KittyBase 合約聲明瞭一個“貓”結構的陣列,如下所示:

Kitty[] kitties;

該陣列包含了所有貓咪的資料,所以它就像一個貓咪資料庫一般。無論什麼時候生成一個新的貓咪,它都會被新增到該陣列內,陣列的索引就是貓咪的 ID 號。下圖顯示的是創世貓,其 ID 號為“1”。

該合約中還包含有從貓咪 ID 號到其所有者地址的一個對映,主要用於追蹤貓咪的所有者。

mapping (uint256 => address) public kittyIndexToOwner;

合約中還定義了其他一些對映,但此處不再深究每一個細節。 當貓咪的所有者從一個人轉移到另外一個人時,kittyIndexToOwner 對映就會更新,從而識別新的所有者。

 /// @dev Assigns ownership of a specific Kitty to an address.
 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)
{
    // These requires are not strictly necessary, our calling code should make
    // sure that these conditions are never broken. However! _createKitty() is already
    // an expensive call (for storage), and it doesn't hurt to be especially careful
    // to ensure our data structures are always valid.
    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;

    // It's probably never going to happen, 4 billion cats is A LOT, but
    // let's just be 100% sure we never let this happen.
    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;
    }

從上面這段程式碼中可以看出,這個函式傳遞了母親和父親的 ID 號、貓咪的世代號、256位遺傳密碼和所有者的地址。然後建立貓咪,並將其加入到 Kitty 陣列中,最後呼叫 _transfer() 將其分配給新的所有者。 是不是很酷!現在我們已經知道 CryptoKitties 遊戲如何將一隻貓咪定義為一種資料型別,如何將所有貓咪都儲存在區塊鏈中,以及如何跟蹤這些貓咪的所有者。

##2.3 KittyOwnership:作為通證的貓咪 CryptoKitties 遊戲遵循 ERC721 通證標準,這是一種不可替代通證,它可以很好地追蹤數字收藏品的所有權,比如數字撲克牌或 MMORPG(大型多人線上角色扮演遊戲)中的稀有物品。 關於可互換性的說明:Ether 是可互換的通證,因為任意5個 ETH 都與其他5個 ETH 一樣有著同樣的價值。但是像 CryptoKitties 這樣的通證是不可替代通證,每隻貓咪的外觀都各不相同,它們不能生而相同,所以無法相互交換。 你可以從合約的定義中看出,KittyOwnership 繼承的是 ERC721 合約。

contract KittyOwnership is KittyBase, ERC721 {

而所有是 ERC721 通證都遵循同樣的標準。下一章節分析ERC721標準。

#3. 非同質化代幣ERC721分析 ##3.1 ERC721是什麼 和ERC20一樣,ERC721同樣是一個代幣標準,ERC721官方簡要解釋是Non-Fungible Tokens,簡寫為NFTs,多翻譯為非同質代幣。 ERC721 是由Dieter Shirley 在2017年9月提出。Dieter Shirley 正是謎戀貓CryptoKitties背後的公司Axiom Zen的技術總監。因此謎戀貓也是第一個實現了ERC721 標準的去中心化應用。ERC721號提議已經被以太坊作為標準接受,但該標準仍處於草稿階段。本文介紹的ERC721標準基於最新(2018/03/23)官方提議。

那怎麼理解非同質代幣呢? 非同質代表獨一無二,謎戀貓為例,每隻貓都被賦予擁有基因,是獨一無二的(一隻貓就是一個NFTs),貓之間是不能置換的。這種獨特性使得某些稀有貓具有收藏價值,也因此受到追捧。 ERC20代幣是可置換的,且可細分為N份(1 = 10 * 0.1), 而ERC721的Token最小的單位為1,無法再分割。

如果同一個集合的兩個物品具有不同的特徵,這兩個物品是非同質的,而同質是某個部分或數量可以被另一個同等部分或數量所代替。

非同質性其實廣泛存在於我們的生活中,如圖書館的每一本,寵物商店的每一隻寵物,歌手所演唱的歌曲,花店裡不同的花等等,因此ERC721合約必定有廣泛的應用場景。通過這樣一個標準,也可建立跨功能的NFTs管理和銷售平臺(就像有支援ERC20的交易所和錢包一樣),使生態更加強大。

##3.2 ERC721標準 ERC721最為一個合約標準,提供了在實現ERC721代幣時必須要遵守的協議,要求每個ERC721標準合約需要實現ERC721及ERC165介面,介面定義如下:

pragma solidity ^0.4.20;

/// @title ERC-721 Non-Fungible Token Standard
/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
///  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,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 Set or reaffirm the approved address for an NFT
    /// @dev The zero address indicates there is no approved address.
    /// @dev 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);
}

介面說明:

  • balanceOf(): 返回由_owner 持有的NFTs的數量。
  • ownerOf(): 返回tokenId代幣持有者的地址。
  • approve(): 授予地址_to具有_tokenId的控制權,方法成功後需觸發Approval 事件。
  • setApprovalForAll(): 授予地址_operator具有所有NFTs的控制權,成功後需觸發ApprovalForAll事件。
  • getApproved()、isApprovedForAll(): 用來查詢授權。
  • safeTransferFrom(): 轉移NFT所有權,一次成功的轉移操作必須發起 Transfer 事件。函式的實現需要做一下幾種檢查: 1] 呼叫者msg.sender應該是當前tokenId的所有者或被授權的地址 2] _from 必須是 _tokenId的所有者 3] _tokenId 應該是當前合約正在監測的NFTs 中的任何一個 4] _to 地址不應該為 0,如果_to 是一個合約應該呼叫其onERC721Received方法, 並且檢查其返回值,如果返回值不為bytes4(keccak256(“onERC721Received(address,uint256,bytes)”))則丟擲異常。
  • transferFrom(): 用來轉移NFTs, 方法成功後需觸發Transfer事件。呼叫者自己確認_to地址能正常接收NFT,否則將丟失此NFT。此函式實現時需要檢查上面條件的前4條。

##3.3 ERC165 標準 ERC721標準同時要求必須符合ERC165標準 ,其介面如下:

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);
}

ERC165同樣是一個合約標準,這個標準要求合約提供其實現了哪些介面,這樣再與合約進行互動的時候可以先呼叫此介面進行查詢。  interfaceID為函式選擇器,計算方式有兩種,如:bytes4(keccak256('supportsInterface(bytes4)'));ERC165.supportsInterface.selector,多個函式的介面ID為函式選擇器的異或值。  ##3.4 可選實現介面:ERC721Enumerable ERC721Metadata 介面用於提供合約的元資料:name , symbol 及 URI(NFT所對應的資源)。  其介面定義如下:

interface ERC721Metadata /* is ERC721 */ {
    function name() external pure returns (string _name);
    function symbol() external pure returns (string _symbol);
    function tokenURI(uint256 _tokenId) external view returns (string);
}

介面說明:

  • name(): 返回合約名字,儘管是可選,但強烈建議實現,即便是返回空字串。
  • symbol(): 返回合約代幣符號,儘管是可選,但強烈建議實現,即便是返回空字串。
  • tokenURI(): 返回_tokenId所對應的外部資原始檔的URI(通常是IPFS或HTTP(S)路徑)。外部資原始檔需要包含名字、描述、圖片,其格式的要求如下:
{
    "title": "Asset Metadata",
    "type": "object",
    "properties": {
        "name": {
            "type": "string",
            "description": "Identifies the asset to which this NFT represents",
        },
        "description": {
            "type": "string",
            "description": "Describes the asset to which this NFT represents",
        },
        "image": {
            "type": "string",
            "description": "A URI pointing to a resource with mime type image/* representing the asset to which this NFT represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive.",
        }
    }
}

tokenURI通常是被web3呼叫,以便在應用層做相應的查詢和展示。

3.5 可選實現介面:ERC721Enumerable

ERC721Enumerable的主要目的是提高合約中NTF的可訪問性,其介面定義如下:

interface ERC721Enumerable /* is ERC721 */ {
    function totalSupply() external view returns (uint256);
    function tokenByIndex(uint256 _index) external view returns (uint256);
    function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);
}

介面說明:

  • totalSupply(): 返回NFT總量
  • tokenByIndex(): 通過索引返回對應的tokenId。
  • tokenOfOwnerByIndex(): 所有者可以一次擁有多個的NFT, 此函式返回_owner擁有的NFT列表中對應索引的tokenId。

3.6 補充說明

NTF IDs

NTF ID,即tokenId,在合約中用唯一的uint265進行標識,每個NFT的ID在智慧合約的生命週期內不允許改變。推薦的實現方式有:  1. 從0開始,每新加一個NFT,NTF ID加1  2. 使用sha3後uuid 轉換為 NTF ID

與ERC-20的相容性

ERC721標準儘可能遵循 ERC-20 的語義,但由於同質代幣與非同質代幣之間的根本差異,並不能完全相容ERC-20。

交易、挖礦、銷燬

在實現transter相關介面時除了滿足上面的的條件外,我們可以根據需要新增自己的邏輯,如加入黑名單等。  同時挖礦、銷燬儘管不是標準的一部分,我們可以根據需要實現。

#4. 彩貝社群介紹

彩貝鏈始於彩貝社群,它致力於為全世界的繪畫創作者提供最好用的繪畫工具,建立最繁榮活躍的繪畫興趣社群,並通過區塊鏈社群方式,構建全球統一的數字版權交易標準和交易支付工具,跨越各國版權交易法律及支付結算的鴻溝,成為全球商業級的作品發表、儲存、檢索和版權交易服務,數字藝術作品的創作生態、版權交易及衍生品服務平臺。

彩貝DAPP是彩貝鏈生態第一個落地去中心化應用,它是全球領先的數字繪畫社群畫吧的國際區塊鏈版本。畫吧已有註冊使用者600萬,原創數字繪畫資產900萬幅,畫吧完全自主開發全球領先的數字繪畫工具集,每幅數字繪畫全過程全平臺矢量回放,可實現2D,3D數字資產高效率高質量生成。彩貝DAPP利用彩貝鏈基礎設施,降低版權交易各國差異性和支付方式差異性,大幅提高交易效率,降低交易成本,同時通過彩貝鏈代幣經濟激勵體系,進一步提升彩貝社群全球參與者活躍度。 彩貝生態鏈的數字繪畫等資產是滿足ERC721標準的一種原創數字資產。彩貝鏈創新點