//

[Smart Contract] CryptoKitties Core Code (KittyCore) Analysis (I)

Written by TsaiYee. Please cite the source when reprinting.

Original link: https://tsaiyee.com/blog/2017/12/11/etherum-crypto-kitties-code-core-1/

The cat breeding game CryptoKitties running on the Ethereum platform has swept the world, causing transaction congestion on Ethereum.

Now let's look at what CryptoKitties has done from a code perspective.

CryptoKitties has three parts of code: core code (KittyCore), auction house (CryptoKittiesSalesAuction), and breeding (CryptoKittiesSiringAuction).

Today we first look at the core code (KittyCore).

The first contract, Ownable, is an Ethereum standard contract, a standard implementation of user permission control, so I won't go into details here.

pragma solidity ^0.4.11;
/**
 * @title Ownable
 * @dev The Ownable contract has an owner address, and provides basic authorization control
 * functions, this simplifies the implementation of "user permissions".
 */
contract Ownable {
  address public owner;
  /**
   * @dev The Ownable constructor sets the original `owner` of the contract to the sender
   * account.
   */
  function Ownable() {
    owner = msg.sender;
  }
  /**
   * @dev Throws if called by any account other than the owner.
   */
  modifier onlyOwner() {
    require(msg.sender == owner);
    _;
  }
  /**
   * @dev Allows the current owner to transfer control of the contract to a newOwner.
   * @param newOwner The address to transfer ownership to.
   */
  function transferOwnership(address newOwner) onlyOwner {
    if (newOwner != address(0)) {
      owner = newOwner;
    }
  }
}

The following code defines the interface standard for ERC721 (ERC: Non-fungible Token Standard #721).

ERC721 is an interface standard for Non-fungible Tokens proposed by the Ethereum community.

The interface is mainly to allow non-fungible tokens (NFTs) to be tracked by standard Ethereum wallets or traded on exchanges.

Similar to ERC20, ERC721 defines interfaces such as balance query, permission query, transfer, authorization, and authorized transfer, adding support for TokenId.

/// @title Interface for contracts conforming to ERC-721: Non-Fungible Tokens
/// @author Dieter Shirley <dete@axiomzen.co> (https://github.com/dete)
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 (https://github.com/ethereum/EIPs/issues/165)
    function supportsInterface(bytes4 _interfaceID) external view returns (bool);
}

Next is the gene calculation interface.

/// @title SEKRETOOOO
contract GeneScienceInterface {
    /// @dev A simple boolean indicating this is the contract we expect.
    function isGeneScience() public pure returns (bool);
 
    /// @dev Given genes of kitten 1 and 2, returns a gene combination, potentially containing a random factor
    /// @param genes1 genes of mom
    /// @param genes2 genes of sire
    /// @return The genes that should be passed to the next generation child
    function mixGenes(uint256 genes1, uint256 genes2, uint256 targetBlock) public returns (uint256);
}

The contracts below define special management permissions for the KittyCore contract.

/// @title Contract for managing special access privileges of CryptoKitties.
/// @author Axiom Zen (https://www.axiomzen.co)
/// @dev See the KittyCore contract documentation to understand how the various contract facets are arranged.
contract KittyAccessControl {
    // This is the special management permission for CryptoKitties. There are three management roles:
    //     - The CEO: The CEO can reassign other roles and can also change the address of the smart contracts we rely on. Only this role can unpause the contract.
    //                The CEO role address is initially set to the address that created the KittyCore smart contract.
    //     - The CFO: The CFO can withdraw funds from KittyCore and its auction contracts.
    //     - The COO: The COO can release generation 0 kittens for auction or create promotional kittens.
    // It should be pointed out that these roles have clearly non-overlapping access capabilities, and the list of capabilities for each role is exhaustive.
    // In fact, the CEO can assign any address to any role, but the CEO itself has no ability to play these roles.
    // This restriction is intentional so that we don't frequently use the CEO address for convenience.
    // The less we use an address, the less likely we are to compromise the account in some way.
    /// @dev Event sent when the contract is upgraded
    event ContractUpgrade(address newContract);
 
    // Accounts or contract addresses that can perform the corresponding role operations
    address public ceoAddress;
    address public cfoAddress;
    address public cooAddress;
 
    // @dev Tracks whether the contract is paused. If paused, most operations will be blocked.
    bool public paused = false;
 
    /// @dev Function modifier: Only accessible by CEO
    modifier onlyCEO() {
        require(msg.sender == ceoAddress);
        _;
    }
 
    /// @dev Function modifier: Only accessible by CFO
    modifier onlyCFO() {
        require(msg.sender == cfoAddress);
        _;
    }
 
    /// @dev Function modifier: Only accessible by COO
    modifier onlyCOO() {
        require(msg.sender == cooAddress);
        _;
    }
 
    ///@dev Function modifier: Only accessible by developers
    modifier onlyCLevel() {
        require(
            msg.sender == cooAddress ||
            msg.sender == ceoAddress ||
            msg.sender == cfoAddress
        );
        _;
    }
 
    /// @dev Transfer CEO permission to a new address, only operable by current CEO
    /// @param _newCEO The address of the new CEO
    function setCEO(address _newCEO) external onlyCEO {
        require(_newCEO != address(0));
 
        ceoAddress = _newCEO;
    }
 
    /// @dev Transfer CFO permission to a new address, only operable by current CEO
    /// @param _newCFO The address of the new CFO
    function setCFO(address _newCFO) external onlyCEO {
        require(_newCFO != address(0));
 
        cfoAddress = _newCFO;
    }
 
    /// @dev Transfer COO permission to a new address, only operable by current CEO
    /// @param _newCOO The address of the new COO
    function setCOO(address _newCOO) external onlyCEO {
        require(_newCOO != address(0));
 
        cooAddress = _newCOO;
    }
    /*** Available functions for OpenZeppelin ***/
    /// @dev Function modifier: Only operable when contract is not paused
    modifier whenNotPaused() {
        require(!paused);
        _;
    }
    /// @dev Function modifier: Only operable when contract is paused
    modifier whenPaused {
        require(paused);
        _;
    }
 
    /// @dev Can be called by any developer permission to pause the contract. Used only when defects or vulnerabilities are detected; we need to limit damage.
    function pause() external onlyCLevel whenNotPaused {
        paused = true;
    }
 
    /// @dev Unpause the contract. Can only be operated by the CEO because we might pause the contract when the CFO or COO account is stolen.
    /// @notice This is a public function, not just an external function, so it can be called by derived contracts.
    function unpause() public onlyCEO whenPaused {
        // can't unpause if contract was upgraded
        paused = false;
    }
}
 

To be continued...