How to Create an ERC-20 Token With Solidity and Foundry
From the guide Token Launchpad: Write, Test and Verify an ERC-20 Meme Token on Ethereum and Base
To create an ERC-20 token, write a short Solidity contract that inherits OpenZeppelin's audited ERC20 and mints a fixed supply in the constructor, test it with a framework like Foundry, deploy it to a testnet such as Sepolia or Base Sepolia, and verify the source code on the block explorer. Once everything checks out on testnet, you repeat the same deployment on mainnet. The contract itself can be under ten lines.
The code is the easy part. The parts people get wrong are testing, key handling, and adding "extra" features that make buyers trust the token less.
What you need before you start
- A code editor and a terminal.
- Foundry, a set of command line tools for building, testing and deploying Solidity contracts. Install it with the official installer from the Foundry documentation, then run
foundryupto get the latest version. - A wallet you use only for deploying, funded with testnet ETH from a faucet for the network you choose. Never use your main wallet's key for development.
- An RPC endpoint for your network (from a node provider or a public endpoint) and, for verification, an API key from the block explorer.
Basic Solidity helps, but you do not need much. Inheriting OpenZeppelin means you are not writing transfer or approval logic yourself.
How to create an ERC-20 token, step by step
- Create a project. Run
forge init my-tokenand move into the folder. - Install OpenZeppelin. Run
forge install OpenZeppelin/openzeppelin-contracts, then add the remapping@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/to aremappings.txtfile. - Write the contract in
src/(shown below). - Write tests in
test/and runforge testuntil every test passes. - Store your deployer key safely. Use
cast wallet import deployer --interactiveso the key lives in an encrypted keystore instead of a plain text.envfile. - Deploy to a testnet, check it on the explorer, and verify the source.
- Deploy to mainnet only after the testnet version behaves exactly as you expect.
The ERC-20 token contract
Here is a complete fixed-supply token using OpenZeppelin 5.x. It mints one billion tokens (with the standard 18 decimals) to a recipient address and then has no special powers at all.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract PebbleFrog is ERC20 {
uint256 public constant TOTAL_SUPPLY = 1_000_000_000 * 10 ** 18;
constructor(address recipient) ERC20("Pebble Frog", "PFROG") {
_mint(recipient, TOTAL_SUPPLY);
}
}
That is the whole token. Inheriting ERC20 gives you the six standard functions (totalSupply, balanceOf, transfer, approve, allowance, transferFrom), the Transfer and Approval events, and name, symbol and decimals. If you pass the zero address as the recipient, OpenZeppelin 5.x reverts, which protects you from deploying with a missing setting.
Notice what is not there: no owner, no public mint, no fees, no pause, no blacklist. The standard says nothing about those features, which is exactly why scam tokens use them. A buyer reading this contract can confirm in seconds that you cannot print more tokens or block their sales.
One warning: do not paste snippets written for OpenZeppelin 4.x into a 5.x project. Hooks, constructor signatures and error types changed between versions, and mixed code can fail to compile or behave in ways you did not expect.
How to test your token with Foundry
You are not testing whether OpenZeppelin works. You are testing whether you used it correctly: the right number of zeros in the supply, the right recipient, the name spelled the way your logo is spelled, and no functions you did not mean to ship. A few core tests:
function setUp() public {
token = new PebbleFrog(holder);
}
function test_FullSupplyMintedToRecipient() public view {
assertEq(token.totalSupply(), 1_000_000_000e18);
assertEq(token.balanceOf(holder), 1_000_000_000e18);
}
function test_TransferMovesExactAmountWithNoFee() public {
vm.prank(holder);
token.transfer(alice, 1_000e18);
assertEq(token.balanceOf(alice), 1_000e18);
}
function test_HasNoPublicMint() public {
(bool ok,) = address(token).call(
abi.encodeWithSignature("mint(address,uint256)", alice, 1)
);
assertFalse(ok);
}
Add tests for metadata, approvals, transferFrom spending the allowance, and reverts when a transfer exceeds a balance or allowance. Then break one test on purpose (change the expected supply) and confirm it fails. A test that cannot fail proves nothing. Run forge test --gas-report to see the deployment gas figure, which you can use to estimate cost at current gas prices.
How to deploy and verify your ERC-20 token
Deploy with a Foundry script or with forge create. For a quick testnet deployment:
forge create src/PebbleFrog.sol:PebbleFrog \
--rpc-url base_sepolia \
--account deployer \
--broadcast \
--constructor-args 0xYourRecipientAddress
Then:
- Find the contract on the testnet explorer and confirm the first event is a
Transferfrom the zero address to your recipient for the full supply. - Verify the source with
forge verify-contract(or the--verifyflag), so anyone can read the exact code on the explorer. - Check it from the command line:
cast call $TOKEN "totalSupply()(uint256)" --rpc-url base_sepoliaand the same forname()(string),symbol()(string)andbalanceOf(address)(uint256). - Rehearse transfers between two test wallets, and if you plan to add liquidity on a DEX, rehearse that on testnet too.
A worked example. Theo wants a token for his gaming community. He writes the contract above with his own name and symbol, writes eleven tests, and runs them. One fails: he had typed the supply as 1_000_000_000 * 10 ** 8 while copying, so it would have minted a tiny fraction of what he announced. He fixes it, deploys to Base Sepolia, verifies the source, and sends tokens to two friends' test wallets. Only after a week of testnet checks does he run the same script against mainnet, with the deployer key in an encrypted keystore. The typo would have been permanent on mainnet, because a contract with no owner cannot be fixed after deployment.
Common mistakes when creating an ERC-20 token
- Adding owner powers "for safety." An
Ownablecontract with mint, pause or blacklist functions gives you exactly the powers buyers fear. Add a test that fails if anowner()function ever appears. - Making
transferFrombehave differently fromtransfer. Charging a fee only when selling to a pool creates the classic "can buy but cannot sell" honeypot shape, even by accident. - Overriding
balanceOf. Never do it. In tokens you evaluate, it is a serious red flag. - Adding a rescue function that can move tokens from arbitrary addresses. Tokens sent by mistake to a contract are a known ERC-20 limitation, not something to "fix" with admin power.
- Skipping testnet. Mainnet deployments are permanent. Rehearse everything first.
- Keeping a private key in a plain text file inside the project, where it can end up in a public repository.
Common questions
How much does it cost to create an ERC-20 token?
Testnet deployment is free apart from faucet ETH. On mainnet, you pay gas to deploy, and the price depends on the network and how busy it is when you deploy. Layer 2 networks like Base are typically much cheaper than Ethereum mainnet. Multiply your gas report figure by the current gas price for an estimate.
Do I need to know Solidity to create an ERC-20 token?
You need enough to read and understand a short contract like the one above, and to write simple tests. No-code token generators exist, but you are then trusting code you may not be able to read, and buyers will check it either way.
What is the difference between ERC20 and ERC-20?
None. They are two spellings of the same Ethereum token standard. It defines the functions and events every compatible token must have, which is why wallets and exchanges can support any ERC-20 token automatically.
Is it legal to create my own token?
Writing and deploying a token contract is generally allowed, but how you sell, promote or distribute it can raise securities, tax and consumer protection questions that depend on where you live. Talk to a lawyer familiar with crypto in your country before you sell or market a token to the public.
Keep reading
- How Does Down Payment Assistance Work? Types and CatchesLearn how down payment assistance works, which types are free and which are loans, who qualifies, what it covers, and how to tell if it is worth it for you.
- Debt Payoff Planner Template (Free, With Excel Formulas)A free debt payoff planner template for Excel, Sheets or paper, how to fill it in, the two formulas that set your payoff dates, and what to check in apps.
- How to Invest in Crypto for Beginners (and Not Get Burned)How to invest in crypto as a beginner: what to do first, how much to put in, which coins to start with, where to keep them, and why trading usually backfires.
- How to Buy Bitcoin Safely, Step by Step for BeginnersHow to buy bitcoin: pick a regulated exchange or app, verify your ID, fund with a bank transfer, check fees before you confirm, then store your coins safely.
- First Time Home Buyer Programs and How to Find YoursHow first time home buyer programs work, who qualifies, the main types of help, and how to find current programs in your state, county and city.
- How to Start Investing (Even With Little Money)Learn how to start investing in 7 clear steps: build a cushion, pick the right account, buy a low-cost index fund and automate it, even with $50 a month.