📜 ⬆️ ⬇️

Create your cryptocics (Part 1)

In the first days of December 2017, the Ethereum blockchain project users faced an unpleasant discovery - any of their transactions simply ceased to be confirmed. In fact, the entire network has ceased to function due to a memory map that has suddenly expanded in size.

Very soon it became clear what the matter was - the CryptoKitties project was to blame . This is a fun toy that works on the Ethereum blockchain and allows users to breed “kittens”, cross them and sell them as usual currency tokens. At some point, 15% of all transactions in Ethereum accounted for cryptocause! And at the time of this writing, the players have already spent $ 23 million on kittens!

So I just could not get past such an interesting topic and decided to tell you how to make this kind of game. This article (and its continuation) will describe in detail how you can create a similar project to Ethereum, primarily using the code analysis of the original CryptoKitties contract.
')
cryptocat

CryptoKitties game source code


Almost all the code of the CryptoKitties game is open, so the best way to understand how it works is to read the source code.


The code has approximately 2000 lines, so in this article we will go over only the most important parts of it. But if you want to read the source code yourself, here is a copy of it , and immediately in the online IDE EthFiddle.


general review


If you hear about CryptoKitties for the first time, then know that players sell, buy and breed digital seals in it. Each cat is unique, its appearance is determined by genes. When you cross two cats, their genes unite in a unique way and you get a very special kitten, which you can sell or continue to cross with other cats.


The CryptoKitties code is divided into a series of small interconnected contracts, so there is no need to store one huge file with all the information.


Solidity allows you to inherit contracts , this is how the chain of inheritance looks in the case of kittens:


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 


So, in essence, the KittyCore contract will be published at the address to which the application refers, and it stores all the information and methods of previous contracts. Run through all the contracts.


1. KittyAccessControl: who controls the contract?


This contract manages various addresses and restrictions that can only be exercised by certain users, let's call them CEO, CFO and COO.



This contract is needed for management and does not apply to the mechanics of the game. In essence, he attributes the methods to the users of “CEO”, “COO” and “CFO” - addresses on the Ethereum platform, who own and control specific functions of the contract.


KittyAccessControl defines function modifiers , for example, onlyCEO (limits the function so that only the CEO user can perform it), and also adds methods for actions such as pause / resume a game or withdraw money:


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

The pause() function was most likely added so that developers could gain time for themselves if there are any bugs in the contract ... But Luke says that she can also allow developers to completely freeze the contract and no one will either transfer, sell, or breed kittens! Not that the developers wanted to do this - but this is interesting, especially given the fact that many people think that the DApp is completely decentralized, because it is on Ethereum.


2. KittyBase: what kind of cat is this?


It is in this contract that we determine the most important code that is responsible for the main functionality, including the main data store, constants, data types and internal functions for managing them.



KittyBase defines many application master data. First, this contract sets the cat as a structure :


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

So the cat is all just a bunch of integers :) Let's look at each element:



Please note that the game CryptoKitties seals asexual, and you can cross any two seals.


Then the KittyBase contract defines arrays of our feline structures:


 Kitty[] kitties; 

This array contains the data of all existing cats, so this is a kind of common database.


When you create a new seal, it is added to the array, and its index in the array becomes the seal account. In the example below, the Genesis-1 seal account.
The index in the array for this cat is “1”!
The index in the array for this cat is “1”!


Also in this contract there is a dictionary (in Solidity this is called mapping, but I think we understand each other), linking the seal accounts and the addresses of their owners, so that you can keep track of who owns a particular seal:


 mapping (uint256 => address) public kittyIndexToOwner; 

The contract also contains other dictionaries, I recommend you to run through them with your own eyes, there is nothing complicated there.


When the owner sends the cat to another person, the kittyIndexToOwner dictionary kittyIndexToOwner updated and begins to link the cat with the new owner:


 /// @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); 

The transfer of the seal establishes the connection kittyIndexToOwner seal account and the address of the new owner _to .


Now let's see what happens when we create a new cat:


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

This feature “accepts” mother and father accounts, cat generation , 256-bit genetic code and owner address . She then creates Kitty , sends it to the main cat array and calls the _transfer() command to bind the cat to the new owner.


Cool - now we can see how CryptoKitties identifies cats in the form of a data type, how it stores them in the blockchain and how it keeps track of who owns this or that cat.


3. KittyOwnership: seals as tokens


This contract contains the methods necessary for basic non-interchangeable operations with tokens in accordance with the ERC-721 standard.



CryptoKitties is compliant with the ERC721 token standard . This is a non-interchangeable token that has proven itself to track ownership of digital collections, such as digital playing cards or rare items in role-playing games.


Note about interchangeability : Ethereal cryptocurrency is interchangeable , because any five ethereums are equal to the other five ethereums. But if we are talking about CryptoKitties tokens, one cat is not equal to the other, so they are not interchangeable .


From the definition of this contract, we can see that the KittyOwnership inherits the ERC721 contract:


 contract KittyOwnership is KittyBase, ERC721 

All ERC721 tokens conform to the same standard, so as a result of inheritance, the KittyOwnership contract is responsible for the following functions:


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

Since all of these methods are open, there is a standard way for users to interact with CryptoKitties tokens — and they can also interact with any other ERC721 token. You can transfer tokens to another user, directly interacting with the CryptoKitties contract on the Ethereum platform without having to use their web interface, so in this sense you are really the owner of your cats. (Unless the CEO stops the contract).


If you are interested in writing your game on Ethereum, you can practice on CryptoZombies : an interactive Solidity programming programming school, Russian localization is present .




Many thanks to Sasha Ivanova for help in the translation!

To be continued.

Source: https://habr.com/ru/post/350740/


All Articles