Table of contents
- What is an ERC20 Token?
- Use Cases
- Example of dApps That Use ERC20 Tokens
- ERC-20 Contents
- Writing our contact
- Creating a smart contract
- Defining the states
- Creating the Construcutor
- Creating the modifier
- Creating the balanceOf() function
- Creating the transfer() function
- Creating the transferFrom() function
- Creating the approve() function
- Creating the allowance() function
- Custom Errors
- Creating the mint() and burn() function
- Conclusion
What is an ERC20 Token?
An ERC20 token is a standard for creating and issuing fungible tokens on the Ethereum blockchain. It defines a common interface that all tokens must adhere to, which includes functionalities like:
Total supply: The total number of tokens in existence.
Balances: Each account’s token balance.
Transfer: Allowing tokens to be sent from one account to another.
Allowance: Permitting a third party (or contract) to spend tokens on behalf of the token owner.
This standardization ensures interoperability between different tokens and dApps across the Ethereum ecosystem.
Key Takeaways
Ethereum Request for Comment 20 (ERC-20) is the implemented standard for fungible tokens created using the Ethereum blockchain.
ERC-20 guides the creation of new tokens on the Ethereum blockchain so that they are interchangeable with other smart contract tokens.
Since ERC-20s implementation, most Ethereum-based tokens have been created using the ERC-20 standard.
ERC-20 tokens should not be mistaken for ether (ETH), the native cryptocurrency of the Ethereum blockchain. While ETH is used to pay for computations and network resources, ERC-20 tokens can be created to represent any real-world or virtual asset.
Use Cases
ERC20 tokens have paved the way for a multitude of applications, including:
Utility Tokens: Used as in-app currency or to access services within a platform.
Stablecoins: Tokens pegged to real-world assets like the US dollar.
Governance Tokens: Allowing holders to vote on changes within decentralized protocols.
Security Tokens: Representing shares or stakes in assets or companies.
Incentive Mechanisms: Reward systems within gaming or social media platforms.
Their widespread use has made them a cornerstone in the decentralized finance (DeFi) movement.
Plenty of well-known digital currencies use the ERC-20 standard. Some popular examples are:
Example of dApps That Use ERC20 Tokens
Many popular decentralized applications have built their ecosystems around ERC20 tokens. Some notable examples include:
Uniswap: A decentralized exchange that uses ERC20 tokens for liquidity pools.
Compound: A lending protocol where ERC20 tokens represent collateral or debt.
MakerDAO: Uses ERC20-based tokens in its stablecoin ecosystem.
Aave: Another lending platform that leverages ERC20 tokens for borrowing and lending operations.
These dApps benefit from the standardization of token behavior, which simplifies integration and user interactions.
ERC-20 Contents
ERC-20 is a list of functions and events that must be implemented into a token to be considered ERC-20 compliant. These functions (called methods in the ERC) describe what must be included in the smart-contract-enabled token, while events describe an action. The functions a token must have are:1
TotalSupply: The total number of tokens that will ever be issued
BalanceOf: The account balance of a token owner's account
Transfer: Automatically executes transfers of a specified number of tokens to a specified address for transactions using the token
TransferFrom: Automatically executes transfers of a specified number of tokens from a specified address using the token
Approve: Allows a spender to withdraw a set number of tokens from a specified account, up to a specific amount
Allowance: Returns a set number of tokens from a spender to the owner
The events that must be included in the token are:
Transfer: An event triggered when a transfer is successful
Approval: A log of an approved event (an event)
Writing our contact
Creating a hardhat project
We will use hardhat to compile and manage our contract. Navigate to a safe directory, and run the following command to initialize a hardhat project initialization wizard:
Before we begin, make sure you have node installed in your computer.
I'm using yarn but you can make use of npm or pnpm. Read more about it in the hardhat documentaion.
yarn add --dev hardhat
npx hardhat init
Creating a smart contract
Now, navigate to the contracts
folder, create a new file named ERC20.sol
. This will be the smart contract file we will work on. Now let's proceed with writing the contract.
Creating the basic structure for a smart contract:
I used KofiToken, But you can name it whatever you want.
Defining the states
Now, let's define the state variables we will use in the contract- these are typically data such as user balances, token names, symbols, and other data. Some of these are listed on the ERC-20 docs- look at them so that you understand why we are adding these in the state.
In the above code, we define the states necessary: token name, symbol, decimals of the token, total supply of the token, user balances, allowances and the events that can be later emitted.
The owner is the contract deployer in this scenario. but you can set a custom address to be the owner if you want.
If you look at the ERC-20 docs, you will notice we have functions for name()
, symbol()
, decimals()
and totalSupply()
. However, we don't need to create separate functions since the variables are public and can be called a function by default.
If you don't know what allowances are, you can allow someone to spend tokens on your behalf. Although, you need to approve the number of tokens beforehand.
Creating the Construcutor
Let's create a constructor to initialize all the data as soon as the contract is deployed on the chain.
In the above code, we create a constructor that inputs the token's name, symbol and total supply and initializes all the values. We also assign the total supply as the balance for the contract deployer so that the minted tokens aren't lost after deployment and are usable. The decimals specify how many decimal places a token has, The default setting is 18, but you can change this by overriding the decimal function.
Creating the modifier
In Solidity, a modifier is a piece of code that modifies the behavior of a function. Modifiers can be used to control access to functions and variables, and to enforce conditions before executing a function
Although, this is optional. We have a modifier named onlyOwner, To restrict access to certain functions so only the owner of the contract can call those functions.
Creating the balanceOf()
function
Let's create a function to get the balance of a specific wallet/contract address. This function also complies with the ERC-20 standards.
In the above code, we check if the address provided is a zero address and revert the transaction if so. If not, we will return the balance of the user by accessing the _balances
mapping. Here we are checking the balance of the contract owner.
Creating the transfer()
function
Let's create the transfer function to help token holders transfer their tokens to other wallets/contracts.
In the above code block, we check if the address we’re sending to is a zero address and revert the transaction if so, Then we check if the transaction sender (also the token sender) has enough token balance to afford this transaction and revert if the sender has insufficient balance.
Then, we access the _balances
mapping and subtracting the amount from the sender and adding the same amount to the receiver. That way, the balance will reduce when sending tokens to other wallets/contracts.
We then emit a Transfer()
event that is part of the ERC-20 standard events. This can help apps to listen when any transfers are made on your token.
Creating the transferFrom()
function
This function is similar to the transfer()
function, although we use this function to send funds on behalf of other wallets. For this, allowances must be set by a wallet that intends to allow its tokens to be transferred by other wallets.
In the above code, we are checking if the transaction sender has enough permissible allowance to send tokens from another wallet, and we perform the same tasks as transfer()
function, but the wallet chosen to send from will be affected by balance changes and not the transaction sender.
We also reduce the permissible allowance by the number of tokens sent in this transaction. This way, nobody can spam the allowance logic and get all the tokens.
Creating the approve()
function
Let's create a function that allows the token holder to set allowances for other wallets to transfer their tokens.
In the above code, we check if the token holder has enough balance for the required allowance and revert if not. Then we are setting the allowance by making changes to the _allowances
mapping. Finally, we are emitting an Approval()
event so that apps can listen to this event.
Creating the allowance()
function
Let's create a function that lets the token holder access their remaining allowance.
The above function is pretty straightforward. We access the _allowance
mapping and return a spender's allowance for a token holder.
Custom Errors
Starting from Solidity v0.8.4, there is a convenient and gas-efficient way to explain to users why an operation failed through the use of custom errors. Until now, you could already use strings to give more information about failures (e.g., revert("Insufficient funds.");), but they are rather expensive(Gas cost), especially when it comes to deploy cost, and it is difficult to use dynamic information in them.
Custom errors are defined using the error statement, which can be used inside and outside of contracts (including interfaces and libraries).
Here we defined it outside our contract.
Creating the mint()
and burn()
function
In Solidity, a "mint" function creates new tokens and adds them to the circulating supply, essentially "printing" new tokens. Also used to distribute new tokens to users, often by a designated authority like the contract creator, while a "burn" function permanently destroys existing tokens, effectively removing them from circulation, both being fundamental operations in managing a token's total supply within a smart contract.
In the code blocks above, We made sure only the owner can call these functions.
Conclusion
In this article, we saw how to write an ERC-20 token from scratch. I hope this article explained how these tokens work under the hood and how every line is very important. If you have any questions or feedback, feel free to comment below.