Skip to main content
Create your own

Creating a Fixed-Supply ERC-20 Token with OpenZeppelin

Welcome back. In our previous lesson, we established the theoretical foundation of the ERC-20 standard, defining the "what" and "why" of its interface. You learned about the core functions and events that ensure interoperability, a concept familiar from standardized data protocols in the financial world.

Today, we transition from theory to practice. Our goal is to take that abstract interface and create a tangible, functional token. Specifically, you will learn how to use the industry-standard OpenZeppelin library to build your first ERC-20 token with a fixed supply. This is the foundational building block for many tokenized assets and a crucial first step toward your goal of developing solutions for tokenized money.

1. The Power of "Don't Reinvent the Wheel"

In your work with enterprise software, you rely on established, robust systems like Oracle or Snowflake for data management rather than building a new database from scratch for each project. The world of smart contracts operates on a similar, albeit more security-critical, principle.

Writing an ERC-20 implementation from scratch is a valuable academic exercise, but for production systems, it introduces unnecessary risk. A single error in tracking balances or handling allowances could lead to catastrophic financial loss. This is why the ecosystem heavily relies on libraries like OpenZeppelin.

OpenZeppelin Contracts is a library of modular, reusable, and, most importantly, community-audited smart contract components. By using its ERC20 implementation, we inherit a secure and gas-efficient foundation, allowing us to focus on our specific application logic.

2. Constructing a Basic ERC-20 Token

Creating a token with OpenZeppelin is elegantly simple. It leverages the principle of inheritance, which we covered in a previous module. Our custom token contract will inherit from OpenZeppelin's ERC20 contract, instantly gaining all the required functionality.

The minimal code to create a token looks like this:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MyToken is ERC20 {
    constructor() ERC20("MyToken", "MTK") {
        // More to come here
    }
}

Let's break this down:

  • import "@openzeppelin/contracts/token/ERC20/ERC20.sol";: This line imports the base contract from the OpenZeppelin library.
  • contract MyToken is ERC20: This declares our new contract and specifies that it inherits from ERC20.
  • constructor() ERC20("MyToken", "MTK"): When our MyToken contract is deployed, its constructor is called. This, in turn, calls the constructor of the parent ERC20 contract, passing the token's full name ("MyToken") and its symbol ("MTK"). These correspond to the optional name() and symbol() functions we discussed.

This contract is valid, but it has a total supply of zero. To create a fixed-supply token, we need to mint all the tokens when the contract is deployed.

3. Minting the Initial Supply

To create new tokens, we use the _mint function provided by the OpenZeppelin ERC20 contract. This is an internal function, meaning it can only be called from within the contract itself during its creation or by functions within it. This prevents anyone from arbitrarily creating new tokens after deployment.

To create a fixed supply, we call _mint inside the constructor. This action happens only once, at the moment of deployment.

ERC-20

The OpenZeppelin documentation provides the canonical example for creating a simple ERC-20 token. It also contains a vital explanation of how decimals are handled, a crucial concept for any financial application on-chain.

First, read the initial section and the code example under the heading "Constructing an ERC-20 Token Contract". This shows how to modify the constructor to accept an initialSupply and call the _mint function. Next, read the section "A Note on decimals" carefully. This is one of the most important concepts to master. Since Solidity doesn't handle floating-point numbers, token amounts are represented as integers. The decimals value (typically 18) tells user interfaces how to format that integer for display. For example, to represent 1,000 tokens, you must mint 1000 * (10**18) base units.

After incorporating the concepts from the reading, our contract now looks like this:

// contracts/MyToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MyToken is ERC20 {
    constructor(uint256 initialSupply) ERC20("MyToken", "MTK") {
        // The _mint function creates tokens and assigns them to an address.
        // msg.sender is the address deploying the contract.
        // The initialSupply needs to be provided in the smallest unit (factoring in decimals).
        _mint(msg.sender, initialSupply);
    }
}

When this contract is deployed, the deployer must provide the initialSupply (e.g., 1000000 * 10**18 for 1 million tokens). The entire supply is then created and assigned to the deployer's address. Because there are no other functions that call _mint, the total supply is forever fixed.

