Crypto Zombies (3) — ERC721 & Crypto-Collectibles

lukeleeai
lukeleeai
Published in
3 min readFeb 8, 2022

--

Token

You can think of it as a crypto asset. NFT is also a token. A token is one of the smart contracts that follow some common rules. This introduces us to a useful feature: we can effortlessly interact with any tokens that follow the same protocol.

Let me borrow some examples from Ethereum.org.

Tokens can represent virtually anything in Ethereum:

reputation points in an online platform, skills of a character in a game, lottery tickets, financial assets like a share in a company, a fiat currency like USD, an ounce of gold, and more…

ERC20

The ERC-20 introduces a standard for Fungible Tokens, in other words, they have a property that makes each Token be exactly the same (in type and value) of another Token. For example, an ERC-20 Token acts just like the ETH, meaning that 1 Token is and will always be equal to all the other Tokens.

It is a standard widely used across all Ethereum apps.

The basic functions include

  1. total supply
  2. balance of
  3. allowance
  4. transfer
  5. approve
  6. transfer from

Building an app that’s compatible with one ERC20 token means it can basically interact with any other tokens with the same protocol too.

ERC721

When building a zombie game, however, we’d like to implement logic that allows us to trade zombies. Yet a zombie is not divisible like cryptocurrency. This is where ERC721 comes into play. It’s simply a protocol for NFT collectables.

To use it, you simply copy and paste the code for erc721.

pragma solidity >=0.5.0 <0.6.0;contract ERC721 {event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);function balanceOf(address _owner) external view returns (uint256);function ownerOf(uint256 _tokenId) external view returns (address);function transferFrom(address _from, address _to, uint256 _tokenId) external payable;function approve(address _approved, uint256 _tokenId) external payable;}

erc721.sol just has a list of functions. We import the file, inherit the contract, and implement our custom functions.

Transfer Logic

There are two ways to implement it.

The first one is to simply use transferFrom. The other way is to use approve to specify who can take your particular token from your account.

mapping (uint => address) zombieApprovals;function _transfer(uint _from, uint _to, uint _tokenId) private {
ownerZombieCount[_from]--;
ownerZombieCount[_to]++;
zombieToOwner[_tokenId] = _to;
emit Transfer(_from, _to, _tokenId);
}
function transferFrom(uint _from, uint _to, uint _tokenId) external payable {
require(msg.sender == zombieToOwner[_tokenId] || msg.sender == zombieApprovals[_tokenId]);
_transfer(_from, _to, _tokenId);
}
function approve(uint _approved, uint _tokenId) external payable onlyOwnerOf(_tokenId) {
zombieApprovals[_tokenId] = _approved;
emit Approval(_approved, _tokenId);
}

Prevent overflow and underflow

Use SafeMath here. I’m not sure about it, but I think I can later install the package instead of copy-pasting it. Anyways, for now, just paste the code in another file called safemath.sol.

For uint8 , the largest number you can have is 2⁸ = 255. When you add 1 to this number, you surprisingly get 0. To avoid this issue, we use SafeMath .

import "./simplemath.sol"contract Number {
using SafeMath for uint256;
uint counter = 0;

function increment() external {
counter = counter.add(1);
}
}

What if you also want to cover uint32 ? You should define a new library called SafeMath32 like this:

library SafeMath32 {
...
}

and use it.

import "./simplemath.sol"contract Number {
using SafeMath for uint256;
using SafeMath32 for uint32;
uint counter = 0;

function increment() external {
counter = counter.add(1);
}
}

--

--