Part 4: Tokens and ERCs

From a developer point of view, tokens on Ethereum are simply smart contracts. In the world of drinks, they could be coffee, and anyone could make their own variant.

You’ve probably heard of ERC20, ERC721, or other standards. These are simply a set of basic functions that the community of developers agreed to adopt. Nobody will stop you from using your own functions, and creating a script that will manage virtual coins however you like.

A famous quote from Pirates of the Caribbean apply very well in this case:

But following a standard has a lot of advantages that you should not overlook. First of all, when you make a token that comply with a standard, everybody will know what your token does and how to interact with it, and therefore, will trust it a bit more. DApps, like Mist, will recognize it as a token, and will show it with a special UI. Also, you’ll find a generic implementation of the token smart contract already written by the community, in a framework like OpenZeppelin’s for example, that is well tested by many experts, and gives you a solid starting point.

In this tutorial, we will write a basic and incomplete ERC20 token from grounds up, then we will turn it into an ERC721 (which is fundamentally different) so we can see the differences between the two.

The reason for that is, you will understand how a token works, that it is not a closed black box, and that the ERC20, an accepted standard that has been in work for two years so far, has failure-points that you’ll not see if you are only running a couple commands to create your token instantly from a framework.

Let’s make our token

The ERC20 was created to standardize fungible tokens so they can be re-used by other applications: from wallets to decentralized exchanges.

Fungible means that it can be interchanged with a token of the same type, in other words, all the tokens are identical (like money, a dollar is the same as any other dollar). A non-fungible token would be one that represents a unique asset (like a house, a property, a piece of art etc.). While a fungible token hold value in itself, a non-fungible token is just the representation of an asset in a smart contract.

To make an ERC20 compliant token, we must implement the following functions and events:

Text Box

contract ERC20Interface {

    function totalSupply() public constant returns (uint);
    function balanceOf(address tokenOwner) public constant returns (uint balance);
    function allowance(address tokenOwner, address spender) public constant returns (uint remaining);
    function transfer(address to, uint tokens) public returns (bool success);
    function approve(address spender, uint tokens) public returns (bool success);
    function transferFrom(address from, address to, uint tokens) public returns (bool success);

    event Transfer(address indexed from, address indexed to, uint tokens);
    event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
}

The standard doesn’t provide the body of these functions, that’s because you could write them however you like, and it’s well within the standard to return null/false values if you don’t want to support some functions.

Note: In this tutorial, it’s not interesting to copy the code, you’ll benefit more from understanding what happens, full examples will be linked at the end of this tutorial anyway.

Implementation

At first, we would want to give our token a name, so we will use a public variable:

    string public name = "Our Tutorial Coin";

Then give it a symbol:

    string public symbol = "OTC";

And of course the number of decimals:

    uint8 public decimals = 2;

Since Solidity doesn’t fully support fixed-point numbers, you have to represent all numbers like integers. Now, a value of “123456”, will be either “1234.56” tokens when you use 2 decimals, or “12.3456” if you use 4 decimals for example. A value of 0 decimals is when you don’t want you token to be “divisible”. Ether, the cryptocurrency of Ethereum, uses 18 decimals.

Generally, you wouldn’t use more than 18 decimals for your token, unless you want an expert from the other side of the world to tell you how stupid you are, and ask you why would you use more than 18 decimals, and tell you how 18 is the holy number because Ether uses 18 decimals.

We will count the total supply of our token, and keep track of how much tokens everyone has:

    uint256 public totalSupply;

    mapping(address => uint256) balances;

Of course you will start with 0 tokens, unless you generate some in the constructor of your token smart contract, like this for example:

Text Box

// The constructor function of our Token smart contract
  function TutoCoin() public {
    // We create 100 tokens (With 2 decimals, in reality it's 1.00 token)
    totalSupply = 100;

    // We give all the token to the msg.sender (in this case, it's the creator of the contract)
    balances[msg.sender] = 100;
    
    // With coins, don't forget to keep track of who has how much in the smart contract, or they'll be "lost".
  }

The “totalSupply()” function is just a getter for the “totalSupply” variable:    

Text Box

function totalSupply() public constant returns (uint256 _totalSupply) {
  return totalSupply;
}
Same for the “balanceOf()” function:

Text Box

// Gets the balance of the specified address.
function balanceOf(address tokenOwner) public view returns (uint256 balance) {
   return balances[tokenOwner];
}
Now, the real magic happens in the “transfer()” function, it’s where an address can send tokens to another one

