Розробка токена ERC-20
ERC-20 — це інтерфейс, не реалізація. Шість обов'язкових функцій (totalSupply, balanceOf, transfer, transferFrom, approve, allowance) і два события (Transfer, Approval). Всі інші деталі — це деталі реалізації, які мають значення.
Що насправді потрібно вирішити перед написанням коду
Перед відкриттям Foundry відповідьте на кілька питань, які визначають архітектуру:
Токен оновлюється чи ні? Оновлюваний (Proxy + Implementation) надає гнучкість, але додає складність і ризик. Для більшості простих utility токенів — незмінний контракт — правильний вибір. Якщо параметри токена можуть змінюватись через governance — потрібен proxy.
Хто і як може створювати токени? Фіксована пропозиція (весь supply при розгортанні), mintable (minter може створювати), mintable з лімітом (не більше X total supply). Mintable потребує строгого контролю доступу.
Вам потрібні спеціальні механізми? Burn (спалювання), pause (аварійна зупинка), blacklist (USDC та Tether це роблять), transfer tax (дефляційні токени — зазвичай погана ідея).
Базова реалізація
OpenZeppelin — стандарт для ERC-20. Не пишіть ERC-20 з нуля. Аудитори, біржи та інтегратори знають кодову базу OpenZeppelin, її вразливості вже знайдені, виправлені і перевірені роками.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
import "@openzeppelin/contracts/access/Ownable2Step.sol";
contract MyToken is ERC20, ERC20Burnable, ERC20Permit, Ownable2Step {
uint256 public constant MAX_SUPPLY = 100_000_000 * 10**18; // 100M токенів
constructor(
address initialOwner,
address treasury
)
ERC20("My Token", "MTK")
ERC20Permit("My Token")
Ownable2Step()
{
_transferOwnership(initialOwner);
_mint(treasury, MAX_SUPPLY); // весь supply при розгортанні
}
}
ERC20Permit (EIP-2612) — важливе розширення: дозволяє approve без окремої транзакції через off-chain підпис. Це покращує UX (одна транзакція замість двох для першої взаємодії з DeFi протоколами) і знижує вартість для користувача.
Ownable2Step замість Ownable — передача власності потребує підтвердження від нового власника. Захист від випадкової передачі контролю на неправильну адресу.
Mintable токен з контролем доступу
Якщо токен повинен мати функцію mint після розгортання:
import "@openzeppelin/contracts/access/AccessControl.sol";
contract MintableToken is ERC20, ERC20Burnable, ERC20Permit, AccessControl {
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
uint256 public immutable maxSupply;
constructor(
string memory name,
string memory symbol,
uint256 _maxSupply,
address admin
) ERC20(name, symbol) ERC20Permit(name) {
maxSupply = _maxSupply;
_grantRole(DEFAULT_ADMIN_ROLE, admin);
_grantRole(MINTER_ROLE, admin);
}
function mint(address to, uint256 amount) external onlyRole(MINTER_ROLE) {
require(totalSupply() + amount <= maxSupply, "Exceeds max supply");
_mint(to, amount);
}
}
MINTER_ROLE повинна бути призначена смарт-контракту (staking reward contract, vesting contract), а не EOA. Якщо minter — людина, це централізований ризик: ключ витечає або власник може необмежено печатати токени.
Decimals: стандарт і винятки
За умовчанням ERC-20 decimals = 18 (як ETH). Це правильно для більшості токенів. Виняток: USDC/USDT використовують 6 decimals, тому що доларові суми не потребують 18 місць. Якщо створюєте stablecoin або завиваєте відомий актив — перевірте decimals оригіналу.
Ніколи не використовуйте 0 decimals, якщо токен буде торгуватись на DEX — AMM погано працює з цілими кількостями.
Поширені помилки
Transfer tax / fee-on-transfer: кожен transfer бере X% і відправляє його кудись. Це ламає більшість DeFi протоколів — пули AMM, кредитні протоколи не очікують отримати менше після transferFrom. Такий токен буде несумісний з Uniswap V2/V3, Aave, Compound. Якщо все ж таки потрібен — використовуйте whitelist для DeFi контрактів.
Централізований blacklist без timelock: USDC має blacklist, і це нормально для регульованого stablecoin. Для community токена — ні. Якщо blacklist потрібен — обмежте його через multisig + timelock.
Reentrancy в transfer hooks: ERC-20 без hooks не має reentrancy ризику в transfer. Якщо додаєте _beforeTokenTransfer або _afterTokenTransfer — переконайтеся, що callbacks не викликають зовнішній код.
Верифікація та розгортання
# Тести
forge test -vvv
# Розгортання з верифікацією
forge script script/Deploy.s.sol \
--rpc-url $RPC_URL \
--private-key $PRIVATE_KEY \
--broadcast \
--verify \
--etherscan-api-key $ETHERSCAN_KEY
Після розгортання — верифікуйте контракт на Etherscan/Polygonscan. Користувачі та біржи перевіряють код перед взаємодією. Неверифіцований токен викликає законне підозріння.
Терміни розробки токена ERC-20: 3–5 днів включаючи тести та розгортання на mainnet. Якщо потрібен аудит — додатково 2–4 тижні.







