ERC20 Smart Contract Breakdown
500K contracts use the ERC20 standard but you’re probably not here for this number.
You’re here because you're interested in how these contracts implement the standard. You want to see the actual Solidity code behind all these tokens.
In this article, we will learn what the ERC20 standard is and we will break down how Uniswap and OpenZeppelin implement it.
Here is the outline of this article:
- What is a standard?
- What is the ERC20 standard?
- How do Uniswap and OpenZeppelin implement ERC20?
- Additional functionality: mint, burn
- Additional functionality: hooks
- Additional functionality: allowances
- Additional functionality: meta transactions
- Fully annotated code
What is a standard?
An Ethereum standard is just a standardized pattern of creating something on Ethereum.
A standard just says what should be done. But it does not specify the details (the how). It's like an interface in Java.
Why do we need standards? Mainly for interoperability. For example, wallets can write one code that will work with all tokens that follow the same ERC20 standard.
What is the ERC20 standard?
ERC20 is a standard used for implementing arbitrary tokens. For example, Dogecoin, Shiba Inu, USDC, all use the ERC20 token standard.
More formally: ERC20 is a standard for fungible tokens. (fungible means all tokens are all the same). NFTs (non-fungible tokens) on the other hand are all unique.
The ERC20 standard says that if a contract implements the functions below, then this contract can be considered a standard ERC20 token contract.

name
is the name of the token like“Dogecoin”
symbol
is the symbol likeDOGE
decimals
is the number of decimal points that the token has. The default value is 18. Ethereum also uses 18 decimal points (1 ETH = 10¹⁸ wei).totalSupply
is the total number of tokens measured in the smallest unit. For example, if there were only 5 ETH in total for ETH token,totalSupply
should then return 5*10¹⁸.balanceOf
is the balance of the provided address also measured in the smallest unit.transfer
is the function transferring the providedvalue
(again measured in the smallest units) from the sender to theto
address.
In combination, these functions cover the entire behavior of a token that can be used as a currency.
How do Uniswap and OpenZeppelin implement ERC20?
First of all, why does Uniswap need an ERC20 token? Uniswap needs it to represent the token that the liquidity providers earn in return for providing the liquidity (the funds) to the market. Here is the contract that Uniswap uses for its ERC20 token.
OpenZeppelin is just a library of standard contract implementations meant to be secure, audited, and reused. Here is the contract that OpenZeppelin uses for its ERC20 token.
Now let’s dive into the details of ERC20 contract implementation.
Here is the Uniswap implementation of all functions of the standard except the transfer
function.

These are just variables, you might say. Where are the actual function implementations? The trick is that the getter functions are automatically generated by Solidity for public variables. So there is no need to implement these functions manually. For example, you automatically get this function generated for the name
variable:

A correct getter function is also generated for the balanceOf
mapping so no need to implement that function either.
OpenZeppelin implements these functions in a similar (although more cumbersome) way

The difference between Uniswap and OpenZeppelin implementations is that OpenZeppelin allows you to set the name
and symbol
at the contract creation time (rather than at compile time).
The transfer
function
There is still the transfer
function that we didn’t get to yet.

Let’s see how this function is implemented in Uniswap:

It’s pretty self-explanatory except line 86 where we emit a Transfer
event. Events are used in Ethereum to monitor for certain events from a front end. For example, you might want to update the balance after the transfer goes through.
And here is the OpenZeppelin implementation:

An address zero check is required because nobody actually owns address zero in Ethereum (that is, nobody knows a private key whose matching public key is transformed to the zero address). When people use that address, it is usually a software bug — so we fail if the zero address is used as the sender or the recipient. (this paragraph is taken from the Ethereum page).
OpenZeppelin also checks if the transferring account has enough balance for the transfer. Uniswap does not do that because the Uniswap transfer function is only called by other Uniswap contracts (it’s not meant to be used as a stand-alone contract) which make sure that the transferring account always has enough balance.
Additional functionality: mint, burn
Although not part of the standard, Uniswap and OpenZeppelin implement the mint
and burn
functions which are meant to “create money out of thin air” and “burn money” respectively. These functions are used to control the amount of currency in circulation. Let’s see how they are implemented:

