Understanding ERC-20 token contracts
(last updated ) · Jim McDonald · 11 minutes read · 2239 words ·Earlier this week the ERC-20⧉ token interface became a formal improvement proposal, freezing the definition. This article takes a look at tokens and explains the features and functions of ERC-20 to provide an understanding of what token contracts are and how developers can work with them.
ERC-20 came about as an attempt to provide a common set of features and interfaces for token contracts in Ethereum, and has proved to be very successful. ERC-20 has many benefits, including allowing wallets to provide token balances for hundreds of different tokens and creating a means for exchanges to list more tokens by providing nothing more than the address of the token’s contract. The benefits of creating a token as ERC-20 compliant is such that very few token contracts today are created any other way.
Note that there is a newer token standard ERC-777⧉ that should be considered instead of ERC-20; a separate article⧉ provides information on understading ERC-777 token contracts.
What is a token contract?
A token contract is a smart contract that contains a map of account addresses and their balances, as shown below. The balance represents a value that is defined by the contract creator: one token contract might use balances to represent physical objects, another monetary value, and a third the holder’s reputation.The unit of this balance is commonly called a token.
It should be noted that an end user could have any number of addresses that contain tokens. There are various reasons for this, including the user wanting to separate their holding to separate logical accounts (savings, tax, spending, etc.) or representing separate sources (ICO, investments, service payments, etc.).
When tokens are transferred from one account to another the token contract updates the balance of the two accounts. For example, a transfer of 10 tokens from \(0x2299…3ab7\) to \(0x1f59…3492\) would result in the balances being updated as shown below:
The total supply of tokens can be increased by minting new tokens (usually a function reserved for the token contract owner). For example, minting 100 tokens to \(0x4ba5..ae22\) would result in the balances being updated as shown below:
The total supply of tokens can be decreased by burning existing tokens (available to any token holder if the contract allows it). For example, \(0x4919…413d\) burning 50 tokens would result in the balances being updated as shown below:
An alternative method of burning tokens is to send the tokens to an address to which the private key is not known, commonly the 0⧉ address. This has the same effect as burning tokens in terms of making the tokens unavailable to spend, but does not decrease the total number of tokens. For example, \(0x93f1…1b09\) burning 50 tokens in this fashion would result in the balances being updated as shown below:
Simple token contracts hold the above information in a mapping of address to balance. When more complex scenarios come in to play, such as providing dividends, then alternative or additional structures will often be more powerful. Regardless of the implementation details, however, the view of token balances to the outside world should always look like the diagrams shown above.
The definition of an ERC-20 token contract
An ERC-20 token contract is defined by the contract’s address and the total supply of tokens available to it, but has a number of optional items that are usually provided as well to provide more detail to users. These are the token’s name, its symbol, and the number of decimals. Each of these is covered in detail below.
Before going in to the details, it is important to understand that there is no central registry for token contracts so the uniqueness of a particular name or symbol is not guaranteed. Once you have created a token contract you should ask for it to be added to common sites such as Etherscan, MyEtherWallet and CoinMarketCap, although be sure to follow the instructions at the links provided for your best chance of the submission being accepted.
The name
of the token contract is the long name by which the token contract should be known, for example “My token”. There are no restrictions on the length of this name but long names are likely to be truncated in some wallet applications so it’s best to keep the name short.
The symbol
of the token contract is the symbol by which the token contract should be known, for example “MYT”. It is broadly equivalent to a stock ticker, and although it has no restriction on its size it is usually 3 or 4 characters in length.
decimals
are a common source of confusion, but are quite easy to understand with suitable explanation. decimals
refers to how divisible a token can be, from 0 (not at all divisible) to 18 (pretty much continuous) and even higher if required. Technically speaking, the decimals
value is the number of digits that come after the decimal place when displaying token values on-screen. The reason that decimals
exists is that Ethereum does not deal with decimal numbers, representing all numeric values as integers. Consider the following two examples:
The first example uses LicenseToken, a token contract that represents software license assignment for a given software product; users holding a LicenseToken have access to the software. It makes no sense for a user to hold a fraction of a license, so the token creator sets decimals
to 0
. Some holders of LicenseToken are represented graphically below.
Here it can be seen that there is a total of 100 licenses, mainly held by one account. As users purchase a license a single token will be transferred from the holding account to the purchaser. The license validator can check to see if a particular account holds a license token and act accordingly.
The second example uses GoldToken, a token contract that represents ownership of physical gold. The token creator wants the unit of to represent 1 kilogram of gold, but also wants to allow users to hold and trade amounts of gold down to the gram level (but no lower). As Ethereum does not support decimal numbers a token must represent 1g of gold, and to represent 1,000g as a single 1Kg unit to the external world the decimals
are set to 3
(as there are \(10^3\) grams in the kilogram of gold that the token creator wishes to be displayed as 1 token). Some holders of GoldToken are represented graphically below.
Here it can be seen that there is a total of 50Kg of gold represented (1g per token \(\times\) 50000 tokens). However, as decimals
is set to 3
the view to the user will be as follows:
It can be seen that setting decimals
to 3
literally means that when displaying GoldToken balances there should be 3 digits of the balance after the decimal point.
decimals
is often called a humanising factor because it allows token contracts to define how they would like balances to be displayed to users. GoldToken doesn’t deal with decimals internally, and never uses decimals
in its own calculations as everything is in grams, but allows users to work with a common unit of gold (the Kg) rather than that used in the contract (g).
The idea of divisibility as shown above in GoldToken allows token contracts to represent very fine-grained decimal values, and tokens are commonly built with decimals
set to 18
to give the token a near-continuous range of values.
In summary, when picking a suitable value for decimals these rules should be followed:
- does the token contract represent an indivisible entity? Then set
decimals
to0
- does the token contract represent an item with a fixed number of decimal places? Then set
decimals
to that number - if neither of the above apply set
decimals
to18
It is important to understand the impact of decimals on token creation. The number of tokens that should be created is equal to the whole number of tokens that are required multiplied by \(10^{decimals}\). As can be seen with the GoldToken example, the token creator wanted to create tokens representing 50Kg of gold but to do so with 3 decimals they must mint 50,000 tokens (\(50\times10^3\)).
totalsupply
is the final item that defines an ERC20 token contract, and as mentioned is the sole mandatory parameter. Although not explicitly stated in the ERC-20 specification the definition of totalsupply
is simple: totalsupply
equals the sum of all balances. totalsupply
has been seen in action in the above examples so needs no further discussion here.
Functions of an ERC-20 token contract
ERC-20 token contracts come with a number of functions to allow users to find out the balances of accounts as well as to transfer them from one account to another under varying conditions. These functions are described below.
The balanceOf()
function provides the number of tokens held by a given address. Note that anyone can query any address’ balance, as all data on the blockchain is public.
There are two ways to send tokens from one address to another. The transfer()
function transfers a number of tokens directly from the message sender to another address. Note that there are no checks made on the recipient address, so it is incumbent on the sender to ensure that the recipient is as intended.
Although transfer()
is fine for sending tokens from one user to another it doesn’t work so well when tokens are being used to pay for a function in a smart contract. This is because at the time the smart contract runs it has no access to details of which addresses transferred funds where and so cannot assert that the user calling the contract has paid the required amount of funds to operate the contract.
Imagine that there is a contract Doer
deployed on the network. Doer
has a function doSomething()
that requires 10 Do tokens to operate. Joe wants to call doSomething()
and has 50 Do tokens in his account. How can Joe pay Doer so that it can run doSomething()
successfully?
approve()
and transferFrom()
are two functions that allow the above scenario to work using a two-step process. In the first step a token holder gives another address (usually of a smart contract) approval to transfer up to a certain number of tokens, known as an allowance. The token holder uses approve()
to provide this information.
In the above example the second line shows that Joe with address \(0x1f59…3492\) has allowed Doer
at \(0xd8f0…c028\) to transfer up to 25 tokens from Joe’s account.
Once an allowance has been created the smart contract can take up to the allowed number of tokens from a user’s allowance as part of the contract’s operation. Continuing the example, Joe can now call doSomething()
and doSomething()
can use transferFrom()
to take 10 Do tokens from Joe’s account and carry on its work. If Joe doesn’t have 10 tokens in their account, or their allowance is less than 10 tokens, doSomething()
will fail.
The allowance()
function provides the number of tokens allowed to be transferred from a given address by another given address. Note that anyone can query any address’ allowance, as all data on the blockchain is public. It is important to understand that allowances are “soft”, in that both individual and cumulative allowances can exceed an address’ balance. In the approval table shown above, for example, the holder \(0x2299…3ab7\) has approved a transfer of up to 500 tokens but their balance as seen previously is only 90 tokens. Any contract using allowance()
must additionally consider the balance of the holder when calculating the number of tokens available to it.
There are additional methods for smart contracts to user tokens as payment for services: a companion article⧉ goes in to detail on these methods.
Events of an ERC-20 token contract
ERC-20 defines two events that must be triggered when the contract takes the relevant action. The first event is Transfer()
which emits details of the movement of tokens from one address to another. The second event is Approval()
which emits details of approvals of tokens from one address to another. These can be used to keep track of balance and allowance changes for addresses without needing to poll the blockchain.
Minting tokens emits a Transfer()
event with the 0
address as the source.
There is no event emitted when tokens are burned. Due to this, ERC-20 token contracts that burn tokens commonly transfer()
the tokens to the 0
address in lieu of real burning.
Beyond ERC-20
ERC-20 provides a great basis to build token contracts, but is not without its faults. The ERC-223⧉ proposal provides additional features and safety measures, but is not compatible with ERC-20. Token contracts built today should continue to follow ERC-20 and developers should keep track of and contribute to the ERC-223 proposal.