Creating an ERC-20 token is a foundational skill for blockchain developers, especially in the rapidly evolving ecosystem of EVM-compatible chains like Berachain. With Foundry—a powerful, fast, and modern Ethereum development toolkit—you can write, test, and deploy smart contracts efficiently using Solidity. This guide walks you through every step of creating a fully functional ERC-20 token using Foundry, from project setup to deployment on Berachain.
Whether you're building a community token, launching a DeFi project, or experimenting with blockchain development, this tutorial provides a clear and practical path forward.
Setting Up Your Development Environment
Before diving into code, ensure your system has Foundry installed. Foundry simplifies smart contract development with built-in tools like forge (for compiling, testing, and deploying) and cast (for interacting with the blockchain).
👉 Get started with Foundry and streamline your smart contract workflow today.
To install Foundry, run:
curl -L https://foundry.paradigm.xyz | bashThen launch the installer and follow the prompts. Once installed, verify it works by running:
foundry --versionInitializing the Project
Start by creating a dedicated directory for your ERC-20 project:
mkdir create-erc20-contract-using-foundry
cd create-erc20-contract-using-foundryNow initialize a new Foundry project:
forge initThis command generates a standard project structure:
.
├── README.md
├── foundry.toml
├── lib
│ └── forge-std
├── script
│ └── Counter.s.sol
├── src
│ └── Counter.sol
└── test
└── Counter.t.solYou now have a working Solidity environment ready for customization.
Installing OpenZeppelin Contracts
ERC-20 tokens require standardized implementations for functions like transfer, approve, and balanceOf. Instead of writing these from scratch, use OpenZeppelin Contracts, a trusted library for secure smart contract development.
Install it via Foundry:
forge install OpenZeppelin/openzeppelin-contractsThis adds OpenZeppelin’s contracts to your lib/ directory, enabling you to import ERC20.sol directly in your code.
Writing the ERC-20 Token Contract
Rename the default Counter.sol to reflect your token:
mv src/Counter.sol src/BingBongToken.solReplace its content with the following Solidity code:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract BingBongToken is ERC20 {
/**
* @dev Initializes token with name, symbol, and initial supply
*/
constructor(string memory name_, string memory symbol_, uint256 mintedTokens_) ERC20(name_, symbol_) {
_mint(msg.sender, mintedTokens_);
}
}This contract inherits from OpenZeppelin’s ERC20, automatically implementing all standard methods. The _mint call in the constructor issues the initial supply to the deployer.
Updating Test Files
Since we renamed the source file, update the test file accordingly:
mv test/Counter.t.sol test/BingBongToken.t.solReplace its content with a comprehensive test suite:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import {Test, console2, stdError} from "forge-std/Test.sol";
import {BingBongToken} from "../src/BingBongToken.sol";
contract BingBongTokenTest is Test {
BingBongToken public token;
address supplyOwnerAddress = makeAddr("BerachainWalletUser");
address randomWalletAddress = makeAddr("GiveMeTokens");
address anotherWalletAddress = makeAddr("AnotherAddress");
function setUp() public {
vm.prank(supplyOwnerAddress);
token = new BingBongToken("BingBong Token", "BBT", 10000);
}
function test_name() public {
assertEq(token.name(), "BingBong Token");
}
function test_symbol() public {
assertEq(token.symbol(), "BBT");
}
function test_decimals() public {
assertEq(token.decimals(), 18);
}
function test_totalSupply() public {
assertEq(token.totalSupply(), 10000);
}
function test_balanceOfAddressSupplyOwner() public {
assertEq(token.balanceOf(supplyOwnerAddress), 10000);
}
function test_transfer() public {
vm.prank(supplyOwnerAddress);
token.transfer(randomWalletAddress, 100);
assertEq(token.balanceOf(randomWalletAddress), 100);
assertEq(token.balanceOf(supplyOwnerAddress), 9900);
}
function test_transferRevertInvalidReceiver() public {
vm.prank(supplyOwnerAddress);
vm.expectRevert(abi.encodeWithSignature("ERC20InvalidReceiver(address)", address(0)));
token.transfer(address(0), 100);
}
}Run the tests:
forge test -vvvA successful output confirms all tests pass—your token behaves as expected.
Creating the Deployment Script
Navigate to the script/ folder and rename the default script:
mv script/Counter.s.sol script/BingBongToken.s.solUpdate it with the deployment logic:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import {Script} from "forge-std/Script.sol";
import "../src/BingBongToken.sol";
contract BingBongTokenScript is Script {
function run() public {
uint256 deployerPrivateKey = vm.envUint("WALLET_PRIVATE_KEY");
vm.startBroadcast(deployerPrivateKey);
BingBongToken newToken = new BingBongToken("BingBongToken", "BBT", 5678);
console2.log("Token deployed at:", address(newToken));
console2.log("Total supply:", newToken.totalSupply());
vm.stopBroadcast();
}
}This script reads your private key from an environment variable and deploys the contract.
Deploying to Berachain Testnet
Create a .env file to store your private key securely:
WALLET_PRIVATE_KEY=your_private_key_hereEnsure your wallet has BERA tokens for gas fees on Berachain Testnet.
Deploy using:
forge script script/BingBongToken.s.sol \
--rpc-url https://rpc.berachain.com/ \
--broadcastUpon success, you’ll see the transaction hash and deployed contract address.
👉 Secure your deployment process with best practices and tools for managing keys and transactions.
Verifying the Contract (Coming Soon)
Currently, contract verification via forge verify-contract may face limitations on Berachain due to API compatibility. However, once supported, you can verify your contract using:
forge verify-contract <CONTRACT_ADDRESS> BingBongToken \
--verifier-url https://berascan.com/api \
--constructor-args $(cast abi-encode "constructor(string,string,uint256)" "BingBongToken" "BBT" 5678)Verification enhances trust by making your source code publicly auditable.
Core Keywords
- ERC-20 contract
- Foundry framework
- Solidity development
- Berachain deployment
- Smart contract testing
- OpenZeppelin Contracts
- Blockchain token creation
- Contract verification
These terms naturally appear throughout this guide, supporting SEO while maintaining readability and relevance.
Frequently Asked Questions
Q: What is Foundry used for in blockchain development?
A: Foundry is a complete toolkit for Ethereum and EVM-compatible chains that enables developers to write, test, compile, and deploy smart contracts using Solidity—all from the command line.
Q: Why use OpenZeppelin for ERC-20 tokens?
A: OpenZeppelin provides secure, community-audited implementations of ERC standards. Using their contracts reduces bugs and vulnerabilities in custom token logic.
Q: Can I deploy to Berachain mainnet using this method?
A: Yes. Replace the RPC URL with Berachain’s mainnet endpoint and ensure your wallet holds enough BERA for gas fees.
Q: How do I handle private keys securely?
A: Never hardcode keys in source files. Use environment variables (like WALLET_PRIVATE_KEY) and consider hardware wallets or key management solutions for production use.
Q: What if my deployment fails?
A: Check that your RPC URL is correct, your wallet has sufficient funds, and your private key is properly formatted. Use anvil locally to simulate deployments first.
Q: Is Foundry better than Hardhat?
A: Foundry offers faster execution (written in Rust), no need for JavaScript/TypeScript, and native Solidity testing. It's ideal for developers who prefer minimal tooling overhead.
With this guide, you’ve learned how to build, test, and deploy an ERC-20 token on Berachain using Foundry—a modern, efficient approach to blockchain development. As ecosystems like Berachain grow, mastering these tools positions you at the forefront of Web3 innovation.
👉 Accelerate your blockchain journey with powerful tools and resources.