[crypto][presentation slide] CryptoKitties - A Quick Walkthrough Of Its Smart Contract

in #cryptokitties7 years ago

CryptoKitties

A Quick Walkthrough Of Its Smart Contract

by Rachael Pai 2018/01/23


真正的標題是 - 喵之鍊金術師


鍊金術三大步驟

  • 理解
  • 分解
  • 重構

理解


甚麼是 Cryptokitties ?

  • 中譯 : 加密貓 / 乙太貓
  • 在乙太鍊上使用智能合約建構的 貓咪育種遊戲

加密貓遊戲概念


貓特性 : Generation/Cooldown/Cattributes

![]( =550x550)


買貓跟賣貓: Marketplace


兩種育種: Breeding & Siring


Breeding: 產下的貓是屬於自己的

![]( =600x350)


Siring: 出租自己的貓跟別隻貓育種,收租金。



分解


加密貓的智能合約是由有 16 Contracts 組成


Contract KittyBase


Struct Kitty {}


Q : 為何地球上的人類不是可以人人都可以有一隻貓 ?


一隻貓的誕生


function _createKitty(
        uint256 _matronId,
        uint256 _sireId,
        uint256 _generation,
        uint256 _genes,
        address _owner
    )
        internal
        returns (uint) //貓的ID and Index {
        
}

所有的 kittty 都存在

Kitty[] kitties;


  • 怎麼決定 cooldownIndex ?
  • 怎麼決定 generation ?
  • genes 怎麼來的 ?

cooldownIndex (in_createKitty)

uint16 cooldownIndex = uint16(_generation / 2);
if (cooldownIndex > 13) {
    cooldownIndex = 13;
}

In KittyBreeding Contract

 if (_kitten.cooldownIndex < 13) {
    _kitten.cooldownIndex += 1;
}


generation (contract breading)

parent generation(sir or matron 選大的) + 1

//In Contract Breeding  
uint256 kittenId = _createKitty(_matronId, matron.siringWithId, parentGen + 1, childGenes, owner);

uint16 parentGen = matron.generation;
if (sire.generation > matron.generation) {
    parentGen = sire.generation;
}


genes (contract breeding)

uint256 childGenes = geneScience.mixGenes(matron.genes, sire.genes, matron.cooldownEndBlock - 1);
/// @title SEKRETOOOO
contract GeneScienceInterface {
    /// @dev simply a boolean to indicate this is the contract we expect to be
    function isGeneScience() public pure returns (bool);

    /// @dev given genes of kitten 1 & 2, return a genetic combination - may have a random factor
    /// @param genes1 genes of mom
    /// @param genes2 genes of sire
    /// @return the genes that are supposed to be passed down the child
    function mixGenes(uint256 genes1, uint256 genes2, uint256 targetBlock) public returns (uint256);
}

其他重要的資料


  • mapping (uint256 => address) public kittyIndexToOwner;

    • 這個 ID (隻貓)的擁有者是哪個 address (用戶)
  • mapping (address => uint256) ownershipTokenCount;

    • 這個 address (用戶) 擁有多少 token (貓)

  • mapping (uint256 => address) public kittyIndexToApproved;

    • 這個 Adrress 被允許轉送這隻貓
  • mapping (uint256 => address) public sireAllowedToAddress;

    • 這個 Adrress 被允許 Sire (出租育種)這隻貓

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


ERC721

  • none-fungible token (NFT) : 不替代的 Token
  • fungible assets 可替代資產
    • 每個都一樣與等值, 比如台幣每一塊都一樣
    • ERC20 Token
  • none-fungible
    • 任何的收集都是 none-fungible
  • 棒球 --> fungible, 簽名棒球 --> none-fungible
  • ERC721 Token 不可以分割


Contract ERC721 : Interface


contract ERC721 {
    // Required methods
    function totalSupply() public view returns (uint256 total);
    function balanceOf(address _owner) public view returns (uint256 balance);
    function ownerOf(uint256 _tokenId) external view returns (address owner);
    function approve(address _to, uint256 _tokenId) external;
    function transfer(address _to, uint256 _tokenId) external;
    function transferFrom(address _from, address _to, uint256 _tokenId) external;

    // Events
    event Transfer(address from, address to, uint256 tokenId);
    event Approval(address owner, address approved, uint256 tokenId);

    // Optional
    // function name() public view returns (string name);
    // function symbol() public view returns (string symbol);
    // function tokensOfOwner(address _owner) external view returns (uint256[] tokenIds);
    // function tokenMetadata(uint256 _tokenId, string _preferredTransport) public view returns (string infoUrl);

    // ERC-165 Compatibility 
    function supportsInterface(bytes4 _interfaceID) external view returns (bool);
}


  • Ownership
    • function ownerOf(uint256 _tokenId) external view returns (address owner);
    • Optional
      • function tokensOfOwner(address _owner) external view returns (uint256[] tokenIds);
      • function tokenMetadata(uint256 _tokenId, string _preferredTransport) public view returns (string infoUrl);

