ERC20 Token Standard

From The Ethereum Wiki
Revision as of 00:50, 21 March 2017 by Cryptopascal (talk | contribs)
Jump to: navigation, search

See also Ethereum Based Tokens and ERC20 Wallet Support


The ERC20 token standard describes the functions and events that an Ethereum token contract has to implement.

The ERC20 Token Standard Interface

Following is an interface contract declaring the required functions and events to meet the ERC20 standard:

 1 // https://github.com/ethereum/EIPs/issues/20
 2 contract ERC20 {
 3     function totalSupply() constant returns (uint totalSupply);
 4     function balanceOf(address _owner) constant returns (uint balance);
 5     function transfer(address _to, uint _value) returns (bool success);
 6     function transferFrom(address _from, address _to, uint _value) returns (bool success);
 7     function approve(address _spender, uint _value) returns (bool success);
 8     function allowance(address _owner, address _spender) constant returns (uint remaining);
 9     event Transfer(address indexed _from, address indexed _to, uint _value);
10     event Approval(address indexed _owner, address indexed _spender, uint _value);
11 }

Most of the major tokens on the Ethereum blockchain are ERC20-compliant. The GNT Golem Network Token is only partially-ERC20-compliant as it does not implement the approve(...), allowance(..) and transferFrom(...) functions, and the Approval(...) event.

Some of the tokens include further information describing the token contract:

1     string public constant name = "Token Name";
2     string public constant symbol = "SYM";
3     uint8 public constant decimals = 18;  // 18 is the most common number of decimal places


How Does A Token Contract Work?

Following is a fragment of a token contract to demonstrate how a token contract maintains the token balance of Ethereum accounts:

 1 contract TokenContractFragment {
 2  
 3     // Balances for each account
 4     mapping(address => uint256) balances;
 5  
 6     // Owner of account approves the transfer of an amount to another account
 7     mapping(address => mapping (address => uint256)) allowed;
 8  
 9     // What is the balance of a particular account?
10     function balanceOf(address _owner) constant returns (uint256 balance) {
11         return balances[_owner];
12     }
13  
14     // Transfer the balance from owner's account to another account
15     function transfer(address _to, uint256 _amount) returns (bool success) {
16         if (balances[msg.sender] >= _amount 
17             && _amount > 0
18             && balances[_to] + _amount > balances[_to]) {
19             balances[msg.sender] -= _amount;
20             balances[_to] += _amount;
21             return true;
22         } else {
23             return false;
24         }
25     }
26  
27     // Send _value amount of tokens from address _from to address _to
28     // The transferFrom method is used for a withdraw workflow, allowing contracts to send
29     // tokens on your behalf, for example to "deposit" to a contract address and/or to charge
30     // fees in sub-currencies; the command should fail unless the _from account has
31     // deliberately authorized the sender of the message via some mechanism; we propose
32     // these standardized APIs for approval:
33     function transferFrom(
34         address _from,
35         address _to,
36         uint256 _amount
37     ) returns (bool success) {
38         if (balances[_from] >= _amount
39             && allowed[_from][msg.sender] >= _amount
40             && _amount > 0
41             && balances[_to] + _amount > balances[_to]) {
42             balances[_from] -= _amount;
43             allowed[_from][msg.sender] -= _amount;
44             balances[_to] += _amount;
45             return true;
46         } else {
47             return false;
48         }
49     }
50  
51     // Allow _spender to withdraw from your account, multiple times, up to the _value amount.
52     // If this function is called again it overwrites the current allowance with _value.
53     function approve(address _spender, uint256 _amount) returns (bool success) {
54         allowed[msg.sender][_spender] = _amount;
55         return true;
56     }
57 }

Token Balance

For an example, assume that this token contract has two token holders:

  • 0x1111111111111111111111111111111111111111 with a balance of 100 units
  • 0x2222222222222222222222222222222222222222 with a balance of 200 units

The token contract's balances data structure will contain the following information:

balances[0x1111111111111111111111111111111111111111] = 100
balances[0x2222222222222222222222222222222222222222] = 200

The balanceOf(...) function will return the following values:

tokenContract.balanceOf(0x1111111111111111111111111111111111111111) will return 100
tokenContract.balanceOf(0x2222222222222222222222222222222222222222) will return 200

Transfer Token Balance

If 0x1111111111111111111111111111111111111111 wants to transfer 10 tokens to 0x2222222222222222222222222222222222222222, 0x1111111111111111111111111111111111111111 will execute the function:

tokenContract.transfer(0x2222222222222222222222222222222222222222, 10)

The token contract's transfer(...) function will alter the balances data structure to contain the following information:

balances[0x1111111111111111111111111111111111111111] = 90
balances[0x2222222222222222222222222222222222222222] = 210

The balanceOf(...) function will now return the following values:

tokenContract.balanceOf(0x1111111111111111111111111111111111111111) will return 90
tokenContract.balanceOf(0x2222222222222222222222222222222222222222) will return 210

Approve And TransferFrom Token Balance

If 0x1111111111111111111111111111111111111111 wants to authorise 0x2222222222222222222222222222222222222222 to transfer some tokens to 0x2222222222222222222222222222222222222222, 0x1111111111111111111111111111111111111111 will execute the function:

tokenContract.approve(0x2222222222222222222222222222222222222222, 30)

The approve data structure will now contain the following information:

tokenContract.allowed[0x1111111111111111111111111111111111111111][0x2222222222222222222222222222222222222222] = 30

If 0x2222222222222222222222222222222222222222 wants to later transfer some tokens from 0x1111111111111111111111111111111111111111 to itself, 0x2222222222222222222222222222222222222222 executes the transferFrom(...) function:

tokenContract.transferFrom(0x1111111111111111111111111111111111111111, 20)