Text Box

function transfer(address _to, uint256 _value) public returns (bool) {
  // avoid sending tokens to the 0x0 address
  require(_to != address(0));
  // make sure the sender has enough tokens
  require(_value <= balances[msg.sender]);
  
  // we substract the tokens from the sender's balance
  balances[msg.sender] = balances[msg.sender] - _value;
  // then add them to the receiver
  balances[_to] = balances[_to] + _value;
  
  // We trigger an event, note that Transfer have a capital "T", it's not the function itself with a lowercase "t"
  Transfer(msg.sender, _to, _value);
  
  // the transfer was successfull, we return a true
  return true;
}

That’s really the core of an ERC20 token.

“approve()”, “transferFrom()”, and “allowance()” functions are part of what makes a token ERC20 compliant too, but they are vulnerable.

When an address “approve()” another one, the approved address could spend some tokens from the approving address balance on its behalf using the “transferFrom()” function. “allowance()” is just a getter function to see how much an address could “transferFrom()” from the balance of another address.

These functions actually represent security issues, because, when an address approve another one to spend X tokens, and for some reason decide to increase or decrease that amount to Y tokens, the approved address could quickly transfer the X tokens of the first allowance before the transaction to change the allowance is executed, and after it’s executed, the approved address could transfer the Y newly approved tokens again. I said in the last parts that there is no certainty when a transaction is mined, and miners can slightly tamper with when some transactions are executed.

Now, while some safer “transferFrom()” implementations were suggested to make the function more fail-proof (as outlined above, the standard is just a bunch of prototypes of functions and expected behavior, and it’s up to your to write the bodies) some other proposals are being discussed right now, because there are other shortcomings to the ERC20. Two of these propositions are the ERC223 and ERC777.

The motive behind the ERC223 proposal is to avoid sending tokens to the wrong addresses or contracts that doesn’t support using these tokens, because millions of dollars where lost due to that as outlined in the 223th Ethereum Request for Comments. The ERC777 tries to notify the receiving address of the token it will receive, among other things. The ERC777 proposal seems to have the most momentum in the community right now to replace the ERC20.

ERC721

Now, the ERC721 is fundamentally a different thing from the ERC20 and its family.

In ERC721, tokens are unique. The ERC721 was proposed a few months ago, and the implementation that has made it famous is CryptoKitties, a game where people collect virtual cats, and these cats are represented with non-fungible tokens inside the smart contract that runs the game.

Now, if we want to turn an ERC20 contract to an ERC721 one, we will need to see how the second keep track of the tokens.

In ERC20, every address has a balance of tokens. In an ERC721 contract, every address will have a list of its tokens:

mapping(address => uint[]) internal listOfOwnerTokens;

Since Solidity has its limitations, and there is no “indexOf()” method for arrays, we have to keep track of a token in the owner array manually:.

mapping(uint => uint) internal tokenIndexInOwnerArray;

We could of course implement our own library that finds the index of an element, but taking into account possibly long running loops, it’s better to use a mapping.

And of course, to track tokens easily, we can add a mapping that shows the owner of each token:

mapping(uint => address) internal tokenIdToOwner;

That’s all the difference between how the two proposals manage tokens.

The “transfer()” functions inside an ERC721 contract will set a new owner for the token:

Text Box

function transfer(address _to, uint _tokenId) public (_tokenId)
  // we make sure the token exists
  require(tokenIdToOwner[_tokenId] != address(0));
  // the sender owns the token
  require(tokenIdToOwner[_tokenId] == msg.sender);
  // avoid sending it to a 0x0
  require(_to != address(0)); 

  // we remove the token from last owner list
  uint length = listOfOwnerTokens[msg.sender].length; // length of owner tokens
  uint index = tokenIndexInOwnerArray[_tokenId]; // index of token in owner array
  uint swapToken = listOfOwnerTokens[msg.sender][length - 1]; // last token in array

  listOfOwnerTokens[msg.sender][index] = swapToken; // last token pushed to the place of the one that was transferred
  tokenIndexInOwnerArray[swapToken] = index; // update the index of the token we moved

  delete listOfOwnerTokens[msg.sender][length - 1]; // remove the case we emptied
  listOfOwnerTokens[msg.sender].length--; // shorten the array's length

  // We set the new owner of the token
  tokenIdToOwner[_tokenId] = _to;
  
  // we add the token to the list of the new owner
  listOfOwnerTokens[_to].push(_tokenId);
  tokenIndexInOwnerArray[_tokenId] = listOfOwnerTokens[_to].length - 1;

  Transfer(msg.sender, _to, _tokenId);
}

