Skip to main content

Someone wanted to learn this too, so Grasp built them a personal learning path.

You can keep exploring this one or create your own

Creating Controlled Tokens with a Mint Function

Hello! Welcome back to your course on building a blockchain application.

In our last lesson, we configured your token's core identity by setting its name, symbol, and decimals. We created a token with a fixed supply, where all tokens were created and assigned to you (the deployer) the moment the contract was deployed.

Today, we're going to make our token's supply more flexible. Many token models require the ability to create new tokens after deployment—for example, to fund project development, reward users, or manage an inflationary monetary policy. This lesson focuses on exactly that. Your learning outcome is to implement a minting function to create new tokens, controlled by an owner.


1. From Fixed Supply to Mintable Supply

A fixed supply is simple and predictable, but a mintable supply provides powerful administrative control. To create a mint function, we face a critical security question: who should be allowed to create new tokens?

Allowing anyone to mint tokens would render the token worthless. Therefore, we need a robust access control mechanism. The most common pattern in smart contracts is to assign an owner to the contract, who is the only one with the authority to perform sensitive actions like minting.

Given your background in backend development, you can think of this as analogous to an admin role in a web application. A regular user can't change system-wide settings, but an admin can.

2. The Ownable Contract Pattern

Instead of building an ownership system from scratch, we'll use a secure, industry-standard solution: OpenZeppelin's Ownable contract.

Ownable.sol is a small, reusable contract that provides a basic access control mechanism. When you inherit from it, your contract gets:

  • An owner who is set to the address that deploys the contract (msg.sender).
  • An onlyOwner modifier, which you can add to functions to restrict their access to the owner only.
  • A function to transfer ownership to a new address.

Let's integrate this into our token contract.

First, we need to import Ownable.sol and add it to our contract's inheritance list. We also need to call its constructor to set the deployer as the initial owner.

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

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
// 1. Import the Ownable contract
import "@openzeppelin/contracts/access/Ownable.sol";

// 2. Add Ownable to the inheritance list
contract MyToken is ERC20, Ownable {
    // 3. Call the Ownable constructor to set the owner
    constructor() ERC20("My Awesome Token", "MAT") Ownable(msg.sender) {
        // The contract is deployed with 0 tokens.
        // The owner can now mint tokens as needed.
    }

    // Our mint function will go here
}

Notice we've removed the initial minting from the constructor. Now, the token will be deployed with a total supply of zero, giving the owner full control over creating the initial supply and any future tokens.


3. Implementing an Owner-Controlled mint Function

Now that we have an ownership system in place, we can create our public mint function. This function will act as a controlled gateway to the internal _mint function provided by OpenZeppelin's ERC20 contract.

Here's how to write the function:

    function mint(address to, uint256 amount) public onlyOwner {
        _mint(to, amount * (10 ** decimals()));
    }

Let's break this down:

  • function mint(address to, uint256 amount): Defines a function that takes two arguments: the address to receive the new tokens and the amount of tokens to create.
  • public: The function is visible and can be called from outside the contract.
  • onlyOwner: This is the modifier from the Ownable contract. It automatically adds a check to ensure that msg.sender (the caller) is the contract's owner. If anyone else tries to call this function, the transaction will fail.
  • _mint(to, amount * (10 ** decimals()));: This is the core logic. It calls the internal _mint function. We use the amount * (10 ** decimals()) pattern from our previous lesson to correctly handle the token's divisibility.

The combination of a public function guarded by the onlyOwner modifier is a cornerstone of secure smart contract development.

OpenZeppelin Contracts Wizard for ERC-20 Token with Minting Function
This image from the OpenZeppelin Contracts Wizard visually represents what we are building. By selecting the "Mintable" feature, the wizard automatically generates the code to inherit from `Ownable` and includes a `mint` function protected by the `onlyOwner` modifier.

How To Create a Token (Step-by-Step ERC20 Code Explained)

For a step-by-step demonstration of these concepts, the following video from Whiteboard Crypto is excellent. It shows how to add a mint function and then secure it using the onlyOwner pattern.

Watch from 08:05 to 13:56. The first part (until 10:40) explains how to create a basic public mint function. The second part (from 11:48) demonstrates the crucial step of adding the Ownable contract and the onlyOwner modifier to secure it. Note that the video uses Remix, an online IDE, which is great for quick prototyping.


4. The Complete Mintable Token Contract

Let's put all the pieces together. Here is the final version of our MyToken.sol, now a mintable ERC-20 token.

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

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

/**
 * @title MyToken
 * @dev An ERC20 token that is mintable by its owner.
 */
contract MyToken is ERC20, Ownable {
    /**
     * @dev Sets the values for name, symbol, and owner.
     */
    constructor() ERC20("My Awesome Token", "MAT") Ownable(msg.sender) {}

    /**
     * @dev Creates `amount` new tokens and assigns them to `to`,
     * increasing the total supply.
     *
     * Requirements:
     * - This function can only be called by the owner.
     */
    function mint(address to, uint256 amount) public onlyOwner {
        _mint(to, amount);
    }
}

Wait, what happened to the decimals calculation? In the code snippet above, I've simplified the mint function to _mint(to, amount). This means the amount you pass to the function should already be in the token's smallest unit (factoring in the 18 decimals). The version _mint(to, amount * (10 ** decimals())) is often more convenient as it lets you think in whole tokens. Both are valid, but it's crucial to be consistent in how you and your applications call the function. For clarity in our project, we'll stick with the version that includes the decimal calculation.

How to Create an ERC20 Token: Complete Solidity Tutorial

The following guide from Speedrun Ethereum provides another clear, concise example of a mintable token. It's a great reference to reinforce your understanding of the code structure.

In this resource, find the section with the mint function. Read the function's code and the corresponding explanation in the 'Code Breakdown' section. This will solidify the concepts of the onlyOwner modifier and the role of the _mint function.

Test your understanding!

Good practice dictates that functions should validate their inputs. Modify the mint function to ensure that no one can mint tokens to the zero address (address(0)) and that the amount to be minted is greater than zero.

Hint: Use the require() statement.

Show answer
function mint(address to, uint256 amount) public onlyOwner {
    require(to != address(0), "ERC20: mint to the zero address");
    require(amount > 0, "ERC20: mint zero amount");
    _mint(to, amount * (10 ** decimals()));
}

This is a common security pattern. The zero address is often used to represent burning tokens or uninitialized addresses, so sending new tokens there is usually an error.


Conclusion

You have now successfully upgraded your basic ERC-20 token into one with a flexible, owner-managed supply. You've learned one of the most fundamental patterns for smart contract security and administration.

Key Takeaways:

  • Mintable vs. Fixed Supply: You can design your token to have a fixed supply set at deployment or a flexible supply that can be increased later.
  • Access Control is Critical: Any function that alters the token's state or supply, like mint, must be protected.
  • The Ownable Pattern: OpenZeppelin's Ownable contract is the industry standard for implementing simple, single-owner access control.
  • onlyOwner Modifier: This modifier is a powerful tool to restrict function execution to the contract owner, preventing unauthorized actions.

Preview of the Next Lesson:
Minting creates tokens, but what about destroying them? The ability to "burn" tokens is the other side of supply management and is key to creating deflationary token models. In the next lesson, we will implement a burning function to permanently destroy tokens.

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

Sign up