Index

CryptoZombies

  1. Lesson 1: CryptoZombies
    1. Chapter 2 Contracts
    2. Chapter 3: State Variables & Integers
    3. Chapter 4: Math Operations
    4. Chapter 5: Structs
    5. Chapter 6: Arrays
    6. Chapter 7: Function Declarations
    7. Chapter 8: Working With Structs and Arrays
    8. Chapter 9: Private / Public Functions
    9. Chapter 10: More on Functions
    10. Chapter 11: Keccak256 and Typecasting
    11. Chapter 12: Putting It Together
    12. Chapter 13: Events
    13. Chapter 14: Web3.js
  2. Lesson 2: Zombies Attack Their Victims
    1. Chapter 2: Mappings and Addresses
    2. Chapter 3: Msg.sender
    3. Chapter 4: Require
    4. Chapter 5: Inheritance
    5. Chapter 6: Import
    6. Chapter 7: Storage vs Memory
    7. Chapter 8: Zombie DNA
    8. Chapter 9: More on Function Visibility
    9. Chapter 10: What Do Zombies Eat?
    10. Chapter 11: Using an Interface
    11. Chapter 12: Handling Multiple Return Values
    12. Chapter 13: Bonus: Kitty Genes
    13. Chapter 14: Wrapping It Up
  3. Lesson 3: Advanced Solidity Concepts
    1. Chapter 2: Ownable Contracts
    2. Chapter 3: onlyOwner Function Modifier
    3. Chapter 4: Gas
    4. Chapter 5: Time Units
    5. Chapter 6: Zombie Cooldowns
    6. Chapter 7: Public Functions & Security
    7. Chapter 8: More on Function Modifiers
    8. Chapter 9: Zombie Modifiers
    9. Chapter 10: Saving Gas With 'View' Functions
    10. Chapter 11: Storage is Expensive
    11. Chapter 12: For Loops
    12. Chapter 13: Wrapping It Up
  4. Lesson 4: Zombie Battle System
    1. Chapter 1: Payable
    2. Chapter 2: Withdraws
    3. Chapter 3: Zombie Battles
    4. Chapter 4: Random Numbers
    5. Chapter 5: Zombie Fightin'
    6. Chapter 6: Refactoring Common Logic
    7. Chapter 7: More Refactoring
    8. Chapter 8: Back to Attack!
    9. Chapter 9: Zombie Wins and Losses
    10. Chapter 10: Zombie Victory 😄
    11. Chapter 11: Zombie Loss 😞
  5. Lesson 5: ERC721 & Crypto-Collectibles
    1. Chapter 1: Tokens on Ethereum
    2. Chapter 2: ERC721 Standard, Multiple Inheritance
    3. Chapter 3: balanceOf & ownerOf
    4. Chapter 4: Refactoring
    5. Chapter 5: ERC721: Transfer Logic
    6. Chapter 6: ERC721: Transfer Cont'd
    7. Chapter 7: ERC721: Approve
    8. Chapter 8: ERC721: Approve
    9. Chapter 9: Preventing Overflows
    10. Chapter 10: SafeMath Part 2
    11. Chapter 11: SafeMath Part 3
    12. Chapter 12: SafeMath Part 4
    13. Chapter 13: Comments
    14. Chapter 14: Wrapping It Up
  6. App Front-ends & Web3.js
    1. Chapter 1: Intro to Web3.js
    2. Chapter 2: Web3 Providers
    3. Chapter 3: Talking to Contracts
    4. Chapter 4: Calling Contract Functions
    5. Chapter 5: Metamask & Accounts
    6. Chapter 6: Displaying our Zombie Army
    7. Chapter 7: Sending Transactions
    8. Chapter 8: Calling Payable Functions
    9. Chapter 9: Subscribing to Events
    10. Chapter 10: Wrapping It Up

Chapter 11: SafeMath Part 3


Chapter 11: SafeMath Part 3


Great, now our ERC721 implementation is safe from overflows & underflows!
Going back through the code we wrote in previous lessons, there's a few other places in our code that could be vulnerable to overflows or underflows.
For example, in ZombieAttack we have:
myZombie.winCount++;
myZombie.level++;
enemyZombie.lossCount++;

We should prevent overflows here as well just to be safe. (It's a good idea in general to just use SafeMath instead of the basic math operations. Maybe in a future version of Solidity these will be implemented by default, but for now we have to take extra security precautions in our code).
However we have a slight problem — winCount and lossCount are uint16s, and level is a uint32. So if we use SafeMath's add method with these as arguments, it won't actually protect us from overflow since it will convert these types to uint256:
function add(uint256 a, uint256 b) internal pure returns (uint256) {
  uint256 c = a + b;
  assert(c >= a);
  return c;
}

// If we call `.add` on a `uint8`, it gets converted to a `uint256`.
// So then it won't overflow at 2^8, since 256 is a valid `uint256`.

This means we're going to need to implement 2 more libraries to prevent overflow/underflows with our uint16s and uint32s. We can call them SafeMath16 and SafeMath32.
The code will be exactly the same as SafeMath, except all instances of
uint256 will be replaced with uint32 or uint16.
We've gone ahead and implemented that code for you — go ahead and look at
safemath.sol to see the code.
Now we need to implement it in ZombieFactory.

Putting it to the Test


Assignment:
1. Declare that we're using
SafeMath32 for uint32.

2. Declare that we're using
SafeMath16 for uint16.

3. There's one more line of code in ZombieFactory where we should use a SafeMath method. We've left a comment to indicate where.


pragma solidity ^0.4.25;

import "./ownable.sol";
import "./safemath.sol";

contract ZombieFactory is Ownable {

  using SafeMath for uint256;
  // 1. Declare using SafeMath32 for uint32
  using SafeMath32 for uint32;
  // 2. Declare using SafeMath16 for uint16
  using SafeMath16 for uint16;

  event NewZombie(uint zombieId, string name, uint dna);

  uint dnaDigits = 16;
  uint dnaModulus = 10 ** dnaDigits;
  uint cooldownTime = 1 days;

  struct Zombie {
    string name;
    uint dna;
    uint32 level;
    uint32 readyTime;
    uint16 winCount;
    uint16 lossCount;
  }

  Zombie[] public zombies;

  mapping (uint => address) public zombieToOwner;
  mapping (address => uint) ownerZombieCount;

  function _createZombie(string _name, uint _dna) internal {
    // Note: We chose not to prevent the year 2038 problem... So don't need
    // worry about overflows on readyTime. Our app is screwed in 2038 anyway ;)
    uint id = zombies.push(Zombie(_name, _dna, 1, uint32(now + cooldownTime), 00)) - 1;
    zombieToOwner[id] = msg.sender;
    // 3. Let's use SafeMath's `add` here:
    // ownerZombieCount[msg.sender]++;
    ownerZombieCount[msg.sender] = ownerZombieCount[msg.sender].add(1);
    emit NewZombie(id, _name, _dna);
  }

  function _generateRandomDna(string _str) private view returns (uint) {
    uint rand = uint(keccak256(abi.encodePacked(_str)));
    return rand % dnaModulus;
  }

  function createRandomZombie(string _name) public {
    require(ownerZombieCount[msg.sender] == 0);
    uint randDna = _generateRandomDna(_name);
    randDna = randDna - randDna % 100;
    _createZombie(_name, randDna);
  }

}