Что такое токены стандарта ERC20?
Стандарт ERC20 — это минимальный набор функций и переменных, которые должны быть в коде токена. Вы можете создать не соответствующий стандарту ERC20 токен и он будет прекрасно работать, однако биржи откажутся принимать такой токен, а также возникнут проблемы с правильным отображением вашего токена в Ethereum-кошельках. В этой статье разберем по косточкам все составляющие этого стандарта.
Итак, что должен содержать в себе токен стандарта ERC20:
- публичную переменную (или константу) name с именем токена;
- публичную переменную (или константу) symbol с 3-4 буквенным кодом токена;
- публичную переменную (или константу) totalSupply, содержащую в себе общее количество выпущенных токенов;
- публичный мэппинг balanceOf, содержащий в себе балансы пользователей в виде адрес=>количество;
- событие Transfer, запускающееся в случае успешного выполнения функции transfer;
- функция transfer, позволяющая переводить токены с адреса на адрес;
- функцию approve, позволяющая делегировать токены;
- событие Approval, запускающееся в случае делегирования токенов;
- функцию transferFrom, позволяющая переводить делегированные токены;
- мэппинг allowance, хранящий в себе адреса и кол-во делегированных токенов.
Все функции, переменные, мэппинги и события должны иметь именно такие имена. Если по какой-то причине в вашем смарт-контракте переменная, содержащая имя токена, названа не name, а imya, то в код функции следует включить публичную функцию с именем name, возвращающую имя токена:
function name() view returns (string name){ returns imya; }
Также подобную функцию стоит применять в тех случаях, если согласно логике вашего смарт-контракта переменная приватная, а не публичная.
Токен может содержать в себе более богатый функционал, но если вы делаете токен стандарта ERC20 все вышеперечисленные пункты должны в нем содержаться.
Последние 4 пункта (approve + Approval + transferFrom + allowance) могут ввести новичков в ступор, но обо всем по-порядку.
Более подробно о модификаторах видимости (публичные, приватные) я расскажу в следующих уроках.
Содержание
Код стандарта ERC20
Давайте рассмотрим код токена стандарта ERC20.
pragma solidity ^0.4.23; contract SimpleERC20Token { mapping (address => uint256) public balanceOf; string public name = "Simple ERC20 Token"; string public symbol = "SET"; uint8 public decimals = 18; uint256 public totalSupply = 1000000 * (uint256(10) ** decimals); event Transfer(address indexed from, address indexed to, uint256 value); constructor() public { balanceOf[msg.sender] = totalSupply; emit Transfer(address(0), msg.sender, totalSupply); } function transfer(address to, uint256 value) public returns (bool success) { require(balanceOf[msg.sender] >= value); balanceOf[msg.sender] -= value; balanceOf[to] += value; emit Transfer(msg.sender, to, value); return true; } event Approval(address indexed owner, address indexed spender, uint256 value); mapping(address => mapping(address => uint256)) public allowance; function approve(address spender, uint256 value) public returns (bool success) { allowance[msg.sender][spender] = value; emit Approval(msg.sender, spender, value); return true; } function transferFrom(address from, address to, uint256 value) public returns (bool success) { require(value <= balanceOf[from]); require(value <= allowance[from][msg.sender]); balanceOf[from] -= value; balanceOf[to] += value; allowance[from][msg.sender] -= value; emit Transfer(from, to, value); return true; } }
В первой строке видим объявление версии Солидити, на которой написан контракт.
В 3 строке объявляется контракт (класс) SimpleERC20Token. Контрактов (классов) в смарт-контракте может быть несколько, они могут наследоваться и подключаться из других файлов.
balanceOf
В 6 строке объявляется мэппинг balanceOf. Мэппинг подобен массивам, но со своими особенностями, их рассмотрим в других уроках. Мэппинг хранит в себе значения в виде ключ=>значение , в нашем случае он хранит адрес=>кол-во токенов, то есть всех владельцев токена. Если владельцев 10, то будет 10 записей, если 100 000, то соответственно 100 000.
Name, Symbol
В строках 8, 9 объявляются публичные переменные name и symbol, содержащие в себе имя токена и краткий символ соответственно. Для публичной переменной (как и для публичного мэппига) автоматически создается геттер-функция для просмотра значения, то есть нам не надо писать отдельной функции для просмотра значения типа:
function name() view returns (string name){ return name; }
Солидити автоматически создает такие функции для публичных переменных.
decimals
Солидити не умеет работать с плавающей запятой, поэтому используется указание кол-во знаков после запятой. 18 — наиболее популярное значение, потому что и сам эфир состоит из 10 в степени 18 вей (1ETH = 1000000000000000000 вей). Значит «мельчайшей копейкой» нашего токена будет 0,000000000000000001. Можно указать decimals=2, тогда «мельчайшей копейкой» станет 0,02.
decimals необязательно должна присутствовать в коде. Без нее токен не будет содержать чисел после запятой, это значит что отправлять можно будет только целое число токенов без «копеек».
Хоть decimals и не обязательна, но если вы ее используете, то называться она должна именно decimals и никак иначе.
totalSupply
В переменной totalSupply хранится общее кол-во выпущенных токенов. Эта переменная обязательно должна присутствовать в контракте.
Событие Transfer
Событие Transfer, как можно догадаться из названия, информирует блокэксплореры, web3 интерфейсы и кошельки о том, что выполнилась функция transfer. И запускается оно в функции transfer. Это событие должно быть обязательно в контракте.
Конструктор
Констуктор выполняется 1 раз в момент деплоя контракта в блокчейн. Начиная с версии Солидити 0.4.22 изменился способ записи конструктора. Если раньше это была функция с именем контракта:
function SimpleERC20Token() public { balanceOf[msg.sender] = totalSupply; emit Transfer(address(0), msg.sender, totalSupply); }
теперь конструктор записывается словом constructor:
constructor() public { balanceOf[msg.sender] = totalSupply; emit Transfer(address(0), msg.sender, totalSupply); }
Конструктор перечисляет все созданные токены создателю токена и запускает событие transfer. Ссобытие можно и не размещать в конструкторе, просто считается хорошим тоном его там иметь, так как токены переводятся на баланс создателя пусть и без использования функции transfer.
transfer
С помощью функции transfer владелец токенов может отправить их на другой ETH-адрес. Функция требует адрес отправителя и кол-во токенов и после выполнения возвращает булево значение (true в случае успеха и false в случае неуспеха).
- require(balanceOf[msg.sender] >= value); проверяет что на балансе отправителя больше или равно отправляемому значению, чтобы не отправил больше чем есть.
В случае неуспеха прекратит работу функции и вернет false, комиссия (газ) за транзакцию не спишется; - balanceOf[msg.sender] -= value; строка эквивалентрна записи balanceOf[msg.sender] = balanceOf[msg.sender] — value; Вычитаем из баланса отправителя количество отправляемых токенов;
- balanceOf[to] += value; — эквивалентно записи balanceOf[to] = balanceOf[to] + value; Прибавляем кол-во токенов на баланс получателя;
- emit Transfer(msg.sender, to, value); — запускает событие Transfer с адресом отправителя, адресом получателя и количеством отправленных токенов;
- return true; — возвратит true.
Делегирование токенов
Делегирование токенов осуществляется с помощью approve + Approval + transferFrom + allowance.
Представьте, что у Боба есть 10 000 токенов. Боб может позволить (делегировать) Алисе распоряжаться 500 своих токенов.
Боб идет на сайт Myetherwallet и запускает функцию approve, записывая в соответствующие строки адрес Алисы и количество токенов.
Функция approve записывает адрес Боба, адрес Алисы и кол-во токенов в мэппинг allowance. Можем посмотреть это запустив мэппинг:
Теперь Алиса может перейти в MyEtherWallet и в любой момент перевести эти 500 (точнее меньше или равно 500) токенов себе или кому-либо еще с помощью функции transferFrom, вставив в соответствующие поле _from adress адрес Боба, в поле _to address любой адрес, включая свой, в поле _value количество токенов.
Пока Алиса не перевела токены (не запустила transferFrom) Боб может зайти в MyEtherWallet и обломать Алисе кайф обнулить количество делегированных токенов.
Зачем все это нужно?
Представьте биржу типа Эфердельты. У вас есть 5000 токенов и вы хотите их продать. Вы заходите на биржу и выставляете в стакан заявку на продажу ваших 5000 токенов за 2 ETH. Биржа с помощью Метамаска проверяет что токены принадлежат вам, потом обращается к смарт-контракту ваших токенов и запускает функцию approve, позволяющей ей распорядится вашими 5000 токенами. Через несколько минут в стакане находится покупатель ваших токенов, и тогда биржа переводит с помощью transferFrom ваши 5000 токенов на адрес покупателя, а вам взамен присылает 2 ETH с адреса покупателя. Обмен завершен, все довольны.