ERC-20 Contract Walk-Through
Introduction
One of the most common uses for Ethereum is for a group to create a tradable token, in a sense their own currency. These tokens typically follow a standard, ERC-20. This standard makes it possible to write tools, such as liquidity pools and wallets, that work with all ERC-20 tokens. In this article we will analyze the OpenZeppelin Solidity ERC20 implementation(opens in a new tab), as well as the interface definition(opens in a new tab).
This is annotated source code. If you want to implement ERC-20, read this tutorial(opens in a new tab).
The Interface
The purpose of a standard like ERC-20 is to allow many tokens implementations that are interoperable across applications, like wallets and decentralized exchanges. To achieve that, we create an interface(opens in a new tab). Any code that needs to use the token contract can use the same definitions in the interface and be compatible with all token contracts that use it, whether it is a wallet such as MetaMask, a dapp such as etherscan.io, or a different contract such as liquidity pool.
If you are an experienced programmer, you probably remember seeing similar constructs in Java(opens in a new tab) or even in C header files(opens in a new tab).
This is a definition of the ERC-20 Interface(opens in a new tab) from OpenZeppelin. It is a translation of the human readable standard(opens in a new tab) into Solidity code. Of course, the interface itself does not define how to do anything. That is explained in the contract source code below.
1// SPDX-License-Identifier: MIT2நகலெடு
Solidity files are supposed to includes a license identifier. You can see the list of licenses here(opens in a new tab). If you need a different license, just explain it in the comments.
1pragma solidity >=0.6.0 <0.8.0;2நகலெடு
The Solidity language is still evolving quickly, and new versions may not be compatible with old code (see here(opens in a new tab)). Therefore, it is a good idea to specify not just a minimum version of the language, but also a maximum version, the latest with which you tested the code.
1/**2 * @dev Interface of the ERC20 standard as defined in the EIP.3 */4நகலெடு
The @dev
in the comment is part of the NatSpec format(opens in a new tab), used to produce
documentation from the source code.
1interface IERC20 {2நகலெடு
By convention, interface names start with I
.
1 /**2 * @dev Returns the amount of tokens in existence.3 */4 function totalSupply() external view returns (uint256);5நகலெடு
This function is external
, meaning it can only be called from outside the contract(opens in a new tab).
It returns the total supply of tokens in the contract. This value is returned using the most common type in Ethereum, unsigned 256 bits (256 bits is the
native word size of the EVM). This function is also a view
, which means that it does not change the state, so it can be executed on a single node instead of having
every node in the blockchain run it. This kind of function does not generate a transaction and does not cost gas.
Note: In theory it might appear that a contract's creator could cheat by returning a smaller total supply than the real value, making each token appear more valuable than it actually is. However, that fear ignores the true nature of the blockchain. Everything that happens on the blockchain can be verified by every node. To achieve this, every contract's machine language code and storage is available on every node. While you are not required to publish the Solidity code for your contract, nobody would take you seriously unless you publish the source code and the version of Solidity with which it was complied, so it can be verified against the machine language code you provided. For example, see this contract(opens in a new tab).
1 /**2 * @dev Returns the amount of tokens owned by `account`.3 */4 function balanceOf(address account) external view returns (uint256);5நகலெடு
As the name says, balanceOf
returns the balance of an account. Ethereum accounts are identified in Solidity using the address
type, which holds 160 bits.
It is also external
and view
.
1 /**2 * @dev Moves `amount` tokens from the caller's account to `recipient`.3 *4 * Returns a boolean value indicating whether the operation succeeded.5 *6 * Emits a {Transfer} event.7 */8 function transfer(address recipient, uint256 amount) external returns (bool);9நகலெடு
The transfer
function transfers a tokens from the caller to a different address. This involves a change of state, so it isn't a view
.
When a user calls this function it creates a transaction and costs gas. It also emits an event, Transfer
, to inform everybody on
the blockchain of the event.
The function has two types of output for two different types of callers:
- Users that call the function directly from a user interface. Typically the user submits a transaction
and does not wait for a response, which could take an indefinite amount of time. The user can see what happened
by looking for the transaction receipt (which is identified by the transaction hash) or by looking for the
Transfer
event. - Other contracts, which call the function as part of an overall transaction. Those contracts get the result immediately, because they run in the same transaction, so they can use the function return value.
The same type of output is created by the other functions that change the contract's state.
Allowances permit an account to spend some tokens that belong to a different owner. This is useful, for example, for contracts that act as sellers. Contracts cannot monitor for events, so if a buyer were to transfer tokens to the seller contract directly that contract wouldn't know it was paid. Instead, the buyer permits the seller contract to spend a certain amount, and the seller transfers that amount. This is done through a function the seller contract calls, so the seller contract can know if it was successful.
1 /**2 * @dev Returns the remaining number of tokens that `spender` will be3 * allowed to spend on behalf of `owner` through {transferFrom}. This is4 * zero by default.5 *6 * This value changes when {approve} or {transferFrom} are called.7 */8 function allowance(address owner, address spender) external view returns (uint256);9நகலெடு
The allowance
function lets anybody query to see what is the allowance that one
address (owner
) lets another address (spender
) spend.
1 /**2 * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.3 *4 * Returns a boolean value indicating whether the operation succeeded.5 *6 * IMPORTANT: Beware that changing an allowance with this method brings the risk7 * that someone may use both the old and the new allowance by unfortunate8 * transaction ordering. One possible solution to mitigate this race9 * condition is to first reduce the spender's allowance to 0 and set the10 * desired value afterwards:11 * https://github.com/ethereum/EIPs/issues/20#issuecomment-26352472912 *13 * Emits an {Approval} event.14 */15 function approve(address spender, uint256 amount) external returns (bool);16அனைத்தையும் காட்டு