The code is longer, but it’s simply the necessary steps to move a token.

One point to not forget is, the ERC721 has also the “approve()” and “transferFrom()” methods, so inside our transfer function, we would have to add an other instruction inside our “transfer()” method, so an approved address for a token can no longer move the token once it has a new owner, something like the following one:

Text Box

function transfer(address _to, uint _tokenId) public (_tokenId)
{
  // ...
  approvedAddressToTransferTokenId[_tokenId] = address(0);
}

Minting

One thing that can apply to both ERC20 and ERC721 tokens is, we would probably want to generate more fungible tokens, or create a new non-fungible token, we would do that with a function generally named “Mint()”.

An example for such a function is the code below:

Text Box

function mint(address _owner, uint256 _tokenId) public (_tokenId)
{
  // We make sure that the token doesn't already exist
  require(tokenIdToOwner[_tokenId] == address(0));
  
  // We assign the token to someone
  tokenIdToOwner[_tokenId] = _owner;
  listOfOwnerTokens[_owner].push(_tokenId);
  tokenIndexInOwnerArray[_tokenId] = listOfOwnerTokens[_owner].length - 1;

  // We update the total supply of managed tokens by this contract
  totalSupply = totalSupply + 1;
  
  // We emit an event
  Minted(_owner, _tokenId);
}

We create a new token with an arbitrary number. Depending on your use case, you would probably want to authorize only certain address(es) to be able to mint new tokens inside your contract.

An important thing to note here is, while the “mint()” function is not present on the interface of the standards, we added it, just like we can add other functions to enhance and add more functionalities to our token. For example, we could add a system of buying and selling tokens for an amount of ether, or a function to remove tokens that we don’t want anymore.

Metadata

Now, we said that non-fungible tokens represent an asset, so, in most cases, we would actually want to describe that asset. We could do using a string like the following:

mapping(uint => string) internal referencedMetadata;

See, the smart contract is a certification rather than something that contains an object. You can’t store a car inside a smart contract for example, but you could very well store its license plate, or some other legal identification.

One of the most used techniques right now, when it comes to virtual assets, is to use an IPFS hash as metadata. An IPFS hash is the address of a file stored on IPFS. To put it in simple words, IPFS is like a torrent version of HTTP. When a file is added on IPFS, it would become virtually always available on at least one of the computers that are connected to the IPFS network.

While the file is accessible on IPFS or a HTTP link for everyone to see, the “certification of ownership” is registered in a smart contract. This is really not programming, but a new application of non-fungible tokens. It has a name “Crypto-collectibles”, and it’s hot right now.

Now, back to our codes, the original discussion of the ERC721 proposal is a bit dead as of now, and the original poster hasn’t updated the thread in a while, so there is a new continuation of that discussion here. It’s called ERC841 and they changed the name of non-fungible tokens to “deeds”.

There is also another proposal, ERC821, that wishes to implement newer and better design patterns inspired by the ERC223 & ERC777 proposals. ERC821 and ERC841 seek to achieve the same goal, but with a slightly different approach, both are not perfected yet, and you can join the discussion around these two potential standards if you have a valuable input.

You can find example implementations of both ERC20 and ERC721 (that you should not use in production) on the Github repository for this part:

Alternatively, it will be a good idea to take a look at the OpenZepplin framework, they have excellent, (mostly) audited, and modular smart contracts (Of course, you should read the content of every contract before deciding which ones to use).

This conclude this fourth part of series. In the next one we will see how to create a DApp.

If you liked this tutorial, you can find me @dev_zl.

Bonus: ICOs & Crowdsales

Initial coin offerings (ICOs) are a bit outside of the development part of an Ethereum project, but in essence, they are just crowdfunding.

If a startup needs some funds, they create their own token, and sell some during a period of time, called a crowdsale or ICO.

Before smart contracts and the blockchain technology, startups would use a crowdfunding website to raise money, but those websites take generally a handsome fee in the process. Now, with an ICO, you cut the middleman and raise the money directly.

Right now, there are more scams than real projects raising money, so from an investor point of view, you should be wary where to put your money. From a developer point of view, a crowdsale is just a smart contract, that sells some tokens from a start to an ending date in exchange for ether. There is no standard way to achieve that, but you will find a good implementation on OpenZepplin’s repo for example. Alternatively, there is an easy tutorial on Ethereum’s website.

Comments