ERC20 Token Standard

From The Ethereum Wiki
Revision as of 16:34, 4 December 2018 by BokkyPooBah (talk | contribs) (Sample Fixed Supply Token Contract)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
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 // ----------------------------------------------------------------------------
 2 // ERC Token Standard #20 Interface
 3 // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
 4 // ----------------------------------------------------------------------------
 5 contract ERC20Interface {
 6     function totalSupply() public view returns (uint);
 7     function balanceOf(address tokenOwner) public view returns (uint balance);
 8     function allowance(address tokenOwner, address spender) public view returns (uint remaining);
 9     function transfer(address to, uint tokens) public returns (bool success);
10     function approve(address spender, uint tokens) public returns (bool success);
11     function transferFrom(address from, address to, uint tokens) public returns (bool success);
12 
13     event Transfer(address indexed from, address indexed to, uint tokens);
14     event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
15 }

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     // Get the token balance for account `tokenOwner`
10     function balanceOf(address tokenOwner) public constant returns (uint balance) {
11         return balances[tokenOwner];
12     }
13  
14     // Transfer the balance from owner's account to another account
15     function transfer(address to, uint tokens) public returns (bool success) {
16         balances[msg.sender] = balances[msg.sender].sub(tokens);
17         balances[to] = balances[to].add(tokens);
18         Transfer(msg.sender, to, tokens);
19         return true;
20     }
21  
22     // Send `tokens` amount of tokens from address `from` to address `to`
23     // The transferFrom method is used for a withdraw workflow, allowing contracts to send
24     // tokens on your behalf, for example to "deposit" to a contract address and/or to charge
25     // fees in sub-currencies; the command should fail unless the _from account has
26     // deliberately authorized the sender of the message via some mechanism; we propose
27     // these standardized APIs for approval:
28     function transferFrom(address from, address to, uint tokens) public returns (bool success) {
29         balances[from] = balances[from].sub(tokens);
30         allowed[from][msg.sender] = allowed[from][msg.sender].sub(tokens);
31         balances[to] = balances[to].add(tokens);
32         Transfer(from, to, tokens);
33         return true;
34     }
35  
36     // Allow `spender` to withdraw from your account, multiple times, up to the `tokens` amount.
37     // If this function is called again it overwrites the current allowance with _value.
38     function approve(address spender, uint tokens) public returns (bool success) {
39         allowed[msg.sender][spender] = tokens;
40         Approval(msg.sender, spender, tokens);
41         return true;
42     }
43 }

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, 0x2222222222222222222222222222222222222222, 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 Fixed Supply Token contract with a fixed supply of 1,000,000 units that are initially assigned to the owner of the contract. This token has 18 decimal places:

  1 pragma solidity ^0.5.0;
  2 
  3 // ----------------------------------------------------------------------------
  4 // 'FIXED' 'Example Fixed Supply Token' token contract
  5 //
  6 // Symbol      : FIXED
  7 // Name        : Example Fixed Supply Token
  8 // Total supply: 1,000,000.000000000000000000
  9 // Decimals    : 18
 10 //
 11 // Enjoy.
 12 //
 13 // (c) BokkyPooBah / Bok Consulting Pty Ltd 2018. The MIT Licence.
 14 // ----------------------------------------------------------------------------
 15 
 16 
 17 // ----------------------------------------------------------------------------
 18 // Safe maths
 19 // ----------------------------------------------------------------------------
 20 library SafeMath {
 21     function add(uint a, uint b) internal pure returns (uint c) {
 22         c = a + b;
 23         require(c >= a);
 24     }
 25     function sub(uint a, uint b) internal pure returns (uint c) {
 26         require(b <= a);
 27         c = a - b;
 28     }
 29     function mul(uint a, uint b) internal pure returns (uint c) {
 30         c = a * b;
 31         require(a == 0 || c / a == b);
 32     }
 33     function div(uint a, uint b) internal pure returns (uint c) {
 34         require(b > 0);
 35         c = a / b;
 36     }
 37 }
 38 
 39 
 40 // ----------------------------------------------------------------------------
 41 // ERC Token Standard #20 Interface
 42 // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
 43 // ----------------------------------------------------------------------------
 44 contract ERC20Interface {
 45     function totalSupply() public view returns (uint);
 46     function balanceOf(address tokenOwner) public view returns (uint balance);
 47     function allowance(address tokenOwner, address spender) public view returns (uint remaining);
 48     function transfer(address to, uint tokens) public returns (bool success);
 49     function approve(address spender, uint tokens) public returns (bool success);
 50     function transferFrom(address from, address to, uint tokens) public returns (bool success);
 51 
 52     event Transfer(address indexed from, address indexed to, uint tokens);
 53     event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
 54 }
 55 
 56 
 57 // ----------------------------------------------------------------------------
 58 // Contract function to receive approval and execute function in one call
 59 //
 60 // Borrowed from MiniMeToken
 61 // ----------------------------------------------------------------------------
 62 contract ApproveAndCallFallBack {
 63     function receiveApproval(address from, uint256 tokens, address token, bytes memory data) public;
 64 }
 65 
 66 
 67 // ----------------------------------------------------------------------------
 68 // Owned contract
 69 // ----------------------------------------------------------------------------
 70 contract Owned {
 71     address public owner;
 72     address public newOwner;
 73 
 74     event OwnershipTransferred(address indexed _from, address indexed _to);
 75 
 76     constructor() public {
 77         owner = msg.sender;
 78     }
 79 
 80     modifier onlyOwner {
 81         require(msg.sender == owner);
 82         _;
 83     }
 84 
 85     function transferOwnership(address _newOwner) public onlyOwner {
 86         newOwner = _newOwner;
 87     }
 88     function acceptOwnership() public {
 89         require(msg.sender == newOwner);
 90         emit OwnershipTransferred(owner, newOwner);
 91         owner = newOwner;
 92         newOwner = address(0);
 93     }
 94 }
 95 
 96 
 97 // ----------------------------------------------------------------------------
 98 // ERC20 Token, with the addition of symbol, name and decimals and a
 99 // fixed supply
