Перейти к основному контенту
Tech Path Finder
КурсыИнтервьюКод-ревьюБлог
Tech Path Finder

Персонализированный путеводитель в IT. Квизы, мок-интервью, код ревью и аналитика прогресса.

@potapov_me

Платформа

  • Курсы
  • Прогресс
  • Мок-интервью
  • Код ревью
  • Живое ревью с ИИ
  • Тренажёр переговоров
  • Закладки

Контент

  • Блог
  • Главная
  • Обратная связь

Компания

  • О проекте
  • Тарифы
  • Условия использования
  • Конфиденциальность
  • Согласие на обработку данных
  • Cookie
  • Реквизиты

Аккаунт

  • Войти
  • Зарегистрироваться
  • Профиль

© 2026 Tech Path Finder. Все права защищены.

·ИП Потапов К.С.·Политика конфиденциальности·
Сделано с ❤️ в России
  1. Токен ERC-20
erc20_token

Токен ERC-20

Стандарт ERC-20, реализация с нуля, allowance/transferFrom, mint/burn, OpenZeppelin

Токен ERC-20

ERC-20 — самый распространённый стандарт токенов в Ethereum. На нём построены USDC, DAI, UNI и тысячи других активов

#Что такое ERC-20

ERC-20 (Ethereum Request for Comments 20) — стандарт взаимозаменяемых (fungible) токенов. "Взаимозаменяемый" означает: один токен неотличим от другого того же типа (как 1 рубль = 1 рублю). Стандарт определяет минимальный набор функций, который должен реализовать каждый токен для совместимости с биржами, кошельками и DeFi-протоколами.

#Минимальный интерфейс ERC-20

Стандарт требует реализации следующих функций:

interface IERC20 { // Общее количество токенов function totalSupply() external view returns (uint256); // Баланс конкретного адреса function balanceOf(address account) external view returns (uint256); // Прямой перевод от msg.sender к to function transfer(address to, uint256 amount) external returns (bool); // Сколько spender может тратить от имени owner function allowance(address owner, address spender) external view returns (uint256); // Разрешить spender тратить amount от имени msg.sender function approve(address spender, uint256 amount) external returns (bool); // Перевод от from к to, если spender (msg.sender) имеет разрешение function transferFrom(address from, address to, uint256 amount) external returns (bool); // Обязательные события event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); }

#Реализация с нуля

Напишем полный ERC-20 токен без использования библиотек — это лучший способ понять механизм изнутри:

// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; contract MyToken { string public name; string public symbol; uint8 public decimals = 18; // Стандарт: 18 знаков после запятой uint256 public totalSupply; mapping(address => uint256) public balanceOf; mapping(address => mapping(address => uint256)) public allowance; event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); constructor(string memory _name, string memory _symbol, uint256 initialSupply) { name = _name; symbol = _symbol; totalSupply = initialSupply * 10**decimals; // Учитываем decimals balanceOf[msg.sender] = totalSupply; emit Transfer(address(0), msg.sender, totalSupply); } function transfer(address to, uint256 amount) external returns (bool) { require(to != address(0), "Transfer to zero address"); require(balanceOf[msg.sender] >= amount, "Insufficient balance"); balanceOf[msg.sender] -= amount; balanceOf[to] += amount; emit Transfer(msg.sender, to, amount); return true; } function approve(address spender, uint256 amount) external returns (bool) { require(spender != address(0), "Approve to zero address"); allowance[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } function transferFrom(address from, address to, uint256 amount) external returns (bool) { require(to != address(0), "Transfer to zero address"); require(balanceOf[from] >= amount, "Insufficient balance"); require(allowance[from][msg.sender] >= amount, "Insufficient allowance"); allowance[from][msg.sender] -= amount; balanceOf[from] -= amount; balanceOf[to] += amount; emit Transfer(from, to, amount); return true; } }

#Механизм Approve + TransferFrom

Это ключевая концепция ERC-20. Она позволяет смарт-контракту управлять вашими токенами с вашего согласия — без передачи контроля над кошельком.

Сценарий: вы хотите добавить ликвидность в Uniswap.

  1. approve(uniswap, 1000e18) — вы разрешаете Uniswap тратить 1000 ваших токенов
  2. uniswap.addLiquidity(token, 1000e18, ...) — Uniswap вызывает token.transferFrom(you, pool, 1000e18)

Это двухшаговый процесс. Именно поэтому при работе с новым протоколом вы сначала видите транзакцию "Approve", а потом "Deposit" или "Swap".

Важная уязвимость: никогда не делайте approve(spender, type(uint256).max) для ненадёжных контрактов — это бессрочное разрешение на весь баланс. Многие кошельки предупреждают об этом.

#Расширение: mint и burn

Базовый ERC-20 не включает создание и уничтожение токенов — это расширения.

contract MintableBurnableToken is MyToken { address public minter; constructor(string memory name, string memory symbol) MyToken(name, symbol, 0) // Начинаем с нуля { minter = msg.sender; } // Создание новых токенов (инфляция) function mint(address to, uint256 amount) external { require(msg.sender == minter, "Not minter"); require(to != address(0), "Mint to zero address"); totalSupply += amount; balanceOf[to] += amount; emit Transfer(address(0), to, amount); // Mint = transfer from zero } // Уничтожение токенов (дефляция) function burn(uint256 amount) external { require(balanceOf[msg.sender] >= amount, "Insufficient balance"); balanceOf[msg.sender] -= amount; totalSupply -= amount; emit Transfer(msg.sender, address(0), amount); // Burn = transfer to zero } }

Конвенция: mint эмитирует Transfer(address(0), to, amount), burn — Transfer(from, address(0), amount). Это позволяет внешним наблюдателям (The Graph, Etherscan) корректно показывать историю токена.

#OpenZeppelin ERC20

На практике не пишут ERC-20 с нуля — используют проверенные контракты OpenZeppelin. Они прошли аудит и содержат защиту от edge cases:

// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; contract GameToken is ERC20, Ownable { constructor() ERC20("GameToken", "GAME") Ownable(msg.sender) { _mint(msg.sender, 1_000_000 * 10**decimals()); } function mint(address to, uint256 amount) external onlyOwner { _mint(to, amount); } }

OpenZeppelin также предоставляет расширения: ERC20Permit (approve через подпись без транзакции), ERC20Votes (для governance), ERC20Capped (ограничение totalSupply), ERC20Burnable.

Далее: NFT и стандарт ERC-721