Uniswap and OpenZeppelin implement this functionality almost identically.
Additional functionality: hooks
OpenZeppelin has another useful functionality: hooks before and after transfer. What are hooks and why might we need them?
Hooks are just functions that are inserted as hooks into an otherwise independent flow. For example, here is how OpenZeppelin inserts the beforeTokenTransfer
and afterTokenTransfer
hooks:

These functions are empty by default, but anyone who uses the OpenZeppelin library to implement the ERC20 token can implement these functions to perform certain functionality before and after every transfer.
Hooks are useful because the transfer function remains unchanged even after adding additional functionality. The OpenZeppelin ERC-20 code has already been audited and shown to be secure. So by leaving the code intact, your code becomes secure automatically.
Here are the definitions of these hooks:

Additional functionality: allowances
The ERC20 standard actually has one more functionality that needs to be implemented by all tokens. I omitted it initially to simplify the explanation. Let’s bring it back now.
The omitted functionality is allowances. Allowances allow someone else to withdraw funds from you. Where can this be useful?
Imagine Amazon was a dApp on Ethereum. It has one big smart contract that handles all sales. When you buy something from Amazon, Amazon’s smart contract needs to withdraw tokens from you. But this is not possible on Ethereum. Amazon has no permission to initiate a transaction on behalf of someone else (you). They will need your private key or signature to initiate a transaction that will transfer your tokens to them.
Allowances can help you solve this problem. Here is how: Instead of sending the tokens to Amazon directly, you increase Amazon’s allowance from you by the same amount. You then buy your product from Amazon. When you buy it, Amazon checks its allowance from you. If it’s ≥ the product’s price, Amazon will sell the product to you and withdraw the amount from you (the allowance will be decreased accordingly). Only Amazon has the right to spend the allowance.
You might argue that you can transfer some tokens to Amazon’s account directly. But Amazon’s smart contract has no way of knowing the origin of the tokens that were just transferred to it. So we still need allowances.
This is why allowances were added as part of the ERC20 standard. Here are the functions that need to be implemented as part of the ERC20 standard:

allowance
function returns thespender
’s allowance fromowner
.approve
function approves thespender
an allowance of an amount equal tovalue
from the sendertransferFrom
function is used by the spender to actually spend the allowance.
Here is how Uniswap implements these functions (OpenZeppelin implements them similarly):

Additional functionality: meta transactions
Uniswap contract implements another functionality that might be useful in some scenarios. This functionality is called meta transactions.
Meta transactions allow you to sign an allowance transaction, send it to someone, and that someone can later submit your signed transaction to the contract and get an allowance from you on your behalf.
You can think of it as a more complicated way of adding an allowance. I’m not 100% sure when such functionality can be useful (if you do, let me know in the comments), but Uniswap implemented it, so let’s take a look at it. Here is how Uniswap implements it:

This looks complicated, but I want you to focus on the last 3 lines:

All the code before these 3 lines is only needed to recover the address of the account that signed the signature. Once we recover the address, we check whether the address matches the provided owner
address. If it does, we approve an allowance from owner
to spender
in the amount of value
.
Fully annotated code
Here is the entire Uniswap smart contract annotated with its functionality:
Interface (source code):

Implementation (source code):

Similarly, OpenZeppelin interface (source code):

Implementations (source code):

That’s it! I hope this was helpful. Let me know in the comments if you have any questions.
I am planning to do more breakdowns of popular smart contracts like Uniswap and Axie Infinity, so follow me either on Medium or Twitter to get updates.
You can also check out breakdowns of other smart contracts and more stuff for Solidity noobs at solidnoob.com.
Want to Connect With the Author?Follow me on Twitter.