contract KittyOwnership is KittyBase, ERC721


  • totalSupply
function totalSupply() public view returns (uint) {
        return kitties.length - 1;
}
  • balanceOf
 function balanceOf(address _owner) public view returns (uint256 count) {
        return ownershipTokenCount[_owner];
    }


  • ownerOf
  /// @notice Returns the address currently assigned ownership of a given Kitty.
    /// @dev Required for ERC-721 compliance.
    function ownerOf(uint256 _tokenId)
        external
        view
        returns (address owner)
    {
        owner = kittyIndexToOwner[_tokenId];

        require(owner != address(0));
    }


  • approve
  function approve(
        address _to,
        uint256 _tokenId
    )
        external
        whenNotPaused
    {
        // Only an owner can grant transfer approval.
        require(_owns(msg.sender, _tokenId));
        // Register the approval (replacing any previous approval).
        _approve(_tokenId, _to);
        // Emit approval event.
        Approval(msg.sender, _to, _tokenId);
    }
 function _approve(uint256 _tokenId, address _approved) internal {
        kittyIndexToApproved[_tokenId] = _approved;
    }


  • transfer
 function transfer(
        address _to,
        uint256 _tokenId
    )
        external
        whenNotPaused
    {
        require(_to != address(0));
        require(_to != address(this));.
        require(_to != address(saleAuction));
        require(_to != address(siringAuction));
        // You can only send your own cat.
        require(_owns(msg.sender, _tokenId));
        // Reassign ownership, clear pending approvals, emit Transfer event.
        _transfer(msg.sender, _to, _tokenId);
    }

  • transferFrom
    function transferFrom(
        address _from,
        address _to,
        uint256 _tokenId
    )
        external
        whenNotPaused
    {
         require(_to != address(0));
         require(_to != address(this));
        // Check for approval and valid ownership
        require(_approvedFor(msg.sender, _tokenId));
        require(_owns(_from, _tokenId));
        // Reassign ownership (also clears pending approvals and emits Transfer event).
        _transfer(_from, _to, _tokenId);
    }

ERC165 : Pseudo-Introspection, or standard interface detection


  • 解決
    • ERC Interface 是否存在 ?
  • 作法
    • 每個 standard interface 應該被指派一個 bytes32 unique identifier
    • Implement 下面的 function(s)
/// @returns true iff the interface is supported
function supportsInterface(bytes32 interfaceID) constant returns (bool);

Implementation of supportsInterface

   function supportsInterface(bytes4 _interfaceID) external view returns (bool)
    {
        // DEBUG ONLY
        //require((InterfaceSignature_ERC165 == 0x01ffc9a7) && (InterfaceSignature_ERC721 == 0x9a20483d));

        return ((_interfaceID == InterfaceSignature_ERC165) || (_interfaceID == InterfaceSignature_ERC721));
    }


問題是要怎產生 Interface 的 Signature ?


Signature Making : function selector^


bytes4 constant InterfaceSignature_ERC165 =
    bytes4(keccak256('supportsInterface(bytes4)'));

bytes4 constant InterfaceSignature_ERC721 =
    bytes4(keccak256('name()')) ^
    bytes4(keccak256('symbol()')) ^
    bytes4(keccak256('totalSupply()')) ^
    bytes4(keccak256('balanceOf(address)')) ^
    bytes4(keccak256('ownerOf(uint256)')) ^
    bytes4(keccak256('approve(address,uint256)')) ^
    bytes4(keccak256('transfer(address,uint256)')) ^
    bytes4(keccak256('transferFrom(address,address,uint256)')) ^
    bytes4(keccak256('tokensOfOwner(address)')) ^
    bytes4(keccak256('tokenMetadata(uint256,string)'));


Contract KittyAccessControl



  • 3 special roles
    • CEO :
      • 改變 smart contract address
      • unpause smart contact: 因為一開始是 pause 的
    • CFO
      • 可以從 kittyCore 還有 auction 取款
    • COO
      • 產生 gen0
      • 產生 promotion cat
      • 把 kitties 放到 auction

address public ceoAddress;
    modifier onlyCEO() {
        require(msg.sender == ceoAddress);
        _;
    }
function setCFO(address _newCFO) external onlyCEO {...}
    
function setSaleAuctionAddress(address _address) external onlyCEO {...}



重構


加密狗!?


Reference


一些統計圖表 。。。




Q & A & 與大家分享時間


Sort:  

Hi! I am a robot. I just upvoted you! I found similar content that readers might be interested in:
https://github.com/cyy13/test0/commit/8527e4576fee25faa74373831fd7402cb8ad72de