100 // ----------------------------------------------------------------------------
101 contract FixedSupplyToken is ERC20Interface, Owned {
102     using SafeMath for uint;
103 
104     string public symbol;
105     string public  name;
106     uint8 public decimals;
107     uint _totalSupply;
108 
109     mapping(address => uint) balances;
110     mapping(address => mapping(address => uint)) allowed;
111 
112 
113     // ------------------------------------------------------------------------
114     // Constructor
115     // ------------------------------------------------------------------------
116     constructor() public {
117         symbol = "FIXED";
118         name = "Example Fixed Supply Token";
119         decimals = 18;
120         _totalSupply = 1000000 * 10**uint(decimals);
121         balances[owner] = _totalSupply;
122         emit Transfer(address(0), owner, _totalSupply);
123     }
124 
125 
126     // ------------------------------------------------------------------------
127     // Total supply
128     // ------------------------------------------------------------------------
129     function totalSupply() public view returns (uint) {
130         return _totalSupply.sub(balances[address(0)]);
131     }
132 
133 
134     // ------------------------------------------------------------------------
135     // Get the token balance for account `tokenOwner`
136     // ------------------------------------------------------------------------
137     function balanceOf(address tokenOwner) public view returns (uint balance) {
138         return balances[tokenOwner];
139     }
140 
141 
142     // ------------------------------------------------------------------------
143     // Transfer the balance from token owner's account to `to` account
144     // - Owner's account must have sufficient balance to transfer
145     // - 0 value transfers are allowed
146     // ------------------------------------------------------------------------
147     function transfer(address to, uint tokens) public returns (bool success) {
148         balances[msg.sender] = balances[msg.sender].sub(tokens);
149         balances[to] = balances[to].add(tokens);
150         emit Transfer(msg.sender, to, tokens);
151         return true;
152     }
153 
154 
155     // ------------------------------------------------------------------------
156     // Token owner can approve for `spender` to transferFrom(...) `tokens`
157     // from the token owner's account
158     //
159     // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md
160     // recommends that there are no checks for the approval double-spend attack
161     // as this should be implemented in user interfaces
162     // ------------------------------------------------------------------------
163     function approve(address spender, uint tokens) public returns (bool success) {
164         allowed[msg.sender][spender] = tokens;
165         emit Approval(msg.sender, spender, tokens);
166         return true;
167     }
168 
169 
170     // ------------------------------------------------------------------------
171     // Transfer `tokens` from the `from` account to the `to` account
172     //
173     // The calling account must already have sufficient tokens approve(...)-d
174     // for spending from the `from` account and
175     // - From account must have sufficient balance to transfer
176     // - Spender must have sufficient allowance to transfer
177     // - 0 value transfers are allowed
178     // ------------------------------------------------------------------------
179     function transferFrom(address from, address to, uint tokens) public returns (bool success) {
180         balances[from] = balances[from].sub(tokens);
181         allowed[from][msg.sender] = allowed[from][msg.sender].sub(tokens);
182         balances[to] = balances[to].add(tokens);
183         emit Transfer(from, to, tokens);
184         return true;
185     }
186 
187 
188     // ------------------------------------------------------------------------
189     // Returns the amount of tokens approved by the owner that can be
190     // transferred to the spender's account
191     // ------------------------------------------------------------------------
192     function allowance(address tokenOwner, address spender) public view returns (uint remaining) {
193         return allowed[tokenOwner][spender];
194     }
195 
196 
197     // ------------------------------------------------------------------------
198     // Token owner can approve for `spender` to transferFrom(...) `tokens`
199     // from the token owner's account. The `spender` contract function
200     // `receiveApproval(...)` is then executed
201     // ------------------------------------------------------------------------
202     function approveAndCall(address spender, uint tokens, bytes memory data) public returns (bool success) {
203         allowed[msg.sender][spender] = tokens;
204         emit Approval(msg.sender, spender, tokens);
205         ApproveAndCallFallBack(spender).receiveApproval(msg.sender, tokens, address(this), data);
206         return true;
207     }
208 
209 
210     // ------------------------------------------------------------------------
211     // Don't accept ETH
212     // ------------------------------------------------------------------------
213     function () external payable {
214         revert();
215     }
216 
217 
218     // ------------------------------------------------------------------------
219     // Owner can transfer out any accidentally sent ERC20 tokens
220     // ------------------------------------------------------------------------
221     function transferAnyERC20Token(address tokenAddress, uint tokens) public onlyOwner returns (bool success) {
222         return ERC20Interface(tokenAddress).transfer(owner, tokens);
223     }
224 }

See How to issue your own token on Ethereum in less than 20 minutes for steps to deploy a token contract.

Further Information On Ethereum Tokens