Перейти к содержанию

ERC-20 в Solidity: токен и базовые единицы

У библиотеки появился отдельный учебный объект — кредит, который можно передавать между адресами. Это не издательская роль и не запись курса. Для взаимозаменяемых единиц подходит интерфейс ERC-20. Начнём с количества: без точного масштаба даже правильно написанный transfer может передать не ту сумму.

Наш LibraryCredit используется только в лаборатории. Он не заявляет денежной стоимости, обеспечения или прав на материалы. Создадим фиксированное начальное предложение и выберем шесть знаков отображения. Код использует OpenZeppelin Contracts 5.4.0 и Solidity 0.8.30.

Целое количество внутри контракта

Баланс ERC-20 хранится целым числом базовых единиц. Decimals сообщает, как представить его человеку. Если decimals=6, один отображаемый кредит соответствует миллиону базовых единиц. Число 1 в transfer означает одну базовую единицу, а не один целый кредит.

Это похоже на работу с сатоши в Bitcoin-курсе, но масштаб выбран другой. Нельзя перенести константу 100 миллионов только из привычки к Bitcoin. Для каждой системы количество, масштаб и название единицы должны быть известны отдельно.

Базовых единиц Отображение при decimals=6
1 0,000001 LCR
1 000 000 1 LCR
2 500 000 2,5 LCR
1 000 000 000 1000 LCR

Таблица содержит обычную арифметику, не результаты сетевого чтения. Она позволяет заранее сверить, что начальный выпуск и пользовательская форма используют один масштаб. Если интерфейс показывает шесть знаков, а программа создаёт количество для восемнадцати, величины расходятся очень сильно.

Полный учебный токен

// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract LibraryCredit is ERC20 {
    constructor(address initialHolder) ERC20("Library Credit", "LCR") {
        _mint(initialHolder, 1000 * 10 ** 6);
    }
    function decimals() public pure override returns (uint8) {
        return 6;
    }
}

Конструктор библиотеки задаёт имя и символ. Наш конструктор выпускает тысячу отображаемых кредитов выбранному initialHolder. Внешнего метода mint здесь нет, поэтому доступный интерфейс этой модели не даёт дальнейшего выпуска. Нулевой получатель начального mint отклоняется библиотекой.

Мы переопределили decimals, а не арифметику balanceOf. Баланс хранит 1 000 000 000 базовых единиц независимо от того, каким языком сайт подписывает число. Изменить количество в конструкторе только на 1000 означало бы выпуск 0,001 отображаемого кредита, а не сохранение прежнего предложения.

Точный адрес токена также важен. Символ LCR не является уникальным глобальным идентификатором: другой контракт может иметь такое же название. Приложение связывает сеть и адрес с известной моделью. Иконка и символ удобны для интерфейса, но не выбирают источник баланса.

Ввод пользователя

Пользователь вводит «2,5». Клиент должен разобрать согласованное десятичное представление и получить 2 500 000 целых единиц. Не стоит умножать произвольный JavaScript Number и рассчитывать, что любой большой результат останется точным. Для количества используются целочисленные операции или проверенная библиотечная функция преобразования строк.

Нужно заранее решить, какая десятичная запись принимается интерфейсом: точка, запятая либо локализованное поле с явным преобразованием. Строки «2,5», «2.5» и «2 500» не должны случайно означать одно и то же после удаления всех знаков. Проверка формы является частью пользовательского договора.

Слишком длинная дробная часть тоже требует решения. При шести знаках значение 0,0000001 не представляется как положительное целое количество. Можно отклонить его с понятным сообщением. Молчаливое округление до нуля создаст неожиданную операцию, хотя контракт технически умеет обработать нулевой transfer.

Масштаб не является ценой

Decimals не говорит, сколько кредит стоит в ETH или рублях. Это только представление количества. Даже число с восемнадцатью знаками не становится эфиром. Чтобы добавить расчёт цены, понадобится отдельный источник и договор, которых в нашем коде нет.

Баланс также не доказывает право публикации или юридическое владение текстом. CourseCatalog ничего не знает об LCR. Связь токена с доступом к материалу появляется только после отдельного введения правил сервера либо контракта. Не нужно обещать читателю свойства из одного слова token.

Другой ERC-20 может вести себя иначе

Стандарт описывает интерфейс, а реализация определяет дополнительные правила. Метаданные name, symbol и decimals в исходном стандарте имеют оговорённую необязательность. Для нашего известного LibraryCredit они доступны. Общий клиент чужих токенов не должен безоговорочно требовать их наличия.

Также возможны дополнительные комиссии, ограничение переводов или другой масштаб. Токен с десятичными знаками 6 не обязан быть взаимозаменяем по экономике с нашим LCR. Приём внешнего адреса создаёт новую границу доверия, которую исследуем дальше.

В этом учебном примере нет таких расширений. Обычные балансы и totalSupply помогают изучить передачу. Мы не замеряли газ и не выпускали реальные единицы. Все суммы в таблице относятся к ожидаемой арифметике полного собственного исходника.

Сохраняем правило количества

В записи лаборатории полезно указать сеть, адрес, символ для отображения и decimals=6. Сам баланс лучше передавать как целую строку либо bigint, а форматирование выполнять на границе интерфейса. Так обычное обновление языка страницы не меняет количество на цепочке.

Для упражнения измените только decimals в новом отдельном исходнике на 18, оставив начальный mint прежним. TotalSupply в базовых единицах остаётся тем же, а отображаемое количество становится другим. Это показывает, что метаданные влияют на интерпретацию, а не автоматически пересчитывают уже созданные единицы.

Мы получили фиксированный учебный токен с понятным масштабом. Следующая страница разберёт transfer и убедится, что количество, отправитель и получатель имеют самостоятельные роли. ERC-20 и исходник ERC20 5.4.0 помогают сверить этот договор.

Импорт данных в обычный сайт

Если локальная база хранит количество как десятичную строку, укажите, что это базовые единицы. Строка "2500000" не должна позже трактоваться как два с половиной миллиона отображаемых кредитов. Для форматирования вместе с ней используется известный масштаб выбранного экземпляра. Число и единица проходят через интерфейс как связанная пара.

Для самостоятельного уточнения: eip-20, ERC20.sol (5.4.0).

Оглавление курса. Листинги, расчёты и наблюдения здесь разобраны как ожидаемое поведение; компиляция, учебные программы и проверки при написании не запускались.

Оглавление серии