CoinsBench

Where blockchain developers share their stories, experiences & ideas.

Follow publication

ERC20 Smart Contract Breakdown

Nazar Ilamanov
CoinsBench
Published in
8 min readFeb 3, 2022

--

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 like DOGE
  • 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 provided value (again measured in the smallest units) from the sender to the to 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 the spender’s allowance from owner.
  • approve function approves the spender an allowance of an amount equal to value from the sender
  • transferFrom 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.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Responses (4)

Write a response