Разработка ERC-20 токена
ERC-20 — это интерфейс, не реализация. Шесть обязательных функций (totalSupply, balanceOf, transfer, transferFrom, approve, allowance) и два события (Transfer, Approval). Всё остальное — детали реализации, которые имеют значение.
Что на самом деле нужно решить перед написанием кода
Прежде чем открывать Foundry, нужно ответить на несколько вопросов, которые определяют архитектуру:
Токен апгрейдируемый или нет? Апгрейдируемый (Proxy + Implementation) даёт гибкость, но добавляет сложность и риск. Для большинства simple utility токенов — immutable контракт правильный выбор. Если параметры токена могут меняться через governance — нужен proxy.
Кто и как может создавать токены? Fixed supply (весь supply при деплое), mintable (мinter может создавать), mintable с cap (не более 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 — transfer ownership требует подтверждения от нового владельца. Защита от случайной передачи контроля на неверный адрес.
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 или wrap известного актива — проверьте decimals оригинала.
Никогда не используйте 0 decimals если токен будет торговаться на DEX — AMM плохо работает с целочисленными количествами.
Распространённые ошибки
Transfer tax / fee-on-transfer: каждый transfer берёт X% и отправляет куда-то. Это ломает большинство DeFi протоколов — AMM пулы, lending protocols не ожидают что после transferFrom получат меньше. Такой токен будет несовместим с Uniswap V2/V3, Aave, Compound. Если всё же нужен — используйте whitelist для DeFi контрактов.
Centralised 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 недели.







