Notes from Cryptozombies | Lesson 1

Introduction

Cryptozombies is a series of interactive coding tutorials that teaches programmers how to build a zombie battle game in Ethereum. Inspired by the viral game, Cryptokitties, it uses smart contracts and the ERC-721 token standard to create collectible in-game objects that are scarce, non-fungible, and unique — just like real objects. Think digital Pokémon cards — each one is unique, and has value that corresponds to its rarity.

The tutorial is usually the first stop for anyone learning blockchain development. Though I’ve already finished the series, I keep having to revisit the tutorials to refresh basic concepts. This series of blog posts will be my attempt to distil concepts from the tutorials so I can build a more persistent mental model of the game.

Smart Contract Boilerplate

Before we get into the lesson itself, you should learn what smart contracts are, and how to make one.

A contract is the fundamental building block of Ethereum applications — all variables and functions belong to a contract, and this will be the starting point of all your projects.

An empty contract named HelloWorld would look like this:

pragma solidity ^0.4.7;
contract HelloWorld {

}

The first line denotes the “version pragma” — a declaration of the version of the Solidity compiler our code should use. This is to prevent issues with future compiler versions potentially introducing changes that would break your code. Special attention should be paid to this line. As I learned in my first few weeks of Solidity development, there are new compiler versions almost every month, and code gets deprecated quickly.

Lesson 1 : The Zombie Factory

In this lesson, we’re supposed to build a “zombie factory” that generates an army of zombies. It’ll have three components:

  1. The factory will maintain a database of all the zombies in our army

  2. The factory will have a function for creating new zombies

  3. Each zombie will have a random and unique appearance

Zombie DNA

The zombie’s appearance will be based on its Zombie DNA. Zombie DNA is simple — it’s a 16-digit integer:

8356281049284737

Just like real DNA, different parts of this number will map to different traits. The first 2 digits map to the zombie’s head type, the second 2 digits to the zombie’s eyes, etc.

State Variables & Integers

In Solidity, variables are stored permanently in contract storage (i.e. on the Ethereum blockchain), by default. This means we need to be mindful of how much storage our variables take, since writing to the Ethereum blockchain is a computationally expensive task and can cost us real money.

For this reason, Ethereum has different types of integers. In this project, we will use unsigned integers or uint the most often. By default, the variable uint is interpreted as uint256, which takes up 256 bits. We can also use uint8, uint16, uint32, and so on if we want to use less bits.

Note: The “unsigned” bit means that the integers are always non-negative

Structs

Solidity also allows you to create more complicated data types, such as struct. Structs are kind of like objects in JavaScript. They have properties that are represented as key-value pairs. Our zombies will all be represented as structs.

struct Zombie {
  string name;
  uint dna;
}

Arrays

There are two kinds of arrays in Solidity, fixed arrays and dynamic arrays:

// Array with a fixed length of 2 elements:
uint[2] fixedArray;
// another fixed Array, can contain 5 strings:
string[5] stringArray;
// a dynamic Array - has no fixed size, can keep growing:
uint[] dynamicArray;

Private & Public Functions

The distinction between private and public is also very important in Solidity.

When your function is public, anyone can call your contract’s function and execute its code. This is obviously a huge security vulnerability, so its good practice to mark all your functions as private by default, and then only make those functions public that you want the world to be able to see.

Like so:

function _addToArray(uint _number) private {
  numbers.push(_number);
}

Function Modifiers: view & pure

You can add modifiers at the end of function declarations to mark them as pure or view. Regular functions read and modify contract data. view functions can only view, not modify, contract data. pure functions don’t interact with the blockchain at all.

function _multiply(uint a, uint b) private pure returns (uint) {
  return a * b;
}

Keccak256 and Typecasting

Ethereum has the hash function keccak256 built in, which is a version of SHA3. It’s useful for many purposes, but in our program, we’ll mainly be using it for random number generation.

Example:

//6e91ec6b618bb462a4a6ee5aa2cb0e9cf30f7a052bb467b0ba58b8748c00d2e5
keccak256("aaaab");
//b1f078126895a1424524de5321b339ab00408010b7cf0e6ed451514981e58aa9
keccak256("aaaac");

Note: Secure random-number generation in blockchain is a very difficult problem. Our method here is insecure, but since security isn’t top priority for our Zombie DNA, it will be good enough for our purposes.

Events

The last thing we’ll be creating in this smart contract is an event. Events are a way for our smart contract to communicate that something happened on the blockchain to our app’s front-end, which can be ‘listening’ for certain events and take action when they happen.

In our case, we’re going to create a NewZombie event that will notify our front-end web3.js that a new zombie has been created.

pragma solidity ^0.4.19;

contract ZombieFactory {

    /* an event to let our front-end know every time a new zombie was created,
    so the app can display it */
    Event NewZombie(uint zombieId, string name, uint dna);

    uint dnaDigits = 16;
    uint dnaModulus = 10 ** dnaDigits; // to make sure our DNA is only 16 characters

    // this represents our zombie
    struct Zombie {
        string name;
        uint dna;
    }

    Zombie[] public zombies; // an array to store our army of zombies

    function _createZombie(string _name, uint _dna) private {
        uint id = zombies.push(Zombie(_name, _dna)) - 1;
        NewZombie(id, _name, _dna)
    }

    // generates zombie DNA from a random string
    function _generateRandomDna(string _str) private view returns (uint) {
        uint rand = uint(keccak256(_str));
        return rand % dnaModulus;
    }

    // creates a zombie with random attributes
    function createRandomZombie(string _name) public {
        uint randDna = _generateRandomDna(_name);
        _createZombie(_name, randDna);
    }

}
Author

Aadil Ayub

Designer, developer, and information junkie.