Understanding ERC-20 tokens

December 17, 2017 by Stefan Huber

With the ERC-20 token standard an interface for ethereum smart contracts was defined to represent tokens on the ethereum blockchain. A common standard for the representation has many benefits for the future advent of blockchain based applications. Various applications can be built based on the token standard, which will allow to plugin every token complying with the standard and make it workable with the application:

  • ERC-20 compliant wallets: Basically all tokens complying can be managed within one and the same wallet. E.g. MyEtherWallet, Mist Browser
  • ERC-20 compliant exchanges: A token can be tradeable basically on every exchange which accepts ERC-20 compliant tokens. E.g. EtherDelta, Liqui
  • ERC-20 compliant explorers: Token balances and token transfers can be monitored. E.g. Ethplorer, Etherscan Token Explorer

Generic token representation

Tokens are represented within a very simple balance sheet to track ownership and transfers of tokens. In the following an example of a token balance is given. Three token holders A, B and C and a total supply of 10,000 tokens are shown.

Owner Token balance
A 640
B 50
C 100
Total supply 10.000

In the case of A wanting to transfer 40 tokens to B a simple transformation of the balance sheet is happening. The balance of A is decreased by 40 and the balance of B is increased by 40. This would leave the balance sheet in the following form:

Owner Token balance
A 600
B 90
C 100
Total supply 10.000

Basic ERC-20 token contract

A basic ERC-20 token contract has 4 defining attributes name, symbol, decimals and totalSupply. The attributes name, symbol and decimals are generally optional within the standard, however are important for user interfaces to present the token accordingly.

  • name: The token name is a string to give the token a full name. There is no maxlength defined for the name, but it should probably be short yet concise.
  • symbol: Each token has a symbol, which is basically a short identifier of the token. E.g. PAY for TenX Tokens. Generelly there are no rules for the length of the symbol, but this should be rather short. Additionally there is no central registry of symbols, so reusing existing symbols can be done, but propably should be avoided if a unique token should be created.
  • decimals: With the amount of decimals the divisibility of a single token is represented. E.g. with a decimals value of 5 a token can have 5 decimal places 1.00000. A token balance is always represented as an integer inside the ethereum smart contract, this would mean that a token with 5 decimals is represented as 100,000 inside the balance sheet. Therefore the amount of tokens available is always (amount of full tokens) * 10^decimals, whereas the 10^decimals are only considered fractions of a token (called atoms by some).
  • totalSupply: The totalSupply is the amount of tokens available in total including the fractional amounts. As mentioned before with fractional tokens the total amount is totalSupply = (amount of full tokens) * 10^decimals.

Besides the 4 attributes the token standard defines functions for transfering tokens between holders and querying balances of holders. The transfer function has the following signature:

function transfer(address _to, uint256 _value) returns (bool success)

The signature of the function for querying the balance is as follows:

function balanceOf(address _owner) constant returns (uint256 balance)

The transfer function is responsible for transfering a specified token amount from one entry in the balance sheet to another. In the case of a transfer the message sender (msg.sender) is initiating the transfer and thus on his entry the specified amount is deducted and the given _to address is receiving the amount. The tranfer function is always returning a boolean (success) for showing a successful or unsuccessful transfer.

The balanceOf function is returning the token balance for a given _owner address. The balance entry is represented as an integer which represents tokens + token fractions according to the given decimals.

In the following an examplary solidity smart contract is shown, which implements an Example Token. The amount of full tokens available is 10,000 and 18 decimal places are defined, which means the total supply of tokens is 10,000 * 10^18. Within the constructor function of the token the total supply of tokens is transfered to the balance of the creator of the contract (msg.sender). This means the creator is in possession of all tokens and can transfer them to other accounts.

contract BasicERC20TokenExample {
  
  string public constant name = "Example Token";
  string public constant symbol = "EXP";
  uint8 public constant decimals = 18;
  uint256 public constant totalSupply = 10000 * (10 ** uint256(decimals));

  mapping(address => uint256) balances;

  function BasicERC20TokenExample() public {
    balances[msg.sender] = totalSupply;
  }

  function transfer(address _to, uint256 _value) public returns (bool) {
    require(_to != address(0));
    require(_value <= balances[msg.sender]);
    balances[msg.sender] = balances[msg.sender] - _value;
    balances[_to] = balances[_to] + _value;    
    return true;
  }

  function balanceOf(address _owner) public view returns (uint256 balance) {
    return balances[_owner];
  }

}  

Additional ERC-20 token functions

Besides the generic transfer function described above, an ERC-20 token provides functions for granting allowances. This offers the possibility to do token transfers on behalf of a token holder. ERC-20 token exchanges rely on this functionality to do token exchanges between holders of two different ERC-20 tokens (Check the EtherDelta smart contract as an example). Therefore an approve function is introduced for token holders to set an allowance for spenders. Supplementary to the approve function a transferFrom function allows the spender to transfer to a given _to address up to the approved value and from the approved balance entry.

In the following the approve function signature is shown. A token holder can approve a specific _value for transferals for a given _spender address.

function approve(address _spender, uint256 _value) returns (bool success)

The transferFrom provides the functionallity for the approved _spender to transfer a _value from the _from address to a given _to address. The transferFrom can be used multiple times as long as the allowance for a specific _from address is consumed.

function transferFrom(address _from, address _to, uint256 _value) returns (bool success)

Lastly a function for querying the allowance of a _spender address on a specified _owner address should be available.

function allowance(address _owner, address _spender) constant returns (uint256 remaining)

Further notes

  • Events: For the ERC-20 token there exist two events Transfer and Approval, which are triggered when one of the respective actions is happening. Events might be used by light clients, which don’t have full access to the ethereum blockchain.
  • Possible Attack: If a token holder changes the allowance of an approved spender from e.g. N tokens to M tokens. The approved spender could add a transferFrom call before the approve call from the token holder. This could happen only if the approved spender somehow finds out about the allowance change of the token holder, before the approve call is included in a block. By misfortunate transaction ordering inside the block (transferFrom N tokens, approve M tokens, tranferFrom M tokens) the attack is made possible. The result is that the approved spender (attacker in this case) could withdraw N+M tokens at max (depending on how much of N allowance is still available). This can be mitigated simply by changing the allowance to 0 tokens and then to M tokens with 2 separate approve calls.

© 2019, Stefan Huber