Как создать собственный токен на базе ERC20 от Ethereum и запустить ICO за 20 минут
В последнее время я все чаще изучаю особенности блокчейна и децентрализованных приложений для собственного саморазвития. Для разнообразия процесса, я решил разработать свой собственный токен, основанный на Ethereum, и определить процесс запуска собственного ICO.
Путем наглядного примера по запуску собственного ICO, эта статья нацелена на объяснение основ функционирования смарт-контрактов (англ. smart-contract) в блокчейн-сети Ethereum.
Основы blockchain-сети Ethereum
Вот некоторые основные термины, которые нам понадобятся по мере создания токена. В случае, если большая часть терминов вам уже знакома, можете сразу переходить к следующему разделу.
Токены на основе ERC20 от Ethereum ERC20. В системе Ethereum, токены представляют собой любые товары, поддающиеся торговли, например, монеты, очки лояльности и т. д. На основе Ethereum вы можете создать собственные криптовалюты. Вдобавок, преимущество стандарта ERC20 заключается в совместимости всех токенов с любыми программными клиентами или цифровыми-кошельками, использующие одинаковые стандарты.
Смарт-контракты: Смарт-контракты — это самозапускающиеся программные блоки, которые функционируют на блокчейне Ethereum. Смарт-контракты способны обрабатывать не только кодовую часть, но также и информационную. Такие контракты способны принимать решения, взаимодействовать с другими контрактами, хранить данные и обмениваться Ether (единица криптовалюты в блокчейне Ethereum) между пользователями.
Solidity: Solidity — это язык для создания смарт-контрактов.MetaMask/Mist/Кошелек MEW: Все это является цифровой средой, которая хранит ваши Ether-средства и другие токены, на основе Ethereum.Шаг 1: Код
Откройте ваш любимый текстовый редактор и вставьте следующий код:
pragma solidity ^0.4.4;
contract Token {
/// @Возвращает общее количество токенов function totalSupply() constant returns (uint256 supply) {}
/// @Параметры владельца. Адрес, с которого будут извлекаться токены. /// @Возвращает текущий баланс. function balanceOf(address _owner) constant returns (uint256 balance) {}
/// @Уведомляет об отправке `_value` токенов на адрес `_to` из `msg.sender` /// @Параметр _to означает адрес получателя /// @Параметр _value означает количество токенов, которые будут отправлены /// @Возвращает информацию, была ли транзакция успешной или нет function transfer(address _to, uint256 _value) returns (bool success) {}
/// @Уведомляет об отправке `_value` токенов на адрес `_to` из `_from` при условии, что подтверждено `_from` /// @Параметр _from означает адрес отправителя /// @Параметр _to означает адрес получателя /// @Параметр _value означает количество токенов, которые будут отправлены /// @Возвращает информацию, была ли транзакция успешной или нет function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {}
/// @Уведомляет `msg.sender` подтвердить `_addr` для отправки `_value` токенов /// @Параметр _spender означает адрес счета, с которого можно отправлять токены /// @Параметр _value означает количество токенов, которое разрешено отправить /// @Возвращает информацию, была ли транзакция успешной или нет function approve(address _spender, uint256 _value) returns (bool success) {}
/// @Параметр _owner означает адрес владельца токенов /// @Параметр _spender означает адрес счета, с которого можно отправлять токены /// @Возвращает информацию об оставшемся количестве токенов, которое можно потратить function allowance(address _owner, address _spender) constant returns (uint256 remaining) {} event Transfer(address indexed _from, address indexed _to, uint256 _value); event Approval(address indexed _owner, address indexed _spender, uint256 _value);}
contract StandardToken is Token { function transfer(address _to, uint256 _value) returns (bool success) { //По умолчанию предполагается, что totalSupply не может быть больше (2^256 - 1). //Если токен не содержит totalSupply и можно неограниченно выпускать токены, необходимо следить за переконвертацией токена. //Замените оператор if на this one. //if (balances[msg.sender] >= _value && balances[_to] + _value > balances[_to]) { if (balances[msg.sender] >= _value && _value > 0) { balances[msg.sender] -= _value; balances[_to] += _value; Transfer(msg.sender, _to, _value); return true; } else { return false; } }
function transferFrom(address _from, address _to, uint256 _value) returns (bool success) { //Как и указано свыше, замените эту строку ниженаписанной, если желаете защитить контракт от переконвертированных токенов. //if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value && balances[_to] + _value > balances[_to]) { if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value && _value > 0) { balances[_to] += _value; balances[_from] -= _value; allowed[_from][msg.sender] -= _value; Transfer(_from, _to, _value); return true; } else { return false; } }
function balanceOf(address _owner) constant returns (uint256 balance) { return balances[_owner]; } function approve(address _spender, uint256 _value) returns (bool success) { allowed[msg.sender][_spender] = _value; Approval(msg.sender, _spender, _value); return true; } function allowance(address _owner, address _spender) constant returns (uint256 remaining) { return allowed[_owner][_spender]; } mapping (address => uint256) balances; mapping (address => mapping (address => uint256)) allowed; uint256 public totalSupply;} contract HashnodeTestCoin is StandardToken { // ПОДЛЕЖИТ ИЗМЕНЕНИЮ. Обновить название контракта. /* Публичные переменные токена */ /* ВНИМАНИЕ: Нижеизложенные переменные ОПЦИОНАЛЬНЫ. Нет строгой необходимости их включать. Они позволяют кастомизировать контракт токена и не влияют на основные функции. Некоторые цифровые-кошельки/интерфейсы могут не поддерживать эти функции. */ string public name; // Название токена uint8 public decimals; // Как много показывать десятичных. По умолчание устанавливает значение, равное 18 string public symbol; // Идентификатор: например SBX, XPR и т.д... string public version = 'H1.0'; uint256 public unitsOneEthCanBuy; // Как много единиц вашего токена можно купить за 1 ETH? uint256 public totalEthInWei; // WEI равняется минимальному значению ETH (эквивалентно центу в USD или сатоши в BTC). Здесь мы будем хранить все привлеченные ETH через ICO address public fundsWallet; // Куда должны перенаправляться привлеченные ETH? // Это конструктор-функция, ее имя должно соответствовать вышенаписанному названию function HashnodeTestCoin() { balances[msg.sender] = 1000000000000000000000; // Предоставить создателю контракта все начальные токены. В нашем случае количество равно 1000. Если вы хотите, чтобы количество равнялось число X, а десятичные равнялись 5, установите следующее значение X * 100000. (ПОДЛЕЖИТ ИЗМЕНЕНИЮ) totalSupply = 1000000000000000000000; // Обновить общий выпуск (1000 для примера) (ПОДЛЕЖИТ ИЗМЕНЕНИЮ) name = "HashnodeTestCoin"; // Установить название токена для отображения на дисплее (ПОДЛЕЖИТ ИЗМЕНЕНИЮ) decimals = 18; // Количество десятичных знаков после запятой для отображения на дисплее (ПОДЛЕЖИТ ИЗМЕНЕНИЮ) symbol = "HTCN"; // Идентификатор токена для отображения на дисплее (ПОДЛЕЖИТ ИЗМЕНЕНИЮ) unitsOneEthCanBuy = 10; // Установить цену за единицу вашего токена для ICO (ПОДЛЕЖИТ ИЗМЕНЕНИЮ) fundsWallet = msg.sender; // Владелец контракта получает ETH } function() payable{ totalEthInWei = totalEthInWei + msg.value; uint256 amount = msg.value * unitsOneEthCanBuy; require(balances[fundsWallet] >= amount); balances[fundsWallet] = balances[fundsWallet] - amount; balances[msg.sender] = balances[msg.sender] + amount; Transfer(fundsWallet, msg.sender, amount); // Передать сообщение блокчейн-сети //Отправить Ether в fundsWallet fundsWallet.transfer(msg.value); } /* Верификация и затем вызов контракта */ function approveAndCall(address _spender, uint256 _value, bytes _extraData) returns (bool success) { allowed[msg.sender][_spender] = _value; Approval(msg.sender, _spender, _value); //вызов функции receiveApproval в контракте, который вы хотите уведомить. Этот процесс по умолчанию создает подпись функции, но в нашем случае это не нужно включать в контракт. //receiveApproval(address _from, uint256 _value, address _tokenContract, bytes _extraData) //к этому моменту, вызов к функции должен пройти успешно. if(!_spender.call(bytes4(bytes32(sha3("receiveApproval(address,uint256,address,bytes)"))), msg.sender, _value, this, _extraData)) { throw; } return true; }}
Вышеуказанный код для создания простого ERC20 токена написан на языке Solidity. Он хорошо прокомментирован и его можно легко понять. Вставив код в ваш текстовый редактор, найдите надпись «CHANGE THIS». Здесь вам необходимо данные, в зависимости от характеристик вашего токена. В моем случае, я назвал мой токен HashnodeTestCoin (HTCN). Общий объем выпуска ограничен и составляет 1000 токенов HTCN, однако минимально-возможное количество для хранения HTCN равняется 0.000000000000000001, поскольку в строке «decimal» установлено значение 18 — стандартное количество знаков после запятой.
Вдобавок, владелец контракта (тот, кто его исполняет), получает все начальные токены. Я установил ценовое соотношение для ICO по следующим образом:1 ETH = 10 HTCNЭто значит, что если кто-то отправит нашему смарт-контракту 1 ETH, то в обмен получит 10 HTCN.Шаг 2
Скачайте MetaMask — расширения для Chrome, чтобы сгенерировать цифровой-кошелек. Владелец этого кошелька является и владельцем смарт-контракта. В качестве альтернативы, можно использовать Mist или My Ether Wallet. Для простоты примера, в нашем проекте мы будем использовать расширение MetaMask.
Скачав и установив расширение, создайте аккаунт, защищенный паролем. Затем, в верхнем левом углу расширения выберите «Ropsten TestNet». Прежде чем запустить контракт в Main-сети Ethereum’а, мы протестируем наш контракт с помощью TestNet, чтобы убедиться, что все работает как было запланировано. Выглядит все это следующим образом:
Теперь перейдите к Remix IDE (онлайн-компилятор и отладчик Solidity) и вставьте код, который вы изменили под свои параметры. Не обращайте внимания на все возможные предупреждения. Затем перейдите в настройки и уберите флажок «Enable optimization», если он установлен.
Дальше перейдите во вкладку «Run» и нажмите на кнопку «Create», которая расположена под надписью вашего токена. Нажав на кнопку, MetaMask предложить приобрести несколько единиц Ether для теста и отправит транзакцию. Выглядит примерно так:
Убедитесь, что вы используете сеть Ropsten TestNet, а не MainNet и нажмите «Submit». Вас перенаправит на сервис Etherscan, где можно отследить текущую транзакцию. Для верификации транзакции может потребоваться примерно 30 секунд. Как только произойдет подтверждение, выглядеть все будет следующим образом:
Поздравляю! Вы только что запустили собственный смарт-контракт. Заметьте, что адрес, указанный на изображении свыше — это адрес вашего смарт-контракта.
Теперь пора проверить, работает ли все на самом деле.
Шаг 3
Если ранее все было выполнено в соответствии с инструкцией, вам должны прийти все начальные токены (в моем случае — 1000 HTCN), как только вы добавить их в свой кошелек. Поэтому скопируйте адрес контракта, перейдите в MetaMask - > Add Token и вставьте скопированный адрес. Выглядит все это дело так:
Нажмите на кнопку «Add» и перезагрузите MetaMask. Теперь вы должны увидеть всю начальную поставку (в моем случае — это все те же 1000 HTCN).
Шаг 4
Теперь, при условии, что все работает должным образом, нам нужно верифицировать наш смарт-контракт, что все участники блокчейн-сети смогли воспользоваться им. Верификация — важная составляющая, поскольку она помогает установить доверие.Теперь перейдите в Etherscan на адрес ваше смарт-контракта и нажмите вкладку «Contract Code».
Нажмите на ссылку «verify and publish». Перейдя на новую страницу, заполните все детали, начиная с версии компилятора и возможностью оптимизации. Также не забудьте вставить исходный код нашего контракта, который мы скомпилировали в первом шаге.
Поздравляю! Теперь любой может посетить страницу вашего контракта и прочитать исходный код.
Шаг 5
Чтобы окончательно запустить контракт, остается только переключить с TestNet на MainNet в расширении MetaMask (в верхнем левом углу расширения) и повторить шаги со 2 по 4. Обратите внимание, что для запуска контракта на MainNet понадобится потратить настоящий Ether. Поэтому не запускайте контракт до тех пор, пока не будете уверены на 100% (Смарт-контракты невозможно изменить и нельзя обновить после запуска). В нашем уроке, мы продолжим использовать TestNet.
Покупка токенов за Ether
В вашем ICO, пользователи будут покупать ваши токены за ETH. Помните, что мы установили цену в 1 ETH = 10 HTCN, пока разрабатывали контракт? Так вот, если пользователь захочет на вашем ICO купить 10 HTCN, ему придется заплатить 1 ETH. Давайте проверим это на практике.Перейдите в MetaMask, создайте новый аккаунт и заполните его для теста несколькими Ether. Как только на аккаунте будут средства, нажмите «Send» и в поле адреса, вставьте адрес вашего смарт-контракта. В поле для количества токенов, введите 2 (ETH).
Отправьте 2 ETH и дождитесь подтверждения транзакции. Перезагрузите MetaMask и проверьте ваши токены через несколько секунд. Новый пробный аккаунт должен получит 20 HTCN (или другую сумму, в зависимости от того характеристик контракта) а у владельца контракта (в нашем случае это вы) должно остаться 980 (или около того) токенов HTCN.
Вдобавок вы должны получить 2 ETH.Поздравляю с успехом!
Запуск страницы ICO Чтобы отобразить количество ETH, привлеченное нашим проектом, мы будем использовать а нашем сайте библиотеку JavaScript, под названием Web3.js.
Откройте исходник страницы Hashnode Test Coin ICO и проверьте код в последнем <script> теге. Все очень просто и использует данные Web3.js наряду с двоичным интерфейсом приложения, полученные из вашего контракта.
Искренние поздравление, если вы добрались до этого шага! Помните, что настоящие смарт-контракты и ICO требуют значительных усилий и множества тестирований. Хоть этот урок и дал базовые знания для написания смарт-контрактов для ICO, он не предназначен для публичного использования. Не пытайтесь запустить наш пример на MainNet без детального тестирования.
Перевод - crypt-mining.net
Читать оригинал на английском языке.