Project 1.1: Token ERC20 com Taxas (ERC20 Token with Fees)
Educational Smart Contract Project - Production-quality code with comprehensive documentation, security analysis, and architecture design decisions.
Author: Wesley Santos | Status: Educational/Testnet Ready | Last Updated: February 2026
🎯 Navigate Efficiently: See DOCUMENTATION_INDEX.md for reading paths and detailed descriptions
This project includes 10 comprehensive documents (~200 pages) covering everything from beginner tutorials to production deployment planning:
| Document | Length | Audience | Purpose |
|---|---|---|---|
| README.md | 20 pages | Everyone | Project overview, features, installation |
| TUTORIAL.md | 25 pages | Beginners | Step-by-step hands-on guide |
| QUICK_REFERENCE.md | 15 pages | Developers | Command cheatsheet |
| Document | Length | Audience | Purpose |
|---|---|---|---|
| SECURITY.md | 30 pages | Auditors, Security | Threat model & attack vectors |
| ARCHITECTURE.md | 25 pages | Engineers | Design decisions & trade-offs |
| TEST_COVERAGE.md | 20 pages | QA, Auditors | Test strategy & coverage |
| Document | Length | Audience | Purpose |
|---|---|---|---|
| PRODUCTION_READINESS.md | 25 pages | Founders, CTOs | Mainnet checklist ($80k-225k budget) |
| PORTFOLIO_SHOWCASE.md | 25 pages | Job Seekers | Career value & interview tips |
| PROJECT_SUMMARY.md | 5 pages | Everyone | Quick reference card |
| Document | Length | Audience | Purpose |
|---|---|---|---|
| CHALLENGES.md | 15 pages | Intermediate+ | Enhancement ideas |
- 10 files | ~200 pages | ~50,000 words | 50+ code examples | 30+ tables
This token mechanism is used by several real-world projects and platforms. Here are practical examples:
1. Deflationary Community Tokens
- Use Case: Create scarcity and value appreciation through automatic token burning
- Example: A community launches a token with 1 billion supply. With 2% burn on transfers, the circulating supply decreases over time, potentially increasing value for holders
- Real Projects:
- SafeMoon - Implements transaction fees with burn mechanism (3% burn, 2% redistribution)
- Shiba Inu - Uses burn mechanisms to reduce supply over time
- BabyDoge - 5% fee split between holders and liquidity
2. DeFi Platforms with Transaction Fees
- Use Case: Generate revenue for protocol treasury or liquidity pools
- Example: A DEX uses a governance token where 2% of transfers go to staking rewards or protocol development
- Real Projects:
- Reflect Finance (RFI) - Pioneered the reflection/fee mechanism
- Safuu - Auto-staking with rebase mechanism and fees
- EverGrow - Rewards holders in stablecoins from transaction fees
3. Loyalty and Rewards Programs
- Use Case: Corporate loyalty programs where large transfers have fees but small purchases don't
- Example: A retail chain creates a token. Customers earn tokens (whitelisted), but secondary market trading has fees to discourage speculation
- Real Projects:
- Crypto.com (CRO) - Used for cashback and rewards (no transfer fees, but similar whitelist concepts)
- Binance Coin (BNB) - Fee discounts on platform
4. Gaming Economies
- Use Case: In-game currency with anti-bot and anti-farming measures
- Example: Game tokens where player-to-player trades have small fees, but official game transactions are whitelisted
- Real Projects:
- Axie Infinity (SLP/AXS) - Gaming tokens with marketplace fees
- The Sandbox (SAND) - Gaming platform token with transaction controls
5. Charity and Redistribution Tokens
- Use Case: Automatic donations to charity wallets or holder redistribution
- Example: 2% of every transfer goes to a charity wallet (whitelisted), creating passive donations
- Real Projects:
- ElonGate - 10% transaction fee split between charity and holders
- FEG Token - Redistribution to holders on every transaction
Tokens with Transfer Fees:
- SafeMoon (SAFEMOON) - 10% fee (5% to holders, 5% to liquidity)
- Reflect Finance (RFI) - 1% fee redistributed to all holders
- Bonfire (BONFIRE) - 10% fee (5% burn, 5% liquidity)
- PigToken (PIG) - Automatic burn mechanism
Platforms Using Fee Mechanisms:
- Uniswap V2/V3 - 0.3% swap fee
- PancakeSwap - 0.25% trading fee
- SushiSwap - Fee sharing with token holders
- Curve Finance - Trading fees distributed to veCRV holders
Key Differences:
- This implementation uses automatic burning instead of redistribution
- Whitelist system for flexible fee exemptions
- Owner-controlled fee percentage (adjustable)
- Maximum transaction limits for security
- Pausable for emergency situations
✅ Good for:
- Community-driven tokens with deflationary tokenomics
- Loyalty programs with secondary market controls
- Gaming tokens with anti-bot measures
- Projects wanting to reward long-term holders
- Tokens needing revenue generation mechanism
❌ Avoid for:
- Payment tokens (fees reduce usability)
- Utility tokens with high-frequency usage
- Stablecoins (fees affect peg stability)
- Integration with DeFi protocols (fees complicate liquidity)
- Overview
- Features
- Technical Specifications
- Prerequisites
- Installation
- Project Structure
- Smart Contract Details
- Testing
- Deployment
- Interacting with the Contract
- Security Considerations
- Gas Optimization
- Learning Outcomes
- Common Issues
- References
This project implements a custom ERC20 token with a 2% transfer fee mechanism. It demonstrates fundamental Solidity concepts including:
- ERC20 standard implementation using OpenZeppelin
- Custom fee logic with automatic burning
- Whitelist functionality for fee exemption
- Access control with Ownable pattern
- Pausable mechanism for emergency situations
- Maximum transaction limits
Learning Focus: This is a beginner-level project designed to solidify understanding of ERC20 tokens, state management, modifiers, events, and best practices in Solidity development.
-
Transfer with 2% Fee
- Every transfer deducts a 2% fee from the sender
- Fee is automatically burned (removed from total supply)
- Reduces inflation over time
-
Whitelist System
- Addresses on the whitelist pay no fees
- Owner can add/remove addresses from whitelist
- Deployer is automatically whitelisted
-
Configurable Fee
- Owner can change fee percentage (0% to 10% maximum)
- Fee stored in basis points (200 = 2%)
- Cannot exceed MAX_FEE_PERCENT (1000 = 10%)
-
Maximum Transaction Limit
- Optional maximum amount per transaction
- Can be set or removed by owner
- Helps prevent large dumps
-
Pausable Transfers
- Owner can pause all transfers in emergency
- Can be unpaused when situation is resolved
- Useful for security incidents or upgrades
- Custom Errors: Gas-efficient error handling (Solidity 0.8.4+)
- Events: Comprehensive event emission for off-chain tracking
- View Functions: Calculate fees and check contract state
- NatSpec Documentation: Full inline documentation for all functions
- Solidity Version: 0.8.33
- License: MIT
- Standards: ERC20 (OpenZeppelin 5.x)
- Development Framework: Hardhat
- Testing: Chai + Ethers.js
- Network: Hardhat Network (local), Sepolia (testnet)
{
"hardhat": "^2.22.0",
"@nomicfoundation/hardhat-toolbox": "^5.0.0",
"@openzeppelin/contracts": "^5.x"
}-
Node.js (v20 or higher)
node --version # Should show v20.x or higher -
npm (comes with Node.js)
npm --version # Should show 10.x or higher -
Git (for version control)
git --version
- VS Code with Solidity extension
- MetaMask (for testnet deployment)
- Sepolia ETH (for testnet deployment - get from faucet)
cd .../solidity-projects/01-simple-token# Install Node.js dependencies
# Instalar dependências Node.js
npm installThis installs:
- Hardhat and all plugins
- OpenZeppelin contracts
- Testing libraries (Chai, Ethers.js)
- Development tools
# Check Hardhat installation
# Verificar instalação do Hardhat
npx hardhat version
# Compile contracts to test setup
# Compilar contratos para testar configuração
npx hardhat compileYou should see:
Solidity 0.8.33 is not fully supported yet...
Compiled 8 Solidity files successfully
01-simple-token/
├── contracts/
│ └── SimpleToken.sol # Main ERC20 token with fees
├── test/
│ └── SimpleToken.test.js # Comprehensive test suite (47 tests)
├── scripts/
│ └── deploy.js # Deployment script with verification
├── ignition/
│ └── modules/ # Alternative deployment method (Hardhat Ignition)
├── hardhat.config.js # Hardhat configuration
├── package.json # Project dependencies
├── .gitignore # Git ignore rules
└── README.md # This file
Inheritance Hierarchy:
SimpleToken
├── ERC20 (OpenZeppelin)
├── Ownable (OpenZeppelin)
└── Pausable (OpenZeppelin)
uint256 public feePercent; // Current fee in basis points (200 = 2%)
uint256 public constant MAX_FEE_PERCENT = 1000; // Maximum 10% fee
uint256 public maxTransactionAmount; // Max amount per transaction (0 = no limit)
mapping(address => bool) public whitelist; // Fee-exempt addresses
uint256 public totalFeesBurned; // Cumulative fees burned// Transfer tokens with automatic fee deduction
// Transferir tokens com dedução automática de taxa
function transfer(address to, uint256 amount) public override returns (bool)
// Transfer from another address (requires approval)
// Transferir de outro endereço (requer aprovação)
function transferFrom(address from, address to, uint256 amount) public override returns (bool)// Change fee percentage (0-1000 basis points)
// Mudar porcentagem de taxa (0-1000 pontos base)
function setFeePercent(uint256 newFeePercent) external onlyOwner
// Add address to whitelist (no fees)
// Adicionar endereço à whitelist (sem taxas)
function addToWhitelist(address account) external onlyOwner
// Remove address from whitelist
// Remover endereço da whitelist
function removeFromWhitelist(address account) external onlyOwner
// Set maximum transaction amount
// Definir valor máximo de transação
function setMaxTransactionAmount(uint256 amount) external onlyOwner
// Pause/Unpause all transfers
// Pausar/Despausar todas as transferências
function pause() external onlyOwner
function unpause() external onlyOwner// Calculate fee for a given amount
// Calcular taxa para um dado valor
function calculateFee(uint256 amount) external view returns (uint256 feeAmount, uint256 transferAmount)
// Check if address is whitelisted
// Verificar se endereço está na whitelist
function isWhitelisted(address account) external view returns (bool)
// Get all contract information in one call
// Obter todas informações do contrato em uma chamada
function getContractInfo() external view returns (...)event FeePercentChanged(uint256 oldFeePercent, uint256 newFeePercent, uint256 timestamp);
event WhitelistAdded(address indexed account, uint256 timestamp);
event WhitelistRemoved(address indexed account, uint256 timestamp);
event FeeBurned(address indexed from, address indexed to, uint256 amount, uint256 timestamp);
event MaxTransactionAmountChanged(uint256 oldAmount, uint256 newAmount, uint256 timestamp);error FeePercentTooHigh(uint256 requested, uint256 maximum);
error InvalidAddress();
error TransactionAmountTooHigh(uint256 amount, uint256 maximum);
error TransferToZeroAddress();
error ZeroAmount();# Run complete test suite
# Executar suite completa de testes
npx hardhat testExpected output:
SimpleToken
Deployment
✓ Should set the correct token name and symbol
✓ Should mint initial supply to deployer
... (47 tests total)
47 passing (2s)
npx hardhat test test/SimpleToken.test.js# Enable gas reporting
# Habilitar relatório de gas
REPORT_GAS=true npx hardhat test# Generate coverage report
# Gerar relatório de cobertura
npx hardhat coverage-
Deployment Tests (9 tests)
- Token initialization
- Parameter validation
- Initial state
-
Transfer Tests (8 tests)
- Fee deduction
- Whitelist functionality
- Fee burning
- Edge cases
-
TransferFrom Tests (2 tests)
- Allowance mechanism
- Fee with approvals
-
Whitelist Management (5 tests)
- Add/remove addresses
- Access control
- Event emission
-
Fee Management (5 tests)
- Change fee percentage
- Validation
- Events
-
Max Transaction Tests (4 tests)
- Set/remove limits
- Enforcement
-
Pausable Tests (5 tests)
- Pause/unpause
- Transfer prevention
-
View Functions (3 tests)
- Read-only operations
- Calculations
-
Edge Cases (4 tests)
- Complex scenarios
- Multiple transfers
- Different fee percentages
-
Gas Optimization (2 tests)
- Custom errors usage
# Start local Hardhat node (in separate terminal)
# Iniciar node Hardhat local (em terminal separado)
npx hardhat node
# Deploy to local network (in another terminal)
# Fazer deploy na rede local (em outro terminal)
npx hardhat run scripts/deploy.js --network localhostGet test ETH from a Sepolia faucet:
Create a .env file in project root:
# Create .env file
# Criar arquivo .env
touch .envAdd your private key and RPC URL:
PRIVATE_KEY=your_private_key_here
SEPOLIA_RPC_URL=https://sepolia.infura.io/v3/YOUR_INFURA_KEY
ETHERSCAN_API_KEY=your_etherscan_api_keyIMPORTANT: Never commit .env to Git! It's already in .gitignore.
Uncomment the Sepolia network configuration:
sepolia: {
url: process.env.SEPOLIA_RPC_URL || "",
accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
chainId: 11155111,
},# Deploy to Sepolia
# Fazer deploy na Sepolia
npx hardhat run scripts/deploy.js --network sepolia# Verify contract (use output from deploy script)
# Verificar contrato (usar output do script de deploy)
npx hardhat verify --network sepolia <CONTRACT_ADDRESS> "Simple Token" "SMPL" "1000000000000000000000000" "200"# Open Hardhat console
# Abrir console Hardhat
npx hardhat console --network localhost// Get contract instance
// Obter instância do contrato
const SimpleToken = await ethers.getContractFactory("SimpleToken");
const token = await SimpleToken.attach("CONTRACT_ADDRESS");
// Check balance
// Verificar saldo
const [owner] = await ethers.getSigners();
const balance = await token.balanceOf(owner.address);
console.log("Balance:", ethers.formatEther(balance));
// Transfer tokens
// Transferir tokens
const tx = await token.transfer("RECIPIENT_ADDRESS", ethers.parseEther("100"));
await tx.wait();
// Add to whitelist
// Adicionar à whitelist
await token.addToWhitelist("ADDRESS");
// Change fee
// Mudar taxa
await token.setFeePercent(300); // Change to 3%Create a script in scripts/interact.js:
const { ethers } = require("hardhat");
async function main() {
const tokenAddress = "YOUR_CONTRACT_ADDRESS";
const token = await ethers.getContractAt("SimpleToken", tokenAddress);
// Get contract info
// Obter informações do contrato
const info = await token.getContractInfo();
console.log("Token Name:", info.name_);
console.log("Symbol:", info.symbol_);
console.log("Total Supply:", ethers.formatEther(info.totalSupply_));
// Calculate fee for 1000 tokens
// Calcular taxa para 1000 tokens
const [fee, transfer] = await token.calculateFee(ethers.parseEther("1000"));
console.log("Fee:", ethers.formatEther(fee));
console.log("Amount after fee:", ethers.formatEther(transfer));
}
main().catch(console.error);Run it:
npx hardhat run scripts/interact.js --network localhost-
Access Control
- Only owner can modify critical parameters
- Ownable pattern from OpenZeppelin
-
Input Validation
- Custom errors for invalid inputs
- Zero address checks
- Fee percentage limits
-
Pausable Pattern
- Emergency stop mechanism
- Owner can pause/unpause
-
Checks-Effects-Interactions
- State changes before external calls
- Prevents reentrancy
-
Custom Errors
- Gas-efficient error handling
- Clear error messages
-
Centralization
- Owner has significant control
- Single point of failure
- Mitigation: Use multi-sig wallet for production
-
Fee Mechanism
- Fee affects user experience
- Could impact token liquidity
- Mitigation: Keep fees reasonable, clear documentation
-
Burn Mechanism
- Deflationary (reduces supply)
- Cannot be reversed
- Mitigation: This is by design
- ✅ Latest Solidity version (0.8.33)
- ✅ OpenZeppelin battle-tested contracts
- ✅ Comprehensive test coverage
- ✅ Custom errors (gas optimization)
- ✅ Events for all state changes
- ✅ NatSpec documentation
- ✅ Input validation
- ✅ Access control
Before mainnet deployment:
- Internal code review
- External security audit
- Bug bounty program
- Test on testnet for extended period
-
Custom Errors
// Instead of: require(condition, "Error message"); // Use: if (!condition) revert CustomError();
Saves ~50 gas per error
-
Unchecked Math
unchecked { _balances[from] -= amount; _balances[to] += amount; }
Safe when overflow is impossible
-
Storage Packing
- uint256 variables grouped together
- Bool variables grouped together
-
View Functions
- No gas cost when called externally
- Used for calculations and queries
| Operation | Gas Cost (approx) |
|---|---|
| Transfer (no fee) | ~52,000 |
| Transfer (with fee) | ~65,000 |
| Add to whitelist | ~45,000 |
| Change fee | ~30,000 |
By completing this project, you learned:
- ✅ ERC20 token standard
- ✅ Inheritance and interfaces
- ✅ State variables and storage
- ✅ Functions and modifiers
- ✅ Events and logging
- ✅ Custom errors
- ✅ Access control patterns
- ✅ Pausable pattern
- ✅ Hardhat framework setup
- ✅ Smart contract compilation
- ✅ Unit testing with Chai
- ✅ Deployment scripts
- ✅ Contract verification
- ✅ Git version control
- ✅ Code organization
- ✅ Documentation (NatSpec)
- ✅ Security considerations
- ✅ Gas optimization
- ✅ Testing strategies
- ✅ Error handling
- ✅ Node.js and npm
- ✅ Hardhat
- ✅ OpenZeppelin
- ✅ Ethers.js
- ✅ Chai testing library
npm installMake sure you have enough ETH for gas on the network you're deploying to.
Reset your MetaMask account or use:
npx hardhat cleanCheck Solidity version compatibility:
// hardhat.config.js
solidity: {
version: "0.8.33"
}Clear cache and recompile:
npx hardhat clean
npx hardhat compile
npx hardhat testAfter mastering this project:
-
Enhance the Token
- Add snapshot functionality
- Implement voting power
- Add reward distribution
-
Build a Frontend
- React + Ethers.js
- Token swap interface
- Dashboard with analytics
-
Next Project
- Move to Project 1.2: Voting System
- Or explore Project 1.3: NFT Collection
-
Go Deeper
- Study DeFi protocols
- Learn about security audits
- Practice on Ethernaut challenges
MIT License - See LICENSE file for details
- Author: Solidity Learning Projects
- GitHub: [My GitHub]
Built with ❤️ for the Solidity community
Last Updated: February 2026