Hello! In our previous lesson, we explored the landscape of stablecoins, comparing the fiat-backed, crypto-collateralized, and algorithmic models. We concluded that the fiat-backed design, which prioritizes peg stability and capital efficiency by relying on a centralized, trusted issuer, provides the most robust and widely adopted foundation for tokenized money today.
This lesson marks our transition from theory to practice. We will now build the core engine of a fiat-backed stablecoin. Your goal is to implement a stablecoin contract by extending OpenZeppelin's ERC-20, adding mint and burn functions. You will learn how the real-world process of depositing and withdrawing fiat currency is translated into on-chain functions that create and destroy tokens, thereby managing the token's circulating supply.
The Mint and Burn Cycle in Code
In a fiat-backed model, the total supply of the stablecoin must precisely mirror the amount of fiat currency held in reserve. This requires a mechanism to elastically adjust the supply. This mechanism is the mint and burn cycle.

Look closely at this diagram. It separates the world into three domains: the User, the Stablecoin Issuer (the central entity managing the reserves), and the Blockchain. Our work as developers happens in the "Blockchain" and "Stablecoin Issuer" domains. We will write the smart contract functions (mint() and burn()) that the issuer calls in response to user actions.
1. Setting Up the Basic Stablecoin Contract
We'll begin by creating the basic skeleton of our stablecoin contract. True to best practices, we will build upon the solid foundation of OpenZeppelin's audited contracts. Since our stablecoin is a fungible token, we will inherit from their ERC20 implementation.
Let's create a new contract file in your Hardhat project named USDStablecoin.sol. Here is the initial code:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
/**
* @title USDStablecoin
* @dev A simple ERC-20 stablecoin contract pegged to USD.
*/
contract USDStablecoin is ERC20 {
constructor() ERC20("USD Stablecoin", "USDS") {
// The constructor sets the name and symbol of our token.
// No initial supply is minted upon deployment.
}
/**
* @dev Overrides the default ERC20 decimals function.
* While 18 is the default for many tokens (like Ether),
* major stablecoins like USDC use 6 decimals, which more
* naturally represents a currency with cents.
*/
function decimals() public pure override returns (uint8) {
return 6;
}
}
This is a standard ERC-20 token with a custom name, symbol, and a decimals setting of 6, which is a common convention for USD-pegged stablecoins. Notice that we are not minting any tokens in the constructor. The supply will be zero at deployment, ready to be expanded or contracted via our mint and burn functions.
2. Implementing the burn Function with ERC20Burnable
Let's first tackle the "burn" side of the cycle. When a user wishes to redeem their stablecoins for fiat, the tokens they return to the issuer must be permanently removed from circulation. OpenZeppelin provides an excellent extension, ERC20Burnable, that makes this straightforward.
Build ERC20 Tokens with OpenZeppelin Contract Wizard | Generate Solidity Token Code
This short clip from Blockman Codes explains how the burnable feature in the OpenZeppelin wizard works by inheriting from the ERC20Burnable contract.
Please watch the explanation from 03:22 to 03:52. The key takeaway is that inheriting from this contract gives our token burning capabilities without us needing to write the function logic from scratch.
To use this, we simply import ERC20Burnable.sol and add it to our contract's inheritance list.
// Add this import to the top of your file
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
// Update the contract declaration
contract USDStablecoin is ERC20, ERC20Burnable {
// ... constructor and decimals function remain the same
}
By making this small change, our USDStablecoin contract now automatically has two public functions:
burn(uint256 amount): Allows any address to destroyamountof its own tokens.burnFrom(address account, uint256 amount): Allows an address to destroyamountof tokens from anotheraccount, provided it has been given a sufficient allowance via theapprovefunction.
This ERC20Burnable extension provides the core functionality for the redemption part of our cycle. A user could call burn to destroy their tokens as part of a redemption transaction, or send tokens to an issuer-controlled address which then calls burn.
3. Implementing the mint Function
Now for the "mint" side. Unlike burning, which any user might be allowed to do with their own tokens, minting in a fiat-backed model is a highly privileged action. Only the stablecoin issuer should ever be able to create new tokens, and only after verifying that a corresponding amount of fiat has been deposited into the reserves.
Because this action is so critical and application-specific, OpenZeppelin does not provide a simple, public ERC20Mintable extension. Instead, we must implement the mint function ourselves and ensure it is properly secured. The base ERC20 contract gives us all the tools we need, specifically an internal function called _mint.
How To Create a Token (Step-by-Step ERC20 Code Explained)
The presenter in this Whiteboard Crypto video demonstrates how to create a public function that uses OpenZeppelin's internal _mint function to create new tokens.
Watch the segment from 08:22 to 10:12. Focus on how the custom mint50 function is just a wrapper that calls _mint(msg.sender, amount). This is the fundamental pattern we will use. Note that their function is public and unsecured, which we will address.
We will add a mint function to our contract that takes a recipient address and an amount as arguments. Inside this function, we will call _mint.
Here is the code to add inside your USDStablecoin contract:
/**
* @dev Creates `amount` new tokens and assigns them to `to`.
* This function is the core of the minting process for the stablecoin.
*
* NOTE: This function is currently `public` for demonstration purposes.
* In a real-world scenario, it MUST be restricted to an authorized
* minter role, which we will implement in the next lesson.
*/
function mint(address to, uint256 amount) public {
_mint(to, amount);
}
This function is simple but powerful. It directly increases the total supply of the token, executing the on-chain step of the "Mint" cycle.
The public visibility here is a critical point. As it stands, anyone could call this function and mint infinite tokens for themselves, completely breaking the 1:1 peg. Your experience with data warehousing and access controls in investment management software should immediately flag this as a severe security risk, analogous to giving every user database admin privileges. We are leaving it public for this lesson only to isolate the implementation of the function itself.
Here is the complete USDStablecoin.sol contract for this lesson:

Conclusion
Congratulations! You have successfully implemented the fundamental mechanics of a fiat-backed stablecoin. By building on OpenZeppelin's ERC20 standard, you've created a token contract with an elastic supply controlled by mint and burn functions.
Key Takeaways:
- Fiat-Backed Logic: The core logic of a fiat-backed stablecoin involves
mintandburnfunctions that the issuer uses to mirror off-chain fiat reserves. - OpenZeppelin Composability: We leveraged OpenZeppelin's modularity by inheriting from
ERC20for the standard interface andERC20Burnablefor a ready-to-use token destruction mechanism. - Internal vs. External Functions: We implemented a custom
mintfunction that acts as a controlled gateway to the powerful internal_mintfunction provided by the baseERC20contract. - The Need for Access Control: You've implemented a
mintfunction that works, but it is critically insecure. The necessity of restricting this powerful function is now clear.
In our next lesson, we will address this security flaw head-on. We will use the AccessControl pattern you learned about in Module 6 to restrict the mint and burn functions, ensuring that only an authorized administrator role can manage the token supply, thus completing the secure design of our stablecoin's core.