The balances data structure will be altered to contain the following information:

tokenContract.balances[0x1111111111111111111111111111111111111111] = 70
tokenContract.balances[0x2222222222222222222222222222222222222222] = 230

And the approve data structure will now contain the following information:

tokenContract.allowed[0x1111111111111111111111111111111111111111][0x2222222222222222222222222222222222222222] = 10

0x2222222222222222222222222222222222222222 can still spend 10 tokens from 0x1111111111111111111111111111111111111111.

The balanceOf(...) function will now return the following values:

tokenContract.balanceOf(0x1111111111111111111111111111111111111111) will return 70
tokenContract.balanceOf(0x2222222222222222222222222222222222222222) will return 230


Sample Fixed Supply Token Contract

Following is a sample token contract with a fixed supply of 1000000 units that are initially assigned to the owner of the contract:

  1 pragma solidity ^0.4.8;
  2  
  3 // ----------------------------------------------------------------------------------------------
  4 // Sample fixed supply token contract
  5 // Enjoy. (c) BokkyPooBah 2017. The MIT Licence.
  6 // ----------------------------------------------------------------------------------------------
  7  
  8 // ERC Token Standard #20 Interface
  9 // https://github.com/ethereum/EIPs/issues/20
 10 contract ERC20Interface {
 11     // Get the total token supply
 12     function totalSupply() constant returns (uint256 totalSupply);
 13  
 14     // Get the account balance of another account with address _owner
 15     function balanceOf(address _owner) constant returns (uint256 balance);
 16  
 17     // Send _value amount of tokens to address _to
 18     function transfer(address _to, uint256 _value) returns (bool success);
 19  
 20     // Send _value amount of tokens from address _from to address _to
 21     function transferFrom(address _from, address _to, uint256 _value) returns (bool success);
 22  
 23     // Allow _spender to withdraw from your account, multiple times, up to the _value amount.
 24     // If this function is called again it overwrites the current allowance with _value.
 25     // this function is required for some DEX functionality
 26     function approve(address _spender, uint256 _value) returns (bool success);
 27  
 28     // Returns the amount which _spender is still allowed to withdraw from _owner
 29     function allowance(address _owner, address _spender) constant returns (uint256 remaining);
 30  
 31     // Triggered when tokens are transferred.
 32     event Transfer(address indexed _from, address indexed _to, uint256 _value);
 33  
 34     // Triggered whenever approve(address _spender, uint256 _value) is called.
 35     event Approval(address indexed _owner, address indexed _spender, uint256 _value);
 36 }
 37  
 38 contract FixedSupplyToken is ERC20Interface {
 39     string public constant symbol = "FIXED";
 40     string public constant name = "Example Fixed Supply Token";
 41     uint8 public constant decimals = 18;
 42     uint256 _totalSupply = 1000000;
 43     
 44     // Owner of this contract
 45     address public owner;
 46  
 47     // Balances for each account
 48     mapping(address => uint256) balances;
 49  
 50     // Owner of account approves the transfer of an amount to another account
 51     mapping(address => mapping (address => uint256)) allowed;
 52  
 53     // Functions with this modifier can only be executed by the owner
 54     modifier onlyOwner() {
 55         if (msg.sender != owner) {
 56             throw;
 57         }
 58         _;
 59     }
 60  
 61     // Constructor
 62     function FixedSupplyToken() {
 63         owner = msg.sender;
 64         balances[owner] = _totalSupply;
 65     }
 66  
 67     function totalSupply() constant returns (uint256 totalSupply) {
 68         totalSupply = _totalSupply;
 69     }
 70  
 71     // What is the balance of a particular account?
 72     function balanceOf(address _owner) constant returns (uint256 balance) {
 73         return balances[_owner];
 74     }
 75  
 76     // Transfer the balance from owner's account to another account
 77     function transfer(address _to, uint256 _amount) returns (bool success) {
 78         if (balances[msg.sender] >= _amount 
 79             && _amount > 0
 80             && balances[_to] + _amount > balances[_to]) {
 81             balances[msg.sender] -= _amount;
 82             balances[_to] += _amount;
 83             Transfer(msg.sender, _to, _amount);
 84             return true;
 85         } else {
 86             return false;
 87         }
 88     }
 89  
 90     // Send _value amount of tokens from address _from to address _to
 91     // The transferFrom method is used for a withdraw workflow, allowing contracts to send
 92     // tokens on your behalf, for example to "deposit" to a contract address and/or to charge
 93     // fees in sub-currencies; the command should fail unless the _from account has
 94     // deliberately authorized the sender of the message via some mechanism; we propose
 95     // these standardized APIs for approval:
 96     function transferFrom(
 97         address _from,
 98         address _to,
 99         uint256 _amount
100     ) returns (bool success) {
101         if (balances[_from] >= _amount
102             && allowed[_from][msg.sender] >= _amount
103             && _amount > 0
104             && balances[_to] + _amount > balances[_to]) {
105             balances[_from] -= _amount;
106             allowed[_from][msg.sender] -= _amount;
107             balances[_to] += _amount;
108             Transfer(_from, _to, _amount);
109             return true;
110         } else {
111             return false;
112         }
113     }
114  
115     // Allow _spender to withdraw from your account, multiple times, up to the _value amount.
116     // If this function is called again it overwrites the current allowance with _value.
117     function approve(address _spender, uint256 _amount) returns (bool success) {
118         allowed[msg.sender][_spender] = _amount;
119         Approval(msg.sender, _spender, _amount);
120         return true;
121     }
122  
123     function allowance(address _owner, address _spender) constant returns (uint256 remaining) {
124         return allowed[_owner][_spender];
125     }
126 }

Further Information On Ethereum Tokens