Template notice: Replace every
{placeholder}in this README, then delete this notice.
{One sentence describing what this project does.}
| Contract | Description |
|---|---|
ERC20MintableBurnable |
ERC-20 token with owner-controlled minting and public burn |
Replace the table above with your own contracts after cloning.
src/
├── interfaces/
│ └── IERC20MintableBurnable.sol # Extended ERC-20 interface (mint / burn surface)
└── ERC20MintableBurnable.sol # Concrete implementation (Solady ERC-20 + Ownable)
tests/
├── utils/
│ └── BaseTest.t.sol # Shared test base: named actors, helpers, event matchers
└── ERC20MintableBurnable.t.sol # Unit + fuzz test suite
script/
├── Base.s.sol # Broadcaster resolution, chain alias helpers, logDeploy
└── Deploy.s.sol # Env-var-driven deployment script
See docs/decisions/ for architecture decision records (ADRs).
- Forge: compile, test, fuzz, format, and deploy
- Bun: Node.js package manager (replaces Foundry git submodules)
- Solady: gas-optimised ERC-20 with built-in EIP-2612 permit
- OpenZeppelin Contracts: battle-tested contract library
- Forge Std: testing utilities and cheatcodes
- Prettier: formatter for non-Solidity files
- Solhint: Solidity linter
bun installforge build# Run all tests (1 000 fuzz runs)
forge test
# Verbose output for debugging
forge test -vvvv
# Run a single test file
forge test --match-path tests/ERC20MintableBurnable.t.sol
# CI profile (10 000 fuzz runs, verbosity 4)
FOUNDRY_PROFILE=ci forge testforge coverage
# Generate lcov HTML report (requires lcov: brew install lcov)
forge coverage --report lcov && genhtml lcov.info --output-directory coverage# Generate / update .gas-snapshot
forge snapshot
# Assert no regression vs committed snapshot
forge snapshot --check# Auto-fix Solidity formatting
forge fmt
# Check only (no writes)
forge fmt --check# Full lint: Solidity format check + solhint + prettier
bun run lint
# Auto-fix Solidity formatting
bun run forge-write
# Auto-fix JSON / Markdown / YAML formatting
bun run prettier:writeforge test --gas-reportforge clean
# or
bun run cleanCopy .env.example to .env and fill in the values:
cp .env.example .env| Variable | Required | Description |
|---|---|---|
TOKEN_NAME |
Yes | Human-readable token name (e.g. "My Token") |
TOKEN_SYMBOL |
Yes | Ticker symbol (e.g. "MTK") |
TOKEN_DECIMALS |
No | Decimal places (default: 18) |
TOKEN_OWNER |
No | Initial owner address (default: broadcaster) |
MNEMONIC |
Yes* | BIP-39 mnemonic for the deployer wallet |
API_KEY_ALCHEMY |
Yes* | Alchemy API key (required for fork tests and Alchemy RPC) |
API_KEY_ETHERSCAN |
No | Etherscan API key (required for --verify) |
* Required unless ETH_FROM is set via CLI.
anvil &
TOKEN_NAME="My Token" TOKEN_SYMBOL="MTK" \
forge script script/Deploy.s.sol --broadcast --rpc-url http://localhost:8545source .env
forge script script/Deploy.s.sol \
--broadcast \
--rpc-url $RPC_URL \
--verifyThis project uses Bun instead of Foundry git submodules.
# 1. Install the package
bun add <package-name>
# 2. Add the import remapping to remappings.txt
echo "<alias>/=node_modules/<package-name>/src/" >> remappings.txtExtend BaseTest instead of Test directly to get named actors and helper utilities:
import { BaseTest } from "./utils/BaseTest.t.sol";
contract MyContractTest is BaseTest {
function setUp() public override {
super.setUp(); // creates admin, alice, bob, carol, dave
}
function test_Example() public {
prank(alice);
// alice does something...
}
}Test naming convention used in this project:
| Prefix | Purpose |
|---|---|
test_ |
Deterministic unit test |
testFuzz_ |
Fuzz test (random inputs via Forge) |
testRevert_ |
Expected-revert test |
testFork_ |
Requires live-network fork (skipped without API key) |
Install the Hardhat Solidity
extension. Pre-configured settings are in .vscode/settings.json.
CI runs on every push to main and every pull request: lint → build → test (10 000 fuzz runs).
This project is licensed under MIT.