4. Visualizing and Building the Token

To make this process even more concrete, the ecosystem provides tools that can generate this code for you. The OpenZeppelin Contracts Wizard is a web-based tool that does exactly that.

The OpenZeppelin Contracts Wizard, showing a basic ERC-20 configuration for "MyToken" (MTK). This tool automatically generates the Solidity code based on user-selected settings.

The wizard is a great way to see how different features translate into code. The following short video demonstrates how to use the wizard to generate the code for a fixed-supply ("preminted") token.

Build ERC20 Tokens with OpenZeppelin Contract Wizard | Generate Solidity Token Code

This video from Blockman Codes provides a quick tour of the OpenZeppelin Contracts Wizard.

Watch from the beginning. The key parts are the explanation of the Name, Symbol, and especially the Premint field, which corresponds directly to the fixed initialSupply we are creating in our constructor.

While the wizard is useful for generating boilerplate, true understanding comes from writing, compiling, and deploying the contract yourself. The video below provides a practical, step-by-step walkthrough of this exact process using the Remix IDE, a browser-based development environment perfect for quick deployments.

Create an ERC20 token with Remix and OpenZeppelin

This tutorial from BlockchainBob demonstrates how to take the simple ERC-20 contract code, deploy it to a testnet, and interact with it.

Please watch the following segments: Contract Creation (04:40 - 06:19): This shows how to write the 5-line contract in Remix, importing the OpenZeppelin contract and setting up the constructor to mint the initial supply. Compilation and Deployment (06:57 - 10:22): Follow how the contract is compiled and deployed. Pay close attention to how the initialSupply argument is passed during deployment, including the common error of forgetting to account for the 18 decimals. Interaction (11:04 - 12:45): See how to add the newly created token to MetaMask and use the Remix UI to call view functions like name, symbol, totalSupply, and balanceOf. Although we are using Hardhat in this course, following along in Remix for this exercise is a valuable way to quickly see your code come to life.

5. Confirming a Truly Fixed Supply

We've defined our goal as creating a fixed-supply token. How do we ensure it stays fixed? The key is in what our code doesn't include. The following guide provides an excellent explanation.

How to Create an ERC20 Token: Complete Solidity Tutorial

This Speedrun Ethereum guide offers a great breakdown of the code and highlights important best practices, particularly regarding supply mechanics.

First, review the "Code Breakdown" section. It clearly explains the role of the constructor and the _mint call. Then, focus on the best practice tip under the heading "Important Tips and Best Practices". Read the point titled "Fixed Supply vs. Mintable". This clarifies that to ensure a fixed supply, you simply omit any public mint function that would allow for the creation of new tokens after deployment. Our simple contract does exactly this.

Conclusion

In this lesson, you've bridged the gap from theory to practice by creating your first smart contract for a tokenized asset. You leveraged the security and efficiency of OpenZeppelin to build a standard ERC-20 token with a fixed supply, a fundamental pattern in the world of tokenized money.

Key Takeaways:

  • Inheritance is Key: By inheriting from OpenZeppelin's ERC20 contract, you get a fully compliant and secure token implementation with minimal code.
  • The Constructor Mints the Supply: For a fixed-supply token, all tokens are created once and only once by calling the internal _mint function inside the contract's constructor.
  • The Deployer is the First Owner: The msg.sender in the constructor context is the address that deploys the contract, which receives the entire initial supply.
  • Decimals are for Display: On-chain, all token amounts are handled as large integers. The decimals property (usually 18) is crucial metadata used by off-chain applications to display user-friendly fractional amounts.
  • Absence of Code is a Feature: A token has a fixed supply because there is no function exposed that allows for further minting.

You have now written and conceptually deployed a working token. However, in professional development, creating the contract is only half the battle. We must prove that it behaves exactly as intended. In the next lesson, we will do just that by writing our first set of tests to verify the token's name, symbol, and total supply upon deployment.

Can't find a good explanation? Sign up and we'll make it for you

Sign up