Стандарт ERC-20: что такое токен

ERC-20 — это набор из шести функций и двух событий, согласившись на который любой токен становится понятен всем кошелькам и биржам.
Токен — это не «монета внутри сети». Это просто число в маппинге одного контракта. Магия в том, что все контракты договорились о едином интерфейсе.

ERC-20 — стандарт взаимозаменяемых (fungible) токенов: каждая единица равна любой другой, как рубли. Стандарт описывает интерфейс: totalSupply(), balanceOf(address), transfer(to, amount), approve(spender, amount), allowance(owner, spender), transferFrom(from, to, amount), плюс события Transfer и Approval.

Балансы хранятся в маппинге внутри контракта токена. «Перевести токены» — значит уменьшить число у одного адреса и увеличить у другого. Никакого отдельного «кошелька с монетами» нет.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

interface IERC20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address to, uint256 amount) external returns (bool);
    function approve(address spender, uint256 amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    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: балансы — это просто маппинг
   ===================================
   contract Token {
     balanceOf:  alice -> 100
                 bob   ->  30
                 carol ->   0
   }
   transfer(bob, 40) от alice:
     alice: 100 -> 60      bob: 30 -> 70
   (никаких «монет», только числа в одном контракте)

Про decimals

У большинства токенов decimals = 18. Это значит, что «1 токен» в интерфейсе — это 1 * 10^18 базовых единиц в контракте. Solidity не знает дробей, поэтому все суммы хранят в наименьших единицах, а decimals — лишь подсказка кошелькам, где рисовать запятую.

Как работает под капотом (EVM/газ)

Совместимость держится на ABI: кошелёк знает селекторы функций ERC-20 и формирует вызовы по ним. Поскольку интерфейс одинаков у всех токенов, MetaMask, Uniswap и эксплореры работают с любым ERC-20 без доработок. Событие Transfer с indexed from/to позволяет кошелькам строить историю переводов, не читая весь storage. decimals — обычная view-функция, она ничего не меняет, лишь сообщает масштаб.

# Та же логика на Python: ядро ERC-20 — баланс как dict
DECIMALS = 18
ONE = 10 ** DECIMALS  # 1 «токен» в базовых единицах

balance_of = {"alice": 100 * ONE, "bob": 30 * ONE}
total_supply = sum(balance_of.values())

def transfer(frm, to, amount):
    assert balance_of.get(frm, 0) >= amount, "REVERT: low balance"
    balance_of[frm] -= amount
    balance_of[to] = balance_of.get(to, 0) + amount
    print(f"Transfer {frm}->{to}: {amount // ONE} токенов")

transfer("alice", "bob", 40 * ONE)
print("alice:", balance_of["alice"] // ONE, "| bob:", balance_of["bob"] // ONE)
print("total supply неизменен:", total_supply == sum(balance_of.values()))

«Та же логика на Python ▶». Перевод не создаёт и не уничтожает токены — общий сапплай сохраняется, меняются лишь числа в «маппинге».

Частые ошибки

  • Думать, что «1 токен» равно числу 1 — почти всегда это 10^18 единиц из-за decimals.
  • Считать, что у токена есть собственный баланс ETH — нет, это отдельная сущность.
  • Отправлять токены на адрес контракта, не поддерживающего их — токены «застрянут» (для безопасных переводов есть отдельные паттерны).

Best practices

  • Не пишите ERC-20 с нуля для продакшена — берите аудированный ERC20 от OpenZeppelin.
  • Всегда излучайте Transfer и Approval — без них кошельки не увидят движения.
  • Работайте с суммами в базовых единицах и конвертируйте по decimals только в интерфейсе.

Итоги

ERC-20 — это договорённость об интерфейсе, благодаря которой токен-число в маппинге понятен всей экосистеме. decimals задаёт масштаб, события строят историю. Дальше разберём связку approve + transferFrom.

Проверьте себя
1. Где физически хранятся балансы ERC-20 токена?
AВ отдельных кошельках пользователей
BВ маппинге address => uint256 внутри контракта токена
CВ сети как монеты ETH
DВ событиях Transfer
2. Что означает decimals = 18 у типичного токена?
AТокен делится максимум на 18 частей
B«1 токен» в интерфейсе равен 10^18 базовых единиц в контракте
CУ токена 18 владельцев
DСумма не может превышать 18